Merge "PatchPanel: Cleanup and refactoring"
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index 672b832..fa41c06 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -48,7 +48,6 @@
 {
     ALOGV("MetadataRetrieverClient constructor pid(%d)", pid);
     mPid = pid;
-    mThumbnail = NULL;
     mAlbumArt = NULL;
     mRetriever = NULL;
 }
@@ -77,7 +76,6 @@
     ALOGV("disconnect from pid %d", mPid);
     Mutex::Autolock lock(mLock);
     mRetriever.clear();
-    mThumbnail.clear();
     mAlbumArt.clear();
     IPCThreadState::self()->flushCommands();
 }
@@ -201,7 +199,6 @@
             (long long)timeUs, option, colorFormat, metaOnly);
     Mutex::Autolock lock(mLock);
     Mutex::Autolock glock(sLock);
-    mThumbnail.clear();
     if (mRetriever == NULL) {
         ALOGE("retriever is not initialized");
         return NULL;
@@ -220,7 +217,6 @@
             index, colorFormat, metaOnly, thumbnail);
     Mutex::Autolock lock(mLock);
     Mutex::Autolock glock(sLock);
-    mThumbnail.clear();
     if (mRetriever == NULL) {
         ALOGE("retriever is not initialized");
         return NULL;
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.h b/media/libmediaplayerservice/MetadataRetrieverClient.h
index e774c8f..63ba44a 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.h
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.h
@@ -73,9 +73,8 @@
     sp<MediaMetadataRetrieverBase>         mRetriever;
     pid_t                                  mPid;
 
-    // Keep the shared memory copy of album art and capture frame (for thumbnail)
+    // Keep the shared memory copy of album art
     sp<IMemory>                            mAlbumArt;
-    sp<IMemory>                            mThumbnail;
 };
 
 }; // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 3a28bbd..57a0198 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -358,8 +358,12 @@
 
         // AudioSink has rendered some frames.
         int64_t nowUs = ALooper::GetNowUs();
-        int64_t nowMediaUs = mAudioSink->getPlayedOutDurationUs(nowUs)
-                + mAudioFirstAnchorTimeMediaUs;
+        int64_t playedOutDurationUs = mAudioSink->getPlayedOutDurationUs(nowUs);
+        if (playedOutDurationUs == 0) {
+            *mediaUs = mAudioFirstAnchorTimeMediaUs;
+            return OK;
+        }
+        int64_t nowMediaUs = playedOutDurationUs + mAudioFirstAnchorTimeMediaUs;
         mMediaClock->updateAnchor(nowMediaUs, nowUs, -1);
     }
 
diff --git a/media/libnbaio/PipeReader.cpp b/media/libnbaio/PipeReader.cpp
index 2486b76..35a43d8 100644
--- a/media/libnbaio/PipeReader.cpp
+++ b/media/libnbaio/PipeReader.cpp
@@ -26,7 +26,7 @@
 
 PipeReader::PipeReader(Pipe& pipe) :
         NBAIO_Source(pipe.mFormat),
-        mPipe(pipe), mFifoReader(mPipe.mFifo, false /*throttlesWriter*/, true /*flush*/),
+        mPipe(pipe), mFifoReader(mPipe.mFifo, false /*throttlesWriter*/, false /*flush*/),
         mFramesOverrun(0),
         mOverruns(0)
 {
diff --git a/media/libstagefright/FrameDecoder.cpp b/media/libstagefright/FrameDecoder.cpp
index 09a8be5..e2605ca 100644
--- a/media/libstagefright/FrameDecoder.cpp
+++ b/media/libstagefright/FrameDecoder.cpp
@@ -42,7 +42,6 @@
 static const int64_t kBufferTimeOutUs = 10000ll; // 10 msec
 static const size_t kRetryCount = 50; // must be >0
 
-//static
 sp<IMemory> allocVideoFrame(const sp<MetaData>& trackMeta,
         int32_t width, int32_t height, int32_t dstBpp, bool metaOnly = false) {
     int32_t rotationAngle;
@@ -94,7 +93,6 @@
     return frameMem;
 }
 
-//static
 bool findThumbnailInfo(
         const sp<MetaData> &trackMeta, int32_t *width, int32_t *height,
         uint32_t *type = NULL, const void **data = NULL, size_t *size = NULL) {
@@ -107,30 +105,15 @@
                 type ?: &dummyType, data ?: &dummyData, size ?: &dummySize);
 }
 
-//static
-sp<IMemory> FrameDecoder::getMetadataOnly(
-        const sp<MetaData> &trackMeta, int colorFormat, bool thumbnail) {
-    OMX_COLOR_FORMATTYPE dstFormat;
-    int32_t dstBpp;
-    if (!getDstColorFormat(
-            (android_pixel_format_t)colorFormat, &dstFormat, &dstBpp)) {
-        return NULL;
-    }
-
-    int32_t width, height;
-    if (thumbnail) {
-        if (!findThumbnailInfo(trackMeta, &width, &height)) {
-            return NULL;
-        }
-    } else {
-        CHECK(trackMeta->findInt32(kKeyWidth, &width));
-        CHECK(trackMeta->findInt32(kKeyHeight, &height));
-    }
-    return allocVideoFrame(trackMeta, width, height, dstBpp, true /*metaOnly*/);
+bool findGridInfo(const sp<MetaData> &trackMeta,
+        int32_t *tileWidth, int32_t *tileHeight, int32_t *gridRows, int32_t *gridCols) {
+    return trackMeta->findInt32(kKeyTileWidth, tileWidth) && (*tileWidth > 0)
+        && trackMeta->findInt32(kKeyTileHeight, tileHeight) && (*tileHeight > 0)
+        && trackMeta->findInt32(kKeyGridRows, gridRows) && (*gridRows > 0)
+        && trackMeta->findInt32(kKeyGridCols, gridCols) && (*gridCols > 0);
 }
 
-//static
-bool FrameDecoder::getDstColorFormat(
+bool getDstColorFormat(
         android_pixel_format_t colorFormat,
         OMX_COLOR_FORMATTYPE *dstFormat,
         int32_t *dstBpp) {
@@ -162,46 +145,57 @@
     return false;
 }
 
-sp<IMemory> FrameDecoder::extractFrame(
-        int64_t frameTimeUs, int option, int colorFormat) {
+//static
+sp<IMemory> FrameDecoder::getMetadataOnly(
+        const sp<MetaData> &trackMeta, int colorFormat, bool thumbnail) {
+    OMX_COLOR_FORMATTYPE dstFormat;
+    int32_t dstBpp;
     if (!getDstColorFormat(
-            (android_pixel_format_t)colorFormat, &mDstFormat, &mDstBpp)) {
+            (android_pixel_format_t)colorFormat, &dstFormat, &dstBpp)) {
         return NULL;
     }
 
-    status_t err = extractInternal(frameTimeUs, 1, option);
-    if (err != OK) {
-        return NULL;
+    int32_t width, height;
+    if (thumbnail) {
+        if (!findThumbnailInfo(trackMeta, &width, &height)) {
+            return NULL;
+        }
+    } else {
+        CHECK(trackMeta->findInt32(kKeyWidth, &width));
+        CHECK(trackMeta->findInt32(kKeyHeight, &height));
     }
-
-    return mFrames.size() > 0 ? mFrames[0] : NULL;
+    return allocVideoFrame(trackMeta, width, height, dstBpp, true /*metaOnly*/);
 }
 
-status_t FrameDecoder::extractFrames(
-        int64_t frameTimeUs, size_t numFrames, int option, int colorFormat,
-        std::vector<sp<IMemory> >* frames) {
+FrameDecoder::FrameDecoder(
+        const AString &componentName,
+        const sp<MetaData> &trackMeta,
+        const sp<IMediaSource> &source)
+    : mComponentName(componentName),
+      mTrackMeta(trackMeta),
+      mSource(source),
+      mDstFormat(OMX_COLOR_Format16bitRGB565),
+      mDstBpp(2),
+      mHaveMoreInputs(true),
+      mFirstSample(true) {
+}
+
+FrameDecoder::~FrameDecoder() {
+    if (mDecoder != NULL) {
+        mDecoder->release();
+        mSource->stop();
+    }
+}
+
+status_t FrameDecoder::init(
+        int64_t frameTimeUs, size_t numFrames, int option, int colorFormat) {
     if (!getDstColorFormat(
             (android_pixel_format_t)colorFormat, &mDstFormat, &mDstBpp)) {
         return ERROR_UNSUPPORTED;
     }
 
-    status_t err = extractInternal(frameTimeUs, numFrames, option);
-    if (err != OK) {
-        return err;
-    }
-
-    for (size_t i = 0; i < mFrames.size(); i++) {
-        frames->push_back(mFrames[i]);
-    }
-    return OK;
-}
-
-status_t FrameDecoder::extractInternal(
-        int64_t frameTimeUs, size_t numFrames, int option) {
-
-    MediaSource::ReadOptions options;
     sp<AMessage> videoFormat = onGetFormatAndSeekOptions(
-            frameTimeUs, numFrames, option, &options);
+            frameTimeUs, numFrames, option, &mReadOptions);
     if (videoFormat == NULL) {
         ALOGE("video format or seek mode not supported");
         return ERROR_UNSUPPORTED;
@@ -217,7 +211,8 @@
         return (decoder.get() == NULL) ? NO_MEMORY : err;
     }
 
-    err = decoder->configure(videoFormat, NULL /* surface */, NULL /* crypto */, 0 /* flags */);
+    err = decoder->configure(
+            videoFormat, NULL /* surface */, NULL /* crypto */, 0 /* flags */);
     if (err != OK) {
         ALOGW("configure returned error %d (%s)", err, asString(err));
         decoder->release();
@@ -237,43 +232,46 @@
         decoder->release();
         return err;
     }
+    mDecoder = decoder;
 
-    Vector<sp<MediaCodecBuffer> > inputBuffers;
-    err = decoder->getInputBuffers(&inputBuffers);
+    return OK;
+}
+
+sp<IMemory> FrameDecoder::extractFrame() {
+    status_t err = extractInternal();
     if (err != OK) {
-        ALOGW("failed to get input buffers: %d (%s)", err, asString(err));
-        decoder->release();
-        mSource->stop();
+        return NULL;
+    }
+
+    return mFrames.size() > 0 ? mFrames[0] : NULL;
+}
+
+status_t FrameDecoder::extractFrames(std::vector<sp<IMemory> >* frames) {
+    status_t err = extractInternal();
+    if (err != OK) {
         return err;
     }
 
-    Vector<sp<MediaCodecBuffer> > outputBuffers;
-    err = decoder->getOutputBuffers(&outputBuffers);
-    if (err != OK) {
-        ALOGW("failed to get output buffers: %d (%s)", err, asString(err));
-        decoder->release();
-        mSource->stop();
-        return err;
+    for (size_t i = 0; i < mFrames.size(); i++) {
+        frames->push_back(mFrames[i]);
     }
+    return OK;
+}
 
-    sp<AMessage> outputFormat = NULL;
-    bool haveMoreInputs = true;
-    size_t index, offset, size;
-    int64_t timeUs;
-    size_t retriesLeft = kRetryCount;
+status_t FrameDecoder::extractInternal() {
+    status_t err = OK;
     bool done = false;
-    bool firstSample = true;
+    size_t retriesLeft = kRetryCount;
     do {
-        size_t inputIndex = -1;
+        size_t index;
         int64_t ptsUs = 0ll;
         uint32_t flags = 0;
-        sp<MediaCodecBuffer> codecBuffer = NULL;
 
         // Queue as many inputs as we possibly can, then block on dequeuing
         // outputs. After getting each output, come back and queue the inputs
         // again to keep the decoder busy.
-        while (haveMoreInputs) {
-            err = decoder->dequeueInputBuffer(&inputIndex, 0);
+        while (mHaveMoreInputs) {
+            err = mDecoder->dequeueInputBuffer(&index, 0);
             if (err != OK) {
                 ALOGV("Timed out waiting for input");
                 if (retriesLeft) {
@@ -281,16 +279,21 @@
                 }
                 break;
             }
-            codecBuffer = inputBuffers[inputIndex];
+            sp<MediaCodecBuffer> codecBuffer;
+            err = mDecoder->getInputBuffer(index, &codecBuffer);
+            if (err != OK) {
+                ALOGE("failed to get input buffer %zu", index);
+                break;
+            }
 
             MediaBufferBase *mediaBuffer = NULL;
 
-            err = mSource->read(&mediaBuffer, &options);
-            options.clearSeekTo();
+            err = mSource->read(&mediaBuffer, &mReadOptions);
+            mReadOptions.clearSeekTo();
             if (err != OK) {
                 ALOGW("Input Error or EOS");
-                haveMoreInputs = false;
-                if (!firstSample && err == ERROR_END_OF_STREAM) {
+                mHaveMoreInputs = false;
+                if (!mFirstSample && err == ERROR_END_OF_STREAM) {
                     err = OK;
                 }
                 break;
@@ -299,7 +302,7 @@
             if (mediaBuffer->range_length() > codecBuffer->capacity()) {
                 ALOGE("buffer size (%zu) too large for codec input size (%zu)",
                         mediaBuffer->range_length(), codecBuffer->capacity());
-                haveMoreInputs = false;
+                mHaveMoreInputs = false;
                 err = BAD_VALUE;
             } else {
                 codecBuffer->setRange(0, mediaBuffer->range_length());
@@ -309,45 +312,46 @@
                         (const uint8_t*)mediaBuffer->data() + mediaBuffer->range_offset(),
                         mediaBuffer->range_length());
 
-                onInputReceived(codecBuffer, mediaBuffer->meta_data(), firstSample, &flags);
-                firstSample = false;
+                onInputReceived(codecBuffer, mediaBuffer->meta_data(), mFirstSample, &flags);
+                mFirstSample = false;
             }
 
             mediaBuffer->release();
 
-            if (haveMoreInputs && inputIndex < inputBuffers.size()) {
+            if (mHaveMoreInputs) {
                 ALOGV("QueueInput: size=%zu ts=%" PRId64 " us flags=%x",
                         codecBuffer->size(), ptsUs, flags);
 
-                err = decoder->queueInputBuffer(
-                        inputIndex,
+                err = mDecoder->queueInputBuffer(
+                        index,
                         codecBuffer->offset(),
                         codecBuffer->size(),
                         ptsUs,
                         flags);
 
                 if (flags & MediaCodec::BUFFER_FLAG_EOS) {
-                    haveMoreInputs = false;
+                    mHaveMoreInputs = false;
                 }
             }
         }
 
         while (err == OK) {
+            size_t offset, size;
             // wait for a decoded buffer
-            err = decoder->dequeueOutputBuffer(
+            err = mDecoder->dequeueOutputBuffer(
                     &index,
                     &offset,
                     &size,
-                    &timeUs,
+                    &ptsUs,
                     &flags,
                     kBufferTimeOutUs);
 
             if (err == INFO_FORMAT_CHANGED) {
                 ALOGV("Received format change");
-                err = decoder->getOutputFormat(&outputFormat);
+                err = mDecoder->getOutputFormat(&mOutputFormat);
             } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) {
                 ALOGV("Output buffers changed");
-                err = decoder->getOutputBuffers(&outputBuffers);
+                err = OK;
             } else {
                 if (err == -EAGAIN /* INFO_TRY_AGAIN_LATER */ && --retriesLeft > 0) {
                     ALOGV("Timed-out waiting for output.. retries left = %zu", retriesLeft);
@@ -355,12 +359,15 @@
                 } else if (err == OK) {
                     // If we're seeking with CLOSEST option and obtained a valid targetTimeUs
                     // from the extractor, decode to the specified frame. Otherwise we're done.
-                    ALOGV("Received an output buffer, timeUs=%lld", (long long)timeUs);
-                    sp<MediaCodecBuffer> videoFrameBuffer = outputBuffers.itemAt(index);
-
-                    err = onOutputReceived(videoFrameBuffer, outputFormat, timeUs, &done);
-
-                    decoder->releaseOutputBuffer(index);
+                    ALOGV("Received an output buffer, timeUs=%lld", (long long)ptsUs);
+                    sp<MediaCodecBuffer> videoFrameBuffer;
+                    err = mDecoder->getOutputBuffer(index, &videoFrameBuffer);
+                    if (err != OK) {
+                        ALOGE("failed to get output buffer %zu", index);
+                        break;
+                    }
+                    err = onOutputReceived(videoFrameBuffer, mOutputFormat, ptsUs, &done);
+                    mDecoder->releaseOutputBuffer(index);
                 } else {
                     ALOGW("Received error %d (%s) instead of output", err, asString(err));
                     done = true;
@@ -370,9 +377,6 @@
         }
     } while (err == OK && !done);
 
-    mSource->stop();
-    decoder->release();
-
     if (err != OK) {
         ALOGE("failed to get video frame (err %d)", err);
     }
@@ -380,6 +384,20 @@
     return err;
 }
 
+//////////////////////////////////////////////////////////////////////
+
+VideoFrameDecoder::VideoFrameDecoder(
+        const AString &componentName,
+        const sp<MetaData> &trackMeta,
+        const sp<IMediaSource> &source)
+    : FrameDecoder(componentName, trackMeta, source),
+      mIsAvcOrHevc(false),
+      mSeekMode(MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC),
+      mTargetTimeUs(-1ll),
+      mNumFrames(0),
+      mNumFramesDecoded(0) {
+}
+
 sp<AMessage> VideoFrameDecoder::onGetFormatAndSeekOptions(
         int64_t frameTimeUs, size_t numFrames, int seekMode, MediaSource::ReadOptions *options) {
     mSeekMode = static_cast<MediaSource::ReadOptions::SeekMode>(seekMode);
@@ -511,33 +529,47 @@
     return ERROR_UNSUPPORTED;
 }
 
+////////////////////////////////////////////////////////////////////////
+
+ImageDecoder::ImageDecoder(
+        const AString &componentName,
+        const sp<MetaData> &trackMeta,
+        const sp<IMediaSource> &source)
+    : FrameDecoder(componentName, trackMeta, source),
+      mFrame(NULL),
+      mWidth(0),
+      mHeight(0),
+      mGridRows(1),
+      mGridCols(1),
+      mTilesDecoded(0) {
+}
+
 sp<AMessage> ImageDecoder::onGetFormatAndSeekOptions(
         int64_t frameTimeUs, size_t /*numFrames*/,
         int /*seekMode*/, MediaSource::ReadOptions *options) {
     sp<MetaData> overrideMeta;
-    mThumbnail = false;
     if (frameTimeUs < 0) {
         uint32_t type;
         const void *data;
         size_t size;
-        int32_t thumbWidth, thumbHeight;
 
         // if we have a stand-alone thumbnail, set up the override meta,
         // and set seekTo time to -1.
-        if (!findThumbnailInfo(trackMeta(),
-                &thumbWidth, &thumbHeight, &type, &data, &size)) {
+        if (!findThumbnailInfo(trackMeta(), &mWidth, &mHeight, &type, &data, &size)) {
             ALOGE("Thumbnail not available");
             return NULL;
         }
         overrideMeta = new MetaData(*(trackMeta()));
         overrideMeta->remove(kKeyDisplayWidth);
         overrideMeta->remove(kKeyDisplayHeight);
-        overrideMeta->setInt32(kKeyWidth, thumbWidth);
-        overrideMeta->setInt32(kKeyHeight, thumbHeight);
+        overrideMeta->setInt32(kKeyWidth, mWidth);
+        overrideMeta->setInt32(kKeyHeight, mHeight);
         overrideMeta->setData(kKeyHVCC, type, data, size);
         options->setSeekTo(-1);
-        mThumbnail = true;
     } else {
+        CHECK(trackMeta()->findInt32(kKeyWidth, &mWidth));
+        CHECK(trackMeta()->findInt32(kKeyHeight, &mHeight));
+
         options->setSeekTo(frameTimeUs);
     }
 
@@ -545,17 +577,10 @@
     if (overrideMeta == NULL) {
         // check if we're dealing with a tiled heif
         int32_t tileWidth, tileHeight, gridRows, gridCols;
-        if (trackMeta()->findInt32(kKeyTileWidth, &tileWidth) && tileWidth > 0
-         && trackMeta()->findInt32(kKeyTileHeight, &tileHeight) && tileHeight > 0
-         && trackMeta()->findInt32(kKeyGridRows, &gridRows) && gridRows > 0
-         && trackMeta()->findInt32(kKeyGridCols, &gridCols) && gridCols > 0) {
-            int32_t width, height;
-            CHECK(trackMeta()->findInt32(kKeyWidth, &width));
-            CHECK(trackMeta()->findInt32(kKeyHeight, &height));
-
-            if (width <= tileWidth * gridCols && height <= tileHeight * gridRows) {
+        if (findGridInfo(trackMeta(), &tileWidth, &tileHeight, &gridRows, &gridCols)) {
+            if (mWidth <= tileWidth * gridCols && mHeight <= tileHeight * gridRows) {
                 ALOGV("grid: %dx%d, tile size: %dx%d, picture size: %dx%d",
-                        gridCols, gridRows, tileWidth, tileHeight, width, height);
+                        gridCols, gridRows, tileWidth, tileHeight, mWidth, mHeight);
 
                 overrideMeta = new MetaData(*(trackMeta()));
                 overrideMeta->setInt32(kKeyWidth, tileWidth);
@@ -563,8 +588,8 @@
                 mGridCols = gridCols;
                 mGridRows = gridRows;
             } else {
-                ALOGE("bad grid: %dx%d, tile size: %dx%d, picture size: %dx%d",
-                        gridCols, gridRows, tileWidth, tileHeight, width, height);
+                ALOGE("ignore bad grid: %dx%d, tile size: %dx%d, picture size: %dx%d",
+                        gridCols, gridRows, tileWidth, tileHeight, mWidth, mHeight);
             }
         }
         if (overrideMeta == NULL) {
@@ -600,17 +625,8 @@
     CHECK(outputFormat->findInt32("width", &width));
     CHECK(outputFormat->findInt32("height", &height));
 
-    int32_t imageWidth, imageHeight;
-    if (mThumbnail) {
-        CHECK(trackMeta()->findInt32(kKeyThumbnailWidth, &imageWidth));
-        CHECK(trackMeta()->findInt32(kKeyThumbnailHeight, &imageHeight));
-    } else {
-        CHECK(trackMeta()->findInt32(kKeyWidth, &imageWidth));
-        CHECK(trackMeta()->findInt32(kKeyHeight, &imageHeight));
-    }
-
     if (mFrame == NULL) {
-        sp<IMemory> frameMem = allocVideoFrame(trackMeta(), imageWidth, imageHeight, dstBpp());
+        sp<IMemory> frameMem = allocVideoFrame(trackMeta(), mWidth, mHeight, dstBpp());
         mFrame = static_cast<VideoFrame*>(frameMem->pointer());
 
         addFrame(frameMem);
@@ -638,12 +654,12 @@
 
     // apply crop on bottom-right
     // TODO: need to move this into the color converter itself.
-    if (dstRight >= imageWidth) {
-        crop_right = imageWidth - dstLeft - 1;
+    if (dstRight >= mWidth) {
+        crop_right = mWidth - dstLeft - 1;
         dstRight = dstLeft + crop_right;
     }
-    if (dstBottom >= imageHeight) {
-        crop_bottom = imageHeight - dstTop - 1;
+    if (dstBottom >= mHeight) {
+        crop_bottom = mHeight - dstTop - 1;
         dstBottom = dstTop + crop_bottom;
     }
 
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index cd091a6..9a33168 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -287,7 +287,9 @@
 //static
 bool MediaCodecList::isSoftwareCodec(const AString &componentName) {
     return componentName.startsWithIgnoreCase("OMX.google.")
-        || !componentName.startsWithIgnoreCase("OMX.");
+            || componentName.startsWithIgnoreCase("c2.android.")
+            || (!componentName.startsWithIgnoreCase("OMX.")
+                    && !componentName.startsWithIgnoreCase("c2."));
 }
 
 static int compareSoftwareCodecsFirst(const AString *name1, const AString *name2) {
@@ -298,7 +300,14 @@
         return isSoftwareCodec2 - isSoftwareCodec1;
     }
 
-    // sort order 2: OMX codecs are first (lower)
+    // sort order 2: Codec 2.0 codecs are first (lower)
+    bool isC2_1 = name1->startsWithIgnoreCase("c2.");
+    bool isC2_2 = name2->startsWithIgnoreCase("c2.");
+    if (isC2_1 != isC2_2) {
+        return isC2_2 - isC2_1;
+    }
+
+    // sort order 3: OMX codecs are first (lower)
     bool isOMX1 = name1->startsWithIgnoreCase("OMX.");
     bool isOMX2 = name2->startsWithIgnoreCase("OMX.");
     return isOMX2 - isOMX1;
diff --git a/media/libstagefright/MediaCodecListOverrides.cpp b/media/libstagefright/MediaCodecListOverrides.cpp
index 6920e51..cac53f4 100644
--- a/media/libstagefright/MediaCodecListOverrides.cpp
+++ b/media/libstagefright/MediaCodecListOverrides.cpp
@@ -222,7 +222,7 @@
     AString supportMultipleSecureCodecs = "true";
     for (const auto& info : infos) {
         AString name = info->getCodecName();
-        if (name.startsWith("OMX.google.") ||
+        if (name.startsWith("OMX.google.") || name.startsWith("c2.android.") ||
                 // TODO: reenable below codecs once fixed
                 name == "OMX.Intel.VideoDecoder.VP9.hybrid") {
             continue;
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 5417fef..e6c318c 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -193,11 +193,13 @@
     for (size_t i = 0; i < matchingCodecs.size(); ++i) {
         const AString &componentName = matchingCodecs[i];
         ImageDecoder decoder(componentName, trackMeta, source);
-        sp<IMemory> frame = decoder.extractFrame(
-                thumbnail ? -1 : 0 /*frameTimeUs*/, 0 /*seekMode*/, colorFormat);
+        int64_t frameTimeUs = thumbnail ? -1 : 0;
+        if (decoder.init(frameTimeUs, 1 /*numFrames*/, 0 /*option*/, colorFormat) == OK) {
+            sp<IMemory> frame = decoder.extractFrame();
 
-        if (frame != NULL) {
-            return frame;
+            if (frame != NULL) {
+                return frame;
+            }
         }
         ALOGV("%s failed to extract thumbnail, trying next decoder.", componentName.c_str());
     }
@@ -307,16 +309,17 @@
     for (size_t i = 0; i < matchingCodecs.size(); ++i) {
         const AString &componentName = matchingCodecs[i];
         VideoFrameDecoder decoder(componentName, trackMeta, source);
-        if (outFrame != NULL) {
-            *outFrame = decoder.extractFrame(timeUs, option, colorFormat);
-            if (*outFrame != NULL) {
-                return OK;
-            }
-        } else if (outFrames != NULL) {
-            status_t err = decoder.extractFrames(
-                    timeUs, numFrames, option, colorFormat, outFrames);
-            if (err == OK) {
-                return OK;
+        if (decoder.init(timeUs, numFrames, option, colorFormat) == OK) {
+            if (outFrame != NULL) {
+                *outFrame = decoder.extractFrame();
+                if (*outFrame != NULL) {
+                    return OK;
+                }
+            } else if (outFrames != NULL) {
+                status_t err = decoder.extractFrames(outFrames);
+                if (err == OK) {
+                    return OK;
+                }
             }
         }
         ALOGV("%s failed to extract frame, trying next decoder.", componentName.c_str());
diff --git a/media/libstagefright/data/media_codecs_google_c2_audio.xml b/media/libstagefright/data/media_codecs_google_c2_audio.xml
index b86f4ad..0b554a2 100644
--- a/media/libstagefright/data/media_codecs_google_c2_audio.xml
+++ b/media/libstagefright/data/media_codecs_google_c2_audio.xml
@@ -16,77 +16,77 @@
 
 <Included>
     <Decoders>
-        <MediaCodec name="c2.google.mp3.decoder" type="audio/mpeg">
+        <MediaCodec name="c2.android.mp3.decoder" type="audio/mpeg">
             <Limit name="channel-count" max="2" />
             <Limit name="sample-rate" ranges="8000,11025,12000,16000,22050,24000,32000,44100,48000" />
             <Limit name="bitrate" range="8000-320000" />
         </MediaCodec>
-        <MediaCodec name="c2.google.amrnb.decoder" type="audio/3gpp">
+        <MediaCodec name="c2.android.amrnb.decoder" type="audio/3gpp">
             <Limit name="channel-count" max="1" />
             <Limit name="sample-rate" ranges="8000" />
             <Limit name="bitrate" range="4750-12200" />
         </MediaCodec>
-        <MediaCodec name="c2.google.amrwb.decoder" type="audio/amr-wb">
+        <MediaCodec name="c2.android.amrwb.decoder" type="audio/amr-wb">
             <Limit name="channel-count" max="1" />
             <Limit name="sample-rate" ranges="16000" />
             <Limit name="bitrate" range="6600-23850" />
         </MediaCodec>
-        <MediaCodec name="c2.google.aac.decoder" type="audio/mp4a-latm">
+        <MediaCodec name="c2.android.aac.decoder" type="audio/mp4a-latm">
             <Limit name="channel-count" max="8" />
             <Limit name="sample-rate" ranges="7350,8000,11025,12000,16000,22050,24000,32000,44100,48000" />
             <Limit name="bitrate" range="8000-960000" />
         </MediaCodec>
-        <MediaCodec name="c2.google.g711.alaw.decoder" type="audio/g711-alaw">
+        <MediaCodec name="c2.android.g711.alaw.decoder" type="audio/g711-alaw">
             <Limit name="channel-count" max="1" />
             <Limit name="sample-rate" ranges="8000-48000" />
             <Limit name="bitrate" range="64000" />
         </MediaCodec>
-        <MediaCodec name="c2.google.g711.mlaw.decoder" type="audio/g711-mlaw">
+        <MediaCodec name="c2.android.g711.mlaw.decoder" type="audio/g711-mlaw">
             <Limit name="channel-count" max="1" />
             <Limit name="sample-rate" ranges="8000-48000" />
             <Limit name="bitrate" range="64000" />
         </MediaCodec>
-        <MediaCodec name="c2.google.vorbis.decoder" type="audio/vorbis">
+        <MediaCodec name="c2.android.vorbis.decoder" type="audio/vorbis">
             <Limit name="channel-count" max="8" />
             <Limit name="sample-rate" ranges="8000-96000" />
             <Limit name="bitrate" range="32000-500000" />
         </MediaCodec>
-        <MediaCodec name="c2.google.opus.decoder" type="audio/opus">
+        <MediaCodec name="c2.android.opus.decoder" type="audio/opus">
             <Limit name="channel-count" max="8" />
             <Limit name="sample-rate" ranges="48000" />
             <Limit name="bitrate" range="6000-510000" />
         </MediaCodec>
-        <MediaCodec name="c2.google.raw.decoder" type="audio/raw">
+        <MediaCodec name="c2.android.raw.decoder" type="audio/raw">
             <Limit name="channel-count" max="8" />
             <Limit name="sample-rate" ranges="8000-96000" />
             <Limit name="bitrate" range="1-10000000" />
         </MediaCodec>
-        <MediaCodec name="c2.google.flac.decoder" type="audio/flac">
+        <MediaCodec name="c2.android.flac.decoder" type="audio/flac">
             <Limit name="channel-count" max="8" />
             <Limit name="sample-rate" ranges="1-655350" />
             <Limit name="bitrate" range="1-21000000" />
         </MediaCodec>
     </Decoders>
     <Encoders>
-        <MediaCodec name="c2.google.aac.encoder" type="audio/mp4a-latm">
+        <MediaCodec name="c2.android.aac.encoder" type="audio/mp4a-latm">
             <Limit name="channel-count" max="6" />
             <Limit name="sample-rate" ranges="8000,11025,12000,16000,22050,24000,32000,44100,48000" />
             <!-- also may support 64000, 88200  and 96000 Hz -->
             <Limit name="bitrate" range="8000-960000" />
         </MediaCodec>
-        <MediaCodec name="c2.google.amrnb.encoder" type="audio/3gpp">
+        <MediaCodec name="c2.android.amrnb.encoder" type="audio/3gpp">
             <Limit name="channel-count" max="1" />
             <Limit name="sample-rate" ranges="8000" />
             <Limit name="bitrate" range="4750-12200" />
             <Feature name="bitrate-modes" value="CBR" />
         </MediaCodec>
-        <MediaCodec name="c2.google.amrwb.encoder" type="audio/amr-wb">
+        <MediaCodec name="c2.android.amrwb.encoder" type="audio/amr-wb">
             <Limit name="channel-count" max="1" />
             <Limit name="sample-rate" ranges="16000" />
             <Limit name="bitrate" range="6600-23850" />
             <Feature name="bitrate-modes" value="CBR" />
         </MediaCodec>
-        <MediaCodec name="c2.google.flac.encoder" type="audio/flac">
+        <MediaCodec name="c2.android.flac.encoder" type="audio/flac">
             <Limit name="channel-count" max="2" />
             <Limit name="sample-rate" ranges="1-655350" />
             <Limit name="bitrate" range="1-21000000" />
diff --git a/media/libstagefright/data/media_codecs_google_c2_video.xml b/media/libstagefright/data/media_codecs_google_c2_video.xml
index 593463b..adb45b3 100644
--- a/media/libstagefright/data/media_codecs_google_c2_video.xml
+++ b/media/libstagefright/data/media_codecs_google_c2_video.xml
@@ -16,7 +16,7 @@
 
 <Included>
     <Decoders>
-        <MediaCodec name="c2.google.mpeg4.decoder" type="video/mp4v-es">
+        <MediaCodec name="c2.android.mpeg4.decoder" type="video/mp4v-es">
             <!-- profiles and levels:  ProfileSimple : Level3 -->
             <Limit name="size" min="2x2" max="352x288" />
             <Limit name="alignment" value="2x2" />
@@ -25,7 +25,7 @@
             <Limit name="bitrate" range="1-384000" />
             <Feature name="adaptive-playback" />
         </MediaCodec>
-        <MediaCodec name="c2.google.h263.decoder" type="video/3gpp">
+        <MediaCodec name="c2.android.h263.decoder" type="video/3gpp">
             <!-- profiles and levels:  ProfileBaseline : Level30, ProfileBaseline : Level45
                     ProfileISWV2 : Level30, ProfileISWV2 : Level45 -->
             <Limit name="size" min="2x2" max="352x288" />
@@ -33,7 +33,7 @@
             <Limit name="bitrate" range="1-384000" />
             <Feature name="adaptive-playback" />
         </MediaCodec>
-        <MediaCodec name="c2.google.avc.decoder" type="video/avc">
+        <MediaCodec name="c2.android.avc.decoder" type="video/avc">
             <!-- profiles and levels:  ProfileHigh : Level52 -->
             <Limit name="size" min="2x2" max="4080x4080" />
             <Limit name="alignment" value="2x2" />
@@ -43,7 +43,7 @@
             <Limit name="bitrate" range="1-48000000" />
             <Feature name="adaptive-playback" />
         </MediaCodec>
-        <MediaCodec name="c2.google.hevc.decoder" type="video/hevc">
+        <MediaCodec name="c2.android.hevc.decoder" type="video/hevc">
             <!-- profiles and levels:  ProfileMain : MainTierLevel51 -->
             <Limit name="size" min="2x2" max="4096x4096" />
             <Limit name="alignment" value="2x2" />
@@ -53,7 +53,7 @@
             <Limit name="bitrate" range="1-10000000" />
             <Feature name="adaptive-playback" />
         </MediaCodec>
-        <MediaCodec name="c2.google.vp8.decoder" type="video/x-vnd.on2.vp8">
+        <MediaCodec name="c2.android.vp8.decoder" type="video/x-vnd.on2.vp8">
             <Limit name="size" min="2x2" max="2048x2048" />
             <Limit name="alignment" value="2x2" />
             <Limit name="block-size" value="16x16" />
@@ -62,7 +62,7 @@
             <Limit name="bitrate" range="1-40000000" />
             <Feature name="adaptive-playback" />
         </MediaCodec>
-        <MediaCodec name="c2.google.vp9.decoder" type="video/x-vnd.on2.vp9">
+        <MediaCodec name="c2.android.vp9.decoder" type="video/x-vnd.on2.vp9">
             <Limit name="size" min="2x2" max="2048x2048" />
             <Limit name="alignment" value="2x2" />
             <Limit name="block-size" value="16x16" />
@@ -74,13 +74,13 @@
     </Decoders>
 
     <Encoders>
-        <MediaCodec name="c2.google.h263.encoder" type="video/3gpp">
+        <MediaCodec name="c2.android.h263.encoder" type="video/3gpp">
             <!-- profiles and levels:  ProfileBaseline : Level45 -->
             <Limit name="size" min="176x144" max="176x144" />
             <Limit name="alignment" value="16x16" />
             <Limit name="bitrate" range="1-128000" />
         </MediaCodec>
-        <MediaCodec name="c2.google.avc.encoder" type="video/avc">
+        <MediaCodec name="c2.android.avc.encoder" type="video/avc">
             <!-- profiles and levels:  ProfileBaseline : Level41 -->
             <Limit name="size" min="16x16" max="2048x2048" />
             <Limit name="alignment" value="2x2" />
@@ -90,7 +90,7 @@
             <Limit name="bitrate" range="1-12000000" />
             <Feature name="intra-refresh" />
         </MediaCodec>
-        <MediaCodec name="c2.google.mpeg4.encoder" type="video/mp4v-es">
+        <MediaCodec name="c2.android.mpeg4.encoder" type="video/mp4v-es">
             <!-- profiles and levels:  ProfileCore : Level2 -->
             <Limit name="size" min="16x16" max="176x144" />
             <Limit name="alignment" value="16x16" />
@@ -98,7 +98,7 @@
             <Limit name="blocks-per-second" range="12-1485" />
             <Limit name="bitrate" range="1-64000" />
         </MediaCodec>
-        <MediaCodec name="c2.google.vp8.encoder" type="video/x-vnd.on2.vp8">
+        <MediaCodec name="c2.android.vp8.encoder" type="video/x-vnd.on2.vp8">
             <!-- profiles and levels:  ProfileMain : Level_Version0-3 -->
             <Limit name="size" min="2x2" max="2048x2048" />
             <Limit name="alignment" value="2x2" />
@@ -108,7 +108,7 @@
             <Limit name="bitrate" range="1-40000000" />
             <Feature name="bitrate-modes" value="VBR,CBR" />
         </MediaCodec>
-        <MediaCodec name="c2.google.vp9.encoder" type="video/x-vnd.on2.vp9">
+        <MediaCodec name="c2.android.vp9.encoder" type="video/x-vnd.on2.vp9">
             <!-- profiles and levels:  ProfileMain : Level_Version0-3 -->
             <Limit name="size" min="2x2" max="2048x2048" />
             <Limit name="alignment" value="2x2" />
diff --git a/media/libstagefright/include/FrameDecoder.h b/media/libstagefright/include/FrameDecoder.h
index f6d4727..3d4ea39 100644
--- a/media/libstagefright/include/FrameDecoder.h
+++ b/media/libstagefright/include/FrameDecoder.h
@@ -32,32 +32,26 @@
 class MediaCodecBuffer;
 class IMediaSource;
 class VideoFrame;
+struct MediaCodec;
 
 struct FrameDecoder {
     FrameDecoder(
             const AString &componentName,
             const sp<MetaData> &trackMeta,
-            const sp<IMediaSource> &source) :
-                mComponentName(componentName),
-                mTrackMeta(trackMeta),
-                mSource(source),
-                mDstFormat(OMX_COLOR_Format16bitRGB565),
-                mDstBpp(2) {}
+            const sp<IMediaSource> &source);
 
-    sp<IMemory> extractFrame(int64_t frameTimeUs, int option, int colorFormat);
+    status_t init(
+            int64_t frameTimeUs, size_t numFrames, int option, int colorFormat);
 
-    status_t extractFrames(
-            int64_t frameTimeUs,
-            size_t numFrames,
-            int option,
-            int colorFormat,
-            std::vector<sp<IMemory> >* frames);
+    sp<IMemory> extractFrame();
+
+    status_t extractFrames(std::vector<sp<IMemory> >* frames);
 
     static sp<IMemory> getMetadataOnly(
             const sp<MetaData> &trackMeta, int colorFormat, bool thumbnail = false);
 
 protected:
-    virtual ~FrameDecoder() {}
+    virtual ~FrameDecoder();
 
     virtual sp<AMessage> onGetFormatAndSeekOptions(
             int64_t frameTimeUs,
@@ -92,13 +86,13 @@
     OMX_COLOR_FORMATTYPE mDstFormat;
     int32_t mDstBpp;
     std::vector<sp<IMemory> > mFrames;
+    MediaSource::ReadOptions mReadOptions;
+    sp<MediaCodec> mDecoder;
+    sp<AMessage> mOutputFormat;
+    bool mHaveMoreInputs;
+    bool mFirstSample;
 
-    static bool getDstColorFormat(
-            android_pixel_format_t colorFormat,
-            OMX_COLOR_FORMATTYPE *dstFormat,
-            int32_t *dstBpp);
-
-    status_t extractInternal(int64_t frameTimeUs, size_t numFrames, int option);
+    status_t extractInternal();
 
     DISALLOW_EVIL_CONSTRUCTORS(FrameDecoder);
 };
@@ -107,13 +101,7 @@
     VideoFrameDecoder(
             const AString &componentName,
             const sp<MetaData> &trackMeta,
-            const sp<IMediaSource> &source) :
-                FrameDecoder(componentName, trackMeta, source),
-                mIsAvcOrHevc(false),
-                mSeekMode(MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC),
-                mTargetTimeUs(-1ll),
-                mNumFrames(0),
-                mNumFramesDecoded(0) {}
+            const sp<IMediaSource> &source);
 
 protected:
     virtual sp<AMessage> onGetFormatAndSeekOptions(
@@ -146,10 +134,7 @@
     ImageDecoder(
             const AString &componentName,
             const sp<MetaData> &trackMeta,
-            const sp<IMediaSource> &source) :
-                FrameDecoder(componentName, trackMeta, source),
-                mFrame(NULL), mGridRows(1), mGridCols(1),
-                mTilesDecoded(0), mThumbnail(false) {}
+            const sp<IMediaSource> &source);
 
 protected:
     virtual sp<AMessage> onGetFormatAndSeekOptions(
@@ -172,10 +157,11 @@
 
 private:
     VideoFrame *mFrame;
+    int32_t mWidth;
+    int32_t mHeight;
     int32_t mGridRows;
     int32_t mGridCols;
     int32_t mTilesDecoded;
-    bool mThumbnail;
 };
 
 }  // namespace android
diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp
index 3eb98f3..3e6942b 100644
--- a/media/libstagefright/omx/Android.bp
+++ b/media/libstagefright/omx/Android.bp
@@ -62,6 +62,7 @@
         "libmedia_omx",
         "libstagefright_foundation",
         "libstagefright_xmlparser",
+        "libutils",
     ],
 
     cflags: [
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Omx.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Omx.h
index baa7b81..5a46b26 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Omx.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Omx.h
@@ -22,6 +22,8 @@
 
 #include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
 #include <android/hardware/media/omx/1.0/IOmx.h>
+#include <utils/KeyedVector.h>
+#include <utils/Mutex.h>
 
 namespace android {
 
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index fe08ab9..6d10f1c 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -224,7 +224,7 @@
 
 static bool findId(AMediaDrm *mObj, const AMediaDrmByteArray &id, List<idvec_t>::iterator &iter) {
     for (iter = mObj->mIds.begin(); iter != mObj->mIds.end(); ++iter) {
-        if (iter->array() == id.ptr && iter->size() == id.length) {
+        if (id.length == iter->size() && memcmp(iter->array(), id.ptr, iter->size()) == 0) {
             return true;
         }
     }
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 2b993ee..fc2dbbb 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -64,7 +64,7 @@
 
     virtual bool        isFastTrack() const { return (mFlags & AUDIO_INPUT_FLAG_FAST) != 0; }
 
-            void        setSilenced(bool silenced) { mSilenced = silenced; }
+            void        setSilenced(bool silenced) { if (!isPatchTrack()) mSilenced = silenced; }
             bool        isSilenced() const { return mSilenced; }
 
             status_t    getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones);
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 998cf4e..d037207 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -6614,11 +6614,16 @@
                 if (framesRead != OVERRUN) break;
             }
 
-            // since pipe is non-blocking, simulate blocking input by waiting for 1/2 of
-            // buffer size or at least for 20ms.
-            size_t sleepFrames = max(
-                    min(mPipeFramesP2, mRsmpInFramesP2) / 2, FMS_20 * mSampleRate / 1000);
-            if (framesRead <= (ssize_t) sleepFrames) {
+            const ssize_t availableToRead = mPipeSource->availableToRead();
+            if (availableToRead >= 0) {
+                // PipeSource is the master clock.  It is up to the AudioRecord client to keep up.
+                LOG_ALWAYS_FATAL_IF((size_t)availableToRead > mPipeFramesP2,
+                        "more frames to read than fifo size, %zd > %zu",
+                        availableToRead, mPipeFramesP2);
+                const size_t pipeFramesFree = mPipeFramesP2 - availableToRead;
+                const size_t sleepFrames = min(pipeFramesFree, mRsmpInFramesP2) / 2;
+                ALOGVV("mPipeFramesP2:%zu mRsmpInFramesP2:%zu sleepFrames:%zu availableToRead:%zd",
+                        mPipeFramesP2, mRsmpInFramesP2, sleepFrames, availableToRead);
                 sleepUs = (sleepFrames * 1000000LL) / mSampleRate;
             }
             if (framesRead < 0) {
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index aff1239..49552a1 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -1663,7 +1663,8 @@
         mFramesToDrop(0),
         mResamplerBufferProvider(NULL), // initialize in case of early constructor exit
         mRecordBufferConverter(NULL),
-        mFlags(flags)
+        mFlags(flags),
+        mSilenced(false)
 {
     if (mCblk == NULL) {
         return;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 67b5e06..6b958a8 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -4325,9 +4325,9 @@
     uint32_t numRequestProcessed = 0;
     for (size_t i = 0; i < batchSize; i++) {
         requests[i] = &mNextRequests.editItemAt(i).halRequest;
+        ATRACE_ASYNC_BEGIN("frame capture", mNextRequests[i].halRequest.frame_number);
     }
 
-    ATRACE_ASYNC_BEGIN("batch frame capture", mNextRequests[0].halRequest.frame_number);
     res = mInterface->processBatchCaptureRequests(requests, &numRequestProcessed);
 
     bool triggerRemoveFailed = false;
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index 6015b28..253f290 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -49,9 +49,9 @@
 
     virtual aaudio_result_t close() = 0;
 
-    virtual aaudio_result_t registerStream(android::sp<AAudioServiceStreamBase> stream);
+    aaudio_result_t registerStream(android::sp<AAudioServiceStreamBase> stream);
 
-    virtual aaudio_result_t unregisterStream(android::sp<AAudioServiceStreamBase> stream);
+    aaudio_result_t unregisterStream(android::sp<AAudioServiceStreamBase> stream);
 
     virtual aaudio_result_t startStream(android::sp<AAudioServiceStreamBase> stream,
                                         audio_port_handle_t *clientHandle) = 0;
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index c943008..48d8002 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -105,6 +105,9 @@
             goto error;
         }
 
+        // This is not protected by a lock because the stream cannot be
+        // referenced until the service returns a handle to the client.
+        // So only one thread can open a stream.
         mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService,
                                                          request,
                                                          sharingMode);
@@ -113,6 +116,9 @@
             result = AAUDIO_ERROR_UNAVAILABLE;
             goto error;
         }
+        // Save a weak pointer that we will use to access the endpoint.
+        mServiceEndpointWeak = mServiceEndpoint;
+
         mFramesPerBurst = mServiceEndpoint->getFramesPerBurst();
         copyFrom(*mServiceEndpoint);
     }
@@ -131,13 +137,16 @@
 
     stop();
 
-    if (mServiceEndpoint == nullptr) {
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
         result = AAUDIO_ERROR_INVALID_STATE;
     } else {
-        mServiceEndpoint->unregisterStream(this);
-        AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
-        mEndpointManager.closeEndpoint(mServiceEndpoint);
-        mServiceEndpoint.clear();
+        endpoint->unregisterStream(this);
+        AAudioEndpointManager &endpointManager = AAudioEndpointManager::getInstance();
+        endpointManager.closeEndpoint(endpoint);
+
+        // AAudioService::closeStream() prevents two threads from closing at the same time.
+        mServiceEndpoint.clear(); // endpoint will hold the pointer until this method returns.
     }
 
     {
@@ -153,7 +162,12 @@
 
 aaudio_result_t AAudioServiceStreamBase::startDevice() {
     mClientHandle = AUDIO_PORT_HANDLE_NONE;
-    return mServiceEndpoint->startStream(this, &mClientHandle);
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    return endpoint->startStream(this, &mClientHandle);
 }
 
 /**
@@ -163,16 +177,11 @@
  */
 aaudio_result_t AAudioServiceStreamBase::start() {
     aaudio_result_t result = AAUDIO_OK;
+
     if (isRunning()) {
         return AAUDIO_OK;
     }
 
-    if (mServiceEndpoint == nullptr) {
-        ALOGE("%s() missing endpoint", __func__);
-        result = AAUDIO_ERROR_INVALID_STATE;
-        goto error;
-    }
-
     setFlowing(false);
 
     // Start with fresh presentation timestamps.
@@ -201,10 +210,6 @@
     if (!isRunning()) {
         return result;
     }
-    if (mServiceEndpoint == nullptr) {
-        ALOGE("%s() missing endpoint", __func__);
-        return AAUDIO_ERROR_INVALID_STATE;
-    }
 
     // Send it now because the timestamp gets rounded up when stopStream() is called below.
     // Also we don't need the timestamps while we are shutting down.
@@ -216,7 +221,12 @@
         return result;
     }
 
-    result = mServiceEndpoint->stopStream(this, mClientHandle);
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    result = endpoint->stopStream(this, mClientHandle);
     if (result != AAUDIO_OK) {
         ALOGE("%s() mServiceEndpoint returned %d, %s", __func__, result, getTypeText());
         disconnect(); // TODO should we return or pause Base first?
@@ -233,11 +243,6 @@
         return result;
     }
 
-    if (mServiceEndpoint == nullptr) {
-        ALOGE("%s() missing endpoint", __func__);
-        return AAUDIO_ERROR_INVALID_STATE;
-    }
-
     setState(AAUDIO_STREAM_STATE_STOPPING);
 
     // Send it now because the timestamp gets rounded up when stopStream() is called below.
@@ -249,10 +254,15 @@
         return result;
     }
 
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
     // TODO wait for data to be played out
-    result = mServiceEndpoint->stopStream(this, mClientHandle);
+    result = endpoint->stopStream(this, mClientHandle);
     if (result != AAUDIO_OK) {
-        ALOGE("%s() mServiceEndpoint returned %d, %s", __func__, result, getTypeText());
+        ALOGE("%s() stopStream returned %d, %s", __func__, result, getTypeText());
         disconnect();
         // TODO what to do with result here?
     }
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index d8102be..0ad015e 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -279,7 +279,12 @@
     SimpleDoubleBuffer<Timestamp>  mAtomicTimestamp;
 
     android::AAudioService &mAudioService;
+
+    // The mServiceEndpoint variable can be accessed by multiple threads.
+    // So we access it by locally promoting a weak pointer to a smart pointer,
+    // which is thread-safe.
     android::sp<AAudioServiceEndpoint> mServiceEndpoint;
+    android::wp<AAudioServiceEndpoint> mServiceEndpointWeak;
 
 private:
     aaudio_handle_t         mHandle = -1;
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 34ddb4b..c845309 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -70,14 +70,19 @@
         return result;
     }
 
-    result = mServiceEndpoint->registerStream(keep);
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+
+    result = endpoint->registerStream(keep);
     if (result != AAUDIO_OK) {
-        goto error;
+        return result;
     }
 
     setState(AAUDIO_STREAM_STATE_OPEN);
 
-error:
     return AAUDIO_OK;
 }
 
@@ -118,21 +123,37 @@
 
 aaudio_result_t AAudioServiceStreamMMAP::startClient(const android::AudioClient& client,
                                                        audio_port_handle_t *clientHandle) {
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
     // Start the client on behalf of the application. Generate a new porthandle.
-    aaudio_result_t result = mServiceEndpoint->startClient(client, clientHandle);
+    aaudio_result_t result = endpoint->startClient(client, clientHandle);
     return result;
 }
 
 aaudio_result_t AAudioServiceStreamMMAP::stopClient(audio_port_handle_t clientHandle) {
-    aaudio_result_t result = mServiceEndpoint->stopClient(clientHandle);
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    aaudio_result_t result = endpoint->stopClient(clientHandle);
     return result;
 }
 
 // Get free-running DSP or DMA hardware position from the HAL.
 aaudio_result_t AAudioServiceStreamMMAP::getFreeRunningPosition(int64_t *positionFrames,
                                                                   int64_t *timeNanos) {
-    sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP{
-            static_cast<AAudioServiceEndpointMMAP *>(mServiceEndpoint.get())};
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP =
+            static_cast<AAudioServiceEndpointMMAP *>(endpoint.get());
+
     aaudio_result_t result = serviceEndpointMMAP->getFreeRunningPosition(positionFrames, timeNanos);
     if (result == AAUDIO_OK) {
         Timestamp timestamp(*positionFrames, *timeNanos);
@@ -148,8 +169,15 @@
 // Get timestamp that was written by getFreeRunningPosition()
 aaudio_result_t AAudioServiceStreamMMAP::getHardwareTimestamp(int64_t *positionFrames,
                                                                 int64_t *timeNanos) {
-    sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP{
-            static_cast<AAudioServiceEndpointMMAP *>(mServiceEndpoint.get())};
+
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP =
+            static_cast<AAudioServiceEndpointMMAP *>(endpoint.get());
+
     // TODO Get presentation timestamp from the HAL
     if (mAtomicTimestamp.isValid()) {
         Timestamp timestamp = mAtomicTimestamp.read();
@@ -165,7 +193,12 @@
 aaudio_result_t AAudioServiceStreamMMAP::getAudioDataDescription(
         AudioEndpointParcelable &parcelable)
 {
-    sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP{
-            static_cast<AAudioServiceEndpointMMAP *>(mServiceEndpoint.get())};
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP =
+            static_cast<AAudioServiceEndpointMMAP *>(endpoint.get());
     return serviceEndpointMMAP->getDownDataDescription(parcelable);
 }
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index 864a008..05c5735 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -128,6 +128,12 @@
 
     const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
 
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        result = AAUDIO_ERROR_INVALID_STATE;
+        goto error;
+    }
+
     // Is the request compatible with the shared endpoint?
     setFormat(configurationInput.getFormat());
     if (getFormat() == AAUDIO_FORMAT_UNSPECIFIED) {
@@ -140,20 +146,20 @@
 
     setSampleRate(configurationInput.getSampleRate());
     if (getSampleRate() == AAUDIO_UNSPECIFIED) {
-        setSampleRate(mServiceEndpoint->getSampleRate());
-    } else if (getSampleRate() != mServiceEndpoint->getSampleRate()) {
+        setSampleRate(endpoint->getSampleRate());
+    } else if (getSampleRate() != endpoint->getSampleRate()) {
         ALOGD("%s() mSampleRate = %d, need %d",
-              __func__, getSampleRate(), mServiceEndpoint->getSampleRate());
+              __func__, getSampleRate(), endpoint->getSampleRate());
         result = AAUDIO_ERROR_INVALID_RATE;
         goto error;
     }
 
     setSamplesPerFrame(configurationInput.getSamplesPerFrame());
     if (getSamplesPerFrame() == AAUDIO_UNSPECIFIED) {
-        setSamplesPerFrame(mServiceEndpoint->getSamplesPerFrame());
-    } else if (getSamplesPerFrame() != mServiceEndpoint->getSamplesPerFrame()) {
+        setSamplesPerFrame(endpoint->getSamplesPerFrame());
+    } else if (getSamplesPerFrame() != endpoint->getSamplesPerFrame()) {
         ALOGD("%s() mSamplesPerFrame = %d, need %d",
-              __func__, getSamplesPerFrame(), mServiceEndpoint->getSamplesPerFrame());
+              __func__, getSamplesPerFrame(), endpoint->getSamplesPerFrame());
         result = AAUDIO_ERROR_OUT_OF_RANGE;
         goto error;
     }
@@ -179,7 +185,10 @@
         }
     }
 
-    result = mServiceEndpoint->registerStream(keep);
+    ALOGD("AAudioServiceStreamShared::open() actual rate = %d, channels = %d, deviceId = %d",
+          getSampleRate(), getSamplesPerFrame(), endpoint->getDeviceId());
+
+    result = endpoint->registerStream(keep);
     if (result != AAUDIO_OK) {
         goto error;
     }
@@ -246,7 +255,13 @@
                                                                 int64_t *timeNanos) {
 
     int64_t position = 0;
-    aaudio_result_t result = mServiceEndpoint->getTimestamp(&position, timeNanos);
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        ALOGE("%s() has no endpoint", __func__);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+
+    aaudio_result_t result = endpoint->getTimestamp(&position, timeNanos);
     if (result == AAUDIO_OK) {
         int64_t offset = mTimestampPositionOffset.load();
         // TODO, do not go below starting value