Merge "heic content_encoding field is optional"
diff --git a/include/media/ExtractorUtils.h b/include/media/ExtractorUtils.h
new file mode 120000
index 0000000..e2dd082
--- /dev/null
+++ b/include/media/ExtractorUtils.h
@@ -0,0 +1 @@
+../../media/libmediaextractor/include/media/ExtractorUtils.h
\ No newline at end of file
diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp
index 3f832bc..fc60fd4 100644
--- a/media/extractors/mkv/MatroskaExtractor.cpp
+++ b/media/extractors/mkv/MatroskaExtractor.cpp
@@ -22,6 +22,7 @@
 #include "MatroskaExtractor.h"
 
 #include <media/DataSourceBase.h>
+#include <media/ExtractorUtils.h>
 #include <media/MediaTrack.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AUtils.h>
@@ -1108,7 +1109,7 @@
     meta.setData(kKeyFlacMetadata, 0, codecPrivate, codecPrivateSize);
 
     int32_t maxInputSize = 64 << 10;
-    sp<FLACDecoder> flacDecoder = FLACDecoder::Create();
+    FLACDecoder *flacDecoder = FLACDecoder::Create();
     if (flacDecoder != NULL
             && flacDecoder->parseMetadata((const uint8_t*)codecPrivate, codecPrivateSize) == OK) {
         FLAC__StreamMetadata_StreamInfo streamInfo = flacDecoder->getStreamInfo();
@@ -1120,6 +1121,7 @@
                     && streamInfo.channels != 0
                     && ((streamInfo.bits_per_sample + 7) / 8) >
                         INT32_MAX / streamInfo.max_blocksize / streamInfo.channels) {
+                delete flacDecoder;
                 return ERROR_MALFORMED;
             }
             maxInputSize = ((streamInfo.bits_per_sample + 7) / 8)
@@ -1128,6 +1130,7 @@
     }
     meta.setInt32(kKeyMaxInputSize, maxInputSize);
 
+    delete flacDecoder;
     return OK;
 }
 
@@ -1143,13 +1146,13 @@
     }
 
     const mkvparser::Block::Frame &frame = block->GetFrame(0);
-    sp<ABuffer> abuf = new ABuffer(frame.len);
-    long n = frame.Read(mReader, abuf->data());
+    auto tmpData = heapbuffer<unsigned char>(frame.len);
+    long n = frame.Read(mReader, tmpData.get());
     if (n != 0) {
         return ERROR_MALFORMED;
     }
 
-    if (!MakeAVCCodecSpecificData(trackInfo->mMeta, abuf)) {
+    if (!MakeAVCCodecSpecificData(trackInfo->mMeta, tmpData.get(), frame.len)) {
         return ERROR_MALFORMED;
     }
 
diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp
index 4d49013..b2fe69c 100644
--- a/media/extractors/ogg/OggExtractor.cpp
+++ b/media/extractors/ogg/OggExtractor.cpp
@@ -22,6 +22,7 @@
 
 #include <cutils/properties.h>
 #include <media/DataSourceBase.h>
+#include <media/ExtractorUtils.h>
 #include <media/MediaTrack.h>
 #include <media/VorbisComment.h>
 #include <media/stagefright/foundation/ABuffer.h>
@@ -990,21 +991,11 @@
     return OK;
 }
 
-struct TmpData {
-    uint8_t *data;
-    TmpData(size_t size) {
-        data = (uint8_t*) malloc(size);
-    }
-    ~TmpData() {
-        free(data);
-    }
-};
-
 status_t MyOpusExtractor::verifyOpusComments(MediaBufferBase *buffer) {
     // add artificial framing bit so we can reuse _vorbis_unpack_comment
     int32_t commentSize = buffer->range_length() + 1;
-    TmpData commentDataHolder(commentSize);
-    uint8_t *commentData = commentDataHolder.data;
+    auto tmp = heapbuffer<uint8_t>(commentSize);
+    uint8_t *commentData = tmp.get();
     if (commentData == nullptr) {
         return ERROR_MALFORMED;
     }
diff --git a/media/libheif/HeifDecoderImpl.cpp b/media/libheif/HeifDecoderImpl.cpp
index 2dfbdca..4b17a9b 100644
--- a/media/libheif/HeifDecoderImpl.cpp
+++ b/media/libheif/HeifDecoderImpl.cpp
@@ -332,8 +332,8 @@
 
     if (frameInfo != nullptr) {
         frameInfo->set(
-                videoFrame->mDisplayWidth,
-                videoFrame->mDisplayHeight,
+                videoFrame->mWidth,
+                videoFrame->mHeight,
                 videoFrame->mRotationAngle,
                 videoFrame->mBytesPerPixel,
                 videoFrame->mIccSize,
@@ -410,8 +410,8 @@
 
     if (frameInfo != nullptr) {
         frameInfo->set(
-                videoFrame->mDisplayWidth,
-                videoFrame->mDisplayHeight,
+                videoFrame->mWidth,
+                videoFrame->mHeight,
                 videoFrame->mRotationAngle,
                 videoFrame->mBytesPerPixel,
                 videoFrame->mIccSize,
@@ -430,12 +430,12 @@
         return false;
     }
     VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
-    if (mCurScanline >= videoFrame->mDisplayHeight) {
+    if (mCurScanline >= videoFrame->mHeight) {
         ALOGE("no more scanline available");
         return false;
     }
     uint8_t* src = videoFrame->getFlattenedData() + videoFrame->mRowBytes * mCurScanline++;
-    memcpy(dst, src, videoFrame->mBytesPerPixel * videoFrame->mDisplayWidth);
+    memcpy(dst, src, videoFrame->mBytesPerPixel * videoFrame->mWidth);
     return true;
 }
 
@@ -447,8 +447,8 @@
 
     uint32_t oldScanline = mCurScanline;
     mCurScanline += count;
-    if (mCurScanline > videoFrame->mDisplayHeight) {
-        mCurScanline = videoFrame->mDisplayHeight;
+    if (mCurScanline > videoFrame->mHeight) {
+        mCurScanline = videoFrame->mHeight;
     }
     return (mCurScanline > oldScanline) ? (mCurScanline - oldScanline) : 0;
 }
diff --git a/media/libmedia/TypeConverter.cpp b/media/libmedia/TypeConverter.cpp
index 1096df4..96dd004 100644
--- a/media/libmedia/TypeConverter.cpp
+++ b/media/libmedia/TypeConverter.cpp
@@ -55,6 +55,8 @@
     MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_BUS),
     MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_PROXY),
     MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_USB_HEADSET),
+    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_HEARING_AID),
+    MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_ECHO_CANCELLER),
     MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_DEFAULT),
     // STUB must be after DEFAULT, so the latter is picked up by toString first.
     MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_STUB),
@@ -115,9 +117,7 @@
     MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_DIRECT_PCM),
     MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ),
     MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_VOIP_RX),
-    // FIXME: this cast will be removed when the flag will be
-    // declared in types.hal for audio HAL V4.0 and auto imported to audio-base.h
-    {"AUDIO_OUTPUT_FLAG_INCALL_MUSIC", static_cast<audio_output_flags_t>(AUDIO_OUTPUT_FLAG_INCALL_MUSIC)},
+    MAKE_STRING_FROM_ENUM(AUDIO_OUTPUT_FLAG_INCALL_MUSIC),
     TERMINATOR
 };
 
@@ -131,6 +131,7 @@
     MAKE_STRING_FROM_ENUM(AUDIO_INPUT_FLAG_SYNC),
     MAKE_STRING_FROM_ENUM(AUDIO_INPUT_FLAG_MMAP_NOIRQ),
     MAKE_STRING_FROM_ENUM(AUDIO_INPUT_FLAG_VOIP_TX),
+    MAKE_STRING_FROM_ENUM(AUDIO_INPUT_FLAG_HW_AV_SYNC),
     TERMINATOR
 };
 
@@ -167,6 +168,7 @@
     MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ADTS_LD),
     MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ADTS_HE_V2),
     MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ADTS_ELD),
+    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AAC_ADTS_XHE),
     MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_VORBIS),
     MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_HE_AAC_V1),
     MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_HE_AAC_V2),
@@ -197,6 +199,11 @@
     MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_APTX_HD),
     MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_AC4),
     MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_LDAC),
+    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_MAT),
+    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_E_AC3_JOC),
+    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_MAT_1_0),
+    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_MAT_2_0),
+    MAKE_STRING_FROM_ENUM(AUDIO_FORMAT_MAT_2_1),
     TERMINATOR
 };
 
@@ -206,10 +213,18 @@
     MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_MONO),
     MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_STEREO),
     MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_2POINT1),
+    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_2POINT0POINT2),
+    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_2POINT1POINT2),
+    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_3POINT0POINT2),
+    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_3POINT1POINT2),
     MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_QUAD),
+    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_QUAD_BACK),
+    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_QUAD_SIDE),
     MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_SURROUND),
     MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_PENTA),
     MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_5POINT1),
+    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_5POINT1_BACK),
+    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_5POINT1_SIDE),
     MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_6POINT1),
     MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_OUT_7POINT1),
     TERMINATOR
@@ -222,6 +237,11 @@
     MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_STEREO),
     MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_FRONT_BACK),
     MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_6),
+    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_2POINT0POINT2),
+    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_2POINT1POINT2),
+    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_3POINT0POINT2),
+    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_3POINT1POINT2),
+    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_5POINT1),
     MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO),
     MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO),
     MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_VOICE_CALL_MONO),
diff --git a/media/libmediaextractor/include/media/ExtractorUtils.h b/media/libmediaextractor/include/media/ExtractorUtils.h
new file mode 100644
index 0000000..22f9349
--- /dev/null
+++ b/media/libmediaextractor/include/media/ExtractorUtils.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef EXTRACTOR_UTILS_H_
+
+#define EXTRACTOR_UTILS_H_
+
+#include <memory>
+
+namespace android {
+
+template <class T>
+std::unique_ptr<T[]> heapbuffer(size_t size) {
+    return std::unique_ptr<T[]>(new (std::nothrow) T[size]);
+}
+
+}  // namespace android
+
+#endif  // UTILS_H_
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 9ab9aae..30c0b1c 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -89,6 +89,13 @@
 static const char *kRecorderVideoTimescale = "android.media.mediarecorder.video-timescale";
 static const char *kRecorderWidth = "android.media.mediarecorder.width";
 
+// new fields, not yet frozen in the public Java API definitions
+static const char *kRecorderAudioMime = "android.media.mediarecorder.audio.mime";
+static const char *kRecorderVideoMime = "android.media.mediarecorder.video.mime";
+static const char *kRecorderDurationMs = "android.media.mediarecorder.durationMs";
+static const char *kRecorderPaused = "android.media.mediarecorder.pausedMs";
+static const char *kRecorderNumPauses = "android.media.mediarecorder.NPauses";
+
 
 // To collect the encoder usage for the battery app
 static void addBatteryData(uint32_t params) {
@@ -126,21 +133,18 @@
     }
 
     // log the current record, provided it has some information worth recording
-    if (mAnalyticsDirty && mAnalyticsItem != NULL) {
-        updateMetrics();
-        if (mAnalyticsItem->count() > 0) {
-            mAnalyticsItem->selfrecord();
-        }
-        delete mAnalyticsItem;
-        mAnalyticsItem = NULL;
-    }
+    // NB: this also reclaims & clears mAnalyticsItem.
+    flushAndResetMetrics(false);
 }
 
 void StagefrightRecorder::updateMetrics() {
     ALOGV("updateMetrics");
 
-    // we'll populate the values from the raw fields.
-    // (NOT going to populate as we go through the various set* ops)
+    // we run as part of the media player service; what we really want to
+    // know is the app which requested the recording.
+    mAnalyticsItem->setUid(mClientUid);
+
+    // populate the values from the raw fields.
 
     // TBD mOutputFormat  = OUTPUT_FORMAT_THREE_GPP;
     // TBD mAudioEncoder  = AUDIO_ENCODER_AMR_NB;
@@ -168,7 +172,6 @@
     // TBD mTrackEveryTimeDurationUs = 0;
     mAnalyticsItem->setInt32(kRecorderCaptureFpsEnable, mCaptureFpsEnable);
     mAnalyticsItem->setDouble(kRecorderCaptureFps, mCaptureFps);
-    // TBD mCaptureFps = -1.0;
     // TBD mCameraSourceTimeLapse = NULL;
     // TBD mMetaDataStoredInVideoBuffers = kMetadataBufferTypeInvalid;
     // TBD mEncoderProfiles = MediaProfiles::getInstance();
@@ -177,14 +180,17 @@
     // PII mLongitudex10000 = -3600000;
     // TBD mTotalBitRate = 0;
 
-    // TBD: some duration information (capture, paused)
-    //
-
+    // duration information (recorded, paused, # of pauses)
+    mAnalyticsItem->setInt64(kRecorderDurationMs, (mDurationRecordedUs+500)/1000 );
+    if (mNPauses != 0) {
+        mAnalyticsItem->setInt64(kRecorderPaused, (mDurationPausedUs+500)/1000 );
+        mAnalyticsItem->setInt32(kRecorderNumPauses, mNPauses);
+    }
 }
 
-void StagefrightRecorder::resetMetrics() {
-    ALOGV("resetMetrics");
-    // flush anything we have, restart the record
+void StagefrightRecorder::flushAndResetMetrics(bool reinitialize) {
+    ALOGV("flushAndResetMetrics");
+    // flush anything we have, maybe setup a new record
     if (mAnalyticsDirty && mAnalyticsItem != NULL) {
         updateMetrics();
         if (mAnalyticsItem->count() > 0) {
@@ -193,8 +199,10 @@
         delete mAnalyticsItem;
         mAnalyticsItem = NULL;
     }
-    mAnalyticsItem = new MediaAnalyticsItem(kKeyRecorder);
     mAnalyticsDirty = false;
+    if (reinitialize) {
+        mAnalyticsItem = new MediaAnalyticsItem(kKeyRecorder);
+    }
 }
 
 status_t StagefrightRecorder::init() {
@@ -1030,6 +1038,8 @@
         mAnalyticsDirty = true;
         mStarted = true;
 
+        mStartedRecordingUs = systemTime() / 1000;
+
         uint32_t params = IMediaPlayerService::kBatteryDataCodecStarted;
         if (mAudioSource != AUDIO_SOURCE_CNT) {
             params |= IMediaPlayerService::kBatteryDataTrackAudio;
@@ -1109,6 +1119,14 @@
             return NULL;
     }
 
+    // log audio mime type for media metrics
+    if (mAnalyticsItem != NULL) {
+        AString audiomime;
+        if (format->findString("mime", &audiomime)) {
+            mAnalyticsItem->setCString(kRecorderAudioMime, audiomime.c_str());
+        }
+    }
+
     int32_t maxInputSize;
     CHECK(audioSource->getFormat()->findInt32(
                 kKeyMaxInputSize, &maxInputSize));
@@ -1655,6 +1673,14 @@
             break;
     }
 
+    // log video mime type for media metrics
+    if (mAnalyticsItem != NULL) {
+        AString videomime;
+        if (format->findString("mime", &videomime)) {
+            mAnalyticsItem->setCString(kRecorderVideoMime, videomime.c_str());
+        }
+    }
+
     if (cameraSource != NULL) {
         sp<MetaData> meta = cameraSource->getFormat();
 
@@ -1917,6 +1943,13 @@
     sp<MetaData> meta = new MetaData;
     meta->setInt64(kKeyTime, mPauseStartTimeUs);
 
+    if (mStartedRecordingUs != 0) {
+        // should always be true
+        int64_t recordingUs = mPauseStartTimeUs - mStartedRecordingUs;
+        mDurationRecordedUs += recordingUs;
+        mStartedRecordingUs = 0;
+    }
+
     if (mAudioEncoderSource != NULL) {
         mAudioEncoderSource->pause();
     }
@@ -1975,6 +2008,16 @@
         source->setInputBufferTimeOffset((int64_t)timeOffset);
         source->start(meta.get());
     }
+
+
+    // sum info on pause duration
+    // (ignore the 30msec of overlap adjustment factored into mTotalPausedDurationUs)
+    int64_t pausedUs = resumeStartTimeUs - mPauseStartTimeUs;
+    mDurationPausedUs += pausedUs;
+    mNPauses++;
+    // and a timestamp marking that we're back to recording....
+    mStartedRecordingUs = resumeStartTimeUs;
+
     mPauseStartTimeUs = 0;
 
     return OK;
@@ -2003,10 +2046,28 @@
         mWriter.clear();
     }
 
-    resetMetrics();
+    // account for the last 'segment' -- whether paused or recording
+    if (mPauseStartTimeUs != 0) {
+        // we were paused
+        int64_t additive = stopTimeUs - mPauseStartTimeUs;
+        mDurationPausedUs += additive;
+        mNPauses++;
+    } else if (mStartedRecordingUs != 0) {
+        // we were recording
+        int64_t additive = stopTimeUs - mStartedRecordingUs;
+        mDurationRecordedUs += additive;
+    } else {
+        ALOGW("stop while neither recording nor paused");
+    }
 
+    flushAndResetMetrics(true);
+
+    mDurationRecordedUs = 0;
+    mDurationPausedUs = 0;
+    mNPauses = 0;
     mTotalPausedDurationUs = 0;
     mPauseStartTimeUs = 0;
+    mStartedRecordingUs = 0;
 
     mGraphicBufferProducer.clear();
     mPersistentSurface.clear();
@@ -2085,6 +2146,12 @@
     mLongitudex10000 = -3600000;
     mTotalBitRate = 0;
 
+    // tracking how long we recorded.
+    mDurationRecordedUs = 0;
+    mStartedRecordingUs = 0;
+    mDurationPausedUs = 0;
+    mNPauses = 0;
+
     mOutputFd = -1;
 
     return OK;
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 18c9256..faa2e59 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -95,7 +95,7 @@
 
     MediaAnalyticsItem *mAnalyticsItem;
     bool mAnalyticsDirty;
-    void resetMetrics();
+    void flushAndResetMetrics(bool reinitialize);
     void updateMetrics();
 
     audio_source_t mAudioSource;
@@ -127,6 +127,11 @@
     int32_t mStartTimeOffsetMs;
     int32_t mTotalBitRate;
 
+    int64_t mDurationRecordedUs;
+    int64_t mStartedRecordingUs;
+    int64_t mDurationPausedUs;
+    int32_t mNPauses;
+
     bool mCaptureFpsEnable;
     double mCaptureFps;
     int64_t mTimeBetweenCaptureUs;
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index b296622..86d7c08 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -553,6 +553,7 @@
       mNativeWindowUsageBits(0),
       mLastNativeWindowDataSpace(HAL_DATASPACE_UNKNOWN),
       mIsVideo(false),
+      mIsImage(false),
       mIsEncoder(false),
       mFatalError(false),
       mShutdownInProgress(false),
@@ -1713,6 +1714,7 @@
 
     mIsEncoder = encoder;
     mIsVideo = !strncasecmp(mime, "video/", 6);
+    mIsImage = !strncasecmp(mime, "image/", 6);
 
     mPortMode[kPortIndexInput] = IOMX::kPortModePresetByteBuffer;
     mPortMode[kPortIndexOutput] = IOMX::kPortModePresetByteBuffer;
@@ -1728,10 +1730,11 @@
     // FLAC encoder or video encoder in constant quality mode doesn't need a
     // bitrate, other encoders do.
     if (encoder) {
-        if (mIsVideo && !findVideoBitrateControlInfo(
-                msg, &bitrateMode, &bitrate, &quality)) {
-            return INVALID_OPERATION;
-        } else if (!mIsVideo && strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)
+        if (mIsVideo || mIsImage) {
+            if (!findVideoBitrateControlInfo(msg, &bitrateMode, &bitrate, &quality)) {
+                return INVALID_OPERATION;
+            }
+        } else if (strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)
             && !msg->findInt32("bitrate", &bitrate)) {
             return INVALID_OPERATION;
         }
@@ -2010,7 +2013,7 @@
     (void)msg->findInt32("pcm-encoding", (int32_t*)&pcmEncoding);
     // invalid encodings will default to PCM-16bit in setupRawAudioFormat.
 
-    if (mIsVideo) {
+    if (mIsVideo || mIsImage) {
         // determine need for software renderer
         bool usingSwRenderer = false;
         if (haveNativeWindow && mComponentName.startsWith("OMX.google.")) {
@@ -2290,7 +2293,7 @@
     }
 
     // create data converters if needed
-    if (!mIsVideo && err == OK) {
+    if (!mIsVideo && !mIsImage && err == OK) {
         AudioEncoding codecPcmEncoding = kAudioEncodingPcm16bit;
         if (encoder) {
             (void)mInputFormat->findInt32("pcm-encoding", (int32_t*)&codecPcmEncoding);
@@ -3215,6 +3218,7 @@
     { MEDIA_MIMETYPE_VIDEO_VP8, OMX_VIDEO_CodingVP8 },
     { MEDIA_MIMETYPE_VIDEO_VP9, OMX_VIDEO_CodingVP9 },
     { MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, OMX_VIDEO_CodingDolbyVision },
+    { MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC, OMX_VIDEO_CodingImageHEIC },
 };
 
 static status_t GetVideoCodingTypeFromMime(
@@ -3872,7 +3876,8 @@
             break;
 
         case OMX_VIDEO_CodingHEVC:
-            err = setupHEVCEncoderParameters(msg);
+        case OMX_VIDEO_CodingImageHEIC:
+            err = setupHEVCEncoderParameters(msg, outputFormat);
             break;
 
         case OMX_VIDEO_CodingVP8:
@@ -4378,27 +4383,63 @@
     return configureBitrate(bitrateMode, bitrate);
 }
 
-status_t ACodec::setupHEVCEncoderParameters(const sp<AMessage> &msg) {
-    float iFrameInterval;
-    if (!msg->findAsFloat("i-frame-interval", &iFrameInterval)) {
-        return INVALID_OPERATION;
+status_t ACodec::configureImageGrid(
+        const sp<AMessage> &msg, sp<AMessage> &outputFormat) {
+    int32_t gridWidth, gridHeight, gridRows, gridCols;
+    if (!msg->findInt32("grid-width", &gridWidth) ||
+        !msg->findInt32("grid-height", &gridHeight) ||
+        !msg->findInt32("grid-rows", &gridRows) ||
+        !msg->findInt32("grid-cols", &gridCols)) {
+        return OK;
     }
 
+    OMX_VIDEO_PARAM_ANDROID_IMAGEGRIDTYPE gridType;
+    InitOMXParams(&gridType);
+    gridType.nPortIndex = kPortIndexOutput;
+    gridType.bEnabled = OMX_TRUE;
+    gridType.nGridWidth = gridWidth;
+    gridType.nGridHeight = gridHeight;
+    gridType.nGridRows = gridRows;
+    gridType.nGridCols = gridCols;
+
+    status_t err = mOMXNode->setParameter(
+            (OMX_INDEXTYPE)OMX_IndexParamVideoAndroidImageGrid,
+            &gridType, sizeof(gridType));
+
+    // for video encoders, grid config is only a hint.
+    if (!mIsImage) {
+        return OK;
+    }
+
+    // image encoders must support grid config.
+    if (err != OK) {
+        return err;
+    }
+
+    // query to get the image encoder's real grid config as it might be
+    // different from the requested, and transfer that to the output.
+    err = mOMXNode->getParameter(
+            (OMX_INDEXTYPE)OMX_IndexParamVideoAndroidImageGrid,
+            &gridType, sizeof(gridType));
+
+    if (err == OK && gridType.bEnabled) {
+        outputFormat->setInt32("grid-width", gridType.nGridWidth);
+        outputFormat->setInt32("grid-height", gridType.nGridHeight);
+        outputFormat->setInt32("grid-rows", gridType.nGridRows);
+        outputFormat->setInt32("grid-cols", gridType.nGridCols);
+    }
+
+    return err;
+}
+
+status_t ACodec::setupHEVCEncoderParameters(
+        const sp<AMessage> &msg, sp<AMessage> &outputFormat) {
     OMX_VIDEO_CONTROLRATETYPE bitrateMode;
     int32_t bitrate, quality;
     if (!findVideoBitrateControlInfo(msg, &bitrateMode, &bitrate, &quality)) {
         return INVALID_OPERATION;
     }
 
-    float frameRate;
-    if (!msg->findFloat("frame-rate", &frameRate)) {
-        int32_t tmp;
-        if (!msg->findInt32("frame-rate", &tmp)) {
-            return INVALID_OPERATION;
-        }
-        frameRate = (float)tmp;
-    }
-
     OMX_VIDEO_PARAM_HEVCTYPE hevcType;
     InitOMXParams(&hevcType);
     hevcType.nPortIndex = kPortIndexOutput;
@@ -4426,7 +4467,27 @@
         hevcType.eLevel = static_cast<OMX_VIDEO_HEVCLEVELTYPE>(level);
     }
     // TODO: finer control?
-    hevcType.nKeyFrameInterval = setPFramesSpacing(iFrameInterval, frameRate) + 1;
+    if (mIsImage) {
+        hevcType.nKeyFrameInterval = 1;
+    } else {
+        float iFrameInterval;
+        if (!msg->findAsFloat("i-frame-interval", &iFrameInterval)) {
+            return INVALID_OPERATION;
+        }
+
+        float frameRate;
+        if (!msg->findFloat("frame-rate", &frameRate)) {
+            int32_t tmp;
+            if (!msg->findInt32("frame-rate", &tmp)) {
+                return INVALID_OPERATION;
+            }
+            frameRate = (float)tmp;
+        }
+
+        hevcType.nKeyFrameInterval =
+                setPFramesSpacing(iFrameInterval, frameRate) + 1;
+    }
+
 
     err = mOMXNode->setParameter(
             (OMX_INDEXTYPE)OMX_IndexParamVideoHevc, &hevcType, sizeof(hevcType));
@@ -4434,6 +4495,12 @@
         return err;
     }
 
+    err = configureImageGrid(msg, outputFormat);
+
+    if (err != OK) {
+        return err;
+    }
+
     return configureBitrate(bitrateMode, bitrate, quality);
 }
 
@@ -4875,7 +4942,8 @@
                             (void)getHDRStaticInfoForVideoCodec(kPortIndexInput, notify);
                         }
                         uint32_t latency = 0;
-                        if (mIsEncoder && getLatency(&latency) == OK && latency > 0) {
+                        if (mIsEncoder && !mIsImage &&
+                                getLatency(&latency) == OK && latency > 0) {
                             notify->setInt32("latency", latency);
                         }
                     }
@@ -4931,7 +4999,8 @@
                         notify->setString("mime", mime.c_str());
                     }
                     uint32_t intraRefreshPeriod = 0;
-                    if (mIsEncoder && getIntraRefreshPeriod(&intraRefreshPeriod) == OK
+                    if (mIsEncoder && !mIsImage &&
+                            getIntraRefreshPeriod(&intraRefreshPeriod) == OK
                             && intraRefreshPeriod > 0) {
                         notify->setInt32("intra-refresh-period", intraRefreshPeriod);
                     }
@@ -6346,9 +6415,6 @@
 
     sp<AMessage> notify = new AMessage(kWhatOMXDied, mCodec);
 
-    Vector<AString> matchingCodecs;
-    Vector<AString> owners;
-
     AString componentName;
     CHECK(msg->findString("componentName", &componentName));
 
@@ -6424,7 +6490,7 @@
 
     mCodec->mOMX = omx;
     mCodec->mOMXNode = omxNode;
-    mCodec->mCallback->onComponentAllocated(mCodec->mComponentName.c_str());
+    mCodec->mCallback->onComponentAllocated(mCodec->mComponentName.c_str(), info);
     mCodec->changeState(mCodec->mLoadedState);
 
     return true;
@@ -8226,8 +8292,9 @@
     }
 
     bool isVideo = strncasecmp(mime, "video/", 6) == 0;
+    bool isImage = strncasecmp(mime, "image/", 6) == 0;
 
-    if (isVideo) {
+    if (isVideo || isImage) {
         OMX_VIDEO_PARAM_PROFILELEVELTYPE param;
         InitOMXParams(&param);
         param.nPortIndex = isEncoder ? kPortIndexOutput : kPortIndexInput;
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 65348e5..13d80f5 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -137,7 +137,6 @@
         "libstagefright_omx",
         "libstagefright_omx_utils",
         "libstagefright_xmlparser",
-        "libdl",
         "libRScpp",
         "libhidlallocatorutils",
         "libhidlbase",
diff --git a/media/libstagefright/BufferImpl.cpp b/media/libstagefright/BufferImpl.cpp
index d2eee33..7b3fa02 100644
--- a/media/libstagefright/BufferImpl.cpp
+++ b/media/libstagefright/BufferImpl.cpp
@@ -70,6 +70,10 @@
     if (const_cast<Codec2Buffer *>(this)->base() == nullptr) {
         return false;
     }
+    if (!buffer) {
+        // Nothing to copy, so we can copy by doing nothing.
+        return true;
+    }
     if (buffer->data().type() != C2BufferData::LINEAR) {
         return false;
     }
@@ -89,7 +93,7 @@
 
 bool Codec2Buffer::copyLinear(const std::shared_ptr<C2Buffer> &buffer) {
     // We assume that all canCopyLinear() checks passed.
-    if (buffer->data().linearBlocks().size() == 0u) {
+    if (!buffer || buffer->data().linearBlocks().size() == 0u) {
         setRange(0, 0);
         return true;
     }
@@ -207,4 +211,445 @@
     return std::move(mBufferRef);
 }
 
+// GraphicView2MediaImageConverter
+
+namespace {
+
+class GraphicView2MediaImageConverter {
+public:
+    explicit GraphicView2MediaImageConverter(const C2GraphicView &view)
+        : mInitCheck(NO_INIT),
+          mView(view),
+          mWidth(view.width()),
+          mHeight(view.height()),
+          mAllocatedDepth(0),
+          mBackBufferSize(0),
+          mMediaImage(new ABuffer(sizeof(MediaImage2))) {
+        if (view.error() != C2_OK) {
+            ALOGD("Converter: view.error() = %d", view.error());
+            mInitCheck = BAD_VALUE;
+            return;
+        }
+        MediaImage2 *mediaImage = (MediaImage2 *)mMediaImage->base();
+        const C2PlanarLayout &layout = view.layout();
+        if (layout.numPlanes == 0) {
+            ALOGD("Converter: 0 planes");
+            mInitCheck = BAD_VALUE;
+            return;
+        }
+        mAllocatedDepth = layout.planes[0].allocatedDepth;
+        uint32_t bitDepth = layout.planes[0].bitDepth;
+
+        switch (layout.type) {
+            case C2PlanarLayout::TYPE_YUV:
+                mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_YUV; break;
+            case C2PlanarLayout::TYPE_YUVA:
+                mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_YUVA; break;
+            case C2PlanarLayout::TYPE_RGB:
+                mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_RGB; break;
+            case C2PlanarLayout::TYPE_RGBA:
+                mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_RGBA; break;
+            default:
+                mediaImage->mType = MediaImage2::MEDIA_IMAGE_TYPE_UNKNOWN; break;
+        }
+        mediaImage->mNumPlanes = layout.numPlanes;
+        mediaImage->mWidth = mWidth;
+        mediaImage->mHeight = mHeight;
+        mediaImage->mBitDepth = bitDepth;
+        mediaImage->mBitDepthAllocated = mAllocatedDepth;
+
+        uint32_t bufferSize = 0;
+        for (uint32_t i = 0; i < layout.numPlanes; ++i) {
+            const C2PlaneInfo &plane = layout.planes[i];
+            if (plane.rightShift != 0) {
+                ALOGV("rightShift value of %u unsupported", plane.rightShift);
+                mInitCheck = BAD_VALUE;
+                return;
+            }
+            if (plane.endianness != C2PlaneInfo::NATIVE) {
+                ALOGV("endianness value of %u unsupported", plane.endianness);
+                mInitCheck = BAD_VALUE;
+                return;
+            }
+            if (plane.allocatedDepth != mAllocatedDepth || plane.bitDepth != bitDepth) {
+                ALOGV("different allocatedDepth/bitDepth per plane unsupported");
+                mInitCheck = BAD_VALUE;
+                return;
+            }
+            bufferSize += mWidth * mHeight
+                    / plane.rowSampling / plane.colSampling * (plane.allocatedDepth / 8);
+        }
+
+        mBackBufferSize = bufferSize;
+        mInitCheck = OK;
+    }
+
+    status_t initCheck() const { return mInitCheck; }
+
+    uint32_t backBufferSize() const { return mBackBufferSize; }
+
+    /**
+     * Convert C2GraphicView to MediaImage2. Note that if not wrapped, the content
+     * is not copied over in this function --- the caller should use
+     * CopyGraphicView2MediaImage() function to do that explicitly.
+     *
+     * \param   view[in]          source C2GraphicView object.
+     * \param   alloc[in]         allocator function for ABuffer.
+     * \param   mediaImage[out]   destination MediaImage2 object.
+     * \param   buffer[out]       new buffer object.
+     * \param   wrapped[out]      whether we wrapped around existing map or
+     *                            allocated a new buffer
+     *
+     * \return  true              if conversion succeeds,
+     *          false             otherwise; all output params should be ignored.
+     */
+    sp<ABuffer> wrap() {
+        MediaImage2 *mediaImage = getMediaImage();
+        const C2PlanarLayout &layout = mView.layout();
+        if (layout.numPlanes == 1) {
+            const C2PlaneInfo &plane = layout.planes[0];
+            ssize_t offset = plane.minOffset(mWidth, mHeight);
+            mediaImage->mPlane[0].mOffset = -offset;
+            mediaImage->mPlane[0].mColInc = plane.colInc;
+            mediaImage->mPlane[0].mRowInc = plane.rowInc;
+            mediaImage->mPlane[0].mHorizSubsampling = plane.colSampling;
+            mediaImage->mPlane[0].mVertSubsampling = plane.rowSampling;
+            return new ABuffer(
+                    const_cast<uint8_t *>(mView.data()[0] + offset),
+                    plane.maxOffset(mWidth, mHeight) - offset + 1);
+        }
+        const uint8_t *minPtr = mView.data()[0];
+        const uint8_t *maxPtr = mView.data()[0];
+        int32_t planeSize = 0;
+        for (uint32_t i = 0; i < layout.numPlanes; ++i) {
+            const C2PlaneInfo &plane = layout.planes[i];
+            ssize_t minOffset = plane.minOffset(mWidth, mHeight);
+            ssize_t maxOffset = plane.maxOffset(mWidth, mHeight);
+            if (minPtr > mView.data()[i] + minOffset) {
+                minPtr = mView.data()[i] + minOffset;
+            }
+            if (maxPtr < mView.data()[i] + maxOffset) {
+                maxPtr = mView.data()[i] + maxOffset;
+            }
+            planeSize += std::abs(plane.rowInc) * mHeight
+                    / plane.rowSampling / plane.colSampling * (mAllocatedDepth / 8);
+        }
+
+        if ((maxPtr - minPtr + 1) <= planeSize) {
+            // FIXME: this is risky as reading/writing data out of bound results in
+            //        an undefined behavior.
+            for (uint32_t i = 0; i < layout.numPlanes; ++i) {
+                const C2PlaneInfo &plane = layout.planes[i];
+                mediaImage->mPlane[i].mOffset = mView.data()[i] - minPtr;
+                mediaImage->mPlane[i].mColInc = plane.colInc;
+                mediaImage->mPlane[i].mRowInc = plane.rowInc;
+                mediaImage->mPlane[i].mHorizSubsampling = plane.colSampling;
+                mediaImage->mPlane[i].mVertSubsampling = plane.rowSampling;
+            }
+            return new ABuffer(const_cast<uint8_t *>(minPtr), maxPtr - minPtr + 1);
+        }
+
+        return nullptr;
+    }
+
+    bool setBackBuffer(const sp<ABuffer> &backBuffer) {
+        if (backBuffer->capacity() < mBackBufferSize) {
+            return false;
+        }
+        backBuffer->setRange(0, mBackBufferSize);
+
+        const C2PlanarLayout &layout = mView.layout();
+        MediaImage2 *mediaImage = getMediaImage();
+        uint32_t offset = 0;
+        // TODO: keep interleaved planes together
+        for (uint32_t i = 0; i < layout.numPlanes; ++i) {
+            const C2PlaneInfo &plane = layout.planes[i];
+            mediaImage->mPlane[i].mOffset = offset;
+            mediaImage->mPlane[i].mColInc = mAllocatedDepth / 8;
+            mediaImage->mPlane[i].mRowInc =
+                mediaImage->mPlane[i].mColInc * mWidth / plane.colSampling;
+            mediaImage->mPlane[i].mHorizSubsampling = plane.colSampling;
+            mediaImage->mPlane[i].mVertSubsampling = plane.rowSampling;
+            offset += mediaImage->mPlane[i].mRowInc * mHeight / plane.rowSampling;
+        }
+        mBackBuffer = backBuffer;
+        return true;
+    }
+
+    /**
+     * Copy C2GraphicView to MediaImage2. This function assumes that |mediaImage| is
+     * an output from GraphicView2MediaImage(), so it mostly skips sanity check.
+     *
+     * \param   view[in]          source C2GraphicView object.
+     * \param   mediaImage[in]    destination MediaImage2 object.
+     * \param   buffer[out]       new buffer object.
+     */
+    void copy() {
+        // TODO: more efficient copying --- e.g. one row at a time, copying
+        //       interleaved planes together, etc.
+        const C2PlanarLayout &layout = mView.layout();
+        MediaImage2 *mediaImage = getMediaImage();
+        uint8_t *dst = mBackBuffer->base();
+        for (uint32_t i = 0; i < layout.numPlanes; ++i) {
+            const C2PlaneInfo &plane = layout.planes[i];
+            const uint8_t *src = mView.data()[i];
+            int32_t planeW = mWidth / plane.colSampling;
+            int32_t planeH = mHeight / plane.rowSampling;
+            for (int32_t row = 0; row < planeH; ++row) {
+                for(int32_t col = 0; col < planeW; ++col) {
+                    memcpy(dst, src, mAllocatedDepth / 8);
+                    dst += mediaImage->mPlane[i].mColInc;
+                    src += plane.colInc;
+                }
+                dst -= mediaImage->mPlane[i].mColInc * planeW;
+                dst += mediaImage->mPlane[i].mRowInc;
+                src -= plane.colInc * planeW;
+                src += plane.rowInc;
+            }
+        }
+    }
+
+    const sp<ABuffer> &imageData() const { return mMediaImage; }
+
+private:
+    status_t mInitCheck;
+
+    const C2GraphicView mView;
+    uint32_t mWidth;
+    uint32_t mHeight;
+    uint32_t mAllocatedDepth;
+    uint32_t mBackBufferSize;
+    sp<ABuffer> mMediaImage;
+    std::function<sp<ABuffer>(size_t)> mAlloc;
+
+    sp<ABuffer> mBackBuffer;
+
+    MediaImage2 *getMediaImage() {
+        return (MediaImage2 *)mMediaImage->base();
+    }
+};
+
+}  // namespace
+
+// GraphicBlockBuffer
+
+// static
+sp<GraphicBlockBuffer> GraphicBlockBuffer::Allocate(
+        const sp<AMessage> &format,
+        const std::shared_ptr<C2GraphicBlock> &block,
+        std::function<sp<ABuffer>(size_t)> alloc) {
+    C2GraphicView view(block->map().get());
+    if (view.error() != C2_OK) {
+        ALOGD("C2GraphicBlock::map failed: %d", view.error());
+        return nullptr;
+    }
+    GraphicView2MediaImageConverter converter(view);
+    if (converter.initCheck() != OK) {
+        ALOGD("Converter init failed: %d", converter.initCheck());
+        return nullptr;
+    }
+    bool wrapped = true;
+    sp<ABuffer> buffer = converter.wrap();
+    if (buffer == nullptr) {
+        buffer = alloc(converter.backBufferSize());
+        if (!converter.setBackBuffer(buffer)) {
+            ALOGD("Converter failed to set back buffer");
+            return nullptr;
+        }
+        wrapped = false;
+    }
+    return new GraphicBlockBuffer(
+            format,
+            buffer,
+            std::move(view),
+            block,
+            converter.imageData(),
+            wrapped);
+}
+
+GraphicBlockBuffer::GraphicBlockBuffer(
+        const sp<AMessage> &format,
+        const sp<ABuffer> &buffer,
+        C2GraphicView &&view,
+        const std::shared_ptr<C2GraphicBlock> &block,
+        const sp<ABuffer> &imageData,
+        bool wrapped)
+    : Codec2Buffer(format, buffer),
+      mView(view),
+      mBlock(block),
+      mImageData(imageData),
+      mWrapped(wrapped) {
+    meta()->setBuffer("image-data", imageData);
+}
+
+std::shared_ptr<C2Buffer> GraphicBlockBuffer::asC2Buffer() {
+    uint32_t width = mView.width();
+    uint32_t height = mView.height();
+    if (!mWrapped) {
+        MediaImage2 *mediaImage = imageData();
+        const C2PlanarLayout &layout = mView.layout();
+        for (uint32_t i = 0; i < mediaImage->mNumPlanes; ++i) {
+            const C2PlaneInfo &plane = layout.planes[i];
+            int32_t planeW = width / plane.colSampling;
+            int32_t planeH = height / plane.rowSampling;
+            const uint8_t *src = base() + mediaImage->mPlane[i].mOffset;
+            uint8_t *dst = mView.data()[i];
+            for (int32_t row = 0; row < planeH; ++row) {
+                for (int32_t col = 0; col < planeW; ++col) {
+                    memcpy(dst, src, mediaImage->mBitDepthAllocated / 8);
+                    src += mediaImage->mPlane[i].mColInc;
+                    dst += plane.colInc;
+                }
+                src -= mediaImage->mPlane[i].mColInc * planeW;
+                dst -= plane.colInc * planeW;
+                src += mediaImage->mPlane[i].mRowInc;
+                dst += plane.rowInc;
+            }
+        }
+    }
+    return C2Buffer::CreateGraphicBuffer(
+            mBlock->share(C2Rect(width, height), C2Fence()));
+}
+
+// ConstGraphicBlockBuffer
+
+// static
+sp<ConstGraphicBlockBuffer> ConstGraphicBlockBuffer::Allocate(
+        const sp<AMessage> &format,
+        const std::shared_ptr<C2Buffer> &buffer,
+        std::function<sp<ABuffer>(size_t)> alloc) {
+    if (!buffer
+            || buffer->data().type() != C2BufferData::GRAPHIC
+            || buffer->data().graphicBlocks().size() != 1u) {
+        ALOGD("C2Buffer precond fail");
+        return nullptr;
+    }
+    std::unique_ptr<const C2GraphicView> view(std::make_unique<const C2GraphicView>(
+            buffer->data().graphicBlocks()[0].map().get()));
+    std::unique_ptr<const C2GraphicView> holder;
+
+    GraphicView2MediaImageConverter converter(*view);
+    if (converter.initCheck() != OK) {
+        ALOGD("Converter init failed: %d", converter.initCheck());
+        return nullptr;
+    }
+    bool wrapped = true;
+    sp<ABuffer> aBuffer = converter.wrap();
+    if (aBuffer == nullptr) {
+        aBuffer = alloc(converter.backBufferSize());
+        if (!converter.setBackBuffer(aBuffer)) {
+            ALOGD("Converter failed to set back buffer");
+            return nullptr;
+        }
+        wrapped = false;
+        converter.copy();
+        // We don't need the view.
+        holder = std::move(view);
+    }
+    return new ConstGraphicBlockBuffer(
+            format,
+            aBuffer,
+            std::move(view),
+            buffer,
+            converter.imageData(),
+            wrapped);
+}
+
+// static
+sp<ConstGraphicBlockBuffer> ConstGraphicBlockBuffer::AllocateEmpty(
+        const sp<AMessage> &format,
+        std::function<sp<ABuffer>(size_t)> alloc) {
+    int32_t width, height;
+    if (!format->findInt32("width", &width)
+            || !format->findInt32("height", &height)) {
+        ALOGD("format had no width / height");
+        return nullptr;
+    }
+    sp<ABuffer> aBuffer(alloc(width * height * 4));
+    return new ConstGraphicBlockBuffer(
+            format,
+            aBuffer,
+            nullptr,
+            nullptr,
+            nullptr,
+            false);
+}
+
+ConstGraphicBlockBuffer::ConstGraphicBlockBuffer(
+        const sp<AMessage> &format,
+        const sp<ABuffer> &aBuffer,
+        std::unique_ptr<const C2GraphicView> &&view,
+        const std::shared_ptr<C2Buffer> &buffer,
+        const sp<ABuffer> &imageData,
+        bool wrapped)
+    : Codec2Buffer(format, aBuffer),
+      mView(std::move(view)),
+      mBufferRef(buffer),
+      mWrapped(wrapped) {
+    if (imageData != nullptr) {
+        meta()->setBuffer("image-data", imageData);
+    }
+}
+
+std::shared_ptr<C2Buffer> ConstGraphicBlockBuffer::asC2Buffer() {
+    mView.reset();
+    return std::move(mBufferRef);
+}
+
+bool ConstGraphicBlockBuffer::canCopy(const std::shared_ptr<C2Buffer> &buffer) const {
+    if (mWrapped || mBufferRef) {
+        ALOGD("ConstGraphicBlockBuffer::canCopy: %swrapped ; buffer ref %s",
+                mWrapped ? "" : "not ", mBufferRef ? "exists" : "doesn't exist");
+        return false;
+    }
+    if (!buffer) {
+        // Nothing to copy, so we can copy by doing nothing.
+        return true;
+    }
+    if (buffer->data().type() != C2BufferData::GRAPHIC) {
+        ALOGD("ConstGraphicBlockBuffer::canCopy: buffer precondition unsatisfied");
+        return false;
+    }
+    if (buffer->data().graphicBlocks().size() == 0) {
+        return true;
+    } else if (buffer->data().graphicBlocks().size() != 1u) {
+        ALOGD("ConstGraphicBlockBuffer::canCopy: too many blocks");
+        return false;
+    }
+    GraphicView2MediaImageConverter converter(
+            buffer->data().graphicBlocks()[0].map().get());
+    if (converter.initCheck() != OK) {
+        ALOGD("ConstGraphicBlockBuffer::canCopy: converter init failed: %d", converter.initCheck());
+        return false;
+    }
+    if (converter.backBufferSize() > capacity()) {
+        ALOGD("ConstGraphicBlockBuffer::canCopy: insufficient capacity: req %u has %zu",
+                converter.backBufferSize(), capacity());
+        return false;
+    }
+    return true;
+}
+
+bool ConstGraphicBlockBuffer::copy(const std::shared_ptr<C2Buffer> &buffer) {
+    if (!buffer || buffer->data().graphicBlocks().size() == 0) {
+        setRange(0, 0);
+        return true;
+    }
+    GraphicView2MediaImageConverter converter(
+            buffer->data().graphicBlocks()[0].map().get());
+    if (converter.initCheck() != OK) {
+        ALOGD("ConstGraphicBlockBuffer::copy: converter init failed: %d", converter.initCheck());
+        return false;
+    }
+    sp<ABuffer> aBuffer = new ABuffer(base(), capacity());
+    if (!converter.setBackBuffer(aBuffer)) {
+        ALOGD("ConstGraphicBlockBuffer::copy: set back buffer failed");
+        return false;
+    }
+    converter.copy();
+    meta()->setBuffer("image-data", converter.imageData());
+    mBufferRef = buffer;
+    return true;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/CCodec.cpp b/media/libstagefright/CCodec.cpp
index f62ee41..0bdd808 100644
--- a/media/libstagefright/CCodec.cpp
+++ b/media/libstagefright/CCodec.cpp
@@ -238,14 +238,25 @@
     return mChannel;
 }
 
+status_t CCodec::tryAndReportOnError(std::function<status_t()> job) {
+    status_t err = job();
+    if (err != C2_OK) {
+        mCallback->onError(err, ACTION_CODE_FATAL);
+    }
+    return err;
+}
+
 void CCodec::initiateAllocateComponent(const sp<AMessage> &msg) {
-    {
+    auto setAllocating = [this] {
         Mutexed<State>::Locked state(mState);
         if (state->get() != RELEASED) {
-            mCallback->onError(INVALID_OPERATION, ACTION_CODE_FATAL);
-            return;
+            return INVALID_OPERATION;
         }
         state->set(ALLOCATING);
+        return OK;
+    };
+    if (tryAndReportOnError(setAllocating) != OK) {
+        return;
     }
 
     AString componentName;
@@ -259,7 +270,7 @@
 }
 
 void CCodec::allocate(const AString &componentName) {
-    // TODO: use C2ComponentStore to create component
+    ALOGV("allocate(%s)", componentName.c_str());
     mListener.reset(new CCodecListener(this));
 
     std::shared_ptr<C2Component> comp;
@@ -282,29 +293,30 @@
     }
     ALOGV("Success Create component: %s", componentName.c_str());
     comp->setListener_vb(mListener, C2_MAY_BLOCK);
-    {
+    mChannel->setComponent(comp);
+    auto setAllocated = [this, comp] {
         Mutexed<State>::Locked state(mState);
         if (state->get() != ALLOCATING) {
             state->set(RELEASED);
-            state.unlock();
-            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
-            state.lock();
-            return;
+            return UNKNOWN_ERROR;
         }
         state->set(ALLOCATED);
         state->comp = comp;
+        return OK;
+    };
+    if (tryAndReportOnError(setAllocated) != OK) {
+        return;
     }
-    mChannel->setComponent(comp);
     mCallback->onComponentAllocated(comp->intf()->getName().c_str());
 }
 
 void CCodec::initiateConfigureComponent(const sp<AMessage> &format) {
-    {
+    auto checkAllocated = [this] {
         Mutexed<State>::Locked state(mState);
-        if (state->get() != ALLOCATED) {
-            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
-            return;
-        }
+        return (state->get() != ALLOCATED) ? UNKNOWN_ERROR : OK;
+    };
+    if (tryAndReportOnError(checkAllocated) != OK) {
+        return;
     }
 
     sp<AMessage> msg(new AMessage(kWhatConfigure, this));
@@ -314,21 +326,22 @@
 
 void CCodec::configure(const sp<AMessage> &msg) {
     std::shared_ptr<C2ComponentInterface> intf;
-    {
+    auto checkAllocated = [this, &intf] {
         Mutexed<State>::Locked state(mState);
         if (state->get() != ALLOCATED) {
             state->set(RELEASED);
-            state.unlock();
-            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
-            state.lock();
-            return;
+            return UNKNOWN_ERROR;
         }
         intf = state->comp->intf();
+        return OK;
+    };
+    if (tryAndReportOnError(checkAllocated) != OK) {
+        return;
     }
 
     sp<AMessage> inputFormat(new AMessage);
     sp<AMessage> outputFormat(new AMessage);
-    if (status_t err = [=] {
+    auto doConfig = [=] {
         AString mime;
         if (!msg->findString("mime", &mime)) {
             return BAD_VALUE;
@@ -339,6 +352,11 @@
             encoder = false;
         }
 
+        // TODO: read from intf()
+        if ((!encoder) != (intf->getName().find("encoder") == std::string::npos)) {
+            return UNKNOWN_ERROR;
+        }
+
         sp<RefBase> obj;
         if (msg->findObject("native-window", &obj)) {
             sp<Surface> surface = static_cast<Surface *>(obj.get());
@@ -386,14 +404,22 @@
             if (audio) {
                 outputFormat->setInt32("channel-count", 2);
                 outputFormat->setInt32("sample-rate", 44100);
+            } else {
+                int32_t tmp;
+                if (msg->findInt32("width", &tmp)) {
+                    outputFormat->setInt32("width", tmp);
+                }
+                if (msg->findInt32("height", &tmp)) {
+                    outputFormat->setInt32("height", tmp);
+                }
             }
         }
 
         // TODO
 
         return OK;
-    }() != OK) {
-        mCallback->onError(err, ACTION_CODE_FATAL);
+    };
+    if (tryAndReportOnError(doConfig) != OK) {
         return;
     }
 
@@ -406,6 +432,22 @@
 }
 
 void CCodec::initiateCreateInputSurface() {
+    status_t err = [this] {
+        Mutexed<State>::Locked state(mState);
+        if (state->get() != ALLOCATED) {
+            return UNKNOWN_ERROR;
+        }
+        // TODO: read it from intf() properly.
+        if (state->comp->intf()->getName().find("encoder") == std::string::npos) {
+            return INVALID_OPERATION;
+        }
+        return OK;
+    }();
+    if (err != OK) {
+        mCallback->onInputSurfaceCreationFailed(err);
+        return;
+    }
+
     (new AMessage(kWhatCreateInputSurface, this))->post();
 }
 
@@ -477,15 +519,16 @@
 }
 
 void CCodec::initiateStart() {
-    {
+    auto setStarting = [this] {
         Mutexed<State>::Locked state(mState);
         if (state->get() != ALLOCATED) {
-            state.unlock();
-            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
-            state.lock();
-            return;
+            return UNKNOWN_ERROR;
         }
         state->set(STARTING);
+        return OK;
+    };
+    if (tryAndReportOnError(setStarting) != OK) {
+        return;
     }
 
     (new AMessage(kWhatStart, this))->post();
@@ -493,16 +536,18 @@
 
 void CCodec::start() {
     std::shared_ptr<C2Component> comp;
-    {
+    auto checkStarting = [this, &comp] {
         Mutexed<State>::Locked state(mState);
         if (state->get() != STARTING) {
-            state.unlock();
-            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
-            state.lock();
-            return;
+            return UNKNOWN_ERROR;
         }
         comp = state->comp;
+        return OK;
+    };
+    if (tryAndReportOnError(checkStarting) != OK) {
+        return;
     }
+
     c2_status_t err = comp->start();
     if (err != C2_OK) {
         // TODO: convert err into status_t
@@ -516,17 +561,22 @@
         inputFormat = formats->inputFormat;
         outputFormat = formats->outputFormat;
     }
-    mChannel->start(inputFormat, outputFormat);
+    status_t err2 = mChannel->start(inputFormat, outputFormat);
+    if (err2 != OK) {
+        mCallback->onError(err2, ACTION_CODE_FATAL);
+        return;
+    }
 
-    {
+    auto setRunning = [this] {
         Mutexed<State>::Locked state(mState);
         if (state->get() != STARTING) {
-            state.unlock();
-            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
-            state.lock();
-            return;
+            return UNKNOWN_ERROR;
         }
         state->set(RUNNING);
+        return OK;
+    };
+    if (tryAndReportOnError(setRunning) != OK) {
+        return;
     }
     mCallback->onStartCompleted();
 }
@@ -652,13 +702,26 @@
 }
 
 void CCodec::signalFlush() {
-    {
+    status_t err = [this] {
         Mutexed<State>::Locked state(mState);
+        if (state->get() == FLUSHED) {
+            return ALREADY_EXISTS;
+        }
         if (state->get() != RUNNING) {
-            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
-            return;
+            return UNKNOWN_ERROR;
         }
         state->set(FLUSHING);
+        return OK;
+    }();
+    switch (err) {
+        case ALREADY_EXISTS:
+            mCallback->onFlushCompleted();
+            return;
+        case OK:
+            break;
+        default:
+            mCallback->onError(err, ACTION_CODE_FATAL);
+            return;
     }
 
     (new AMessage(kWhatFlush, this))->post();
@@ -666,15 +729,16 @@
 
 void CCodec::flush() {
     std::shared_ptr<C2Component> comp;
-    {
+    auto checkFlushing = [this, &comp] {
         Mutexed<State>::Locked state(mState);
         if (state->get() != FLUSHING) {
-            state.unlock();
-            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
-            state.lock();
-            return;
+            return UNKNOWN_ERROR;
         }
         comp = state->comp;
+        return OK;
+    };
+    if (tryAndReportOnError(checkFlushing) != OK) {
+        return;
     }
 
     mChannel->stop();
@@ -696,18 +760,19 @@
 }
 
 void CCodec::signalResume() {
-    {
+    auto setResuming = [this] {
         Mutexed<State>::Locked state(mState);
         if (state->get() != FLUSHED) {
-            state.unlock();
-            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
-            state.lock();
-            return;
+            return UNKNOWN_ERROR;
         }
         state->set(RESUMING);
+        return OK;
+    };
+    if (tryAndReportOnError(setResuming) != OK) {
+        return;
     }
 
-    mChannel->start(nullptr, nullptr);
+    (void)mChannel->start(nullptr, nullptr);
 
     {
         Mutexed<State>::Locked state(mState);
@@ -727,6 +792,8 @@
 }
 
 void CCodec::signalEndOfInputStream() {
+    // TODO
+    mCallback->onSignaledInputEOS(INVALID_OPERATION);
 }
 
 void CCodec::signalRequestIDRFrame() {
@@ -735,9 +802,7 @@
 
 void CCodec::onWorkDone(std::list<std::unique_ptr<C2Work>> &workItems) {
     Mutexed<std::list<std::unique_ptr<C2Work>>>::Locked queue(mWorkDoneQueue);
-    for (std::unique_ptr<C2Work> &item : workItems) {
-        queue->push_back(std::move(item));
-    }
+    queue->splice(queue->end(), workItems);
     (new AMessage(kWhatWorkDone, this))->post();
 }
 
@@ -834,8 +899,8 @@
     }
 
     ALOGW("previous call to %s exceeded timeout", name.c_str());
-    mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
     initiateRelease(false);
+    mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
 }
 
 }  // namespace android
diff --git a/media/libstagefright/CCodecBufferChannel.cpp b/media/libstagefright/CCodecBufferChannel.cpp
index 5ce49d9..84962a9 100644
--- a/media/libstagefright/CCodecBufferChannel.cpp
+++ b/media/libstagefright/CCodecBufferChannel.cpp
@@ -60,7 +60,10 @@
     /**
      * Set format for MediaCodec-facing buffers.
      */
-    void setFormat(const sp<AMessage> &format) { mFormat = format; }
+    void setFormat(const sp<AMessage> &format) {
+        CHECK(format != nullptr);
+        mFormat = format;
+    }
 
     /**
      * Returns true if the buffers are operating under array mode.
@@ -179,21 +182,158 @@
 const static size_t kMinBufferArraySize = 16;
 const static size_t kLinearBufferSize = 524288;
 
-sp<LinearBlockBuffer> allocateLinearBuffer(
+/**
+ * Simple local buffer pool backed by std::vector.
+ */
+class LocalBufferPool : public std::enable_shared_from_this<LocalBufferPool> {
+public:
+    /**
+     * Create a new LocalBufferPool object.
+     *
+     * \param poolCapacity  max total size of buffers managed by this pool.
+     *
+     * \return  a newly created pool object.
+     */
+    static std::shared_ptr<LocalBufferPool> Create(size_t poolCapacity) {
+        return std::shared_ptr<LocalBufferPool>(new LocalBufferPool(poolCapacity));
+    }
+
+    /**
+     * Return an ABuffer object whose size is at least |capacity|.
+     *
+     * \param   capacity  requested capacity
+     * \return  nullptr if the pool capacity is reached
+     *          an ABuffer object otherwise.
+     */
+    sp<ABuffer> newBuffer(size_t capacity) {
+        Mutex::Autolock lock(mMutex);
+        auto it = std::find_if(
+                mPool.begin(), mPool.end(),
+                [capacity](const std::vector<uint8_t> &vec) {
+                    return vec.capacity() >= capacity;
+                });
+        if (it != mPool.end()) {
+            sp<ABuffer> buffer = new VectorBuffer(std::move(*it), shared_from_this());
+            mPool.erase(it);
+            return buffer;
+        }
+        if (mUsedSize + capacity > mPoolCapacity) {
+            while (!mPool.empty()) {
+                mUsedSize -= mPool.back().capacity();
+                mPool.pop_back();
+            }
+            if (mUsedSize + capacity > mPoolCapacity) {
+                ALOGD("mUsedSize = %zu, capacity = %zu, mPoolCapacity = %zu",
+                        mUsedSize, capacity, mPoolCapacity);
+                return nullptr;
+            }
+        }
+        std::vector<uint8_t> vec(capacity);
+        mUsedSize += vec.capacity();
+        return new VectorBuffer(std::move(vec), shared_from_this());
+    }
+
+private:
+    /**
+     * ABuffer backed by std::vector.
+     */
+    class VectorBuffer : public ::android::ABuffer {
+    public:
+        /**
+         * Construct a VectorBuffer by taking the ownership of supplied vector.
+         *
+         * \param vec   backing vector of the buffer. this object takes
+         *              ownership at construction.
+         * \param pool  a LocalBufferPool object to return the vector at
+         *              destruction.
+         */
+        VectorBuffer(std::vector<uint8_t> &&vec, const std::shared_ptr<LocalBufferPool> &pool)
+            : ABuffer(vec.data(), vec.capacity()),
+              mVec(std::move(vec)),
+              mPool(pool) {
+        }
+
+        ~VectorBuffer() override {
+            std::shared_ptr<LocalBufferPool> pool = mPool.lock();
+            if (pool) {
+                // If pool is alive, return the vector back to the pool so that
+                // it can be recycled.
+                pool->returnVector(std::move(mVec));
+            }
+        }
+
+    private:
+        std::vector<uint8_t> mVec;
+        std::weak_ptr<LocalBufferPool> mPool;
+    };
+
+    Mutex mMutex;
+    size_t mPoolCapacity;
+    size_t mUsedSize;
+    std::list<std::vector<uint8_t>> mPool;
+
+    /**
+     * Private constructor to prevent constructing non-managed LocalBufferPool.
+     */
+    explicit LocalBufferPool(size_t poolCapacity)
+        : mPoolCapacity(poolCapacity), mUsedSize(0) {
+    }
+
+    /**
+     * Take back the ownership of vec from the destructed VectorBuffer and put
+     * it in front of the pool.
+     */
+    void returnVector(std::vector<uint8_t> &&vec) {
+        Mutex::Autolock lock(mMutex);
+        mPool.push_front(std::move(vec));
+    }
+
+    DISALLOW_EVIL_CONSTRUCTORS(LocalBufferPool);
+};
+
+sp<LinearBlockBuffer> AllocateLinearBuffer(
         const std::shared_ptr<C2BlockPool> &pool,
         const sp<AMessage> &format,
         size_t size,
         const C2MemoryUsage &usage) {
     std::shared_ptr<C2LinearBlock> block;
 
-    status_t err = pool->fetchLinearBlock(size, usage, &block);
-    if (err != OK) {
+    c2_status_t err = pool->fetchLinearBlock(size, usage, &block);
+    if (err != C2_OK) {
         return nullptr;
     }
 
     return LinearBlockBuffer::Allocate(format, block);
 }
 
+sp<GraphicBlockBuffer> AllocateGraphicBuffer(
+        const std::shared_ptr<C2BlockPool> &pool,
+        const sp<AMessage> &format,
+        uint32_t pixelFormat,
+        const C2MemoryUsage &usage,
+        const std::shared_ptr<LocalBufferPool> &localBufferPool) {
+    int32_t width, height;
+    if (!format->findInt32("width", &width) || !format->findInt32("height", &height)) {
+        ALOGD("format lacks width or height");
+        return nullptr;
+    }
+
+    std::shared_ptr<C2GraphicBlock> block;
+    c2_status_t err = pool->fetchGraphicBlock(
+            width, height, pixelFormat, usage, &block);
+    if (err != C2_OK) {
+        ALOGD("fetch graphic block failed: %d", err);
+        return nullptr;
+    }
+
+    return GraphicBlockBuffer::Allocate(
+            format,
+            block,
+            [localBufferPool](size_t capacity) {
+                return localBufferPool->newBuffer(capacity);
+            });
+}
+
 class BuffersArrayImpl;
 
 /**
@@ -383,6 +523,7 @@
 class InputBuffersArray : public CCodecBufferChannel::InputBuffers {
 public:
     InputBuffersArray() = default;
+    ~InputBuffersArray() override = default;
 
     void initialize(
             const FlexBuffersImpl &impl,
@@ -432,7 +573,8 @@
         // TODO: proper max input size
         // TODO: read usage from intf
         C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
-        sp<LinearBlockBuffer> newBuffer = allocateLinearBuffer(mPool, mFormat, kLinearBufferSize, usage);
+        sp<LinearBlockBuffer> newBuffer = AllocateLinearBuffer(
+                mPool, mFormat, kLinearBufferSize, usage);
         if (newBuffer == nullptr) {
             return false;
         }
@@ -458,7 +600,7 @@
                 kMinBufferArraySize,
                 [pool = mPool, format = mFormat] () -> sp<Codec2Buffer> {
                     C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
-                    return allocateLinearBuffer(pool, format, kLinearBufferSize, usage);
+                    return AllocateLinearBuffer(pool, format, kLinearBufferSize, usage);
                 });
         return std::move(array);
     }
@@ -469,7 +611,54 @@
 
 class GraphicInputBuffers : public CCodecBufferChannel::InputBuffers {
 public:
-    GraphicInputBuffers() = default;
+    GraphicInputBuffers() : mLocalBufferPool(LocalBufferPool::Create(1920 * 1080 * 16)) {}
+    ~GraphicInputBuffers() override = default;
+
+    bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
+        // TODO: proper max input size
+        // TODO: read usage from intf
+        C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+        sp<GraphicBlockBuffer> newBuffer = AllocateGraphicBuffer(
+                mPool, mFormat, HAL_PIXEL_FORMAT_YV12, usage, mLocalBufferPool);
+        if (newBuffer == nullptr) {
+            return false;
+        }
+        *index = mImpl.assignSlot(newBuffer);
+        *buffer = newBuffer;
+        return true;
+    }
+
+    std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
+        return mImpl.releaseSlot(buffer);
+    }
+
+    void flush() override {
+        // This is no-op by default unless we're in array mode where we need to keep
+        // track of the flushed work.
+    }
+
+    std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode() final {
+        std::unique_ptr<InputBuffersArray> array(new InputBuffersArray);
+        array->setFormat(mFormat);
+        array->initialize(
+                mImpl,
+                kMinBufferArraySize,
+                [pool = mPool, format = mFormat, lbp = mLocalBufferPool]() -> sp<Codec2Buffer> {
+                    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+                    return AllocateGraphicBuffer(
+                            pool, format, HAL_PIXEL_FORMAT_YV12, usage, lbp);
+                });
+        return std::move(array);
+    }
+
+private:
+    FlexBuffersImpl mImpl;
+    std::shared_ptr<LocalBufferPool> mLocalBufferPool;
+};
+
+class DummyInputBuffers : public CCodecBufferChannel::InputBuffers {
+public:
+    DummyInputBuffers() = default;
 
     bool requestNewBuffer(size_t *, sp<MediaCodecBuffer> *) override {
         return false;
@@ -495,7 +684,8 @@
 
 class OutputBuffersArray : public CCodecBufferChannel::OutputBuffers {
 public:
-    using CCodecBufferChannel::OutputBuffers::OutputBuffers;
+    OutputBuffersArray() = default;
+    ~OutputBuffersArray() override = default;
 
     void initialize(
             const FlexBuffersImpl &impl,
@@ -522,12 +712,14 @@
                     return clientBuffer->canCopy(buffer);
                 });
         if (err != OK) {
-            return false;
-        }
-        if (!c2Buffer->copy(buffer)) {
+            ALOGD("grabBuffer failed: %d", err);
             return false;
         }
         c2Buffer->setFormat(mFormat);
+        if (!c2Buffer->copy(buffer)) {
+            ALOGD("copy buffer failed");
+            return false;
+        }
         *clientBuffer = c2Buffer;
         return true;
     }
@@ -585,6 +777,7 @@
             size_t *index,
             sp<MediaCodecBuffer> *clientBuffer) override {
         sp<Codec2Buffer> newBuffer = wrap(buffer);
+        newBuffer->setFormat(mFormat);
         *index = mImpl.assignSlot(newBuffer);
         *clientBuffer = newBuffer;
         return true;
@@ -627,8 +820,21 @@
         return std::move(array);
     }
 
+    /**
+     * Return an appropriate Codec2Buffer object for the type of buffers.
+     *
+     * \param buffer  C2Buffer object to wrap.
+     *
+     * \return  appropriate Codec2Buffer object to wrap |buffer|.
+     */
     virtual sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) = 0;
 
+    /**
+     * Return an appropriate Codec2Buffer object for the type of buffers, to be
+     * used as an empty array buffer.
+     *
+     * \return  appropriate Codec2Buffer object which can copy() from C2Buffers.
+     */
     virtual sp<Codec2Buffer> allocateArrayBuffer() = 0;
 
 private:
@@ -673,6 +879,34 @@
     }
 };
 
+class RawGraphicOutputBuffers : public FlexOutputBuffers {
+public:
+    RawGraphicOutputBuffers()
+        : mLocalBufferPool(LocalBufferPool::Create(1920 * 1080 * 16)) {
+    }
+    ~RawGraphicOutputBuffers() override = default;
+
+    sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override {
+        return ConstGraphicBlockBuffer::Allocate(
+                mFormat,
+                buffer,
+                [lbp = mLocalBufferPool](size_t capacity) {
+                    return lbp->newBuffer(capacity);
+                });
+    }
+
+    sp<Codec2Buffer> allocateArrayBuffer() override {
+        return ConstGraphicBlockBuffer::AllocateEmpty(
+                mFormat,
+                [lbp = mLocalBufferPool](size_t capacity) {
+                    return lbp->newBuffer(capacity);
+                });
+    }
+
+private:
+    std::shared_ptr<LocalBufferPool> mLocalBufferPool;
+};
+
 }  // namespace
 
 CCodecBufferChannel::QueueGuard::QueueGuard(
@@ -735,55 +969,6 @@
 
 void CCodecBufferChannel::setComponent(const std::shared_ptr<C2Component> &component) {
     mComponent = component;
-
-    C2StreamFormatConfig::input inputFormat(0u);
-    C2StreamFormatConfig::output outputFormat(0u);
-    c2_status_t err = mComponent->intf()->query_vb(
-            { &inputFormat, &outputFormat },
-            {},
-            C2_DONT_BLOCK,
-            nullptr);
-    if (err != C2_OK) {
-        // TODO: error
-        return;
-    }
-
-    {
-        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-
-        bool graphic = (inputFormat.value == C2FormatVideo);
-        if (graphic) {
-            buffers->reset(new GraphicInputBuffers);
-        } else {
-            buffers->reset(new LinearInputBuffers);
-        }
-
-        ALOGV("graphic = %s", graphic ? "true" : "false");
-        std::shared_ptr<C2BlockPool> pool;
-        if (graphic) {
-            err = GetCodec2BlockPool(C2BlockPool::BASIC_GRAPHIC, component, &pool);
-        } else {
-            err = CreateCodec2BlockPool(C2PlatformAllocatorStore::ION,
-                                        component, &pool);
-        }
-
-        if (err == C2_OK) {
-            (*buffers)->setPool(pool);
-        } else {
-            // TODO: error
-        }
-    }
-
-    {
-        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-
-        bool graphic = (outputFormat.value == C2FormatVideo);
-        if (graphic) {
-            buffers->reset(new GraphicOutputBuffers);
-        } else {
-            buffers->reset(new LinearOutputBuffers);
-        }
-    }
 }
 
 status_t CCodecBufferChannel::setInputSurface(
@@ -812,6 +997,7 @@
     if (buffer->meta()->findInt32("csd", &tmp) && tmp) {
         flags |= C2FrameData::FLAG_CODEC_CONFIG;
     }
+    ALOGV("queueInputBuffer: buffer->size() = %zu", buffer->size());
     std::unique_ptr<C2Work> work(new C2Work);
     work->input.flags = (C2FrameData::flags_t)flags;
     work->input.ordinal.timestamp = timeUs;
@@ -893,7 +1079,7 @@
             GraphicBuffer::CLONE_HANDLE,
             blocks.front().width(),
             blocks.front().height(),
-            HAL_PIXEL_FORMAT_YV12,
+            HAL_PIXEL_FORMAT_YCbCr_420_888,
             // TODO
             1,
             (uint64_t)GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
@@ -968,13 +1154,68 @@
     (*buffers)->getArray(array);
 }
 
-void CCodecBufferChannel::start(const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat) {
+status_t CCodecBufferChannel::start(
+        const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat) {
+    C2StreamFormatConfig::input iStreamFormat(0u);
+    C2StreamFormatConfig::output oStreamFormat(0u);
+    c2_status_t err = mComponent->intf()->query_vb(
+            { &iStreamFormat, &oStreamFormat },
+            {},
+            C2_DONT_BLOCK,
+            nullptr);
+    if (err != C2_OK) {
+        return UNKNOWN_ERROR;
+    }
+
     if (inputFormat != nullptr) {
         Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
+
+        bool graphic = (iStreamFormat.value == C2FormatVideo);
+        if (graphic) {
+            if (mInputSurface) {
+                buffers->reset(new DummyInputBuffers);
+            } else {
+                buffers->reset(new GraphicInputBuffers);
+            }
+        } else {
+            buffers->reset(new LinearInputBuffers);
+        }
         (*buffers)->setFormat(inputFormat);
+
+        ALOGV("graphic = %s", graphic ? "true" : "false");
+        std::shared_ptr<C2BlockPool> pool;
+        if (graphic) {
+            err = GetCodec2BlockPool(C2BlockPool::BASIC_GRAPHIC, mComponent, &pool);
+        } else {
+            err = CreateCodec2BlockPool(C2PlatformAllocatorStore::ION,
+                                        mComponent, &pool);
+        }
+        if (err == C2_OK) {
+            (*buffers)->setPool(pool);
+        } else {
+            // TODO: error
+        }
     }
+
     if (outputFormat != nullptr) {
+        bool hasOutputSurface = false;
+        {
+            Mutexed<sp<Surface>>::Locked surface(mSurface);
+            hasOutputSurface = (*surface != nullptr);
+        }
+
         Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
+
+        bool graphic = (oStreamFormat.value == C2FormatVideo);
+        if (graphic) {
+            if (hasOutputSurface) {
+                buffers->reset(new GraphicOutputBuffers);
+            } else {
+                buffers->reset(new RawGraphicOutputBuffers);
+            }
+        } else {
+            buffers->reset(new LinearOutputBuffers);
+        }
         (*buffers)->setFormat(outputFormat);
     }
 
@@ -989,9 +1230,7 @@
                 if (!(*buffers)->requestNewBuffer(&index, &buffer)) {
                     if (i == 0) {
                         ALOGE("start: cannot allocate memory at all");
-                        buffers.unlock();
-                        mOnError(NO_MEMORY, ACTION_CODE_FATAL);
-                        buffers.lock();
+                        return NO_MEMORY;
                     } else {
                         ALOGV("start: cannot allocate memory, only %zu buffers allocated", i);
                     }
@@ -1003,6 +1242,7 @@
     } else {
         (void)mInputSurface->connect(mComponent);
     }
+    return OK;
 }
 
 void CCodecBufferChannel::stop() {
@@ -1010,6 +1250,7 @@
     mFirstValidFrameIndex = mFrameIndex.load();
     if (mInputSurface != nullptr) {
         mInputSurface->disconnect();
+        mInputSurface.reset();
     }
 }
 
@@ -1025,8 +1266,13 @@
 }
 
 void CCodecBufferChannel::onWorkDone(const std::unique_ptr<C2Work> &work) {
-    if (work->result != OK) {
-        ALOGE("work failed to complete: %d", work->result);
+    if (work->result != C2_OK) {
+        if (work->result == C2_NOT_FOUND) {
+            // TODO: Define what flushed work's result is.
+            ALOGD("flushed work; ignored.");
+            return;
+        }
+        ALOGD("work failed to complete: %d", work->result);
         mOnError(work->result, ACTION_CODE_FATAL);
         return;
     }
diff --git a/media/libstagefright/Codec2InfoBuilder.cpp b/media/libstagefright/Codec2InfoBuilder.cpp
index 7ce2ff1..78c4e38 100644
--- a/media/libstagefright/Codec2InfoBuilder.cpp
+++ b/media/libstagefright/Codec2InfoBuilder.cpp
@@ -31,6 +31,63 @@
 
 using ConstTraitsPtr = std::shared_ptr<const C2Component::Traits>;
 
+struct ProfileLevel {
+    uint32_t profile;
+    uint32_t level;
+};
+static const ProfileLevel kAvcProfileLevels[] = {
+    { 0x01, 0x0001 },  // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1  },
+    { 0x01, 0x0002 },  // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1b },
+    { 0x01, 0x0004 },  // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel11 },
+    { 0x01, 0x0008 },  // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel12 },
+    { 0x01, 0x0010 },  // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel13 },
+    { 0x01, 0x0020 },  // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel2  },
+    { 0x01, 0x0040 },  // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel21 },
+    { 0x01, 0x0080 },  // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel22 },
+    { 0x01, 0x0100 },  // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel3  },
+    { 0x01, 0x0200 },  // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel31 },
+    { 0x01, 0x0400 },  // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel32 },
+    { 0x01, 0x0800 },  // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel4  },
+    { 0x01, 0x1000 },  // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel41 },
+    { 0x01, 0x2000 },  // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel42 },
+    { 0x01, 0x4000 },  // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel5  },
+    { 0x01, 0x8000 },  // { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel51 },
+
+    { 0x02, 0x0001 },  // { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel1  },
+    { 0x02, 0x0002 },  // { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel1b },
+    { 0x02, 0x0004 },  // { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel11 },
+    { 0x02, 0x0008 },  // { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel12 },
+    { 0x02, 0x0010 },  // { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel13 },
+    { 0x02, 0x0020 },  // { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel2  },
+    { 0x02, 0x0040 },  // { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel21 },
+    { 0x02, 0x0080 },  // { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel22 },
+    { 0x02, 0x0100 },  // { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel3  },
+    { 0x02, 0x0200 },  // { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel31 },
+    { 0x02, 0x0400 },  // { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel32 },
+    { 0x02, 0x0800 },  // { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel4  },
+    { 0x02, 0x1000 },  // { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel41 },
+    { 0x02, 0x2000 },  // { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel42 },
+    { 0x02, 0x4000 },  // { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel5  },
+    { 0x02, 0x8000 },  // { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel51 },
+
+    { 0x04, 0x0001 },  // { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel1  },
+    { 0x04, 0x0002 },  // { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel1b },
+    { 0x04, 0x0004 },  // { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel11 },
+    { 0x04, 0x0008 },  // { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel12 },
+    { 0x04, 0x0010 },  // { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel13 },
+    { 0x04, 0x0020 },  // { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel2  },
+    { 0x04, 0x0040 },  // { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel21 },
+    { 0x04, 0x0080 },  // { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel22 },
+    { 0x04, 0x0100 },  // { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel3  },
+    { 0x04, 0x0200 },  // { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel31 },
+    { 0x04, 0x0400 },  // { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel32 },
+    { 0x04, 0x0800 },  // { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel4  },
+    { 0x04, 0x1000 },  // { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel41 },
+    { 0x04, 0x2000 },  // { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel42 },
+    { 0x04, 0x4000 },  // { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel5  },
+    { 0x04, 0x8000 },  // { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel51 },
+};
+
 status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) {
     // Obtain C2ComponentStore
     std::shared_ptr<C2ComponentStore> store = GetCodec2PlatformComponentStore();
@@ -86,6 +143,12 @@
                     caps->addDetail(key.c_str(), value.c_str());
                 }
             }
+            // TODO: get this from intf(), and apply to other codecs as well.
+            if (mediaType.find("video/avc") != std::string::npos && !encoder) {
+                for (const auto& pl : kAvcProfileLevels) {
+                    caps->addProfileLevel(pl.profile, pl.level);
+                }
+            }
             // TODO: get this from intf().
             if (mediaType.find("video") != std::string::npos && !encoder) {
                 caps->addColorFormat(0x7F420888);  // COLOR_FormatYUV420Flexible
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 7d5c63a..e59259d 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -278,7 +278,8 @@
     virtual void onReleaseCompleted() override;
     virtual void onFlushCompleted() override;
     virtual void onError(status_t err, enum ActionCode actionCode) override;
-    virtual void onComponentAllocated(const char *componentName) override;
+    virtual void onComponentAllocated(
+            const char *componentName, const sp<MediaCodecInfo> &codecInfo) override;
     virtual void onComponentConfigured(
             const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat) override;
     virtual void onInputSurfaceCreated(
@@ -338,10 +339,14 @@
     notify->post();
 }
 
-void CodecCallback::onComponentAllocated(const char *componentName) {
+void CodecCallback::onComponentAllocated(
+        const char *componentName, const sp<MediaCodecInfo> &codecInfo) {
     sp<AMessage> notify(mNotify->dup());
     notify->setInt32("what", kWhatComponentAllocated);
     notify->setString("componentName", componentName);
+    if (codecInfo != nullptr) {
+        notify->setObject("codecInfo", codecInfo);
+    }
     notify->post();
 }
 
@@ -422,14 +427,12 @@
         const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err, pid_t pid,
         uid_t uid) {
     Vector<AString> matchingCodecs;
-    Vector<AString> owners;
 
     MediaCodecList::findMatchingCodecs(
             mime.c_str(),
             encoder,
             0,
-            &matchingCodecs,
-            &owners);
+            &matchingCodecs);
 
     if (err != NULL) {
         *err = NAME_NOT_FOUND;
@@ -1205,6 +1208,22 @@
     return OK;
 }
 
+status_t MediaCodec::getCodecInfo(sp<MediaCodecInfo> *codecInfo) const {
+    sp<AMessage> msg = new AMessage(kWhatGetCodecInfo, this);
+
+    sp<AMessage> response;
+    status_t err;
+    if ((err = PostAndAwaitResponse(msg, &response)) != OK) {
+        return err;
+    }
+
+    sp<RefBase> obj;
+    CHECK(response->findObject("codecInfo", &obj));
+    *codecInfo = static_cast<MediaCodecInfo *>(obj.get());
+
+    return OK;
+}
+
 status_t MediaCodec::getMetrics(MediaAnalyticsItem * &reply) {
 
     reply = NULL;
@@ -1588,6 +1607,11 @@
 
                     CHECK(msg->findString("componentName", &mComponentName));
 
+                    sp<RefBase> obj;
+                    if (msg->findObject("codecInfo", &obj)) {
+                        mCodecInfo = static_cast<MediaCodecInfo *>(obj.get());
+                    }
+
                     if (mComponentName.c_str()) {
                         mAnalyticsItem->setCString(kCodecCodec, mComponentName.c_str());
                     }
@@ -2595,6 +2619,17 @@
             break;
         }
 
+        case kWhatGetCodecInfo:
+        {
+            sp<AReplyToken> replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            sp<AMessage> response = new AMessage;
+            response->setObject("codecInfo", mCodecInfo);
+            response->postReply(replyID);
+            break;
+        }
+
         case kWhatSetParameters:
         {
             sp<AReplyToken> replyID;
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index f595646..9244886 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -307,11 +307,8 @@
 //static
 void MediaCodecList::findMatchingCodecs(
         const char *mime, bool encoder, uint32_t flags,
-        Vector<AString> *matches, Vector<AString> *owners) {
+        Vector<AString> *matches) {
     matches->clear();
-    if (owners != nullptr) {
-        owners->clear();
-    }
 
     const sp<IMediaCodecList> list = getInstance();
     if (list == nullptr) {
@@ -337,9 +334,6 @@
             ALOGV("skipping SW codec '%s'", componentName.c_str());
         } else {
             matches->push(componentName);
-            if (owners != nullptr) {
-                owners->push(AString(info->getOwnerName()));
-            }
             ALOGV("matching '%s'", componentName.c_str());
         }
     }
diff --git a/media/libstagefright/MetaDataUtils.cpp b/media/libstagefright/MetaDataUtils.cpp
index af8f539..04f6ade 100644
--- a/media/libstagefright/MetaDataUtils.cpp
+++ b/media/libstagefright/MetaDataUtils.cpp
@@ -24,11 +24,12 @@
 
 namespace android {
 
-bool MakeAVCCodecSpecificData(MetaDataBase &meta, const sp<ABuffer> &accessUnit) {
+bool MakeAVCCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size) {
     int32_t width;
     int32_t height;
     int32_t sarWidth;
     int32_t sarHeight;
+    sp<ABuffer> accessUnit = new ABuffer((void*)data,  size);
     sp<ABuffer> csd = MakeAVCCodecSpecificData(accessUnit, &width, &height, &sarWidth, &sarHeight);
     if (csd == nullptr) {
         return false;
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index f6fc813..83ca112 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 #define LOG_TAG "NuMediaExtractor"
 #include <utils/Log.h>
 
diff --git a/media/libstagefright/VideoFrameScheduler.cpp b/media/libstagefright/VideoFrameScheduler.cpp
index 03226c7..6819bba 100644
--- a/media/libstagefright/VideoFrameScheduler.cpp
+++ b/media/libstagefright/VideoFrameScheduler.cpp
@@ -129,6 +129,11 @@
         numSamplesToUse = mNumSamples;
     }
 
+    if ((period >> kPrecision) == 0 ) {
+        ALOGW("Period is 0, or after including precision is 0 - would cause div0, returning");
+        return false;
+    }
+
     int64_t sumX = 0;
     int64_t sumXX = 0;
     int64_t sumXY = 0;
diff --git a/media/libstagefright/codec2/SimpleC2Component.cpp b/media/libstagefright/codec2/SimpleC2Component.cpp
index 0753b08..f3d95f6 100644
--- a/media/libstagefright/codec2/SimpleC2Component.cpp
+++ b/media/libstagefright/codec2/SimpleC2Component.cpp
@@ -51,6 +51,8 @@
     mQueue.push_back({ nullptr, drainMode });
 }
 
+////////////////////////////////////////////////////////////////////////////////
+
 SimpleC2Component::SimpleC2Component(
         const std::shared_ptr<C2ComponentInterface> &intf)
     : mIntf(intf) {
@@ -91,13 +93,13 @@
 }
 
 c2_status_t SimpleC2Component::announce_nb(const std::vector<C2WorkOutline> &items) {
-    (void) items;
+    (void)items;
     return C2_OMITTED;
 }
 
 c2_status_t SimpleC2Component::flush_sm(
-        flush_mode_t flushThrough, std::list<std::unique_ptr<C2Work>>* const flushedWork) {
-    (void) flushThrough;
+        flush_mode_t flushMode, std::list<std::unique_ptr<C2Work>>* const flushedWork) {
+    (void)flushMode;
     {
         Mutexed<ExecState>::Locked state(mExecState);
         if (state->mState != RUNNING) {
@@ -107,6 +109,7 @@
     {
         Mutexed<WorkQueue>::Locked queue(mWorkQueue);
         queue->incGeneration();
+        // TODO: queue->splicedBy(flushedWork, flushedWork->end());
         while (!queue->empty()) {
             std::unique_ptr<C2Work> work = queue->pop_front();
             if (work) {
@@ -122,7 +125,7 @@
         }
     }
 
-    return onFlush_sm();
+    return C2_OK;
 }
 
 c2_status_t SimpleC2Component::drain_nb(drain_mode_t drainMode) {
@@ -160,6 +163,10 @@
     }
     if (!state->mThread.joinable()) {
         mExitRequested = false;
+        {
+            Mutexed<ExitMonitor>::Locked monitor(mExitMonitor);
+            monitor->mExited = false;
+        }
         state->mThread = std::thread(
                 [](std::weak_ptr<SimpleC2Component> wp) {
                     while (true) {
@@ -168,6 +175,8 @@
                             return;
                         }
                         if (thiz->exitRequested()) {
+                            ALOGV("stop processing");
+                            thiz->signalExit();
                             return;
                         }
                         thiz->processQueue();
@@ -179,7 +188,42 @@
     return C2_OK;
 }
 
+void SimpleC2Component::signalExit() {
+    Mutexed<ExitMonitor>::Locked monitor(mExitMonitor);
+    monitor->mExited = true;
+    monitor->mCondition.broadcast();
+}
+
+void SimpleC2Component::requestExitAndWait(std::function<void()> job) {
+    {
+        Mutexed<ExecState>::Locked state(mExecState);
+        if (!state->mThread.joinable()) {
+            return;
+        }
+    }
+    mExitRequested = true;
+    {
+        Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+        queue->mCondition.broadcast();
+    }
+    // TODO: timeout?
+    {
+        Mutexed<ExitMonitor>::Locked monitor(mExitMonitor);
+        while (!monitor->mExited) {
+            monitor.waitForCondition(monitor->mCondition);
+        }
+        job();
+    }
+    Mutexed<ExecState>::Locked state(mExecState);
+    if (state->mThread.joinable()) {
+        ALOGV("joining the processing thread");
+        state->mThread.join();
+        ALOGV("joined the processing thread");
+    }
+}
+
 c2_status_t SimpleC2Component::stop() {
+    ALOGV("stop");
     {
         Mutexed<ExecState>::Locked state(mExecState);
         if (state->mState != RUNNING) {
@@ -195,7 +239,8 @@
         Mutexed<PendingWork>::Locked pending(mPendingWork);
         pending->clear();
     }
-    c2_status_t err = onStop();
+    c2_status_t err;
+    requestExitAndWait([this, &err]{ err = onStop(); });
     if (err != C2_OK) {
         return err;
     }
@@ -203,6 +248,7 @@
 }
 
 c2_status_t SimpleC2Component::reset() {
+    ALOGV("reset");
     {
         Mutexed<ExecState>::Locked state(mExecState);
         state->mState = UNINITIALIZED;
@@ -215,21 +261,13 @@
         Mutexed<PendingWork>::Locked pending(mPendingWork);
         pending->clear();
     }
-    onReset();
+    requestExitAndWait([this]{ onReset(); });
     return C2_OK;
 }
 
 c2_status_t SimpleC2Component::release() {
-    std::thread releasing;
-    {
-        Mutexed<ExecState>::Locked state(mExecState);
-        releasing = std::move(state->mThread);
-    }
-    mExitRequested = true;
-    if (releasing.joinable()) {
-        releasing.join();
-    }
-    onRelease();
+    ALOGV("release");
+    requestExitAndWait([this]{ onRelease(); });
     return C2_OK;
 }
 
@@ -271,10 +309,14 @@
     std::unique_ptr<C2Work> work;
     uint64_t generation;
     int32_t drainMode;
+    bool isFlushPending = false;
     {
         Mutexed<WorkQueue>::Locked queue(mWorkQueue);
         nsecs_t deadline = systemTime() + ms2ns(250);
         while (queue->empty()) {
+            if (exitRequested()) {
+                return;
+            }
             nsecs_t now = systemTime();
             if (now >= deadline) {
                 return;
@@ -287,8 +329,17 @@
 
         generation = queue->generation();
         drainMode = queue->drainMode();
+        isFlushPending = queue->popPendingFlush();
         work = queue->pop_front();
     }
+    if (isFlushPending) {
+        ALOGV("processing pending flush");
+        c2_status_t err = onFlush_sm();
+        if (err != C2_OK) {
+            ALOGD("flush err: %d", err);
+            // TODO: error
+        }
+    }
 
     if (!mOutputBlockPool) {
         c2_status_t err = [this] {
@@ -333,10 +384,12 @@
     }
 
     process(work, mOutputBlockPool);
+    ALOGV("processed frame #%" PRIu64, work->input.ordinal.frameIndex.peeku());
     {
         Mutexed<WorkQueue>::Locked queue(mWorkQueue);
         if (queue->generation() != generation) {
-            ALOGW("work form old generation: was %" PRIu64 " now %" PRIu64, queue->generation(), generation);
+            ALOGD("work form old generation: was %" PRIu64 " now %" PRIu64,
+                    queue->generation(), generation);
             work->result = C2_NOT_FOUND;
             queue.unlock();
             {
@@ -361,9 +414,10 @@
                 unexpected = std::move(pending->at(frameIndex));
                 pending->erase(frameIndex);
             }
-            (void) pending->insert({ frameIndex, std::move(work) });
+            (void)pending->insert({ frameIndex, std::move(work) });
         }
         if (unexpected) {
+            ALOGD("unexpected pending work");
             unexpected->result = C2_CORRUPTED;
             Mutexed<ExecState>::Locked state(mExecState);
             state->mListener->onWorkDone_nb(shared_from_this(), vec(unexpected));
diff --git a/media/libstagefright/codec2/include/SimpleC2Component.h b/media/libstagefright/codec2/include/SimpleC2Component.h
index a4b6ee1..168e79f 100644
--- a/media/libstagefright/codec2/include/SimpleC2Component.h
+++ b/media/libstagefright/codec2/include/SimpleC2Component.h
@@ -52,6 +52,7 @@
     // for thread
     inline bool exitRequested() { return mExitRequested; }
     void processQueue();
+    void signalExit();
 
 protected:
     /**
@@ -157,16 +158,21 @@
 
     class WorkQueue {
     public:
-        inline WorkQueue() : mGeneration(0ul) {}
+        inline WorkQueue() : mFlush(false), mGeneration(0ul) {}
 
         inline uint64_t generation() const { return mGeneration; }
-        inline void incGeneration() { ++mGeneration; }
+        inline void incGeneration() { ++mGeneration; mFlush = true; }
 
         std::unique_ptr<C2Work> pop_front();
         void push_back(std::unique_ptr<C2Work> work);
         bool empty() const;
         uint32_t drainMode() const;
         void markDrain(uint32_t drainMode);
+        inline bool popPendingFlush() {
+            bool flush = mFlush;
+            mFlush = false;
+            return flush;
+        }
         void clear();
 
         Condition mCondition;
@@ -177,6 +183,7 @@
             uint32_t drainMode;
         };
 
+        bool mFlush;
         uint64_t mGeneration;
         std::list<Entry> mQueue;
     };
@@ -185,9 +192,18 @@
     typedef std::unordered_map<uint64_t, std::unique_ptr<C2Work>> PendingWork;
     Mutexed<PendingWork> mPendingWork;
 
+    struct ExitMonitor {
+        inline ExitMonitor() : mExited(false) {}
+        Condition mCondition;
+        bool mExited;
+    };
+    Mutexed<ExitMonitor> mExitMonitor;
+
     std::shared_ptr<C2BlockPool> mOutputBlockPool;
 
     SimpleC2Component() = delete;
+
+    void requestExitAndWait(std::function<void()> job);
 };
 
 }  // namespace android
diff --git a/media/libstagefright/codec2/vndk/C2Buffer.cpp b/media/libstagefright/codec2/vndk/C2Buffer.cpp
index 91b21c2..af2c20d 100644
--- a/media/libstagefright/codec2/vndk/C2Buffer.cpp
+++ b/media/libstagefright/codec2/vndk/C2Buffer.cpp
@@ -647,7 +647,8 @@
     std::shared_ptr<_C2BlockPoolData> mPoolData;
 };
 
-class C2_HIDE _C2MappingBlock2DImpl : public _C2Block2DImpl {
+class C2_HIDE _C2MappingBlock2DImpl
+    : public _C2Block2DImpl, public std::enable_shared_from_this<_C2MappingBlock2DImpl> {
 public:
     using _C2Block2DImpl::_C2Block2DImpl;
 
@@ -658,7 +659,7 @@
     private:
         friend class _C2MappingBlock2DImpl;
 
-        Mapped(const _C2Block2DImpl *impl, bool writable, C2Fence *fence __unused)
+        Mapped(const std::shared_ptr<_C2Block2DImpl> &impl, bool writable, C2Fence *fence __unused)
             : mImpl(impl), mWritable(writable) {
             memset(mData, 0, sizeof(mData));
             const C2Rect crop = mImpl->crop();
@@ -726,7 +727,7 @@
         bool writable() const { return mWritable; }
 
     private:
-        const _C2Block2DImpl *mImpl;
+        const std::shared_ptr<_C2Block2DImpl> mImpl;
         bool mWritable;
         c2_status_t mError;
         uint8_t *mData[C2PlanarLayout::MAX_NUM_PLANES];
@@ -744,7 +745,7 @@
         std::lock_guard<std::mutex> lock(mMappedLock);
         std::shared_ptr<Mapped> existing = mMapped.lock();
         if (!existing) {
-            existing = std::shared_ptr<Mapped>(new Mapped(this, writable, fence));
+            existing = std::shared_ptr<Mapped>(new Mapped(shared_from_this(), writable, fence));
             mMapped = existing;
         } else {
             // if we mapped the region read-only, we cannot remap it read-write
diff --git a/media/libstagefright/codecs/aacdec/C2SoftAac.cpp b/media/libstagefright/codecs/aacdec/C2SoftAac.cpp
index c82ea45..6bd15a5 100644
--- a/media/libstagefright/codecs/aacdec/C2SoftAac.cpp
+++ b/media/libstagefright/codecs/aacdec/C2SoftAac.cpp
@@ -305,37 +305,57 @@
         ALOGV("getting %d from ringbuffer", numSamples);
 
         std::shared_ptr<C2LinearBlock> block;
-        C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
-        // TODO: error handling, proper usage, etc.
-        c2_status_t err = pool->fetchLinearBlock(numSamples * sizeof(int16_t), usage, &block);
-        if (err != C2_OK) {
-            ALOGE("err = %d", err);
-        }
+        std::function<void(const std::unique_ptr<C2Work>&)> fillWork =
+            [&block, numSamples, pool, this]()
+                    -> std::function<void(const std::unique_ptr<C2Work>&)> {
+                auto fillEmptyWork = [](const std::unique_ptr<C2Work> &work, c2_status_t err) {
+                    work->result = err;
+                    work->worklets.front()->output.flags = work->input.flags;
+                    work->worklets.front()->output.buffers.clear();
+                    work->worklets.front()->output.ordinal = work->input.ordinal;
+                    work->workletsProcessed = 1u;
+                };
 
-        C2WriteView wView = block->map().get();
-        // TODO
-        INT_PCM *outBuffer = reinterpret_cast<INT_PCM *>(wView.data());
-        int32_t ns = outputDelayRingBufferGetSamples(outBuffer, numSamples);
-        if (ns != numSamples) {
-            ALOGE("not a complete frame of samples available");
-            mSignalledError = true;
-            // TODO: notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
-            return;
-        }
-        auto fillWork = [buffer = createLinearBuffer(block)](const std::unique_ptr<C2Work> &work) {
-            work->worklets.front()->output.flags = work->input.flags;
-            work->worklets.front()->output.buffers.clear();
-            work->worklets.front()->output.buffers.push_back(buffer);
-            work->worklets.front()->output.ordinal = work->input.ordinal;
-            work->workletsProcessed = 1u;
-        };
+                using namespace std::placeholders;
+                if (numSamples == 0) {
+                    return std::bind(fillEmptyWork, _1, C2_OK);
+                }
+
+                // TODO: error handling, proper usage, etc.
+                C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+                c2_status_t err = pool->fetchLinearBlock(
+                        numSamples * sizeof(int16_t), usage, &block);
+                if (err != C2_OK) {
+                    ALOGD("failed to fetch a linear block (%d)", err);
+                    mSignalledError = true;
+                    return std::bind(fillEmptyWork, _1, C2_NO_MEMORY);
+                }
+                C2WriteView wView = block->map().get();
+                // TODO
+                INT_PCM *outBuffer = reinterpret_cast<INT_PCM *>(wView.data());
+                int32_t ns = outputDelayRingBufferGetSamples(outBuffer, numSamples);
+                if (ns != numSamples) {
+                    ALOGE("not a complete frame of samples available");
+                    mSignalledError = true;
+                    return std::bind(fillEmptyWork, _1, C2_CORRUPTED);
+                }
+                return [buffer = createLinearBuffer(block)](const std::unique_ptr<C2Work> &work) {
+                    work->result = C2_OK;
+                    work->worklets.front()->output.flags = work->input.flags;
+                    work->worklets.front()->output.buffers.clear();
+                    work->worklets.front()->output.buffers.push_back(buffer);
+                    work->worklets.front()->output.ordinal = work->input.ordinal;
+                    work->workletsProcessed = 1u;
+                };
+            }();
+
         if (work && work->input.ordinal.frameIndex == c2_cntr64_t(outInfo.frameIndex)) {
             fillWork(work);
         } else {
             finish(outInfo.frameIndex, fillWork);
         }
 
-        ALOGV("out timestamp %" PRIu64 " / %u", outInfo.timestamp, block->capacity());
+        ALOGV("out timestamp %" PRIu64 " / %u", outInfo.timestamp, block ? block->capacity() : 0);
         mBuffersInfo.pop_front();
     }
 }
diff --git a/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.cpp b/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.cpp
index 4dd0309..406d1ca 100644
--- a/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.cpp
+++ b/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.cpp
@@ -115,7 +115,7 @@
         return;
     }
 
-    const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+    const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front();
     bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
     size_t inOffset = inBuffer.offset();
     size_t inSize = inBuffer.size();
diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
index b9ba251..1fc2c8c 100644
--- a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
+++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
@@ -37,15 +37,6 @@
 
 #include "ih264d_defs.h"
 
-namespace {
-
-template <class T>
-inline int32_t floor32(T arg) {
-   return (int32_t) std::llround(std::floor(arg));
-}
-
-} // namespace
-
 namespace android {
 
 struct iv_obj_t : public ::iv_obj_t {};
@@ -83,138 +74,19 @@
             .build();
 }
 
-#if 0
-using SupportedValuesWithFields = C2SoftAvcDecIntf::SupportedValuesWithFields;
-
-struct ValidateParam {
-    explicit ValidateParam(
-            const std::map<C2ParamField, SupportedValuesWithFields> &supportedValues)
-        : mSupportedValues(supportedValues) {}
-
-    template <class T, bool SIGNED = std::is_signed<T>::value, size_t SIZE = sizeof(T)>
-    struct Getter {
-        static T get(const C2Value::Primitive &) {
-            static_assert(!std::is_arithmetic<T>::value, "non-arithmetic type");
-            static_assert(!std::is_floating_point<T>::value || std::is_same<T, float>::value,
-                    "float is the only supported floating point type");
-            static_assert(sizeof(T) <= 8, "type exceeds 64-bit");
+void CopyPlane(
+        uint8_t *dst, const C2PlaneInfo &plane,
+        const uint8_t *src, uint32_t width, uint32_t height) {
+    for (uint32_t row = 0; row < height; ++row) {
+        for (uint32_t col = 0; col < width; ++col) {
+            *dst = *src;
+            dst += plane.colInc;
+            ++src;
         }
-    };
-
-    template <class T>
-    bool validateField(
-            const C2FieldSupportedValues &supportedValues, const T &value) {
-        switch (supportedValues.type) {
-        case C2FieldSupportedValues::EMPTY:
-            {
-                return false;
-            }
-        case C2FieldSupportedValues::RANGE:
-            {
-                // TODO: handle step, num, denom
-                return Getter<T>::get(supportedValues.range.min) <= value
-                        && value <= Getter<T>::get(supportedValues.range.max);
-            }
-        case C2FieldSupportedValues::VALUES:
-            {
-                for (const auto &val : supportedValues.values) {
-                    if (Getter<T>::get(val) == value) {
-                        return true;
-                    }
-                }
-                return false;
-            }
-        case C2FieldSupportedValues::FLAGS:
-            // TODO
-            return false;
-        }
-        return false;
+        dst -= plane.colInc * width;
+        dst += plane.rowInc;
     }
-
-protected:
-    const std::map<C2ParamField, SupportedValuesWithFields> &mSupportedValues;
-};
-
-template <>
-struct ValidateParam::Getter<float> {
-    static float get(const C2Value::Primitive &value) { return value.fp; }
-};
-template <class T>
-struct ValidateParam::Getter<T, true, 8u> {
-    static int64_t get(const C2Value::Primitive &value) { return value.i64; }
-};
-template <class T>
-struct ValidateParam::Getter<T, true, 4u> {
-    static int32_t get(const C2Value::Primitive &value) { return value.i32; }
-};
-template <class T>
-struct ValidateParam::Getter<T, false, 8u> {
-    static uint64_t get(const C2Value::Primitive &value) { return value.u64; }
-};
-template <class T>
-struct ValidateParam::Getter<T, false, 4u> {
-    static uint32_t get(const C2Value::Primitive &value) { return value.u32; }
-};
-
-template <class T>
-struct ValidateSimpleParam : public ValidateParam {
-    explicit ValidateSimpleParam(
-            const std::map<C2ParamField, SupportedValuesWithFields> &supportedValues)
-        : ValidateParam(supportedValues) {}
-
-    std::unique_ptr<C2SettingResult> operator() (C2Param *c2param) {
-        T* param = (T*)c2param;
-        C2ParamField field(param, &T::value);
-        const C2FieldSupportedValues &supportedValues = mSupportedValues.at(field).supported;
-        if (!validateField(supportedValues, param->value)) {
-            return std::unique_ptr<C2SettingResult>(
-                    new C2SettingResult {C2SettingResult::BAD_VALUE, {field, nullptr}, {}});
-        }
-        return nullptr;
-    }
-};
-
-template <class T>
-struct ValidateVideoSize : public ValidateParam {
-    explicit ValidateVideoSize(
-            const std::map<C2ParamField, SupportedValuesWithFields> &supportedValues)
-        : ValidateParam(supportedValues) {}
-
-    std::unique_ptr<C2SettingResult> operator() (C2Param *c2param) {
-        T* param = (T*)c2param;
-        C2ParamField field(param, &T::width);
-        const C2FieldSupportedValues &supportedWidth = mSupportedValues.at(field).supported;
-        if (!validateField(supportedWidth, param->width)) {
-            return std::unique_ptr<C2SettingResult>(
-                    new C2SettingResult {C2SettingResult::BAD_VALUE, {field, nullptr}, {}});
-        }
-        field = C2ParamField(param, &T::height);
-        const C2FieldSupportedValues &supportedHeight = mSupportedValues.at(field).supported;
-        if (!validateField(supportedHeight, param->height)) {
-            return std::unique_ptr<C2SettingResult>(
-                    new C2SettingResult {C2SettingResult::BAD_VALUE, {field, nullptr}, {}});
-        }
-        return nullptr;
-    }
-};
-
-template <class T>
-struct ValidateCString {
-    explicit ValidateCString(const char *expected) : mExpected(expected) {}
-
-    std::unique_ptr<C2SettingResult> operator() (C2Param *c2param) {
-        T* param = (T*)c2param;
-        if (strncmp(param->m.value, mExpected, param->flexCount()) != 0) {
-            return std::unique_ptr<C2SettingResult>(
-                    new C2SettingResult {C2SettingResult::BAD_VALUE, {C2ParamField(param, &T::m), nullptr}, {}});
-        }
-        return nullptr;
-    }
-
-private:
-    const char *mExpected;
-};
-#endif
+}
 
 void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
     uint32_t flags = 0;
@@ -229,398 +101,6 @@
 
 }  // namespace
 
-#if 0
-#define CASE(member) \
-    case decltype(component->member)::CORE_INDEX: \
-        return std::unique_ptr<C2StructDescriptor>(new C2StructDescriptor( \
-                static_cast<decltype(component->member) *>(nullptr)))
-
-class C2SoftAvcDecIntf::ParamReflector : public C2ParamReflector {
-public:
-    virtual std::unique_ptr<C2StructDescriptor> describe(C2Param::CoreIndex coreIndex) override {
-        constexpr C2SoftAvcDecIntf *component = nullptr;
-        switch (coreIndex.coreIndex()) {
-        CASE(mDomainInfo);
-        CASE(mInputStreamCount);
-        CASE(mInputStreamFormat);
-        // Output counterparts for the above would be redundant.
-        CASE(mVideoSize);
-        CASE(mMaxVideoSizeHint);
-
-        // port mime configs are stored as unique_ptr.
-        case C2PortMimeConfig::CORE_INDEX:
-            return std::unique_ptr<C2StructDescriptor>(new C2StructDescriptor(
-                    static_cast<C2PortMimeConfig *>(nullptr)));
-        }
-        return nullptr;
-    }
-};
-#undef CASE
-
-// static const CodecProfileLevel kProfileLevels[] = {
-//     { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel52 },
-//     { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel52 },
-//     { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel52 },
-// };
-C2SoftAvcDecIntf::C2SoftAvcDecIntf(const char *name, c2_node_id_t id)
-    : mName(name),
-      mId(id),
-      mDomainInfo(C2DomainVideo),
-      mInputStreamCount(1u),
-      mOutputStreamCount(1u),
-      mInputStreamFormat(0u, C2FormatCompressed),
-      mOutputStreamFormat(0u, C2FormatVideo),
-      mProfile(0u, kAvcProfileUnknown),
-      mLevel(0u, kAvcLevelUnknown),
-      mBlockSize(0u),
-      mAlignment(0u),
-      mFrameRate(0u, 0),
-      mBlocksPerSecond(0u, 0),
-      mParamReflector(new ParamReflector) {
-    ALOGV("in %s", __func__);
-    mInputPortMime = C2PortMimeConfig::input::AllocUnique(strlen(CODEC_MIME_TYPE) + 1);
-    strcpy(mInputPortMime->m.value, CODEC_MIME_TYPE);
-    mOutputPortMime = C2PortMimeConfig::output::AllocUnique(strlen(MEDIA_MIMETYPE_VIDEO_RAW) + 1);
-    strcpy(mOutputPortMime->m.value, MEDIA_MIMETYPE_VIDEO_RAW);
-
-    mVideoSize.width = 320;
-    mVideoSize.height = 240;
-    mBlockSize.width = 16;
-    mBlockSize.height = 16;
-    mAlignment.width = 2;
-    mAlignment.height = 2;
-
-    mMaxVideoSizeHint.width = H264_MAX_FRAME_WIDTH;
-    mMaxVideoSizeHint.height = H264_MAX_FRAME_HEIGHT;
-
-    mOutputBlockPools = C2PortBlockPoolsTuning::output::AllocUnique({});
-
-    auto insertParam = [&params = mParams] (C2Param *param) {
-        params[param->index()] = param;
-    };
-
-    auto markReadOnly = [&supported = mSupportedValues] (auto *param) {
-        supported.emplace(
-                C2ParamField(param, &std::remove_pointer<decltype(param)>::type::value),
-                C2FieldSupportedValues(false /* flags */, {}));
-    };
-
-    auto markReadOnlyVideoSize = [&supported = mSupportedValues] (auto *param) {
-        supported.emplace(
-                C2ParamField(param, &std::remove_pointer<decltype(param)>::type::width),
-                C2FieldSupportedValues(false /* flags */, {}));
-        supported.emplace(
-                C2ParamField(param, &std::remove_pointer<decltype(param)>::type::height),
-                C2FieldSupportedValues(false /* flags */, {}));
-    };
-
-    insertParam(&mDomainInfo);
-    markReadOnly(&mDomainInfo);
-    mFieldVerifiers[mDomainInfo.index()] =
-            ValidateSimpleParam<decltype(mDomainInfo)>(mSupportedValues);
-
-    insertParam(mInputPortMime.get());
-    mFieldVerifiers[mInputPortMime->index()] =
-            ValidateCString<std::remove_reference<decltype(*mInputPortMime)>::type>(CODEC_MIME_TYPE);
-
-    insertParam(&mInputStreamCount);
-    markReadOnly(&mInputStreamCount);
-    mFieldVerifiers[mInputStreamCount.index()] =
-            ValidateSimpleParam<decltype(mInputStreamCount)>(mSupportedValues);
-
-    insertParam(mOutputPortMime.get());
-    mFieldVerifiers[mOutputPortMime->index()] =
-            ValidateCString<std::remove_reference<decltype(*mOutputPortMime)>::type>(MEDIA_MIMETYPE_VIDEO_RAW);
-
-    insertParam(&mOutputStreamCount);
-    markReadOnly(&mOutputStreamCount);
-    mFieldVerifiers[mOutputStreamCount.index()] =
-            ValidateSimpleParam<decltype(mOutputStreamCount)>(mSupportedValues);
-
-    insertParam(&mInputStreamFormat);
-    markReadOnly(&mInputStreamFormat);
-    mFieldVerifiers[mInputStreamFormat.index()] =
-            ValidateSimpleParam<decltype(mInputStreamFormat)>(mSupportedValues);
-
-    insertParam(&mOutputStreamFormat);
-    markReadOnly(&mOutputStreamFormat);
-    mFieldVerifiers[mOutputStreamFormat.index()] =
-            ValidateSimpleParam<decltype(mOutputStreamFormat)>(mSupportedValues);
-
-    insertParam(&mVideoSize);
-    markReadOnlyVideoSize(&mVideoSize);
-    mFieldVerifiers[mVideoSize.index()] =
-            ValidateVideoSize<decltype(mVideoSize)>(mSupportedValues);
-
-    insertParam(&mMaxVideoSizeHint);
-    mSupportedValues.emplace(
-            C2ParamField(&mMaxVideoSizeHint, &C2MaxVideoSizeHintPortSetting::width),
-            C2FieldSupportedValues(H264_MIN_FRAME_WIDTH, H264_MAX_FRAME_WIDTH, mAlignment.width));
-    mSupportedValues.emplace(
-            C2ParamField(&mMaxVideoSizeHint, &C2MaxVideoSizeHintPortSetting::height),
-            C2FieldSupportedValues(H264_MIN_FRAME_HEIGHT, H264_MAX_FRAME_HEIGHT, mAlignment.height));
-    mFieldVerifiers[mMaxVideoSizeHint.index()] =
-            ValidateVideoSize<decltype(mMaxVideoSizeHint)>(mSupportedValues);
-
-    insertParam(&mProfile);
-    mSupportedValues.emplace(
-            C2ParamField(&mProfile, &C2AvcProfileInfo::value),
-            C2FieldSupportedValues(false /* flags */, {
-                kAvcProfileUnknown,
-                kAvcProfileBaseline,
-                kAvcProfileMain,
-                kAvcProfileHigh,
-            }));
-    mFieldVerifiers[mProfile.index()] =
-            ValidateSimpleParam<decltype(mProfile)>(mSupportedValues);
-
-    insertParam(&mLevel);
-    mSupportedValues.emplace(
-            C2ParamField(&mLevel, &C2AvcLevelInfo::value),
-            C2FieldSupportedValues(false /* flags */, {
-                kAvcLevelUnknown,
-                kAvcLevel10,
-                kAvcLevel1b,
-                kAvcLevel11,
-                kAvcLevel12,
-                kAvcLevel13,
-                kAvcLevel20,
-                kAvcLevel21,
-                kAvcLevel22,
-                kAvcLevel30,
-                kAvcLevel31,
-                kAvcLevel32,
-                kAvcLevel40,
-                kAvcLevel41,
-                kAvcLevel42,
-                kAvcLevel50,
-                kAvcLevel51,
-                kAvcLevel52,
-            }));
-    mFieldVerifiers[mLevel.index()] =
-            ValidateSimpleParam<decltype(mLevel)>(mSupportedValues);
-
-    insertParam(&mBlockSize);
-    markReadOnlyVideoSize(&mBlockSize);
-    mFieldVerifiers[mBlockSize.index()] =
-            ValidateVideoSize<decltype(mBlockSize)>(mSupportedValues);
-
-    insertParam(&mAlignment);
-    markReadOnlyVideoSize(&mAlignment);
-    mFieldVerifiers[mAlignment.index()] =
-            ValidateVideoSize<decltype(mAlignment)>(mSupportedValues);
-
-    insertParam(&mFrameRate);
-    mSupportedValues.emplace(
-            C2ParamField(&mFrameRate, &C2FrameRateInfo::value),
-            C2FieldSupportedValues(0, 240));
-    mFieldVerifiers[mFrameRate.index()] =
-            ValidateSimpleParam<decltype(mFrameRate)>(mSupportedValues);
-
-    insertParam(&mBlocksPerSecond);
-    mSupportedValues.emplace(
-            C2ParamField(&mFrameRate, &C2BlocksPerSecondInfo::value),
-            C2FieldSupportedValues(0, 244800));
-    mFieldVerifiers[mBlocksPerSecond.index()] =
-            ValidateSimpleParam<decltype(mBlocksPerSecond)>(mSupportedValues);
-
-    mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
-            true, "_domain", &mDomainInfo));
-    mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
-            true, "_input_port_mime", mInputPortMime.get()));
-    mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
-            true, "_input_stream_count", &mInputStreamCount));
-    mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
-            true, "_output_port_mime", mOutputPortMime.get()));
-    mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
-            true, "_output_stream_count", &mOutputStreamCount));
-    mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
-            true, "_input_stream_format", &mInputStreamFormat));
-    mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
-            true, "_output_stream_format", &mOutputStreamFormat));
-    mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
-            false, "_video_size", &mVideoSize));
-    mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
-            false, "_max_video_size_hint", &mMaxVideoSizeHint));
-    mParamDescs.push_back(std::make_shared<C2ParamDescriptor>(
-            false, "_output_block_pools", mOutputBlockPools.get()));
-}
-
-C2SoftAvcDecIntf::~C2SoftAvcDecIntf() {
-    ALOGV("in %s", __func__);
-}
-
-C2String C2SoftAvcDecIntf::getName() const {
-    return mName;
-}
-
-c2_node_id_t C2SoftAvcDecIntf::getId() const {
-    return mId;
-}
-
-c2_status_t C2SoftAvcDecIntf::query_vb(
-        const std::vector<C2Param*> & stackParams,
-        const std::vector<C2Param::Index> & heapParamIndices,
-        c2_blocking_t mayBlock,
-        std::vector<std::unique_ptr<C2Param>>* const heapParams) const {
-    (void)mayBlock;
-    for (C2Param* const param : stackParams) {
-        if (!*param) {
-            continue;
-        }
-
-        uint32_t index = param->index();
-        if (!mParams.count(index)) {
-            // TODO: add support for output-block-pools (this will be done when we move all
-            // config to shared ptr)
-            continue;
-        }
-
-        C2Param *myParam = mParams.find(index)->second;
-        if (myParam->size() != param->size()) {
-            param->invalidate();
-            continue;
-        }
-
-        param->updateFrom(*myParam);
-    }
-
-    for (const C2Param::Index index : heapParamIndices) {
-        if (mParams.count(index)) {
-            C2Param *myParam = mParams.find(index)->second;
-            heapParams->emplace_back(C2Param::Copy(*myParam));
-        }
-    }
-
-    return C2_OK;
-}
-
-c2_status_t C2SoftAvcDecIntf::config_vb(
-        const std::vector<C2Param*> &params,
-        c2_blocking_t mayBlock,
-        std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
-    (void)mayBlock;
-    c2_status_t err = C2_OK;
-    for (C2Param *param : params) {
-        uint32_t index = param->index();
-        if (param->index() == mOutputBlockPools.get()->index()) {
-            // setting output block pools
-            mOutputBlockPools.reset(
-                    (C2PortBlockPoolsTuning::output *)C2Param::Copy(*param).release());
-            continue;
-        }
-
-        if (mParams.count(index) == 0) {
-            // We can't create C2SettingResult with no field, so just skipping in this case.
-            err = C2_BAD_INDEX;
-            continue;
-        }
-        C2Param *myParam = mParams.find(index)->second;
-        std::unique_ptr<C2SettingResult> result;
-        if (!(result = mFieldVerifiers[index](param))) {
-            myParam->updateFrom(*param);
-            updateSupportedValues();
-        } else {
-            failures->push_back(std::move(result));
-            err = C2_BAD_VALUE;
-        }
-    }
-    return err;
-}
-
-c2_status_t C2SoftAvcDecIntf::createTunnel_sm(c2_node_id_t targetComponent) {
-    // Tunneling is not supported
-    (void) targetComponent;
-    return C2_OMITTED;
-}
-
-c2_status_t C2SoftAvcDecIntf::releaseTunnel_sm(c2_node_id_t targetComponent) {
-    // Tunneling is not supported
-    (void) targetComponent;
-    return C2_OMITTED;
-}
-
-std::shared_ptr<C2ParamReflector> C2SoftAvcDecIntf::getParamReflector() const {
-    return mParamReflector;
-}
-
-c2_status_t C2SoftAvcDecIntf::querySupportedParams_nb(
-        std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const {
-    params->insert(params->begin(), mParamDescs.begin(), mParamDescs.end());
-    return C2_OK;
-}
-
-c2_status_t C2SoftAvcDecIntf::querySupportedValues_vb(
-        std::vector<C2FieldSupportedValuesQuery> &fields, c2_blocking_t mayBlock) const {
-    (void)mayBlock;
-    c2_status_t res = C2_OK;
-    for (C2FieldSupportedValuesQuery &query : fields) {
-        if (mSupportedValues.count(query.field) == 0) {
-            query.status = C2_BAD_INDEX;
-            res = C2_BAD_INDEX;
-        } else {
-            query.status = C2_OK;
-            query.values = mSupportedValues.at(query.field).supported;
-        }
-    }
-    return res;
-}
-
-void C2SoftAvcDecIntf::updateSupportedValues() {
-    int32_t maxWidth = H264_MAX_FRAME_WIDTH;
-    int32_t maxHeight = H264_MAX_FRAME_HEIGHT;
-    // cf: Rec. ITU-T H.264 A.3
-    int maxFrameRate = 172;
-    std::vector<C2ParamField> fields;
-    if (mLevel.value != kAvcLevelUnknown) {
-        // cf: Rec. ITU-T H.264 Table A-1
-        constexpr int MaxFS[] = {
-        //  0       1       2       3       4       5       6       7       8       9
-            0,      0,      0,      0,      0,      0,      0,      0,      0,      99,
-            99,     396,    396,    396,    0,      0,      0,      0,      0,      0,
-            396,    792,    1620,   0,      0,      0,      0,      0,      0,      0,
-            1620,   3600,   5120,   0,      0,      0,      0,      0,      0,      0,
-            8192,   8192,   8704,   0,      0,      0,      0,      0,      0,      0,
-            22080,  36864,  36864,
-        };
-        constexpr int MaxMBPS[] = {
-        //  0       1       2       3       4       5       6       7       8       9
-            0,      0,      0,      0,      0,      0,      0,      0,      0,      1485,
-            1485,   3000,   6000,   11880,  0,      0,      0,      0,      0,      0,
-            11880,  19800,  20250,  0,      0,      0,      0,      0,      0,      0,
-            40500,  108000, 216000, 0,      0,      0,      0,      0,      0,      0,
-            245760, 245760, 522240, 0,      0,      0,      0,      0,      0,      0,
-            589824, 983040, 2073600,
-        };
-
-        // cf: Rec. ITU-T H.264 A.3.1
-        maxWidth = std::min(maxWidth, floor32(std::sqrt(MaxFS[mLevel.value] * 8)) * MB_SIZE);
-        maxHeight = std::min(maxHeight, floor32(std::sqrt(MaxFS[mLevel.value] * 8)) * MB_SIZE);
-        int32_t MBs = ((mVideoSize.width + 15) / 16) * ((mVideoSize.height + 15) / 16);
-        maxFrameRate = std::min(maxFrameRate, MaxMBPS[mLevel.value] / MBs);
-        fields.push_back(C2ParamField(&mLevel, &C2AvcLevelInfo::value));
-    }
-
-    SupportedValuesWithFields &maxWidthVals = mSupportedValues.at(
-            C2ParamField(&mMaxVideoSizeHint, &C2MaxVideoSizeHintPortSetting::width));
-    maxWidthVals.supported.range.max = maxWidth;
-    maxWidthVals.restrictingFields.clear();
-    maxWidthVals.restrictingFields.insert(fields.begin(), fields.end());
-
-    SupportedValuesWithFields &maxHeightVals = mSupportedValues.at(
-            C2ParamField(&mMaxVideoSizeHint, &C2MaxVideoSizeHintPortSetting::height));
-    maxHeightVals.supported.range.max = maxHeight;
-    maxHeightVals.restrictingFields.clear();
-    maxHeightVals.restrictingFields.insert(fields.begin(), fields.end());
-
-    SupportedValuesWithFields &frameRate = mSupportedValues.at(
-            C2ParamField(&mFrameRate, &C2FrameRateInfo::value));
-    frameRate.supported.range.max = maxFrameRate;
-    frameRate.restrictingFields.clear();
-    frameRate.restrictingFields.insert(fields.begin(), fields.end());
-}
-#endif
-
 ///////////////////////////////////////////////////////////////////////////////
 
 C2SoftAvcDec::C2SoftAvcDec(
@@ -628,7 +108,7 @@
         c2_node_id_t id)
     : SimpleC2Component(BuildIntf(name, id)),
       mCodecCtx(NULL),
-      mFlushOutBuffer(NULL),
+      mOutBuffer(NULL),
       mIvColorFormat(IV_YUV_420P),
       mChangingResolution(false),
       mSignalledError(false),
@@ -654,6 +134,7 @@
 }
 
 c2_status_t C2SoftAvcDec::onStop() {
+    ALOGV("onStop");
     mSignalledError = false;
     resetDecoder();
     resetPlugin();
@@ -662,6 +143,7 @@
 }
 
 void C2SoftAvcDec::onReset() {
+    ALOGV("onReset");
     (void)onStop();
 }
 
@@ -672,17 +154,6 @@
 c2_status_t C2SoftAvcDec::onFlush_sm() {
     setFlushMode();
 
-    /* Allocate a picture buffer to flushed data */
-    uint32_t displayStride = mWidth;
-    uint32_t displayHeight = mHeight;
-
-    uint32_t bufferSize = displayStride * displayHeight * 3 / 2;
-    mFlushOutBuffer = (uint8_t *)memalign(128, bufferSize);
-    if (NULL == mFlushOutBuffer) {
-        ALOGE("Could not allocate flushOutputBuffer of size %u", bufferSize);
-        return C2_NO_MEMORY;
-    }
-
     while (true) {
         ivd_video_decode_ip_t s_dec_ip;
         ivd_video_decode_op_t s_dec_op;
@@ -698,9 +169,9 @@
         }
     }
 
-    if (mFlushOutBuffer) {
-        free(mFlushOutBuffer);
-        mFlushOutBuffer = NULL;
+    if (mOutBuffer) {
+        free(mOutBuffer);
+        mOutBuffer = NULL;
     }
     return C2_OK;
 }
@@ -778,6 +249,16 @@
 
         return UNKNOWN_ERROR;
     }
+
+    if (mOutBuffer != NULL) {
+        free(mOutBuffer);
+    }
+    uint32_t bufferSize = mWidth * mHeight * 3 / 2;
+    mOutBuffer = (uint8_t *)memalign(128, bufferSize);
+    if (NULL == mOutBuffer) {
+        ALOGE("Could not allocate output buffer of size %u", bufferSize);
+        return C2_NO_MEMORY;
+    }
     return OK;
 }
 
@@ -854,6 +335,7 @@
                 s_video_flush_op.u4_error_code);
         return UNKNOWN_ERROR;
     }
+
     return OK;
 }
 
@@ -993,6 +475,7 @@
 
     ps_dec_ip->u4_size = sizeof(ivd_video_decode_ip_t);
     ps_dec_op->u4_size = sizeof(ivd_video_decode_op_t);
+    ps_dec_op->u4_output_present = 0;
 
     ps_dec_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
 
@@ -1020,14 +503,29 @@
                   outBuffer->width(), outBuffer->height(), width, height);
             return false;
         }
+        ALOGV("width = %u, stride[0] = %u, stride[1] = %u, stride[2] = %u",
+                outBuffer->width(),
+                outBuffer->layout().planes[0].rowInc,
+                outBuffer->layout().planes[1].rowInc,
+                outBuffer->layout().planes[2].rowInc);
+        const C2PlanarLayout &layout = outBuffer->layout();
         ps_dec_ip->s_out_buffer.pu1_bufs[0] = outBuffer->data()[0];
+        if (layout.planes[0].rowInc != (int32_t)mWidth || layout.planes[1].colInc != 1) {
+            ps_dec_ip->s_out_buffer.pu1_bufs[0] = mOutBuffer;
+        }
         ps_dec_ip->s_out_buffer.pu1_bufs[1] = outBuffer->data()[1];
+        if (layout.planes[1].rowInc != (int32_t)mWidth / 2 || layout.planes[1].colInc != 1) {
+            ps_dec_ip->s_out_buffer.pu1_bufs[1] = mOutBuffer + sizeY;
+        }
         ps_dec_ip->s_out_buffer.pu1_bufs[2] = outBuffer->data()[2];
+        if (layout.planes[2].rowInc != (int32_t)mWidth / 2 || layout.planes[2].colInc != 1) {
+            ps_dec_ip->s_out_buffer.pu1_bufs[2] = mOutBuffer + sizeY + sizeUV;
+        }
     } else {
-        // mFlushOutBuffer always has the right size.
-        ps_dec_ip->s_out_buffer.pu1_bufs[0] = mFlushOutBuffer;
-        ps_dec_ip->s_out_buffer.pu1_bufs[1] = mFlushOutBuffer + sizeY;
-        ps_dec_ip->s_out_buffer.pu1_bufs[2] = mFlushOutBuffer + sizeY + sizeUV;
+        // mOutBuffer always has the right size.
+        ps_dec_ip->s_out_buffer.pu1_bufs[0] = mOutBuffer;
+        ps_dec_ip->s_out_buffer.pu1_bufs[1] = mOutBuffer + sizeY;
+        ps_dec_ip->s_out_buffer.pu1_bufs[2] = mOutBuffer + sizeY + sizeUV;
     }
 
     ps_dec_ip->s_out_buffer.u4_num_bufs = 3;
@@ -1065,7 +563,8 @@
 }
 
 void C2SoftAvcDec::finishWork(uint64_t index, const std::unique_ptr<C2Work> &work) {
-    std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(std::move(mAllocatedBlock));
+    std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(mAllocatedBlock);
+    mAllocatedBlock.reset();
     auto fillWork = [buffer](const std::unique_ptr<C2Work> &work) {
         uint32_t flags = 0;
         if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
@@ -1093,9 +592,9 @@
     work->result = C2_OK;
     work->workletsProcessed = 0u;
 
-    const C2ConstLinearBlock &buffer =
+    const C2ConstLinearBlock buffer =
         work->input.buffers[0]->data().linearBlocks().front();
-    if (buffer.capacity() == 0) {
+    if (buffer.size() == 0) {
         ALOGV("empty input: %llu", work->input.ordinal.frameIndex.peekull());
         // TODO: result?
         fillEmptyWork(work);
@@ -1109,6 +608,13 @@
     }
 
     C2ReadView input = work->input.buffers[0]->data().linearBlocks().front().map().get();
+    if (input.error() != C2_OK) {
+        work->result = input.error();
+        fillEmptyWork(work);
+        ALOGD("map error: %d", input.error());
+        return;
+    }
+    ALOGV("buffer.size() = %u, input.capacity() = %u", buffer.size(), input.capacity());
     uint32_t workIndex = work->input.ordinal.frameIndex.peeku() & 0xFFFFFFFF;
     size_t inOffset = 0u;
 
@@ -1117,34 +623,54 @@
             break;
         }
         (void)ensureDecoderState(pool);
-        C2GraphicView output = mAllocatedBlock->map().get();
-        if (output.error() != OK) {
-            ALOGE("mapped err = %d", output.error());
-        }
-
         ivd_video_decode_ip_t s_dec_ip;
         ivd_video_decode_op_t s_dec_op;
         WORD32 timeDelay, timeTaken;
         //size_t sizeY, sizeUV;
 
-        if (!setDecodeArgs(&s_dec_ip, &s_dec_op, &input, &output, workIndex, inOffset)) {
-            ALOGE("Decoder arg setup failed");
-            // TODO: notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
-            mSignalledError = true;
-            break;
+        {
+            C2GraphicView output = mAllocatedBlock->map().get();
+            if (output.error() != C2_OK) {
+                ALOGE("mapped err = %d", output.error());
+                work->result = output.error();
+                fillEmptyWork(work);
+                return;
+            }
+            if (!setDecodeArgs(&s_dec_ip, &s_dec_op, &input, &output, workIndex, inOffset)) {
+                ALOGE("Decoder arg setup failed");
+                // TODO: notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                mSignalledError = true;
+                break;
+            }
+            ALOGV("Decoder arg setup succeeded");
+            // If input dump is enabled, then write to file
+            DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes, mInputOffset);
+
+            GETTIME(&mTimeStart, NULL);
+            /* Compute time elapsed between end of previous decode()
+             * to start of current decode() */
+            TIME_DIFF(mTimeEnd, mTimeStart, timeDelay);
+
+            IV_API_CALL_STATUS_T status;
+            status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
+            ALOGV("status = %d, error_code = %d", status, (s_dec_op.u4_error_code & 0xFF));
+            if (s_dec_op.u4_output_present) {
+                const C2PlanarLayout &layout = output.layout();
+                if (layout.planes[0].rowInc != (int32_t)mWidth || layout.planes[1].colInc != 1) {
+                    CopyPlane(output.data()[0], layout.planes[0], mOutBuffer, mWidth, mHeight);
+                }
+                if (layout.planes[1].rowInc != (int32_t)mWidth / 2 || layout.planes[1].colInc != 1) {
+                    CopyPlane(
+                            output.data()[1], layout.planes[1],
+                            mOutBuffer + (mWidth * mHeight), mWidth / 2, mHeight / 2);
+                }
+                if (layout.planes[2].rowInc != (int32_t)mWidth / 2 || layout.planes[2].colInc != 1) {
+                    CopyPlane(
+                            output.data()[2], layout.planes[2],
+                            mOutBuffer + (mWidth * mHeight * 5 / 4), mWidth / 2, mHeight / 2);
+                }
+            }
         }
-        ALOGV("Decoder arg setup succeeded");
-        // If input dump is enabled, then write to file
-        DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes, mInputOffset);
-
-        GETTIME(&mTimeStart, NULL);
-        /* Compute time elapsed between end of previous decode()
-         * to start of current decode() */
-        TIME_DIFF(mTimeEnd, mTimeStart, timeDelay);
-
-        IV_API_CALL_STATUS_T status;
-        status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
-        ALOGV("status = %d, error_code = %d", status, (s_dec_op.u4_error_code & 0xFF));
 
         bool unsupportedResolution =
             (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_dec_op.u4_error_code & 0xFF));
diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
index 6632bf3..d324a0f 100644
--- a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
+++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
@@ -189,7 +189,7 @@
     struct timeval mTimeEnd;     // Time at the end of decode()
 
     // Internal buffer to be used to flush out the buffers from decoder
-    uint8_t *mFlushOutBuffer;
+    uint8_t *mOutBuffer;
 
 #ifdef FILE_DUMP_ENABLE
     char mInFile[200];
diff --git a/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp b/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp
index 8fb8122..d75e4c1 100644
--- a/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp
+++ b/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp
@@ -909,6 +909,9 @@
     }
 
     ALOGV("width = %d, height = %d", input->width(), input->height());
+    if (mWidth != input->width() || mHeight != input->height()) {
+        return C2_BAD_VALUE;
+    }
     const C2PlanarLayout &layout = input->layout();
     uint8_t *yPlane = const_cast<uint8_t *>(input->data()[C2PlanarLayout::PLANE_Y]);
     uint8_t *uPlane = const_cast<uint8_t *>(input->data()[C2PlanarLayout::PLANE_U]);
diff --git a/media/libstagefright/codecs/cmds/codec2.cpp b/media/libstagefright/codecs/cmds/codec2.cpp
index 5558bcf..295a5b0 100644
--- a/media/libstagefright/codecs/cmds/codec2.cpp
+++ b/media/libstagefright/codecs/cmds/codec2.cpp
@@ -248,7 +248,7 @@
             ALOGV("Render: Frame #%lld", work->worklets.front()->output.ordinal.frameIndex.peekll());
             const std::shared_ptr<C2Buffer> &output = work->worklets.front()->output.buffers[0];
             if (output) {
-                const C2ConstGraphicBlock &block = output->data().graphicBlocks().front();
+                const C2ConstGraphicBlock block = output->data().graphicBlocks().front();
                 native_handle_t *grallocHandle = UnwrapNativeCodec2GrallocHandle(block.handle());
                 sp<GraphicBuffer> buffer(new GraphicBuffer(
                         grallocHandle,
diff --git a/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp
index ce40d6b..87138af 100644
--- a/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp
+++ b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp
@@ -48,6 +48,7 @@
 }
 
 C2SoftFlacDecoder::~C2SoftFlacDecoder() {
+    delete mFLACDecoder;
 }
 
 c2_status_t C2SoftFlacDecoder::onInit() {
@@ -77,6 +78,9 @@
 }
 
 status_t C2SoftFlacDecoder::initDecoder() {
+    if (mFLACDecoder) {
+        delete mFLACDecoder;
+    }
     mFLACDecoder = FLACDecoder::Create();
     if (!mFLACDecoder) {
         ALOGE("initDecoder: failed to create FLACDecoder");
@@ -111,7 +115,7 @@
         return;
     }
 
-    const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+    const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front();
     size_t inOffset = inBuffer.offset();
     size_t inSize = inBuffer.size();
     C2ReadView rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
diff --git a/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.h b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.h
index a5c01a9..43d913b 100644
--- a/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.h
+++ b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.h
@@ -46,7 +46,7 @@
         kMaxBlockSize   = 4096
     };
 
-    sp<FLACDecoder> mFLACDecoder;
+    FLACDecoder *mFLACDecoder;
     FLAC__StreamMetadata_StreamInfo mStreamInfo;
     bool mSignalledError;
     bool mSignalledOutputEos;
diff --git a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp
index 4ab1ab2..d0b72b7 100644
--- a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp
+++ b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp
@@ -57,6 +57,7 @@
 
 SoftFlacDecoder::~SoftFlacDecoder() {
     ALOGV("dtor:");
+    delete mFLACDecoder;
 }
 
 void SoftFlacDecoder::initPorts() {
diff --git a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h
index 4a21c34..0f17ed8 100644
--- a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h
+++ b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h
@@ -50,7 +50,7 @@
         kNumOutputBuffers  = 4,
     };
 
-    sp<FLACDecoder> mFLACDecoder;
+    FLACDecoder *mFLACDecoder;
     FLAC__StreamMetadata_StreamInfo mStreamInfo;
     bool mHasStreamInfo;
     size_t mInputBufferCount;
diff --git a/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.cpp b/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.cpp
index 6c147ad..fa93fe5 100644
--- a/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.cpp
+++ b/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.cpp
@@ -140,7 +140,7 @@
         return;
     }
 
-    const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+    const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front();
     bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
     size_t inOffset = inBuffer.offset();
     size_t inSize = inBuffer.size();
diff --git a/media/libstagefright/codecs/g711/dec/C2SoftG711.cpp b/media/libstagefright/codecs/g711/dec/C2SoftG711.cpp
index d296a3d..1049420 100644
--- a/media/libstagefright/codecs/g711/dec/C2SoftG711.cpp
+++ b/media/libstagefright/codecs/g711/dec/C2SoftG711.cpp
@@ -92,8 +92,8 @@
         return;
     }
 
-    const C2ConstLinearBlock &inBuffer =
-	work->input.buffers[0]->data().linearBlocks().front();
+    const C2ConstLinearBlock inBuffer =
+        work->input.buffers[0]->data().linearBlocks().front();
     C2ReadView rView = inBuffer.map().get();
     size_t inOffset = inBuffer.offset();
     size_t inSize = inBuffer.size();
diff --git a/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.cpp b/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.cpp
index 2a3239f..641c342 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.cpp
@@ -304,7 +304,7 @@
         return;
     }
 
-    const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+    const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front();
     size_t inOffset = inBuffer.offset();
     size_t inSize = inBuffer.size();
     uint32_t workIndex = work->input.ordinal.frameIndex.peeku() & 0xFFFFFFFF;
diff --git a/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp
index 9cac87e..0a8891e 100644
--- a/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp
+++ b/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp
@@ -288,7 +288,7 @@
         return;
     }
 
-    const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+    const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front();
     bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
     size_t inOffset = inBuffer.offset();
     size_t inSize = inBuffer.size();
diff --git a/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp b/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp
index d443b7c..bc5fd79 100644
--- a/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp
+++ b/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp
@@ -184,7 +184,7 @@
     info->emphasis           = (temp << 30) >> 30;  /* 2 */
 
 
-    if (!info->bitrate_index || info->sampling_frequency == 3)
+    if (!info->bitrate_index || info->bitrate_index == 15 || info->sampling_frequency == 3)
     {
         err = UNSUPPORTED_FREE_BITRATE;
     }
diff --git a/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.cpp b/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.cpp
index 0ebe7d6..74ea340 100644
--- a/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.cpp
+++ b/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.cpp
@@ -614,7 +614,7 @@
         return;
     }
 
-    const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+    const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front();
     size_t inOffset = inBuffer.offset();
     size_t inSize = inBuffer.size();
     uint32_t workIndex = work->input.ordinal.frameIndex.peeku() & 0xFFFFFFFF;
diff --git a/media/libstagefright/codecs/on2/dec/C2SoftVpx.cpp b/media/libstagefright/codecs/on2/dec/C2SoftVpx.cpp
index 96b303c..8528f26 100644
--- a/media/libstagefright/codecs/on2/dec/C2SoftVpx.cpp
+++ b/media/libstagefright/codecs/on2/dec/C2SoftVpx.cpp
@@ -209,7 +209,7 @@
         return;
     }
 
-    const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+    const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front();
     size_t inOffset = inBuffer.offset();
     size_t inSize = inBuffer.size();
     C2ReadView rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
diff --git a/media/libstagefright/codecs/opus/dec/C2SoftOpus.cpp b/media/libstagefright/codecs/opus/dec/C2SoftOpus.cpp
index 4eec362..47fb6de 100644
--- a/media/libstagefright/codecs/opus/dec/C2SoftOpus.cpp
+++ b/media/libstagefright/codecs/opus/dec/C2SoftOpus.cpp
@@ -251,7 +251,7 @@
         return;
     }
 
-    const C2ConstLinearBlock &inBuffer = work->input.buffers[0]->data().linearBlocks().front();
+    const C2ConstLinearBlock inBuffer = work->input.buffers[0]->data().linearBlocks().front();
     bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
     size_t inOffset = inBuffer.offset();
     size_t inSize = inBuffer.size();
diff --git a/media/libstagefright/flac/dec/FLACDecoder.cpp b/media/libstagefright/flac/dec/FLACDecoder.cpp
index 8c7137c..e0e9211 100644
--- a/media/libstagefright/flac/dec/FLACDecoder.cpp
+++ b/media/libstagefright/flac/dec/FLACDecoder.cpp
@@ -220,9 +220,10 @@
 }
 
 // static
-sp<FLACDecoder> FLACDecoder::Create() {
-    sp<FLACDecoder> decoder = new FLACDecoder();
-    if (decoder->init() != OK) {
+FLACDecoder *FLACDecoder::Create() {
+    FLACDecoder *decoder = new (std::nothrow) FLACDecoder();
+    if (decoder == NULL || decoder->init() != OK) {
+        delete decoder;
         return NULL;
     }
     return decoder;
diff --git a/media/libstagefright/flac/dec/FLACDecoder.h b/media/libstagefright/flac/dec/FLACDecoder.h
index 36282a8..1a33cae 100644
--- a/media/libstagefright/flac/dec/FLACDecoder.h
+++ b/media/libstagefright/flac/dec/FLACDecoder.h
@@ -26,14 +26,14 @@
 namespace android {
 
 // packet based FLAC decoder, wrapps libFLAC stream decoder.
-class FLACDecoder : public RefBase {
+class FLACDecoder {
 
 public:
     enum {
         kMaxChannels = 8,
     };
 
-    static sp<FLACDecoder> Create();
+    static FLACDecoder *Create();
 
     FLAC__StreamMetadata_StreamInfo getStreamInfo() const {
         return mStreamInfo;
@@ -43,10 +43,10 @@
     status_t decodeOneFrame(const uint8_t *inBuffer, size_t inBufferLen,
             short *outBuffer, size_t *outBufferLen);
     void flush();
+    virtual ~FLACDecoder();
 
 protected:
     FLACDecoder();
-    virtual ~FLACDecoder() override;
 
 private:
     // stream properties
diff --git a/media/libstagefright/include/CCodecBufferChannel.h b/media/libstagefright/include/CCodecBufferChannel.h
index eb3255f..51eee10 100644
--- a/media/libstagefright/include/CCodecBufferChannel.h
+++ b/media/libstagefright/include/CCodecBufferChannel.h
@@ -88,7 +88,7 @@
      * Start queueing buffers to the component. This object should never queue
      * buffers before this call.
      */
-    void start(const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat);
+    status_t start(const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat);
 
     /**
      * Stop queueing buffers to the component. This object should never queue
diff --git a/media/libstagefright/include/Codec2Buffer.h b/media/libstagefright/include/Codec2Buffer.h
index 9766b41..eeb889d 100644
--- a/media/libstagefright/include/Codec2Buffer.h
+++ b/media/libstagefright/include/Codec2Buffer.h
@@ -20,6 +20,7 @@
 
 #include <C2Buffer.h>
 
+#include <media/hardware/VideoAPI.h>
 #include <media/MediaCodecBuffer.h>
 
 namespace android {
@@ -109,6 +110,11 @@
 public:
     /**
      * Allocate a new LinearBufferBlock wrapping around C2LinearBlock object.
+     *
+     * \param   format  mandatory buffer format for MediaCodecBuffer
+     * \param   block   C2LinearBlock object to wrap around.
+     * \return          LinearBlockBuffer object with writable mapping.
+     *                  nullptr if unsuccessful.
      */
     static sp<LinearBlockBuffer> Allocate(
             const sp<AMessage> &format, const std::shared_ptr<C2LinearBlock> &block);
@@ -137,6 +143,11 @@
 public:
     /**
      * Allocate a new ConstLinearBlockBuffer wrapping around C2Buffer object.
+     *
+     * \param   format  mandatory buffer format for MediaCodecBuffer
+     * \param   buffer  linear C2Buffer object to wrap around.
+     * \return          ConstLinearBlockBuffer object with readable mapping.
+     *                  nullptr if unsuccessful.
      */
     static sp<ConstLinearBlockBuffer> Allocate(
             const sp<AMessage> &format, const std::shared_ptr<C2Buffer> &buffer);
@@ -156,6 +167,110 @@
     std::shared_ptr<C2Buffer> mBufferRef;
 };
 
+/**
+ * MediaCodecBuffer implementation wraps around C2GraphicBlock.
+ *
+ * This object exposes the underlying bits via accessor APIs and "image-data"
+ * metadata, created automatically at allocation time.
+ */
+class GraphicBlockBuffer : public Codec2Buffer {
+public:
+    /**
+     * Allocate a new GraphicBlockBuffer wrapping around C2GraphicBlock object.
+     * If |block| is not in good color formats, it allocates YV12 local buffer
+     * and copies the content over at asC2Buffer().
+     *
+     * \param   format  mandatory buffer format for MediaCodecBuffer
+     * \param   block   C2GraphicBlock object to wrap around.
+     * \param   alloc   a function to allocate backing ABuffer if needed.
+     * \return          GraphicBlockBuffer object with writable mapping.
+     *                  nullptr if unsuccessful.
+     */
+    static sp<GraphicBlockBuffer> Allocate(
+            const sp<AMessage> &format,
+            const std::shared_ptr<C2GraphicBlock> &block,
+            std::function<sp<ABuffer>(size_t)> alloc);
+
+    std::shared_ptr<C2Buffer> asC2Buffer() override;
+
+    virtual ~GraphicBlockBuffer() = default;
+
+private:
+    GraphicBlockBuffer(
+            const sp<AMessage> &format,
+            const sp<ABuffer> &buffer,
+            C2GraphicView &&view,
+            const std::shared_ptr<C2GraphicBlock> &block,
+            const sp<ABuffer> &imageData,
+            bool wrapped);
+    GraphicBlockBuffer() = delete;
+
+    inline MediaImage2 *imageData() { return (MediaImage2 *)mImageData->data(); }
+
+    C2GraphicView mView;
+    std::shared_ptr<C2GraphicBlock> mBlock;
+    sp<ABuffer> mImageData;
+    const bool mWrapped;
+};
+
+/**
+ * MediaCodecBuffer implementation wraps around graphic C2Buffer object.
+ *
+ * This object exposes the underlying bits via accessor APIs and "image-data"
+ * metadata, created automatically at allocation time.
+ */
+class ConstGraphicBlockBuffer : public Codec2Buffer {
+public:
+    /**
+     * Allocate a new ConstGraphicBlockBuffer wrapping around C2Buffer object.
+     * If |buffer| is not in good color formats, it allocates YV12 local buffer
+     * and copies the content of |buffer| over to expose.
+     *
+     * \param   format  mandatory buffer format for MediaCodecBuffer
+     * \param   buffer  graphic C2Buffer object to wrap around.
+     * \param   alloc   a function to allocate backing ABuffer if needed.
+     * \return          ConstGraphicBlockBuffer object with readable mapping.
+     *                  nullptr if unsuccessful.
+     */
+    static sp<ConstGraphicBlockBuffer> Allocate(
+            const sp<AMessage> &format,
+            const std::shared_ptr<C2Buffer> &buffer,
+            std::function<sp<ABuffer>(size_t)> alloc);
+
+    /**
+     * Allocate a new ConstGraphicBlockBuffer which allocates YV12 local buffer
+     * and copies the content of |buffer| over to expose.
+     *
+     * \param   format  mandatory buffer format for MediaCodecBuffer
+     * \param   alloc   a function to allocate backing ABuffer if needed.
+     * \return          ConstGraphicBlockBuffer object with no wrapping buffer.
+     */
+    static sp<ConstGraphicBlockBuffer> AllocateEmpty(
+            const sp<AMessage> &format,
+            std::function<sp<ABuffer>(size_t)> alloc);
+
+    std::shared_ptr<C2Buffer> asC2Buffer() override;
+    bool canCopy(const std::shared_ptr<C2Buffer> &buffer) const override;
+    bool copy(const std::shared_ptr<C2Buffer> &buffer) override;
+
+    virtual ~ConstGraphicBlockBuffer() = default;
+
+private:
+    ConstGraphicBlockBuffer(
+            const sp<AMessage> &format,
+            const sp<ABuffer> &aBuffer,
+            std::unique_ptr<const C2GraphicView> &&view,
+            const std::shared_ptr<C2Buffer> &buffer,
+            const sp<ABuffer> &imageData,
+            bool wrapped);
+    ConstGraphicBlockBuffer() = delete;
+
+    sp<ABuffer> mImageData;
+    std::unique_ptr<const C2GraphicView> mView;
+    std::shared_ptr<C2Buffer> mBufferRef;
+    const bool mWrapped;
+};
+
 }  // namespace android
 
 #endif  // CODEC2_BUFFER_H_
diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h
index fa22003..1a5304b 100644
--- a/media/libstagefright/include/media/stagefright/ACodec.h
+++ b/media/libstagefright/include/media/stagefright/ACodec.h
@@ -253,6 +253,7 @@
 
     sp<AMessage> mLastOutputFormat;
     bool mIsVideo;
+    bool mIsImage;
     bool mIsEncoder;
     bool mFatalError;
     bool mShutdownInProgress;
@@ -489,11 +490,12 @@
     status_t setupMPEG4EncoderParameters(const sp<AMessage> &msg);
     status_t setupH263EncoderParameters(const sp<AMessage> &msg);
     status_t setupAVCEncoderParameters(const sp<AMessage> &msg);
-    status_t setupHEVCEncoderParameters(const sp<AMessage> &msg);
+    status_t setupHEVCEncoderParameters(const sp<AMessage> &msg, sp<AMessage> &outputFormat);
     status_t setupVPXEncoderParameters(const sp<AMessage> &msg, sp<AMessage> &outputFormat);
 
     status_t verifySupportForProfileAndLevel(int32_t profile, int32_t level);
 
+    status_t configureImageGrid(const sp<AMessage> &msg, sp<AMessage> &outputFormat);
     status_t configureBitrate(
             OMX_VIDEO_CONTROLRATETYPE bitrateMode, int32_t bitrate, int32_t quality = 0);
     void configureEncoderLatency(const sp<AMessage> &msg);
diff --git a/media/libstagefright/include/media/stagefright/CCodec.h b/media/libstagefright/include/media/stagefright/CCodec.h
index 3a2670d..078b03e 100644
--- a/media/libstagefright/include/media/stagefright/CCodec.h
+++ b/media/libstagefright/include/media/stagefright/CCodec.h
@@ -69,6 +69,8 @@
 private:
     typedef std::chrono::time_point<std::chrono::steady_clock> TimePoint;
 
+    status_t tryAndReportOnError(std::function<status_t()> job);
+
     void initiateStop();
     void initiateRelease(bool sendCallback = true);
 
diff --git a/media/libstagefright/include/media/stagefright/CodecBase.h b/media/libstagefright/include/media/stagefright/CodecBase.h
index 268662f..9606fd9 100644
--- a/media/libstagefright/include/media/stagefright/CodecBase.h
+++ b/media/libstagefright/include/media/stagefright/CodecBase.h
@@ -95,8 +95,11 @@
          *
          * @param componentName the unique name of the component specified in
          *                      MediaCodecList.
+         * @param codecInfo the associated codec info if available.
          */
-        virtual void onComponentAllocated(const char *componentName) = 0;
+        virtual void onComponentAllocated(
+                const char *componentName,
+                const sp<MediaCodecInfo> &codecInfo = nullptr) = 0;
         /**
          * Notify MediaCodec that the underlying component is configured.
          *
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index e7faea5..ef8de1f 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -184,6 +184,8 @@
 
     status_t getName(AString *componentName) const;
 
+    status_t getCodecInfo(sp<MediaCodecInfo> *codecInfo) const;
+
     status_t getMetrics(MediaAnalyticsItem * &reply);
 
     status_t setParameters(const sp<AMessage> &params);
@@ -248,6 +250,7 @@
         kWhatRequestIDRFrame                = 'ridr',
         kWhatRequestActivityNotification    = 'racN',
         kWhatGetName                        = 'getN',
+        kWhatGetCodecInfo                   = 'gCoI',
         kWhatSetParameters                  = 'setP',
         kWhatSetCallback                    = 'setC',
         kWhatSetNotification                = 'setN',
@@ -308,6 +311,7 @@
     sp<ALooper> mCodecLooper;
     sp<CodecBase> mCodec;
     AString mComponentName;
+    sp<MediaCodecInfo> mCodecInfo;
     sp<AReplyToken> mReplyID;
     uint32_t mFlags;
     status_t mStickyError;
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecList.h b/media/libstagefright/include/media/stagefright/MediaCodecList.h
index d46fe85..bb4da09 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecList.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecList.h
@@ -74,8 +74,7 @@
             const char *mime,
             bool createEncoder,
             uint32_t flags,
-            Vector<AString> *matchingCodecs,
-            Vector<AString> *owners = nullptr);
+            Vector<AString> *matchingCodecs);
 
     static bool isSoftwareCodec(const AString &componentName);
 
diff --git a/media/libstagefright/include/media/stagefright/MetaDataUtils.h b/media/libstagefright/include/media/stagefright/MetaDataUtils.h
index 3af2218..d5a8080 100644
--- a/media/libstagefright/include/media/stagefright/MetaDataUtils.h
+++ b/media/libstagefright/include/media/stagefright/MetaDataUtils.h
@@ -23,7 +23,7 @@
 namespace android {
 
 struct ABuffer;
-bool MakeAVCCodecSpecificData(MetaDataBase &meta, const sp<ABuffer> &accessUnit);
+bool MakeAVCCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size);
 bool MakeAACCodecSpecificData(MetaDataBase &meta, unsigned profile, unsigned sampling_freq_index,
         unsigned channel_configuration);
 
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 0b7bd26..0fa9fcb 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -634,7 +634,7 @@
 
         if (mFormat == NULL) {
             mFormat = new MetaData;
-            if (!MakeAVCCodecSpecificData(*mFormat, accessUnit)) {
+            if (!MakeAVCCodecSpecificData(*mFormat, accessUnit->data(), accessUnit->size())) {
                 mFormat.clear();
             }
         }
@@ -1010,7 +1010,7 @@
         }
         if (mFormat == NULL) {
             mFormat = new MetaData;
-            if (!MakeAVCCodecSpecificData(*mFormat, mBuffer)) {
+            if (!MakeAVCCodecSpecificData(*mFormat, mBuffer->data(), mBuffer->size())) {
                 ALOGW("Creating dummy AVC format for scrambled content");
                 mFormat = new MetaData;
                 mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
@@ -1172,7 +1172,9 @@
 
             if (mFormat == NULL) {
                 mFormat = new MetaData;
-                if (!MakeAVCCodecSpecificData(*mFormat, accessUnit)) {
+                if (!MakeAVCCodecSpecificData(*mFormat,
+                        accessUnit->data(),
+                        accessUnit->size())) {
                     mFormat.clear();
                 }
             }
@@ -1493,7 +1495,9 @@
         const uint8_t *data, size_t size) {
     static const char kStartCode[] = "\x00\x00\x01";
 
-    if (size < 3) {
+    // per ISO/IEC 14496-2 6.2.1, a chunk has a 3-byte prefix + 1-byte start code
+    // we need at least <prefix><start><next prefix> to successfully scan
+    if (size < 3 + 1 + 3) {
         return -EAGAIN;
     }
 
@@ -1501,7 +1505,7 @@
         return -EAGAIN;
     }
 
-    size_t offset = 3;
+    size_t offset = 4;
     while (offset + 2 < size) {
         if (!memcmp(&data[offset], kStartCode, 3)) {
             return offset;
@@ -1552,6 +1556,9 @@
                     state = EXPECT_VISUAL_OBJECT_START;
                 } else {
                     discard = true;
+                    offset += chunkSize;
+                    ALOGW("b/74114680, advance to next chunk");
+                    android_errorWriteLog(0x534e4554, "74114680");
                 }
                 break;
             }
diff --git a/media/libstagefright/omx/OMXUtils.cpp b/media/libstagefright/omx/OMXUtils.cpp
index f597e02..f7b569d 100644
--- a/media/libstagefright/omx/OMXUtils.cpp
+++ b/media/libstagefright/omx/OMXUtils.cpp
@@ -163,6 +163,8 @@
             "audio_decoder.ac3", "audio_encoder.ac3" },
         { MEDIA_MIMETYPE_AUDIO_EAC3,
             "audio_decoder.eac3", "audio_encoder.eac3" },
+        { MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC,
+            "image_decoder.heic", "image_encoder.heic" },
     };
 
     static const size_t kNumMimeToRole =
diff --git a/packages/MediaComponents/Android.mk b/packages/MediaComponents/Android.mk
index 22eea25..19e4b24 100644
--- a/packages/MediaComponents/Android.mk
+++ b/packages/MediaComponents/Android.mk
@@ -32,6 +32,7 @@
 
 # TODO: Use System SDK once public APIs are approved
 # LOCAL_SDK_VERSION := system_current
+LOCAL_PRIVATE_PLATFORM_APIS := true
 
 LOCAL_SRC_FILES := \
     $(call all-java-files-under, src) \
diff --git a/packages/MediaComponents/res/layout/media_controller.xml b/packages/MediaComponents/res/layout/media_controller.xml
index aafe8b0..8488c84 100644
--- a/packages/MediaComponents/res/layout/media_controller.xml
+++ b/packages/MediaComponents/res/layout/media_controller.xml
@@ -104,7 +104,7 @@
             <view class="com.android.support.mediarouter.app.MediaRouteButton"
                 android:id="@+id/cast"
                 android:layout_centerVertical="true"
-                android:visibility="visible"
+                android:visibility="gone"
                 style="@style/TitleBarButton" />
         </LinearLayout>
 
diff --git a/packages/MediaComponents/runcts.sh b/packages/MediaComponents/runcts.sh
new file mode 100644
index 0000000..0cf0e44
--- /dev/null
+++ b/packages/MediaComponents/runcts.sh
@@ -0,0 +1,212 @@
+#!/bin/bash
+# Copyright 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Usage '. runcts.sh'
+
+function _runtest_cts_mediacomponent_usage() {
+  echo 'runtest-cts-MediaComponents [option]: Build, flash device,'
+  echo '              and run subset of CtsMediaTestCases that MediaComponents covers.'
+  echo '          *Warning* This bypasses CTS setup (e.g. download media contents from server)'
+  echo '          For running CTS in official way, use atest or cts-tradefed '
+  echo '     -h|--help: This help'
+  echo '     --skip: Skip build and flash. Just rerun-tests'
+  echo '     --min: Only rebuild tests and updatable library.'
+  echo '     -s [device_id]: Specify a device name to run test against.'
+  echo '                     You can define ${ADBHOST} instead.'
+  echo '     -r [count]: Repeat tests for given count. It will stop when fails.'
+  echo '     --ignore: Keep repeating tests even when it fails.'
+  echo '     -t [test]: Only run the specific test. Can be either a class or a method.'
+}
+
+function runtest-cts-MediaComponents() {
+  # Edit here if you want to support other tests.
+  # List up libs and apks in the media_api needed for tests, and place test target at the last.
+  local TEST_PACKAGE_DIR=("frameworks/av/packages/MediaComponents/test")
+  local TEST_PACKAGE=("android.media.cts")
+  local BUILD_TARGETS=("MediaComponents" "CtsMediaTestCases")
+  # Don't include MediaComponents -- if we simply install it, system server
+  # wouldn't use the installed one.
+  local INSTALL_TARGETS=("CtsMediaTestCases")
+  local TEST_RUNNER="android.support.test.runner.AndroidJUnitRunner"
+  local DEPENDENCIES=("mockito-target-minus-junit4" "android-support-test" "compatibility-device-util")
+  local DEFAULT_TEST_TARGET=""
+  DEFAULT_TEST_TARGET+="android.media.cts.MediaBrowser2Test"
+  DEFAULT_TEST_TARGET+=",android.media.cts.MediaController2Test"
+  DEFAULT_TEST_TARGET+=",android.media.cts.MediaMetadata2Test"
+  DEFAULT_TEST_TARGET+=",android.media.cts.MediaSession2Test"
+  DEFAULT_TEST_TARGET+=",android.media.cts.MediaSession2_PermissionTest"
+  DEFAULT_TEST_TARGET+=",android.media.cts.MediaSessionManager_MediaSession2Test"
+  DEFAULT_TEST_TARGET+=",android.media.cts.SessionToken2Test"
+  if [[ -z "${ANDROID_BUILD_TOP}" ]]; then
+    echo "Needs to lunch a target first"
+    return
+  fi
+
+  local old_path=${OLDPWD}
+  while true; do
+    local OPTION_SKIP="false"
+    local OPTION_MIN="false"
+    local OPTION_REPEAT_COUNT="1"
+    local OPTION_IGNORE="false"
+    local OPTION_TEST_TARGET="${DEFAULT_TEST_TARGET}"
+    local adbhost_local
+    while (( "$#" )); do
+      case "${1}" in
+        -h|--help)
+          _runtest_cts_mediacomponent_usage
+          return
+          ;;
+        --skip)
+          OPTION_SKIP="true"
+          ;;
+        --min)
+          OPTION_MIN="true"
+          ;;
+        -s)
+          shift
+          adbhost_local=${1}
+          ;;
+        -r)
+          shift
+          OPTION_REPEAT_COUNT="${1}"
+          ;;
+        --ignore)
+          OPTION_IGNORE="true"
+          ;;
+        -t)
+          shift
+          OPTION_TEST_TARGET="${1}"
+      esac
+      shift
+    done
+
+    # Build adb command.
+    local adb
+    if [[ -z "${adbhost_local}" ]]; then
+      adbhost_local=${ADBHOST}
+    fi
+    if [[ -z "${adbhost_local}" ]]; then
+      local device_count=$(adb devices | sed '/^[[:space:]]*$/d' | wc -l)
+      if [[ "${device_count}" != "2" ]]; then
+        echo "Too many devices. Specify a device." && break
+      fi
+      adb="adb"
+    else
+      adb="adb -s ${adbhost_local}"
+    fi
+
+    local target_dir="${ANDROID_BUILD_TOP}/${TEST_PACKAGE_DIR}"
+    #local TEST_PACKAGE=$(sed -n 's/^.*\bpackage\b="\([a-z0-9\.]*\)".*$/\1/p' ${target_dir}/AndroidManifest.xml)
+
+    if [[ "${OPTION_SKIP}" != "true" ]]; then
+      # Build dependencies if needed.
+      local dependency
+      local build_dependency=""
+      for dependency in ${DEPENDENCIES[@]}; do
+        if [[ "${dependency}" == "out/"* ]]; then
+          if [[ ! -f ${ANDROID_BUILD_TOP}/${dependency} ]]; then
+            build_dependency="true"
+            break
+          fi
+        else
+          if [[ "$(find ${OUT} -name ${dependency}_intermediates | wc -l)" == "0" ]]; then
+            build_dependency="true"
+            break
+          fi
+        fi
+      done
+      if [[ "${build_dependency}" == "true" ]]; then
+        echo "Building dependencies. Will only print stderr."
+        m ${DEPENDENCIES[@]} -j > /dev/null
+      fi
+
+      # Build test apk and required apk.
+      local build_targets="${BUILD_TARGETS[@]}"
+      if [[ "${OPTION_MIN}" != "true" ]]; then
+        build_targets="${build_targets} droid"
+      fi
+      m ${build_targets} -j || break
+
+      local device_build_type="$(${adb} shell getprop ro.build.type)"
+      if [[ "${device_build_type}" == "user" ]]; then
+        # User build. Cannot adb sync
+        ${adb} reboot bootloader
+        fastboot flashall
+      else
+        ${adb} root
+        local device_verity_mode="$(${adb} shell getprop ro.boot.veritymode)"
+        if [[ "${device_verity_mode}" != "disabled" ]]; then
+          ${adb} disable-verity
+          ${adb} reboot
+          ${adb} wait-for-device || break
+          ${adb} root
+        fi
+        ${adb} remount
+        ${adb} shell stop
+        ${adb} shell setprop log.tag.MediaSessionService DEBUG
+        ${adb} sync
+        ${adb} shell start
+      fi
+      ${adb} wait-for-device || break
+      # Ensure package manager is loaded.
+      # TODO(jaewan): Find better way to wait
+      sleep 15
+
+      # Install apks
+      local install_failed="false"
+      for target in ${INSTALL_TARGETS[@]}; do
+        local apk_path=$(find ${OUT}/system ${OUT}/data -name ${target}.apk)
+        local apk_num=$(find ${OUT}/system ${OUT}/data -name ${target}.apk | wc -l)
+        if [[ "${apk_num}" != "1" ]]; then
+          echo "Cannot locate a ${target}.apk. Found ${apk_num} apks" && break
+        fi
+        echo "Installing ${target}.apk. path=${apk_path}"
+        ${adb} install -r ${apk_path}
+        if [[ "${?}" != "0" ]]; then
+          install_failed="true"
+          break
+        fi
+      done
+      if [[ "${install_failed}" == "true" ]]; then
+        echo "Failed to install. Test wouldn't run."
+        break
+      fi
+    fi
+
+    local test_target=""
+    if [[ -n "${OPTION_TEST_TARGET}" ]]; then
+      test_target="-e class ${OPTION_TEST_TARGET}"
+    fi
+
+    local i
+    local tmpfile=$(tempfile)
+    for ((i=1; i <= ${OPTION_REPEAT_COUNT}; i++)); do
+      echo "Run test ${i}/${OPTION_REPEAT_COUNT}"
+      ${adb} shell am instrument ${test_target} -w ${TEST_PACKAGE}/${TEST_RUNNER} >& ${tmpfile}
+      cat ${tmpfile}
+      if [[ "${OPTION_IGNORE}" != "true" ]]; then
+        if [[ -n "$(grep ${tmpfile} -e 'FAILURE\|crashed')" ]]; then
+          # am instrument doesn't return error code so need to grep result message instead
+          break
+        fi
+      fi
+    done
+    rm ${tmpfile}
+    break
+  done
+}
+
+echo "Following functions are added to your environment:"
+_runtest_cts_mediacomponent_usage
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
index 9ee4928..3d812f8 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
@@ -41,6 +41,7 @@
     void onDisconnected();
 
     void onCustomLayoutChanged(in List<Bundle> commandButtonlist);
+    void onAllowedCommandsChanged(in Bundle commands);
 
     void onCustomCommand(in Bundle command, in Bundle args, in ResultReceiver receiver);
 
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index 4a4f250..96fdda0 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -314,12 +314,12 @@
     }
 
     @Override
-    public void skipToPrevious_impl() {
+    public void skipToPreviousItem_impl() {
         sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM);
     }
 
     @Override
-    public void skipToNext_impl() {
+    public void skipToNextItem_impl() {
         sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM);
     }
 
@@ -351,7 +351,7 @@
     @Override
     public void setVolumeTo_impl(int value, int flags) {
         // TODO(hdmoon): sanity check
-        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_SET_VOLUME);
+        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAYBACK_SET_VOLUME);
         if (binder != null) {
             try {
                 binder.setVolumeTo(mSessionCallbackStub, value, flags);
@@ -366,7 +366,7 @@
     @Override
     public void adjustVolume_impl(int direction, int flags) {
         // TODO(hdmoon): sanity check
-        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_SET_VOLUME);
+        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAYBACK_SET_VOLUME);
         if (binder != null) {
             try {
                 binder.adjustVolume(mSessionCallbackStub, direction, flags);
@@ -561,7 +561,7 @@
         Bundle args = new Bundle();
         args.putInt(MediaSession2Stub.ARGUMENT_KEY_ITEM_INDEX, item);
         sendTransportControlCommand(
-                MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_TO_PLAYLIST_ITEM, args);
+                MediaSession2.COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM, args);
         */
     }
 
@@ -651,7 +651,7 @@
     }
 
     @Override
-    public MediaItem2 getCurrentPlaylistItem_impl() {
+    public MediaItem2 getCurrentMediaItem_impl() {
         // TODO(jaewan): Implement
         return null;
     }
@@ -776,6 +776,12 @@
         });
     }
 
+    void onAllowedCommandsChanged(final CommandGroup commands) {
+        mCallbackExecutor.execute(() -> {
+            mCallback.onAllowedCommandsChanged(mInstance, commands);
+        });
+    }
+
     void onCustomLayoutChanged(final List<CommandButton> layout) {
         mCallbackExecutor.execute(() -> {
             mCallback.onCustomLayoutChanged(mInstance, layout);
diff --git a/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java b/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
index 252512c..039ff8f 100644
--- a/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
@@ -44,14 +44,11 @@
     private DataSourceDesc mDataSourceDesc;
 
     // From the public API
-    public MediaItem2Impl(Context context, String mediaId,
-            DataSourceDesc dsd, MediaMetadata2 metadata, @Flags int flags) {
+    public MediaItem2Impl(@NonNull Context context, @NonNull String mediaId,
+            @Nullable DataSourceDesc dsd, @Nullable MediaMetadata2 metadata, @Flags int flags) {
         if (mediaId == null) {
             throw new IllegalArgumentException("mediaId shouldn't be null");
         }
-        if (dsd == null) {
-            throw new IllegalArgumentException("dsd shouldn't be null");
-        }
         if (metadata != null && !TextUtils.equals(mediaId, metadata.getMediaId())) {
             throw new IllegalArgumentException("metadata's id should be matched with the mediaid");
         }
@@ -186,10 +183,7 @@
         }
 
         @Override
-        public Builder setDataSourceDesc_impl(@NonNull DataSourceDesc dataSourceDesc) {
-            if (dataSourceDesc == null) {
-                throw new IllegalArgumentException("dataSourceDesc shouldn't be null");
-            }
+        public Builder setDataSourceDesc_impl(@Nullable DataSourceDesc dataSourceDesc) {
             mDataSourceDesc = dataSourceDesc;
             return mInstance;
         }
diff --git a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
index 1cc9a77..5351394 100644
--- a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
@@ -24,7 +24,7 @@
 import android.media.MediaLibraryService2.MediaLibrarySession.Builder;
 import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
 import android.media.MediaPlayerBase;
-import android.media.MediaPlaylistController;
+import android.media.MediaPlaylistAgent;
 import android.media.MediaSession2;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.MediaSessionService2;
@@ -68,11 +68,11 @@
     public static class MediaLibrarySessionImpl extends MediaSession2Impl
             implements MediaLibrarySessionProvider {
         public MediaLibrarySessionImpl(Context context,
-                MediaPlayerBase player, String id, MediaPlaylistController mplc,
+                MediaPlayerBase player, String id, MediaPlaylistAgent playlistAgent,
                 VolumeProvider2 volumeProvider,
                 PendingIntent sessionActivity, Executor callbackExecutor,
                 MediaLibrarySessionCallback callback) {
-            super(context, player, id, mplc, volumeProvider, sessionActivity, callbackExecutor,
+            super(context, player, id, playlistAgent, volumeProvider, sessionActivity, callbackExecutor,
                     callback);
             // Don't put any extra initialization here. Here's the reason.
             // System service will recognize this session inside of the super constructor and would
@@ -141,7 +141,7 @@
 
         @Override
         public MediaLibrarySession build_impl() {
-            return new MediaLibrarySessionImpl(mContext, mPlayer, mId, mMplc,
+            return new MediaLibrarySessionImpl(mContext, mPlayer, mId, mPlaylistAgent,
                     mVolumeProvider, mSessionActivity, mCallbackExecutor, mCallback).getInstance();
         }
     }
diff --git a/packages/MediaComponents/src/com/android/media/MediaPlaylistAgentImpl.java b/packages/MediaComponents/src/com/android/media/MediaPlaylistAgentImpl.java
new file mode 100644
index 0000000..b3c17f9
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/MediaPlaylistAgentImpl.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.MediaItem2;
+import android.media.MediaMetadata2;
+import android.media.MediaPlaylistAgent;
+import android.media.MediaPlaylistAgent.PlaylistEventCallback;
+import android.media.update.MediaPlaylistAgentProvider;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+public class MediaPlaylistAgentImpl implements MediaPlaylistAgentProvider {
+    private final Context mContext;
+    private final MediaPlaylistAgent mInstance;
+
+    public MediaPlaylistAgentImpl(Context context, MediaPlaylistAgent instance) {
+        mContext = context;
+        mInstance = instance;
+    }
+
+    final public void registerPlaylistEventCallback_impl(
+            @NonNull @CallbackExecutor Executor executor, @NonNull PlaylistEventCallback callback) {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor shouldn't be null");
+        }
+        if (callback == null) {
+            throw new IllegalArgumentException("callback shouldn't be null");
+        }
+        // TODO(jaewan): implement this (b/74090741)
+    }
+
+    final public void unregisterPlaylistEventCallback_impl(
+            @NonNull PlaylistEventCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback shouldn't be null");
+        }
+        // TODO(jaewan): implement this (b/74090741)
+    }
+
+    final public void notifyPlaylistChanged_impl() {
+        // TODO(jaewan): implement this (b/74090741)
+    }
+
+    final public void notifyPlaylistMetadataChanged_impl() {
+        // TODO(jaewan): implement this (b/74090741)
+    }
+
+    final public void notifyShuffleModeChanged_impl() {
+        // TODO(jaewan): implement this (b/74090741)
+    }
+
+    final public void notifyRepeatModeChanged_impl() {
+        // TODO(jaewan): implement this (b/74090741)
+    }
+
+    public @Nullable List<MediaItem2> getPlaylist_impl() {
+        // TODO(jaewan): implement this (b/74090741)
+        return null;
+    }
+
+    public void setPlaylist_impl(@NonNull List<MediaItem2> list,
+            @Nullable MediaMetadata2 metadata) {
+        if (list == null) {
+            throw new IllegalArgumentException("list shouldn't be null");
+        }
+        // TODO(jaewan): implement this (b/74090741)
+    }
+
+    public @Nullable MediaMetadata2 getPlaylistMetadata_impl() {
+        // TODO(jaewan): implement this (b/74090741)
+        return null;
+    }
+
+    public void updatePlaylistMetadata_impl(@Nullable MediaMetadata2 metadata) {
+        // TODO(jaewan): implement this (b/74090741)
+    }
+
+    public void addPlaylistItem_impl(int index, @NonNull MediaItem2 item) {
+        if (item == null) {
+            throw new IllegalArgumentException("item shouldn't be null");
+        }
+        // TODO(jaewan): implement this (b/74090741)
+    }
+
+    public void removePlaylistItem_impl(@NonNull MediaItem2 item) {
+        if (item == null) {
+            throw new IllegalArgumentException("item shouldn't be null");
+        }
+        // TODO(jaewan): implement this (b/74090741)
+    }
+
+    public void replacePlaylistItem_impl(int index, @NonNull MediaItem2 item) {
+        if (index < 0) {
+            throw new IllegalArgumentException("index can not have a negative value");
+        }
+        if (item == null) {
+            throw new IllegalArgumentException("item shouldn't be null");
+        }
+        // TODO(jaewan): implement this (b/74090741)
+    }
+
+    public void skipToPlaylistItem_impl(@NonNull MediaItem2 item) {
+        if (item == null) {
+            throw new IllegalArgumentException("item shouldn't be null");
+        }
+        // TODO(jaewan): implement this (b/74090741)
+    }
+
+    public void skipToPreviousItem_impl() {
+        // TODO(jaewan): implement this (b/74090741)
+    }
+
+    public void skipToNextItem_impl() {
+        // TODO(jaewan): implement this (b/74090741)
+    }
+
+    public int getRepeatMode_impl() {
+        // TODO(jaewan): implement this (b/74090741)
+        return MediaPlaylistAgent.REPEAT_MODE_NONE;
+    }
+
+    public void setRepeatMode_impl(int repeatMode) {
+        // TODO(jaewan): implement this (b/74090741)
+    }
+
+    public int getShuffleMode_impl() {
+        // TODO(jaewan): implement this (b/74090741)
+        return MediaPlaylistAgent.SHUFFLE_MODE_NONE;
+    }
+
+    public void setShuffleMode_impl(int shuffleMode) {
+        // TODO(jaewan): implement this (b/74090741)
+    }
+}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java b/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
index b8e651e..451368f 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
@@ -215,6 +215,27 @@
     }
 
     @Override
+    public void onAllowedCommandsChanged(Bundle commandsBundle) {
+        final MediaController2Impl controller;
+        try {
+            controller = getController();
+        } catch (IllegalStateException e) {
+            Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+            return;
+        }
+        if (controller == null) {
+            // TODO(jaewan): Revisit here. Could be a bug
+            return;
+        }
+        CommandGroup commands = CommandGroup.fromBundle(controller.getContext(), commandsBundle);
+        if (commands == null) {
+            Log.w(TAG, "onAllowedCommandsChanged(): Ignoring null commands");
+            return;
+        }
+        controller.onAllowedCommandsChanged(commands);
+    }
+
+    @Override
     public void onCustomCommand(Bundle commandBundle, Bundle args, ResultReceiver receiver) {
         final MediaController2Impl controller;
         try {
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index 6aab84a..4c6b0a4 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -21,14 +21,12 @@
 import static android.media.SessionToken2.TYPE_SESSION;
 import static android.media.SessionToken2.TYPE_SESSION_SERVICE;
 
-import android.Manifest.permission;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
@@ -39,7 +37,7 @@
 import android.media.MediaMetadata2;
 import android.media.MediaPlayerBase;
 import android.media.MediaPlayerBase.PlayerEventCallback;
-import android.media.MediaPlaylistController;
+import android.media.MediaPlaylistAgent;
 import android.media.MediaSession2;
 import android.media.MediaSession2.Builder;
 import android.media.MediaSession2.Command;
@@ -107,7 +105,7 @@
     @GuardedBy("mLock")
     private MediaPlayerBase mPlayer;
     @GuardedBy("mLock")
-    private MediaPlaylistController mMplc;
+    private MediaPlaylistAgent mPlaylistAgent;
     @GuardedBy("mLock")
     private VolumeProvider2 mVolumeProvider;
     @GuardedBy("mLock")
@@ -120,14 +118,14 @@
      * @param context
      * @param player
      * @param id
-     * @param mplc
+     * @param playlistAgent
      * @param volumeProvider
      * @param sessionActivity
      * @param callbackExecutor
      * @param callback
      */
     public MediaSession2Impl(Context context, MediaPlayerBase player, String id,
-            MediaPlaylistController mplc, VolumeProvider2 volumeProvider,
+            MediaPlaylistAgent playlistAgent, VolumeProvider2 volumeProvider,
             PendingIntent sessionActivity,
             Executor callbackExecutor, SessionCallback callback) {
         // TODO(jaewan): Keep other params.
@@ -142,7 +140,7 @@
         mSessionActivity = sessionActivity;
         mSessionStub = new MediaSession2Stub(this);
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
-        mMplc = mplc;
+        mPlaylistAgent = playlistAgent;
 
         // Infer type from the id and package name.
         String libraryService = getServiceName(context, MediaLibraryService2.SERVICE_INTERFACE, id);
@@ -206,13 +204,13 @@
     }
 
     @Override
-    public void updatePlayer_impl(MediaPlayerBase player, MediaPlaylistController mplc,
+    public void updatePlayer_impl(MediaPlayerBase player, MediaPlaylistAgent playlistAgent,
             VolumeProvider2 volumeProvider) throws IllegalArgumentException {
         ensureCallingThread();
         if (player == null) {
             throw new IllegalArgumentException("player shouldn't be null");
         }
-        mMplc = mplc;
+        mPlaylistAgent = playlistAgent;
         setPlayer(player, volumeProvider);
     }
 
@@ -344,18 +342,16 @@
         ensureCallingThread();
         final MediaPlayerBase player = mPlayer;
         if (player != null) {
-            // TODO: Uncomment or remove
-            //player.stop();
-            player.pause();
+            player.reset();
         } else if (DEBUG) {
             Log.d(TAG, "API calls after the close()", new IllegalStateException());
         }
     }
 
     @Override
-    public void skipToPrevious_impl() {
+    public void skipToPreviousItem_impl() {
         ensureCallingThread();
-        // TODO: Uncomment or remove
+        // TODO(jaewan): Implement this (b/74175632)
         /*
         final MediaPlayerBase player = mPlayer;
         if (player != null) {
@@ -368,14 +364,17 @@
     }
 
     @Override
-    public void skipToNext_impl() {
+    public void skipToNextItem_impl() {
         ensureCallingThread();
+        // TODO(jaewan): Implement this (b/74175632)
+        /*
         final MediaPlayerBase player = mPlayer;
         if (player != null) {
             player.skipToNext();
         } else if (DEBUG) {
             Log.d(TAG, "API calls after the close()", new IllegalStateException());
         }
+        */
     }
 
     @Override
@@ -431,7 +430,13 @@
 
     @Override
     public void setAllowedCommands_impl(ControllerInfo controller, CommandGroup commands) {
-        // TODO(jaewan): Implement
+        if (controller == null) {
+            throw new IllegalArgumentException("controller shouldn't be null");
+        }
+        if (commands == null) {
+            throw new IllegalArgumentException("commands shouldn't be null");
+        }
+        mSessionStub.setAllowedCommands(controller, commands);
     }
 
     @Override
@@ -455,7 +460,7 @@
         /*
         final MediaPlayerBase player = mPlayer;
         if (player != null) {
-            // TODO implement, use the SessionPlaylistController itf
+            // TODO implement and use SessionPlaylistAgent
             //player.setPlaylist(playlist);
             mSessionStub.notifyPlaylistChanged(playlist);
         } else if (DEBUG) {
@@ -885,8 +890,7 @@
 
         @Override
         public void addAllPredefinedCommands_impl() {
-            final int COMMAND_CODE_MAX = 22;
-            for (int i = 1; i <= COMMAND_CODE_MAX; i++) {
+            for (int i = 1; i <= MediaSession2.COMMAND_CODE_MAX; i++) {
                 mCommands.add(new Command(mContext, i));
             }
         }
@@ -974,31 +978,13 @@
             mInstance = instance;
             mUid = uid;
             mPackageName = packageName;
-
-            // TODO(jaewan): Remove this workaround
-            if ("com.android.server.media".equals(packageName)) {
-                mIsTrusted = true;
-            } else if (context.checkPermission(permission.MEDIA_CONTENT_CONTROL, pid, uid) ==
-                    PackageManager.PERMISSION_GRANTED) {
-                mIsTrusted = true;
-            } else {
-                // TODO(jaewan): Also consider enabled notification listener.
-                mIsTrusted = false;
-                // System apps may bind across the user so uid can be differ.
-                // Skip sanity check for the system app.
-                try {
-                    int uidForPackage = context.getPackageManager().getPackageUid(packageName, 0);
-                    if (uid != uidForPackage) {
-                        throw new IllegalArgumentException("Illegal call from uid=" + uid +
-                                ", pkg=" + packageName + ". Expected uid" + uidForPackage);
-                    }
-                } catch (NameNotFoundException e) {
-                    // Rethrow exception with different name because binder methods only accept
-                    // RemoteException.
-                    throw new IllegalArgumentException(e);
-                }
-            }
             mControllerBinder = callback;
+            MediaSessionManager manager =
+                  (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
+            // Ask server whether the controller is trusted.
+            // App cannot know this because apps cannot query enabled notification listener for
+            // another package, but system server can do.
+            mIsTrusted = manager.isTrusted(uid, packageName);
         }
 
         @Override
@@ -1290,7 +1276,7 @@
         String mId;
         Executor mCallbackExecutor;
         C mCallback;
-        MediaPlaylistController mMplc;
+        MediaPlaylistAgent mPlaylistAgent;
         VolumeProvider2 mVolumeProvider;
         PendingIntent mSessionActivity;
 
@@ -1319,11 +1305,11 @@
         }
 
         @Override
-        public void setPlaylistController_impl(MediaPlaylistController mplc) {
-            if (mplc == null) {
-                throw new IllegalArgumentException("mplc shouldn't be null");
+        public void setPlaylistAgent_impl(MediaPlaylistAgent playlistAgent) {
+            if (playlistAgent == null) {
+                throw new IllegalArgumentException("playlistAgent shouldn't be null");
             }
-            mMplc = mplc;
+            mPlaylistAgent = playlistAgent;
         }
 
         public void setVolumeProvider_impl(VolumeProvider2 volumeProvider) {
@@ -1372,7 +1358,7 @@
                 mCallback = new SessionCallback(mContext) {};
             }
 
-            return new MediaSession2Impl(mContext, mPlayer, mId, mMplc,
+            return new MediaSession2Impl(mContext, mPlayer, mId, mPlaylistAgent,
                     mVolumeProvider, mSessionActivity, mCallbackExecutor, mCallback).getInstance();
         }
     }
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index 785248c..d4164f6 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -267,7 +267,7 @@
                 final Bundle paramsBundle = (params != null) ? params.toBundle() : null;
                 final PendingIntent sessionActivity = session.getSessionActivity();
                 final List<MediaItem2> playlist =
-                        allowedCommands.hasCommand(MediaSession2.COMMAND_CODE_PLAYLIST_GET)
+                        allowedCommands.hasCommand(MediaSession2.COMMAND_CODE_PLAYLIST_GET_LIST)
                                 ? session.getInstance().getPlaylist() : null;
                 final List<Bundle> playlistBundle;
                 if (playlist != null) {
@@ -332,23 +332,23 @@
             throws RuntimeException {
         final MediaSession2Impl session = getSession();
         final ControllerInfo controller = getControllerIfAble(
-                caller, MediaSession2.COMMAND_CODE_SET_VOLUME);
+                caller, MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME);
         if (session == null || controller == null) {
             return;
         }
         session.getCallbackExecutor().execute(() -> {
-            if (getControllerIfAble(caller, MediaSession2.COMMAND_CODE_SET_VOLUME) == null) {
+            if (getControllerIfAble(caller, MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME) == null) {
                 return;
             }
             // TODO(jaewan): Sanity check.
             Command command = new Command(
-                    session.getContext(), MediaSession2.COMMAND_CODE_SET_VOLUME);
+                    session.getContext(), MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME);
             boolean accepted = session.getCallback().onCommandRequest(session.getInstance(),
                     controller, command);
             if (!accepted) {
                 // Don't run rejected command.
                 if (DEBUG) {
-                    Log.d(TAG, "Command " + MediaSession2.COMMAND_CODE_SET_VOLUME + " from "
+                    Log.d(TAG, "Command " + MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME + " from "
                             + controller + " was rejected by " + session);
                 }
                 return;
@@ -368,23 +368,23 @@
             throws RuntimeException {
         final MediaSession2Impl session = getSession();
         final ControllerInfo controller = getControllerIfAble(
-                caller, MediaSession2.COMMAND_CODE_SET_VOLUME);
+                caller, MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME);
         if (session == null || controller == null) {
             return;
         }
         session.getCallbackExecutor().execute(() -> {
-            if (getControllerIfAble(caller, MediaSession2.COMMAND_CODE_SET_VOLUME) == null) {
+            if (getControllerIfAble(caller, MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME) == null) {
                 return;
             }
             // TODO(jaewan): Sanity check.
             Command command = new Command(
-                    session.getContext(), MediaSession2.COMMAND_CODE_SET_VOLUME);
+                    session.getContext(), MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME);
             boolean accepted = session.getCallback().onCommandRequest(session.getInstance(),
                     controller, command);
             if (!accepted) {
                 // Don't run rejected command.
                 if (DEBUG) {
-                    Log.d(TAG, "Command " + MediaSession2.COMMAND_CODE_SET_VOLUME + " from "
+                    Log.d(TAG, "Command " + MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME + " from "
                             + controller + " was rejected by " + session);
                 }
                 return;
@@ -435,10 +435,10 @@
                     session.getInstance().stop();
                     break;
                 case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM:
-                    session.getInstance().skipToPrevious();
+                    session.getInstance().skipToPreviousItem();
                     break;
                 case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM:
-                    session.getInstance().skipToNext();
+                    session.getInstance().skipToNextItem();
                     break;
                 case MediaSession2.COMMAND_CODE_PLAYBACK_PREPARE:
                     session.getInstance().prepare();
@@ -452,18 +452,21 @@
                 case MediaSession2.COMMAND_CODE_PLAYBACK_SEEK_TO:
                     session.getInstance().seekTo(args.getLong(ARGUMENT_KEY_POSITION));
                     break;
-                case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_TO_PLAYLIST_ITEM:
+                case MediaSession2.COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM:
                     // TODO(jaewan): Implement
                     /*
                     session.getInstance().skipToPlaylistItem(
                             args.getInt(ARGUMENT_KEY_ITEM_INDEX));
                     */
                     break;
+                    // TODO(jaewan): Remove (b/74116823)
+                    /*
                 case MediaSession2.COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS:
                     session.getInstance().setPlaylistParams(
                             PlaylistParams.fromBundle(session.getContext(),
                                     args.getBundle(ARGUMENT_KEY_PLAYLIST_PARAMS)));
                     break;
+                    */
                 default:
                     // TODO(jaewan): Resend unknown (new) commands through the custom command.
             }
@@ -910,7 +913,7 @@
         final List<ControllerInfo> list = getControllers();
         for (int i = 0; i < list.size(); i++) {
             final IMediaSession2Callback controllerBinder = getControllerBinderIfAble(
-                    list.get(i), MediaSession2.COMMAND_CODE_PLAYLIST_GET);
+                    list.get(i), MediaSession2.COMMAND_CODE_PLAYLIST_GET_LIST);
             if (controllerBinder != null) {
                 try {
                     controllerBinder.onPlaylistChanged(bundleList);
@@ -955,6 +958,22 @@
         }
     }
 
+    public void setAllowedCommands(ControllerInfo controller, CommandGroup commands) {
+        synchronized (mLock) {
+            mAllowedCommandGroupMap.put(controller, commands);
+        }
+        final IMediaSession2Callback controllerBinder = getControllerBinderIfAble(controller);
+        if (controllerBinder == null) {
+            return;
+        }
+        try {
+            controllerBinder.onAllowedCommandsChanged(commands.toBundle());
+        } catch (RemoteException e) {
+            Log.w(TAG, "Controller is gone", e);
+            // TODO(jaewan): What to do when the controller is gone?
+        }
+    }
+
     public void sendCustomCommand(ControllerInfo controller, Command command, Bundle args,
             ResultReceiver receiver) {
         if (receiver != null && controller == null) {
diff --git a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
index 4537d5f..6bd2b2a 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
@@ -138,7 +138,7 @@
 
     private class SessionServiceEventCallback extends PlayerEventCallback {
         @Override
-        public void onPlayerStateChanged(MediaPlayerBase mpb, int state) {
+        public void onPlayerStateChanged(MediaPlayerBase player, int state) {
             // TODO: Implement this
             return;
         }
diff --git a/packages/MediaComponents/src/com/android/media/RoutePlayer.java b/packages/MediaComponents/src/com/android/media/RoutePlayer.java
new file mode 100644
index 0000000..9450d34
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/RoutePlayer.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media;
+
+import android.content.Context;
+import android.media.DataSourceDesc;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.annotation.RequiresApi;
+
+import com.android.support.mediarouter.media.MediaItemStatus;
+import com.android.support.mediarouter.media.MediaRouter;
+import com.android.support.mediarouter.media.MediaSessionStatus;
+import com.android.support.mediarouter.media.RemotePlaybackClient;
+import com.android.support.mediarouter.media.RemotePlaybackClient.ItemActionCallback;
+import com.android.support.mediarouter.media.RemotePlaybackClient.SessionActionCallback;
+import com.android.support.mediarouter.media.RemotePlaybackClient.StatusCallback;
+
+import java.util.Map;
+
+@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+public class RoutePlayer extends MediaSession.Callback {
+    public static final long PLAYBACK_ACTIONS = PlaybackState.ACTION_PAUSE
+            | PlaybackState.ACTION_PLAY | PlaybackState.ACTION_SEEK_TO
+            | PlaybackState.ACTION_FAST_FORWARD | PlaybackState.ACTION_REWIND;
+
+    private RemotePlaybackClient mClient;
+    private String mSessionId;
+    private String mItemId;
+    private PlayerEventCallback mCallback;
+
+    private StatusCallback mStatusCallback = new StatusCallback() {
+        @Override
+        public void onItemStatusChanged(Bundle data,
+                String sessionId, MediaSessionStatus sessionStatus,
+                String itemId, MediaItemStatus itemStatus) {
+            updateSessionStatus(sessionId, sessionStatus);
+            updateItemStatus(itemId, itemStatus);
+        }
+    };
+
+    public RoutePlayer(Context context, MediaRouter.RouteInfo route) {
+        mClient = new RemotePlaybackClient(context, route);
+        mClient.setStatusCallback(mStatusCallback);
+        mClient.startSession(null, new SessionActionCallback() {
+            @Override
+            public void onResult(Bundle data,
+                    String sessionId, MediaSessionStatus sessionStatus) {
+                updateSessionStatus(sessionId, sessionStatus);
+            }
+        });
+    }
+
+    @Override
+    public void onPlay() {
+        mClient.resume(null, new SessionActionCallback() {
+            @Override
+            public void onResult(Bundle data,
+                    String sessionId, MediaSessionStatus sessionStatus) {
+                updateSessionStatus(sessionId, sessionStatus);
+            }
+        });
+    }
+
+    @Override
+    public void onPause() {
+        mClient.pause(null, new SessionActionCallback() {
+            @Override
+            public void onResult(Bundle data,
+                    String sessionId, MediaSessionStatus sessionStatus) {
+                updateSessionStatus(sessionId, sessionStatus);
+            }
+        });
+    }
+
+    @Override
+    public void onSeekTo(long pos) {
+        mClient.seek(mItemId, pos, null, new ItemActionCallback() {
+            @Override
+            public void onResult(Bundle data,
+                    String sessionId, MediaSessionStatus sessionStatus,
+                    String itemId, MediaItemStatus itemStatus) {
+                updateSessionStatus(sessionId, sessionStatus);
+                updateItemStatus(itemId, itemStatus);
+            }
+        });
+    }
+
+    @Override
+    public void onStop() {
+        mClient.stop(null, new SessionActionCallback() {
+            @Override
+            public void onResult(Bundle data,
+                    String sessionId, MediaSessionStatus sessionStatus) {
+                updateSessionStatus(sessionId, sessionStatus);
+            }
+        });
+    }
+
+    public void setPlayerEventCallback(PlayerEventCallback callback) {
+        mCallback = callback;
+    }
+
+    public void openVideo(DataSourceDesc dsd) {
+        mClient.play(dsd.getUri(), "video/mp4", null, 0, null, new ItemActionCallback() {
+            @Override
+            public void onResult(Bundle data,
+                    String sessionId, MediaSessionStatus sessionStatus,
+                    String itemId, MediaItemStatus itemStatus) {
+                updateSessionStatus(sessionId, sessionStatus);
+                updateItemStatus(itemId, itemStatus);
+                playInternal(dsd.getUri());
+            }
+        });
+    }
+
+    public void release() {
+        if (mClient != null) {
+            mClient.release();
+            mClient = null;
+        }
+        if (mCallback != null) {
+            mCallback = null;
+        }
+    }
+
+    private void playInternal(Uri uri) {
+        mClient.play(uri, "video/mp4", null, 0, null, new ItemActionCallback() {
+            @Override
+            public void onResult(Bundle data,
+                    String sessionId, MediaSessionStatus sessionStatus,
+                    String itemId, MediaItemStatus itemStatus) {
+                updateSessionStatus(sessionId, sessionStatus);
+                updateItemStatus(itemId, itemStatus);
+            }
+        });
+    }
+
+    private void updateSessionStatus(String sessionId, MediaSessionStatus sessionStatus) {
+        mSessionId = sessionId;
+    }
+
+    private void updateItemStatus(String itemId, MediaItemStatus itemStatus) {
+        mItemId = itemId;
+        if (itemStatus == null || mCallback == null) return;
+        mCallback.onPlayerStateChanged(itemStatus);
+    }
+
+    public static abstract class PlayerEventCallback {
+        public void onPlayerStateChanged(MediaItemStatus itemStatus) { }
+    }
+}
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
index 9a01ade..07b8788 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
@@ -32,6 +32,7 @@
 import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
 import android.media.MediaMetadata2;
 import android.media.MediaPlayerBase;
+import android.media.MediaPlaylistAgent;
 import android.media.MediaSession2;
 import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandGroup;
@@ -50,6 +51,7 @@
 import android.media.update.MediaItem2Provider;
 import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
 import android.media.update.MediaMetadata2Provider;
+import android.media.update.MediaPlaylistAgentProvider;
 import android.media.update.MediaSession2Provider;
 import android.media.update.MediaSession2Provider.BuilderBaseProvider;
 import android.media.update.MediaSession2Provider.CommandButtonProvider.BuilderProvider;
@@ -76,6 +78,7 @@
 import com.android.media.MediaLibraryService2Impl;
 import com.android.media.MediaLibraryService2Impl.LibraryRootImpl;
 import com.android.media.MediaMetadata2Impl;
+import com.android.media.MediaPlaylistAgentImpl;
 import com.android.media.MediaSession2Impl;
 import com.android.media.MediaSession2Impl.PlaylistParamsImpl;
 import com.android.media.MediaSessionService2Impl;
@@ -301,4 +304,10 @@
     public PlaybackState2 fromBundle_PlaybackState2(Context context, Bundle bundle) {
         return PlaybackState2Impl.fromBundle(context, bundle);
     }
+
+    @Override
+    public MediaPlaylistAgentProvider createMediaPlaylistAgent(Context context,
+            MediaPlaylistAgent instance) {
+        return new MediaPlaylistAgentImpl(context, instance);
+    }
 }
diff --git a/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java b/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java
index 8577123..fc92e85 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoSurfaceView.java
@@ -18,7 +18,7 @@
 
 import android.content.Context;
 import android.graphics.Rect;
-import android.media.MediaPlayer;
+import android.media.MediaPlayer2;
 import android.support.annotation.NonNull;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -33,7 +33,7 @@
     private static final boolean DEBUG = true; // STOPSHIP: Log.isLoggable(TAG, Log.DEBUG);
     private SurfaceHolder mSurfaceHolder = null;
     private SurfaceListener mSurfaceListener = null;
-    private MediaPlayer mMediaPlayer;
+    private MediaPlayer2 mMediaPlayer;
     // A flag to indicate taking over other view should be proceed.
     private boolean mIsTakingOverOldView;
     private VideoViewInterface mOldView;
@@ -62,7 +62,7 @@
     ////////////////////////////////////////////////////
 
     @Override
-    public boolean assignSurfaceToMediaPlayer(MediaPlayer mp) {
+    public boolean assignSurfaceToMediaPlayer(MediaPlayer2 mp) {
         Log.d(TAG, "assignSurfaceToMediaPlayer(): mSurfaceHolder: " + mSurfaceHolder);
         if (mp == null || !hasAvailableSurface()) {
             return false;
@@ -82,7 +82,7 @@
     }
 
     @Override
-    public void setMediaPlayer(MediaPlayer mp) {
+    public void setMediaPlayer(MediaPlayer2 mp) {
         mMediaPlayer = mp;
         if (mIsTakingOverOldView) {
             takeOver(mOldView);
diff --git a/packages/MediaComponents/src/com/android/widget/VideoTextureView.java b/packages/MediaComponents/src/com/android/widget/VideoTextureView.java
index 69a4ebe..024a3aa 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoTextureView.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoTextureView.java
@@ -18,7 +18,7 @@
 
 import android.content.Context;
 import android.graphics.SurfaceTexture;
-import android.media.MediaPlayer;
+import android.media.MediaPlayer2;
 import android.support.annotation.NonNull;
 import android.support.annotation.RequiresApi;
 import android.util.AttributeSet;
@@ -38,7 +38,7 @@
     private SurfaceTexture mSurfaceTexture;
     private Surface mSurface;
     private SurfaceListener mSurfaceListener;
-    private MediaPlayer mMediaPlayer;
+    private MediaPlayer2 mMediaPlayer;
     // A flag to indicate taking over other view should be proceed.
     private boolean mIsTakingOverOldView;
     private VideoViewInterface mOldView;
@@ -66,7 +66,7 @@
     ////////////////////////////////////////////////////
 
     @Override
-    public boolean assignSurfaceToMediaPlayer(MediaPlayer mp) {
+    public boolean assignSurfaceToMediaPlayer(MediaPlayer2 mp) {
         Log.d(TAG, "assignSurfaceToMediaPlayer(): mSurfaceTexture: " + mSurfaceTexture);
         if (mp == null || !hasAvailableSurface()) {
             // Surface is not ready.
@@ -87,7 +87,7 @@
     }
 
     @Override
-    public void setMediaPlayer(MediaPlayer mp) {
+    public void setMediaPlayer(MediaPlayer2 mp) {
         mMediaPlayer = mp;
         if (mIsTakingOverOldView) {
             takeOver(mOldView);
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index c3ca057..0bb659d 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
@@ -16,16 +16,15 @@
 
 package com.android.widget;
 
-import android.annotation.NonNull;
 import android.content.Context;
-import android.content.res.Resources;
 import android.media.AudioAttributes;
 import android.media.AudioFocusRequest;
 import android.media.AudioManager;
 import android.media.DataSourceDesc;
 import android.media.MediaMetadata;
-import android.media.MediaPlayer;
-import android.media.MediaPlayerBase;
+import android.media.MediaPlayer2;
+import android.media.MediaPlayer2.MediaPlayer2EventCallback;
+import android.media.MediaPlayer2Impl;
 import android.media.Cea708CaptionRenderer;
 import android.media.ClosedCaptionRenderer;
 import android.media.MediaItem2;
@@ -45,6 +44,7 @@
 import android.media.update.VideoView2Provider;
 import android.media.update.ViewGroupProvider;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.ResultReceiver;
 import android.support.annotation.Nullable;
@@ -58,15 +58,12 @@
 import android.widget.MediaControlView2;
 import android.widget.VideoView2;
 
-import com.android.media.update.ApiHelper;
-import com.android.media.update.R;
+import com.android.media.RoutePlayer;
+import com.android.support.mediarouter.media.MediaItemStatus;
+import com.android.support.mediarouter.media.MediaControlIntent;
 import com.android.support.mediarouter.media.MediaRouter;
 import com.android.support.mediarouter.media.MediaRouteSelector;
-import com.android.support.mediarouter.media.RemotePlaybackClient;
-import com.android.support.mediarouter.media.MediaItemStatus;
-import com.android.support.mediarouter.media.MediaSessionStatus;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -103,13 +100,11 @@
     private VideoTextureView mTextureView;
     private VideoSurfaceView mSurfaceView;
 
-    private MediaPlayer mMediaPlayer;
+    private MediaPlayer2 mMediaPlayer;
+    private DataSourceDesc mDsd;
     private MediaControlView2 mMediaControlView;
     private MediaSession mMediaSession;
     private MediaController mMediaController;
-    private MediaSession.Callback mRouteSessionCallback = new RouteSessionCallback();
-    private MediaRouter mMediaRouter;
-    private MediaRouteSelector mRouteSelector;
     private Metadata mMetadata;
     private MediaMetadata2 mMediaMetadata;
     private boolean mNeedUpdateMediaType;
@@ -142,9 +137,80 @@
     // TODO: Remove mFallbackSpeed when integration with MediaPlayer2's new setPlaybackParams().
     // Refer: https://docs.google.com/document/d/1nzAfns6i2hJ3RkaUre3QMT6wsDedJ5ONLiA_OOBFFX8/edit
     private float mFallbackSpeed;  // keep the original speed before 'pause' is called.
-
     private long mShowControllerIntervalMs;
 
+    private MediaRouter mMediaRouter;
+    private MediaRouteSelector mRouteSelector;
+    private MediaRouter.RouteInfo mRoute;
+    private RoutePlayer mRoutePlayer;
+
+    private final MediaRouter.Callback mRouterCallback = new MediaRouter.Callback() {
+        @Override
+        public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) {
+            if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
+                // Stop local playback (if necessary)
+                resetPlayer();
+                mRoute = route;
+                mRoutePlayer = new RoutePlayer(mInstance.getContext(), route);
+                mRoutePlayer.setPlayerEventCallback(new RoutePlayer.PlayerEventCallback() {
+                    @Override
+                    public void onPlayerStateChanged(MediaItemStatus itemStatus) {
+                        PlaybackState.Builder psBuilder = new PlaybackState.Builder();
+                        psBuilder.setActions(RoutePlayer.PLAYBACK_ACTIONS);
+                        long position = itemStatus.getContentPosition();
+                        switch (itemStatus.getPlaybackState()) {
+                            case MediaItemStatus.PLAYBACK_STATE_PENDING:
+                                psBuilder.setState(PlaybackState.STATE_NONE, position, 0);
+                                mCurrentState = STATE_IDLE;
+                                break;
+                            case MediaItemStatus.PLAYBACK_STATE_PLAYING:
+                                psBuilder.setState(PlaybackState.STATE_PLAYING, position, 1);
+                                mCurrentState = STATE_PLAYING;
+                                break;
+                            case MediaItemStatus.PLAYBACK_STATE_PAUSED:
+                                psBuilder.setState(PlaybackState.STATE_PAUSED, position, 0);
+                                mCurrentState = STATE_PAUSED;
+                                break;
+                            case MediaItemStatus.PLAYBACK_STATE_BUFFERING:
+                                psBuilder.setState(PlaybackState.STATE_BUFFERING, position, 0);
+                                mCurrentState = STATE_PAUSED;
+                                break;
+                            case MediaItemStatus.PLAYBACK_STATE_FINISHED:
+                                psBuilder.setState(PlaybackState.STATE_STOPPED, position, 0);
+                                mCurrentState = STATE_PLAYBACK_COMPLETED;
+                                break;
+                        }
+
+                        PlaybackState pbState = psBuilder.build();
+                        mMediaSession.setPlaybackState(pbState);
+
+                        MediaMetadata.Builder mmBuilder = new MediaMetadata.Builder();
+                        mmBuilder.putLong(MediaMetadata.METADATA_KEY_DURATION,
+                                itemStatus.getContentDuration());
+                        mMediaSession.setMetadata(mmBuilder.build());
+                    }
+                });
+                // Start remote playback (if necessary)
+                mRoutePlayer.openVideo(mDsd);
+            }
+        }
+
+        @Override
+        public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo route, int reason) {
+            if (mRoute != null && mRoutePlayer != null) {
+                mRoutePlayer.release();
+                mRoutePlayer = null;
+            }
+            if (mRoute == route) {
+                mRoute = null;
+            }
+            if (reason != MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) {
+                // TODO: Resume local playback  (if necessary)
+                openVideo(mDsd);
+            }
+        }
+    };
+
     public VideoView2Impl(VideoView2 instance,
             ViewGroupProvider superProvider, ViewGroupProvider privateProvider) {
         super(instance, superProvider, privateProvider);
@@ -196,7 +262,6 @@
         mSubtitleView.setBackgroundColor(0);
         mInstance.addView(mSubtitleView);
 
-        // TODO: Need a common namespace for attributes those are defined in updatable library.
         boolean enableControlView = (attrs == null) || attrs.getAttributeBooleanValue(
                 "http://schemas.android.com/apk/res/android",
                 "enableControlView", true);
@@ -219,16 +284,20 @@
             Log.d(TAG, "viewType attribute is textureView.");
             // TODO: implement
         }
+
+        MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder();
+        builder.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
+        builder.addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO);
+        builder.addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO);
+        mRouteSelector = builder.build();
     }
 
     @Override
     public void setMediaControlView2_impl(MediaControlView2 mediaControlView, long intervalMs) {
         mMediaControlView = mediaControlView;
         mShowControllerIntervalMs = intervalMs;
-        if (mRouteSelector != null) {
-            ((MediaControlView2Impl) mMediaControlView.getProvider())
-                    .setRouteSelector(mRouteSelector);
-        }
+        // TODO: Call MediaControlView2.setRouteSelector only when cast availalbe.
+        ((MediaControlView2Impl) mMediaControlView.getProvider()).setRouteSelector(mRouteSelector);
 
         if (mInstance.isAttachedToWindow()) {
             attachMediaControlView();
@@ -328,30 +397,6 @@
     }
 
     @Override
-    public void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerBase player) {
-        // TODO: implement this.
-    }
-
-    @Override
-    public void setRouteAttributes_impl(@NonNull List<String> routeCategories,
-            MediaSession.Callback sessionPlayer) {
-        MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder();
-        for (String category : routeCategories) {
-            builder.addControlCategory(category);
-        }
-        mRouteSelector = builder.build();
-        if (mMediaControlView != null) {
-            ((MediaControlView2Impl) mMediaControlView.getProvider())
-                    .setRouteSelector(mRouteSelector);
-        }
-        mMediaRouter = MediaRouter.getInstance(mInstance.getContext());
-        mRouteSessionCallback = sessionPlayer;
-        if (mMediaSession != null) {
-            mMediaRouter.setMediaSession(mMediaSession);
-        }
-    }
-
-    @Override
     public void setVideoPath_impl(String path) {
         mInstance.setVideoUri(Uri.parse(path));
     }
@@ -363,8 +408,9 @@
 
     @Override
     public void setVideoUri_impl(Uri uri, Map<String, String> headers) {
-        mSeekWhenPrepared = 0;
-        openVideo(uri, headers);
+        DataSourceDesc.Builder builder = new DataSourceDesc.Builder();
+        builder.setDataSource(mInstance.getContext(), uri, headers, null);
+        mInstance.setDataSource(builder.build());
     }
 
     @Override
@@ -374,7 +420,9 @@
 
     @Override
     public void setDataSource_impl(DataSourceDesc dsd) {
-        // TODO: implement this
+        mDsd = dsd;
+        mSeekWhenPrepared = 0;
+        openVideo(dsd);
     }
 
     @Override
@@ -431,10 +479,11 @@
         // Create MediaSession
         mMediaSession = new MediaSession(mInstance.getContext(), "VideoView2MediaSession");
         mMediaSession.setCallback(new MediaSessionCallback());
+        mMediaSession.setActive(true);
         mMediaController = mMediaSession.getController();
-        if (mMediaRouter != null) {
-            mMediaRouter.setMediaSession(mMediaSession);
-        }
+        mMediaRouter = MediaRouter.getInstance(mInstance.getContext());
+        mMediaRouter.setMediaSession(mMediaSession);
+        mMediaRouter.addCallback(mRouteSelector, mRouterCallback);
         attachMediaControlView();
         // TODO: remove this after moving MediaSession creating code inside initializing VideoView2
         if (mCurrentState == STATE_PREPARED) {
@@ -545,21 +594,27 @@
     }
 
     private boolean isInPlaybackState() {
-        return (mMediaPlayer != null
+        return (mMediaPlayer != null || mRoutePlayer != null)
                 && mCurrentState != STATE_ERROR
                 && mCurrentState != STATE_IDLE
-                && mCurrentState != STATE_PREPARING);
+                && mCurrentState != STATE_PREPARING;
     }
 
     private boolean needToStart() {
-        return (mMediaPlayer != null
+        return (mMediaPlayer != null || mRoutePlayer != null)
                 && mCurrentState != STATE_PLAYING
-                && mTargetState == STATE_PLAYING);
+                && mTargetState == STATE_PLAYING;
     }
 
-    // Creates a MediaPlayer instance and prepare playback.
-    private void openVideo(Uri uri, Map<String, String> headers) {
+    // Creates a MediaPlayer2 instance and prepare playback.
+    private void openVideo(DataSourceDesc dsd) {
+        Uri uri = dsd.getUri();
+        Map<String, String> headers = dsd.getUriHeaders();
         resetPlayer();
+        if (isRemotePlayback()) {
+            mRoutePlayer.openVideo(dsd);
+            return;
+        }
         if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
             // TODO this should have a focus listener
             AudioFocusRequest focusRequest;
@@ -570,13 +625,13 @@
         }
 
         try {
-            Log.d(TAG, "openVideo(): creating new MediaPlayer instance.");
-            mMediaPlayer = new MediaPlayer();
+            Log.d(TAG, "openVideo(): creating new MediaPlayer2 instance.");
+            mMediaPlayer = new MediaPlayer2Impl();
             mSurfaceView.setMediaPlayer(mMediaPlayer);
             mTextureView.setMediaPlayer(mMediaPlayer);
             mCurrentView.assignSurfaceToMediaPlayer(mMediaPlayer);
 
-            // TODO: create SubtitleController in MediaPlayer, but we need
+            // TODO: create SubtitleController in MediaPlayer2, but we need
             // a context for the subtitle renderers
             final Context context = mInstance.getContext();
             final SubtitleController controller = new SubtitleController(
@@ -588,23 +643,21 @@
             controller.registerRenderer(new SRTRenderer(context));
             mMediaPlayer.setSubtitleAnchor(
                     controller, (SubtitleController.Anchor) mSubtitleView);
-            // TODO: Remove timed text related code later once relevant Renderer is defined.
-            // This is just for debugging purpose.
-            mMediaPlayer.setOnTimedTextListener(mTimedTextListener);
+            Executor executor = new Executor() {
+                @Override
+                public void execute(Runnable runnable) {
+                    runnable.run();
+                }
+            };
+            mMediaPlayer.setMediaPlayer2EventCallback(executor, mMediaPlayer2Callback);
 
-            mMediaPlayer.setOnPreparedListener(mPreparedListener);
-            mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
-            mMediaPlayer.setOnCompletionListener(mCompletionListener);
-            mMediaPlayer.setOnErrorListener(mErrorListener);
-            mMediaPlayer.setOnInfoListener(mInfoListener);
-            mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
             mCurrentBufferPercentage = 0;
-            mMediaPlayer.setDataSource(mInstance.getContext(), uri, headers);
+            mMediaPlayer.setDataSource(dsd);
             mMediaPlayer.setAudioAttributes(mAudioAttributes);
             // we don't set the target state here either, but preserve the
             // target state that was there before.
             mCurrentState = STATE_PREPARING;
-            mMediaPlayer.prepareAsync();
+            mMediaPlayer.prepare();
 
             // Save file name as title since the file may not have a title Metadata.
             mTitle = uri.getPath();
@@ -617,24 +670,12 @@
                 Log.d(TAG, "openVideo(). mCurrentState=" + mCurrentState
                         + ", mTargetState=" + mTargetState);
             }
-            /*
-            for (Pair<InputStream, MediaFormat> pending: mPendingSubtitleTracks) {
-                try {
-                    mMediaPlayer.addSubtitleSource(pending.first, pending.second);
-                } catch (IllegalStateException e) {
-                    mInfoListener.onInfo(
-                            mMediaPlayer, MediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE, 0);
-                }
-            }
-            */
-        } catch (IOException | IllegalArgumentException ex) {
+        } catch (IllegalArgumentException ex) {
             Log.w(TAG, "Unable to open content: " + uri, ex);
             mCurrentState = STATE_ERROR;
             mTargetState = STATE_ERROR;
-            mErrorListener.onError(mMediaPlayer,
-                    MediaPlayer.MEDIA_ERROR_UNKNOWN, MediaPlayer.MEDIA_ERROR_IO);
-        } finally {
-            //mPendingSubtitleTracks.clear();
+            mMediaPlayer2Callback.onError(mMediaPlayer, dsd,
+                    MediaPlayer2.MEDIA_ERROR_UNKNOWN, MediaPlayer2.MEDIA_ERROR_IO);
         }
     }
 
@@ -643,9 +684,18 @@
      */
     private void resetPlayer() {
         if (mMediaPlayer != null) {
-            mMediaPlayer.reset();
-            mMediaPlayer.release();
+            final MediaPlayer2 player = mMediaPlayer;
+            new AsyncTask<MediaPlayer2, Void, Void>() {
+                @Override
+                protected Void doInBackground(MediaPlayer2... players) {
+                    // TODO: Fix NPE while MediaPlayer2.close()
+                    //players[0].close();
+                    return null;
+                }
+            }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, player);
             mMediaPlayer = null;
+            mTextureView.setMediaPlayer(null);
+            mSurfaceView.setMediaPlayer(null);
             //mPendingSubtitleTracks.clear();
             mCurrentState = STATE_IDLE;
             mTargetState = STATE_IDLE;
@@ -660,8 +710,8 @@
     private void updatePlaybackState() {
         if (mStateBuilder == null) {
             // Get the capabilities of the player for this stream
-            mMetadata = mMediaPlayer.getMetadata(MediaPlayer.METADATA_ALL,
-                    MediaPlayer.BYPASS_METADATA_FILTER);
+            mMetadata = mMediaPlayer.getMetadata(MediaPlayer2.METADATA_ALL,
+                    MediaPlayer2.BYPASS_METADATA_FILTER);
 
             // Add Play action as default
             long playbackActions = PlaybackState.ACTION_PLAY;
@@ -701,6 +751,8 @@
         if (mCurrentState != STATE_ERROR
             && mCurrentState != STATE_IDLE
             && mCurrentState != STATE_PREPARING) {
+            // TODO: this should be replaced with MediaPlayer2.getBufferedPosition() once it is
+            // implemented.
             mStateBuilder.setBufferedPosition(
                     (long) (mCurrentBufferPercentage / 100.0) * mMediaPlayer.getDuration());
         }
@@ -792,8 +844,8 @@
             return false;
         }
         PlaybackInfo playbackInfo = mMediaController.getPlaybackInfo();
-        return (playbackInfo != null)
-                && (playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE);
+        return playbackInfo != null
+                && playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE;
     }
 
     private void selectOrDeselectSubtitle(boolean select) {
@@ -817,18 +869,18 @@
     }
 
     private void extractTracks() {
-        MediaPlayer.TrackInfo[] trackInfos = mMediaPlayer.getTrackInfo();
+        List<MediaPlayer2.TrackInfo> trackInfos = mMediaPlayer.getTrackInfo();
         mVideoTrackIndices = new ArrayList<>();
         mAudioTrackIndices = new ArrayList<>();
         mSubtitleTrackIndices = new ArrayList<>();
-        for (int i = 0; i < trackInfos.length; ++i) {
-            int trackType = trackInfos[i].getTrackType();
-            if (trackType == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_VIDEO) {
+        for (int i = 0; i < trackInfos.size(); ++i) {
+            int trackType = trackInfos.get(i).getTrackType();
+            if (trackType == MediaPlayer2.TrackInfo.MEDIA_TRACK_TYPE_VIDEO) {
                 mVideoTrackIndices.add(i);
-            } else if (trackType == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_AUDIO) {
+            } else if (trackType == MediaPlayer2.TrackInfo.MEDIA_TRACK_TYPE_AUDIO) {
                 mAudioTrackIndices.add(i);
-            } else if (trackType == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE
-                    || trackType == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) {
+            } else if (trackType == MediaPlayer2.TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE
+                    || trackType == MediaPlayer2.TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) {
                   mSubtitleTrackIndices.add(i);
             }
         }
@@ -842,127 +894,50 @@
         mMediaSession.sendSessionEvent(MediaControlView2Impl.EVENT_UPDATE_TRACK_STATUS, data);
     }
 
-    MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener =
-            new MediaPlayer.OnVideoSizeChangedListener() {
-                public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
+    MediaPlayer2EventCallback mMediaPlayer2Callback =
+            new MediaPlayer2EventCallback() {
+                @Override
+                public void onVideoSizeChanged(
+                        MediaPlayer2 mp, DataSourceDesc dsd, int width, int height) {
                     if (DEBUG) {
-                        Log.d(TAG, "OnVideoSizeChanged(): size: " + width + "/" + height);
+                        Log.d(TAG, "onVideoSizeChanged(): size: " + width + "/" + height);
                     }
                     mVideoWidth = mp.getVideoWidth();
                     mVideoHeight = mp.getVideoHeight();
                     if (DEBUG) {
-                        Log.d(TAG, "OnVideoSizeChanged(): mVideoSize:" + mVideoWidth + "/"
+                        Log.d(TAG, "onVideoSizeChanged(): mVideoSize:" + mVideoWidth + "/"
                                 + mVideoHeight);
                     }
-
                     if (mVideoWidth != 0 && mVideoHeight != 0) {
                         mInstance.requestLayout();
                     }
                 }
-            };
 
-    MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
-        public void onPrepared(MediaPlayer mp) {
-            if (DEBUG) {
-                Log.d(TAG, "OnPreparedListener(). mCurrentState=" + mCurrentState
-                        + ", mTargetState=" + mTargetState);
-            }
-            mCurrentState = STATE_PREPARED;
-            // Create and set playback state for MediaControlView2
-            updatePlaybackState();
-
-            // TODO: change this to send TrackInfos to MediaControlView2
-            // TODO: create MediaSession when initializing VideoView2
-            if (mMediaSession != null) {
-                extractTracks();
-            }
-
-            if (mMediaControlView != null) {
-                mMediaControlView.setEnabled(true);
-            }
-            int videoWidth = mp.getVideoWidth();
-            int videoHeight = mp.getVideoHeight();
-
-            // mSeekWhenPrepared may be changed after seekTo() call
-            long seekToPosition = mSeekWhenPrepared;
-            if (seekToPosition != 0) {
-                mMediaController.getTransportControls().seekTo(seekToPosition);
-            }
-
-            if (videoWidth != 0 && videoHeight != 0) {
-                if (videoWidth != mVideoWidth || videoHeight != mVideoHeight) {
-                    if (DEBUG) {
-                        Log.i(TAG, "OnPreparedListener() : ");
-                        Log.i(TAG, " video size: " + videoWidth + "/" + videoHeight);
-                        Log.i(TAG, " measuredSize: " + mInstance.getMeasuredWidth() + "/"
-                                + mInstance.getMeasuredHeight());
-                        Log.i(TAG, " viewSize: " + mInstance.getWidth() + "/"
-                                + mInstance.getHeight());
-                    }
-
-                    mVideoWidth = videoWidth;
-                    mVideoHeight = videoHeight;
-                    mInstance.requestLayout();
+                // TODO: Remove timed text related code later once relevant Renderer is defined.
+                // This is just for debugging purpose.
+                @Override
+                public void onTimedText(
+                        MediaPlayer2 mp, DataSourceDesc dsd, TimedText text) {
+                        Log.d(TAG, "TimedText: " + text.getText());
                 }
 
-                if (needToStart()) {
-                    mMediaController.getTransportControls().play();
-                }
-            } else {
-                // We don't know the video size yet, but should start anyway.
-                // The video size might be reported to us later.
-                if (needToStart()) {
-                    mMediaController.getTransportControls().play();
-                }
-            }
-
-            // Get and set duration and title values as MediaMetadata for MediaControlView2
-            MediaMetadata.Builder builder = new MediaMetadata.Builder();
-            if (mMetadata != null && mMetadata.has(Metadata.TITLE)) {
-                mTitle = mMetadata.getString(Metadata.TITLE);
-            }
-            builder.putString(MediaMetadata.METADATA_KEY_TITLE, mTitle);
-            builder.putLong(MediaMetadata.METADATA_KEY_DURATION, mMediaPlayer.getDuration());
-
-            if (mMediaSession != null) {
-                mMediaSession.setMetadata(builder.build());
-
-                // TODO: merge this code with the above code when integrating with MediaSession2.
-                if (mNeedUpdateMediaType) {
-                    mMediaSession.sendSessionEvent(
-                            MediaControlView2Impl.EVENT_UPDATE_MEDIA_TYPE_STATUS, mMediaTypeData);
-                    mNeedUpdateMediaType = false;
-                }
-            }
-        }
-    };
-
-    private MediaPlayer.OnCompletionListener mCompletionListener =
-            new MediaPlayer.OnCompletionListener() {
-                public void onCompletion(MediaPlayer mp) {
-                    mCurrentState = STATE_PLAYBACK_COMPLETED;
-                    mTargetState = STATE_PLAYBACK_COMPLETED;
-                    updatePlaybackState();
-
-                    if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
-                        mAudioManager.abandonAudioFocus(null);
-                    }
-                }
-            };
-
-    private MediaPlayer.OnInfoListener mInfoListener =
-            new MediaPlayer.OnInfoListener() {
-                public boolean onInfo(MediaPlayer mp, int what, int extra) {
-                    if (what == MediaPlayer.MEDIA_INFO_METADATA_UPDATE) {
+                @Override
+                public void onInfo(
+                        MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
+                    if (what == MediaPlayer2.MEDIA_INFO_METADATA_UPDATE) {
                         extractTracks();
+                    } else if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
+                        this.onPrepared(mp, dsd);
+                    } else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
+                        this.onCompletion(mp, dsd);
+                    } else if (what == MediaPlayer2.MEDIA_INFO_BUFFERING_UPDATE) {
+                        this.onBufferingUpdate(mp, dsd, extra);
                     }
-                    return true;
                 }
-            };
 
-    private MediaPlayer.OnErrorListener mErrorListener =
-            new MediaPlayer.OnErrorListener() {
-                public boolean onError(MediaPlayer mp, int frameworkErr, int implErr) {
+                @Override
+                public void onError(
+                        MediaPlayer2 mp, DataSourceDesc dsd, int frameworkErr, int implErr) {
                     if (DEBUG) {
                         Log.d(TAG, "Error: " + frameworkErr + "," + implErr);
                     }
@@ -973,32 +948,103 @@
                     if (mMediaControlView != null) {
                         mMediaControlView.setVisibility(View.GONE);
                     }
-                    return true;
                 }
-            };
 
-    private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener =
-            new MediaPlayer.OnBufferingUpdateListener() {
-                public void onBufferingUpdate(MediaPlayer mp, int percent) {
+                private void onPrepared(MediaPlayer2 mp, DataSourceDesc dsd) {
+                    if (DEBUG) {
+                        Log.d(TAG, "OnPreparedListener(). mCurrentState=" + mCurrentState
+                                + ", mTargetState=" + mTargetState);
+                    }
+                    mCurrentState = STATE_PREPARED;
+                    // Create and set playback state for MediaControlView2
+                    updatePlaybackState();
+
+                    // TODO: change this to send TrackInfos to MediaControlView2
+                    // TODO: create MediaSession when initializing VideoView2
+                    if (mMediaSession != null) {
+                        extractTracks();
+                    }
+
+                    if (mMediaControlView != null) {
+                        mMediaControlView.setEnabled(true);
+                    }
+                    int videoWidth = mp.getVideoWidth();
+                    int videoHeight = mp.getVideoHeight();
+
+                    // mSeekWhenPrepared may be changed after seekTo() call
+                    long seekToPosition = mSeekWhenPrepared;
+                    if (seekToPosition != 0) {
+                        mMediaController.getTransportControls().seekTo(seekToPosition);
+                    }
+
+                    if (videoWidth != 0 && videoHeight != 0) {
+                        if (videoWidth != mVideoWidth || videoHeight != mVideoHeight) {
+                            if (DEBUG) {
+                                Log.i(TAG, "OnPreparedListener() : ");
+                                Log.i(TAG, " video size: " + videoWidth + "/" + videoHeight);
+                                Log.i(TAG, " measuredSize: " + mInstance.getMeasuredWidth() + "/"
+                                        + mInstance.getMeasuredHeight());
+                                Log.i(TAG, " viewSize: " + mInstance.getWidth() + "/"
+                                        + mInstance.getHeight());
+                            }
+                            mVideoWidth = videoWidth;
+                            mVideoHeight = videoHeight;
+                            mInstance.requestLayout();
+                        }
+
+                        if (needToStart()) {
+                            mMediaController.getTransportControls().play();
+                        }
+                    } else {
+                        // We don't know the video size yet, but should start anyway.
+                        // The video size might be reported to us later.
+                        if (needToStart()) {
+                            mMediaController.getTransportControls().play();
+                        }
+                    }
+                    // Get and set duration and title values as MediaMetadata for MediaControlView2
+                    MediaMetadata.Builder builder = new MediaMetadata.Builder();
+                    if (mMetadata != null && mMetadata.has(Metadata.TITLE)) {
+                        mTitle = mMetadata.getString(Metadata.TITLE);
+                    }
+                    builder.putString(MediaMetadata.METADATA_KEY_TITLE, mTitle);
+                    builder.putLong(
+                            MediaMetadata.METADATA_KEY_DURATION, mMediaPlayer.getDuration());
+
+                    if (mMediaSession != null) {
+                        mMediaSession.setMetadata(builder.build());
+
+                        // TODO: merge this code with the above code when integrating with
+                        // MediaSession2.
+                        if (mNeedUpdateMediaType) {
+                            mMediaSession.sendSessionEvent(
+                                    MediaControlView2Impl.EVENT_UPDATE_MEDIA_TYPE_STATUS,
+                                    mMediaTypeData);
+                            mNeedUpdateMediaType = false;
+                        }
+                    }
+                }
+
+                private void onCompletion(MediaPlayer2 mp, DataSourceDesc dsd) {
+                    mCurrentState = STATE_PLAYBACK_COMPLETED;
+                    mTargetState = STATE_PLAYBACK_COMPLETED;
+                    updatePlaybackState();
+                    if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
+                        mAudioManager.abandonAudioFocus(null);
+                    }
+                }
+
+                private void onBufferingUpdate(MediaPlayer2 mp, DataSourceDesc dsd, int percent) {
                     mCurrentBufferPercentage = percent;
                     updatePlaybackState();
                 }
             };
 
-    // TODO: Remove timed text related code later once relevant Renderer is defined.
-    // This is just for debugging purpose.
-    private MediaPlayer.OnTimedTextListener mTimedTextListener =
-            new MediaPlayer.OnTimedTextListener() {
-                public void onTimedText(MediaPlayer mp, TimedText text) {
-                    Log.d(TAG, "TimedText: " + text.getText());
-                }
-            };
-
     private class MediaSessionCallback extends MediaSession.Callback {
         @Override
         public void onCommand(String command, Bundle args, ResultReceiver receiver) {
             if (isRemotePlayback()) {
-                mRouteSessionCallback.onCommand(command, args, receiver);
+                mRoutePlayer.onCommand(command, args, receiver);
             } else {
                 switch (command) {
                     case MediaControlView2Impl.COMMAND_SHOW_SUBTITLE:
@@ -1028,57 +1074,57 @@
 
         @Override
         public void onPlay() {
-            if (isRemotePlayback()) {
-                mRouteSessionCallback.onPlay();
-            } else {
-                if (isInPlaybackState() && mCurrentView.hasAvailableSurface()) {
+            if (isInPlaybackState() && mCurrentView.hasAvailableSurface()) {
+                if (isRemotePlayback()) {
+                    mRoutePlayer.onPlay();
+                } else {
                     applySpeed();
-                    mMediaPlayer.start();
+                    mMediaPlayer.play();
                     mCurrentState = STATE_PLAYING;
                     updatePlaybackState();
                 }
-                mTargetState = STATE_PLAYING;
-                if (DEBUG) {
-                    Log.d(TAG, "onPlay(). mCurrentState=" + mCurrentState
-                            + ", mTargetState=" + mTargetState);
-                }
+                mCurrentState = STATE_PLAYING;
+            }
+            mTargetState = STATE_PLAYING;
+            if (DEBUG) {
+                Log.d(TAG, "onPlay(). mCurrentState=" + mCurrentState
+                        + ", mTargetState=" + mTargetState);
             }
             showController();
         }
 
         @Override
         public void onPause() {
-            if (isRemotePlayback()) {
-                mRouteSessionCallback.onPause();
-            } else {
-                if (isInPlaybackState()) {
-                    if (mMediaPlayer.isPlaying()) {
-                        mMediaPlayer.pause();
-                        mCurrentState = STATE_PAUSED;
-                        updatePlaybackState();
-                    }
+            if (isInPlaybackState()) {
+                if (isRemotePlayback()) {
+                    mRoutePlayer.onPause();
+                    mCurrentState = STATE_PAUSED;
+                } else if (mMediaPlayer.isPlaying()) {
+                    mMediaPlayer.pause();
+                    mCurrentState = STATE_PAUSED;
+                    updatePlaybackState();
                 }
-                mTargetState = STATE_PAUSED;
-                if (DEBUG) {
-                    Log.d(TAG, "onPause(). mCurrentState=" + mCurrentState
-                            + ", mTargetState=" + mTargetState);
-                }
+            }
+            mTargetState = STATE_PAUSED;
+            if (DEBUG) {
+                Log.d(TAG, "onPause(). mCurrentState=" + mCurrentState
+                        + ", mTargetState=" + mTargetState);
             }
             showController();
         }
 
         @Override
         public void onSeekTo(long pos) {
-            if (isRemotePlayback()) {
-                mRouteSessionCallback.onSeekTo(pos);
-            } else {
-                if (isInPlaybackState()) {
-                    mMediaPlayer.seekTo(pos, MediaPlayer.SEEK_PREVIOUS_SYNC);
+            if (isInPlaybackState()) {
+                if (isRemotePlayback()) {
+                    mRoutePlayer.onSeekTo(pos);
+                } else {
+                    mMediaPlayer.seekTo(pos, MediaPlayer2.SEEK_PREVIOUS_SYNC);
                     mSeekWhenPrepared = 0;
                     updatePlaybackState();
-                } else {
-                    mSeekWhenPrepared = pos;
                 }
+            } else {
+                mSeekWhenPrepared = pos;
             }
             showController();
         }
@@ -1086,74 +1132,11 @@
         @Override
         public void onStop() {
             if (isRemotePlayback()) {
-                mRouteSessionCallback.onStop();
+                mRoutePlayer.onStop();
             } else {
                 resetPlayer();
             }
             showController();
         }
     }
-
-    private class RouteSessionCallback extends MediaSession.Callback {
-        RemotePlaybackClient mClient;
-
-        RemotePlaybackClient.StatusCallback mStatusCallback =
-                new RemotePlaybackClient.StatusCallback() {
-            @Override
-            public void onItemStatusChanged(Bundle data,
-                    String sessionId, MediaSessionStatus sessionStatus,
-                    String itemId, MediaItemStatus itemStatus) {
-                // TODO: implement this
-            }
-
-            @Override
-            public void onSessionStatusChanged(Bundle data,
-                    String sessionId, MediaSessionStatus sessionStatus) {
-                // TODO: implement this
-            }
-
-            @Override
-            public void onSessionChanged(String sessionId) {
-                // TODO: implement this
-            }
-        };
-
-        @Override
-        public void onCommand(String command, Bundle args, ResultReceiver receiver) {
-            ensureClient();
-            // TODO: implement this
-        }
-
-        @Override
-        public void onPlay() {
-            ensureClient();
-            // TODO: implement this
-        }
-
-        @Override
-        public void onPause() {
-            ensureClient();
-            // TODO: implement this
-        }
-
-        @Override
-        public void onSeekTo(long pos) {
-            ensureClient();
-            // TODO: implement this
-        }
-
-        @Override
-        public void onStop() {
-            ensureClient();
-            // TODO: implement this
-        }
-
-        private void ensureClient() {
-            if (mClient == null) {
-                mClient = new RemotePlaybackClient(
-                        mInstance.getContext(), mMediaRouter.getSelectedRoute());
-                mClient.setStatusCallback(mStatusCallback);
-            }
-        }
-    }
 }
diff --git a/packages/MediaComponents/src/com/android/widget/VideoViewInterface.java b/packages/MediaComponents/src/com/android/widget/VideoViewInterface.java
index 2a5eb94..854d47e 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoViewInterface.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoViewInterface.java
@@ -17,31 +17,31 @@
 package com.android.widget;
 
 import android.annotation.NonNull;
-import android.media.MediaPlayer;
+import android.media.MediaPlayer2;
 import android.view.View;
 
 interface VideoViewInterface {
     /**
-     * Assigns the view's surface to the given MediaPlayer instance.
+     * Assigns the view's surface to the given MediaPlayer2 instance.
      *
-     * @param mp MediaPlayer
+     * @param mp MediaPlayer2
      * @return true if the surface is successfully assigned, false if not. It will fail to assign
-     *         if any of MediaPlayer or surface is unavailable.
+     *         if any of MediaPlayer2 or surface is unavailable.
      */
-    boolean assignSurfaceToMediaPlayer(MediaPlayer mp);
+    boolean assignSurfaceToMediaPlayer(MediaPlayer2 mp);
     void setSurfaceListener(SurfaceListener l);
     int getViewType();
-    void setMediaPlayer(MediaPlayer mp);
+    void setMediaPlayer(MediaPlayer2 mp);
 
     /**
-     * Takes over oldView. It means that the MediaPlayer will start rendering on this view.
+     * Takes over oldView. It means that the MediaPlayer2 will start rendering on this view.
      * The visibility of oldView will be set as {@link View.GONE}. If the view doesn't have a
-     * MediaPlayer instance or its surface is not available, the actual execution is deferred until
-     * a MediaPlayer instance is set by {@link #setMediaPlayer} or its surface becomes available.
+     * MediaPlayer2 instance or its surface is not available, the actual execution is deferred until
+     * a MediaPlayer2 instance is set by {@link #setMediaPlayer} or its surface becomes available.
      * {@link SurfaceListener.onSurfaceTakeOverDone} will be called when the actual execution is
      * done.
      *
-     * @param oldView The view that MediaPlayer is currently rendering on.
+     * @param oldView The view that MediaPlayer2 is currently rendering on.
      */
     void takeOver(@NonNull VideoViewInterface oldView);
 
diff --git a/packages/MediaComponents/test/Android.mk b/packages/MediaComponents/test/Android.mk
index 8703b9f..ea053e0 100644
--- a/packages/MediaComponents/test/Android.mk
+++ b/packages/MediaComponents/test/Android.mk
@@ -25,4 +25,5 @@
     compatibility-device-util
 
 LOCAL_PACKAGE_NAME := MediaComponentsTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
 include $(BUILD_PACKAGE)
diff --git a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
index d1c7717..e58bd02 100644
--- a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
@@ -339,7 +339,9 @@
         final CountDownLatch latch = new CountDownLatch(1);
         final SessionCallbackProxy callbackProxy = new SessionCallbackProxy(mContext) {
             @Override
-            public void onSubscribe(ControllerInfo info, String parentId, Bundle extras) {
+            public void onSubscribe(@NonNull MediaLibrarySession session,
+                    @NonNull ControllerInfo info, @NonNull String parentId,
+                    @Nullable Bundle extras) {
                 if (Process.myUid() == info.getUid()) {
                     assertEquals(testParentId, parentId);
                     assertTrue(TestUtils.equals(testExtras, extras));
@@ -361,7 +363,8 @@
         final CountDownLatch latch = new CountDownLatch(1);
         final SessionCallbackProxy callbackProxy = new SessionCallbackProxy(mContext) {
             @Override
-            public void onUnsubscribe(ControllerInfo info, String parentId) {
+            public void onUnsubscribe(@NonNull MediaLibrarySession session,
+                    @NonNull ControllerInfo info, @NonNull String parentId) {
                 if (Process.myUid() == info.getUid()) {
                     assertEquals(testParentId, parentId);
                     latch.countDown();
@@ -387,21 +390,29 @@
         final CountDownLatch latch = new CountDownLatch(3);
         final SessionCallbackProxy sessionCallbackProxy = new SessionCallbackProxy(mContext) {
             @Override
-            public CommandGroup onConnect(ControllerInfo controller) {
-                final MockMediaLibraryService2 service = (MockMediaLibraryService2)
-                        TestServiceRegistry.getInstance().getServiceInstance();
-                final MediaLibrarySession session = (MediaLibrarySession) service.getSession();
-                // Shouldn't trigger onChildrenChanged() for the browser, because it hasn't
-                // subscribed.
-                session.notifyChildrenChanged(testParentId1, testChildrenCount, null);
-                session.notifyChildrenChanged(controller, testParentId1, testChildrenCount, null);
-                return super.onConnect(controller);
+            public CommandGroup onConnect(@NonNull MediaSession2 session,
+                    @NonNull ControllerInfo controller) {
+                if (Process.myUid() == controller.getUid()) {
+                    assertTrue(session instanceof MediaLibrarySession);
+                    if (mSession != null) {
+                        mSession.close();
+                    }
+                    mSession = session;
+                    // Shouldn't trigger onChildrenChanged() for the browser, because it hasn't
+                    // subscribed.
+                    ((MediaLibrarySession) session).notifyChildrenChanged(
+                            testParentId1, testChildrenCount, null);
+                    ((MediaLibrarySession) session).notifyChildrenChanged(
+                            controller, testParentId1, testChildrenCount, null);
+                }
+                return super.onConnect(session, controller);
             }
 
             @Override
-            public void onSubscribe(ControllerInfo info, String parentId, Bundle extras) {
+            public void onSubscribe(@NonNull MediaLibrarySession session,
+                    @NonNull ControllerInfo info, @NonNull String parentId,
+                    @Nullable Bundle extras) {
                 if (Process.myUid() == info.getUid()) {
-                    final MediaLibrarySession session =  (MediaLibrarySession) mSession;
                     session.notifyChildrenChanged(testParentId2, testChildrenCount, null);
                     session.notifyChildrenChanged(info, testParentId2, testChildrenCount,
                             testExtras);
@@ -435,12 +446,6 @@
         final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
         final MediaBrowser2 browser = (MediaBrowser2) createController(
                 token, true, controllerCallbackProxy);
-        final MockMediaLibraryService2 service =
-                (MockMediaLibraryService2) TestServiceRegistry.getInstance().getServiceInstance();
-        if (mSession != null) {
-            mSession.close();
-        }
-        mSession = service.getSession();
         assertTrue(mSession instanceof MediaLibrarySession);
         browser.subscribe(testParentId2, null);
         // This ensures that onChildrenChanged() is only called for the expected reasons.
@@ -494,13 +499,17 @@
             mCallbackProxy.onCustomCommand(command, args, receiver);
         }
 
-
         @Override
         public void onCustomLayoutChanged(MediaController2 controller, List<CommandButton> layout) {
             mCallbackProxy.onCustomLayoutChanged(layout);
         }
 
         @Override
+        public void onAllowedCommandsChanged(MediaController2 controller, CommandGroup commands) {
+            mCallbackProxy.onAllowedCommandsChanged(commands);
+        }
+
+        @Override
         public void onGetLibraryRootDone(MediaBrowser2 browser, Bundle rootHints,
                 String rootMediaId, Bundle rootExtra) {
             super.onGetLibraryRootDone(browser, rootHints, rootMediaId, rootExtra);
diff --git a/packages/MediaComponents/test/src/android/media/MediaController2Test.java b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
index e6ad098..2237d18 100644
--- a/packages/MediaComponents/test/src/android/media/MediaController2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
@@ -32,6 +32,7 @@
 import android.os.HandlerThread;
 import android.os.Process;
 import android.os.ResultReceiver;
+import android.support.annotation.NonNull;
 import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -118,8 +119,8 @@
 
     @Ignore
     @Test
-    public void testSkipToPrevious() throws InterruptedException {
-        mController.skipToPrevious();
+    public void testSkipToPreviousItem() throws InterruptedException {
+        mController.skipToPreviousItem();
         try {
             assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
         } catch (InterruptedException e) {
@@ -128,9 +129,10 @@
         assertTrue(mPlayer.mSkipToPreviousCalled);
     }
 
+    @Ignore
     @Test
-    public void testSkipToNext() throws InterruptedException {
-        mController.skipToNext();
+    public void testSkipToNextItem() throws InterruptedException {
+        mController.skipToNextItem();
         try {
             assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
         } catch (InterruptedException e) {
@@ -211,7 +213,7 @@
         } catch (InterruptedException e) {
             fail(e.getMessage());
         }
-        assertTrue(mPlayer.mSetCurrentPlaylistItemCalled);
+        assertTrue(mPlayer.mSetCurrentPlaylistCalled);
         assertEquals(itemIndex, mPlayer.mCurrentItem);
     }
     */
@@ -622,9 +624,9 @@
                     player.notifyPlaybackState(state);
                     controller.stop();
                     player.notifyPlaybackState(state);
-                    controller.skipToNext();
+                    controller.skipToNextItem();
                     player.notifyPlaybackState(state);
-                    controller.skipToPrevious();
+                    controller.skipToPreviousItem();
                 }
                 // This may hang if deadlock happens.
                 latch.countDown();
@@ -656,15 +658,6 @@
         assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
     }
 
-    private void connectToService(SessionToken2 token) throws InterruptedException {
-        if (mSession != null) {
-            mSession.close();
-        }
-        mController = createController(token);
-        mSession = TestServiceRegistry.getInstance().getServiceInstance().getSession();
-        mPlayer = (MockPlayer) mSession.getPlayer();
-    }
-
     @Test
     public void testConnectToService_sessionService() throws InterruptedException {
         testConnectToService(MockMediaSessionService2.ID);
@@ -680,17 +673,24 @@
         final CountDownLatch latch = new CountDownLatch(1);
         final SessionCallbackProxy proxy = new SessionCallbackProxy(mContext) {
             @Override
-            public CommandGroup onConnect(ControllerInfo controller) {
+            public CommandGroup onConnect(@NonNull MediaSession2 session,
+                    @NonNull ControllerInfo controller) {
                 if (Process.myUid() == controller.getUid()) {
+                    if (mSession != null) {
+                        mSession.close();
+                    }
+                    mSession = session;
+                    mPlayer = (MockPlayer) session.getPlayer();
                     assertEquals(mContext.getPackageName(), controller.getPackageName());
                     assertFalse(controller.isTrusted());
                     latch.countDown();
                 }
-                return super.onConnect(controller);
+                return super.onConnect(session, controller);
             }
         };
         TestServiceRegistry.getInstance().setSessionCallbackProxy(proxy);
-        connectToService(TestUtils.getServiceToken(mContext, id));
+
+        mController = createController(TestUtils.getServiceToken(mContext, id));
         assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
         // Test command from controller to session service
@@ -718,11 +718,14 @@
         testControllerAfterSessionIsGone(mSession.getToken().getId());
     }
 
+    // TODO(jaewan): Re-enable this test
     @Ignore
     @Test
     public void testControllerAfterSessionIsGone_sessionService() throws InterruptedException {
+        /*
         connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
         testControllerAfterSessionIsGone(MockMediaSessionService2.ID);
+        */
     }
 
     @Test
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
index 8c1a749..a0b1f18 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
@@ -42,7 +42,6 @@
 import android.support.annotation.NonNull;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.text.TextUtils;
 
 import org.junit.After;
 import org.junit.Before;
@@ -188,19 +187,20 @@
         });
     }
 
+    @Ignore
     @Test
-    public void testSkipToNext() throws Exception {
+    public void testSkipToNextItem() throws Exception {
         sHandler.postAndSync(() -> {
-            mSession.skipToNext();
+            mSession.skipToNextItem();
             assertTrue(mPlayer.mSkipToNextCalled);
         });
     }
 
     @Ignore
     @Test
-    public void testSkipToPrevious() throws Exception {
+    public void testSkipToPreviousItem() throws Exception {
         sHandler.postAndSync(() -> {
-            mSession.skipToPrevious();
+            mSession.skipToPreviousItem();
             assertTrue(mPlayer.mSkipToPreviousCalled);
         });
     }
@@ -356,13 +356,14 @@
         assertEquals(1, callback.commands.size());
         assertEquals(MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE,
                 (long) callback.commands.get(0).getCommandCode());
-        controller.skipToNext();
-        assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-        assertTrue(mPlayer.mSkipToNextCalled);
-        assertFalse(mPlayer.mPauseCalled);
-        assertEquals(2, callback.commands.size());
-        assertEquals(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM,
-                (long) callback.commands.get(1).getCommandCode());
+        // TODO(jaewan): uncomment followings once skipToNextItem is implemented (b/74090741)
+//        controller.skipToNextItem();
+//        assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+//        assertTrue(mPlayer.mSkipToNextCalled);
+//        assertFalse(mPlayer.mPauseCalled);
+//        assertEquals(2, callback.commands.size());
+//        assertEquals(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM,
+//                (long) callback.commands.get(1).getCommandCode());
     }
 
     @Test
@@ -426,6 +427,38 @@
     }
 
     @Test
+    public void testSetAllowedCommands() throws InterruptedException {
+        final CommandGroup commands = new CommandGroup(mContext);
+        commands.addCommand(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PLAY));
+        commands.addCommand(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE));
+        commands.addCommand(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_STOP));
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
+            @Override
+            public void onAllowedCommandsChanged(CommandGroup commandsOut) {
+                assertNotNull(commandsOut);
+                List<Command> expected = commands.getCommands();
+                List<Command> actual = commandsOut.getCommands();
+
+                assertNotNull(actual);
+                assertEquals(expected.size(), actual.size());
+                for (int i = 0; i < expected.size(); i++) {
+                    assertEquals(expected.get(i), actual.get(i));
+                }
+                latch.countDown();
+            }
+        };
+
+        final MediaController2 controller = createController(mSession.getToken(), true, callback);
+        ControllerInfo controllerInfo = getTestControllerInfo();
+        assertNotNull(controllerInfo);
+
+        mSession.setAllowedCommands(controllerInfo, commands);
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
     public void testSendCustomAction() throws InterruptedException {
         final Command testCommand =
                 new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PREPARE);
@@ -457,13 +490,12 @@
     private ControllerInfo getTestControllerInfo() {
         List<ControllerInfo> controllers = mSession.getConnectedControllers();
         assertNotNull(controllers);
-        final String packageName = mContext.getPackageName();
         for (int i = 0; i < controllers.size(); i++) {
-            if (TextUtils.equals(packageName, controllers.get(i).getPackageName())) {
+            if (Process.myUid() == controllers.get(i).getUid()) {
                 return controllers.get(i);
             }
         }
-        fail("Fails to get custom command");
+        fail("Failed to get test controller info");
         return null;
     }
 
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
index b32400f..83706c0 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
@@ -69,6 +69,7 @@
         default void onPlaybackInfoChanged(MediaController2.PlaybackInfo info) {}
         default void onPlaybackStateChanged(PlaybackState2 state) {}
         default void onCustomLayoutChanged(List<CommandButton> layout) {}
+        default void onAllowedCommandsChanged(CommandGroup commands) {}
         default void onCustomCommand(Command command, Bundle args, ResultReceiver receiver) {}
     }
 
@@ -247,6 +248,11 @@
         public void onCustomLayoutChanged(MediaController2 controller, List<CommandButton> layout) {
             mCallbackProxy.onCustomLayoutChanged(layout);
         }
+
+        @Override
+        public void onAllowedCommandsChanged(MediaController2 controller, CommandGroup commands) {
+            mCallbackProxy.onAllowedCommandsChanged(commands);
+        }
     }
 
     public class TestMediaController extends MediaController2 implements TestControllerInterface {
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2_PermissionTest.java b/packages/MediaComponents/test/src/android/media/MediaSession2_PermissionTest.java
index d89cecd..ca36513 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2_PermissionTest.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2_PermissionTest.java
@@ -16,19 +16,38 @@
 
 package android.media;
 
-import static android.media.MediaSession2.*;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_FAST_FORWARD;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_PLAY;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_REWIND;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_SEEK_TO;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM;
+import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_STOP;
+import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_MEDIA_ID;
+import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_SEARCH;
+import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_URI;
+import static android.media.MediaSession2.COMMAND_CODE_PREPARE_FROM_MEDIA_ID;
+import static android.media.MediaSession2.COMMAND_CODE_PREPARE_FROM_SEARCH;
+import static android.media.MediaSession2.COMMAND_CODE_PREPARE_FROM_URI;
+import static android.media.MediaSession2.ControllerInfo;
+import static android.media.MediaSession2.PlaylistParams;
 
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.media.MediaSession2;
 import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.SessionCallback;
@@ -41,8 +60,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mockito;
+
+import java.util.List;
 
 /**
  * Tests whether {@link MediaSession2} receives commands that hasn't allowed.
@@ -154,13 +173,13 @@
     public void testSkipToNext() throws InterruptedException {
         createSessionWithAllowedActions(
                 createCommandGroupWith(COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
-        createController(mSession.getToken()).skipToNext();
+        createController(mSession.getToken()).skipToNextItem();
         verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(
                 matchesSession(), matchesCaller(), matches(COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
 
         createSessionWithAllowedActions(
                 createCommandGroupWithout(COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
-        createController(mSession.getToken()).skipToNext();
+        createController(mSession.getToken()).skipToNextItem();
         verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any(), any());
     }
 
@@ -168,13 +187,13 @@
     public void testSkipToPrevious() throws InterruptedException {
         createSessionWithAllowedActions(
                 createCommandGroupWith(COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
-        createController(mSession.getToken()).skipToPrevious();
+        createController(mSession.getToken()).skipToPreviousItem();
         verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(
                 matchesSession(), matchesCaller(), matches(COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
 
         createSessionWithAllowedActions(
                 createCommandGroupWithout(COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
-        createController(mSession.getToken()).skipToPrevious();
+        createController(mSession.getToken()).skipToPreviousItem();
         verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any(), any());
     }
 
@@ -260,12 +279,13 @@
 
     @Test
     public void testSetVolume() throws InterruptedException {
-        createSessionWithAllowedActions(createCommandGroupWith(COMMAND_CODE_SET_VOLUME));
+        createSessionWithAllowedActions(createCommandGroupWith(COMMAND_CODE_PLAYBACK_SET_VOLUME));
         createController(mSession.getToken()).setVolumeTo(0, 0);
         verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onCommandRequest(
-                matchesSession(), matchesCaller(), matches(COMMAND_CODE_SET_VOLUME));
+                matchesSession(), matchesCaller(), matches(COMMAND_CODE_PLAYBACK_SET_VOLUME));
 
-        createSessionWithAllowedActions(createCommandGroupWithout(COMMAND_CODE_SET_VOLUME));
+        createSessionWithAllowedActions(
+                createCommandGroupWithout(COMMAND_CODE_PLAYBACK_SET_VOLUME));
         createController(mSession.getToken()).setVolumeTo(0, 0);
         verify(mCallback, after(WAIT_TIME_MS).never()).onCommandRequest(any(), any(), any());
     }
@@ -362,4 +382,43 @@
         verify(mCallback, after(WAIT_TIME_MS).never()).onPrepareFromSearch(
                 any(), any(), any(), any());
     }
+
+    @Test
+    public void testChangingPermissionWithSetAllowedCommands() throws InterruptedException {
+        final String query = "testChangingPermissionWithSetAllowedCommands";
+        createSessionWithAllowedActions(
+                createCommandGroupWith(COMMAND_CODE_PREPARE_FROM_SEARCH));
+
+        TestControllerCallbackInterface controllerCallback =
+                mock(TestControllerCallbackInterface.class);
+        MediaController2 controller =
+                createController(mSession.getToken(), true, controllerCallback);
+
+        controller.prepareFromSearch(query, null);
+        verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce()).onPrepareFromSearch(
+                matchesSession(), matchesCaller(), eq(query), isNull());
+        clearInvocations(mCallback);
+
+        // Change allowed commands.
+        mSession.setAllowedCommands(getTestControllerInfo(),
+                createCommandGroupWithout(COMMAND_CODE_PREPARE_FROM_SEARCH));
+        verify(controllerCallback, timeout(TIMEOUT_MS).atLeastOnce())
+                .onAllowedCommandsChanged(any());
+
+        controller.prepareFromSearch(query, null);
+        verify(mCallback, after(WAIT_TIME_MS).never()).onPrepareFromSearch(
+                any(), any(), any(), any());
+    }
+
+    private ControllerInfo getTestControllerInfo() {
+        List<ControllerInfo> controllers = mSession.getConnectedControllers();
+        assertNotNull(controllers);
+        for (int i = 0; i < controllers.size(); i++) {
+            if (Process.myUid() == controllers.get(i).getUid()) {
+                return controllers.get(i);
+            }
+        }
+        fail("Failed to get test controller info");
+        return null;
+    }
 }
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
index fb02f7a..df516c5 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
@@ -27,6 +27,8 @@
 import android.media.TestServiceRegistry.SessionCallbackProxy;
 import android.media.TestUtils.SyncHandler;
 import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.util.Log;
 
 import java.io.FileDescriptor;
@@ -96,8 +98,8 @@
 
     @Override
     public void onCreate() {
-        super.onCreate();
         TestServiceRegistry.getInstance().setServiceInstance(this);
+        super.onCreate();
     }
 
     @Override
@@ -144,9 +146,9 @@
         }
 
         @Override
-        public CommandGroup onConnect(MediaSession2 session,
-                ControllerInfo controller) {
-            return mCallbackProxy.onConnect(controller);
+        public CommandGroup onConnect(@NonNull MediaSession2 session,
+                @NonNull ControllerInfo controller) {
+            return mCallbackProxy.onConnect(session, controller);
         }
 
         @Override
@@ -211,15 +213,16 @@
         }
 
         @Override
-        public void onSubscribe(MediaLibrarySession session, ControllerInfo controller,
-                String parentId, Bundle extras) {
-            mCallbackProxy.onSubscribe(controller, parentId, extras);
+        public void onSubscribe(@NonNull MediaLibrarySession session,
+                @NonNull ControllerInfo controller, @NonNull String parentId,
+                @Nullable Bundle extras) {
+            mCallbackProxy.onSubscribe(session, controller, parentId, extras);
         }
 
         @Override
-        public void onUnsubscribe(MediaLibrarySession session, ControllerInfo controller,
-                String parentId) {
-            mCallbackProxy.onUnsubscribe(controller, parentId);
+        public void onUnsubscribe(@NonNull MediaLibrarySession session,
+                @NonNull ControllerInfo controller, String parentId) {
+            mCallbackProxy.onUnsubscribe(session, controller, parentId);
         }
     }
 
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java b/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
index ce7ce8b..64b1acd 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
@@ -27,6 +27,7 @@
 import android.media.MediaSession2.SessionCallback;
 import android.media.TestServiceRegistry.SessionCallbackProxy;
 import android.media.TestUtils.SyncHandler;
+import android.support.annotation.NonNull;
 
 import java.util.concurrent.Executor;
 
@@ -46,8 +47,8 @@
 
     @Override
     public void onCreate() {
-        super.onCreate();
         TestServiceRegistry.getInstance().setServiceInstance(this);
+        super.onCreate();
         mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
     }
 
@@ -103,9 +104,9 @@
         }
 
         @Override
-        public CommandGroup onConnect(MediaSession2 session,
-                ControllerInfo controller) {
-            return mCallbackProxy.onConnect(controller);
+        public CommandGroup onConnect(@NonNull MediaSession2 session,
+                @NonNull ControllerInfo controller) {
+            return mCallbackProxy.onConnect(session, controller);
         }
     }
 }
diff --git a/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java b/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
index 08e0cf0..27b6f89 100644
--- a/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
+++ b/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.fail;
 
 import android.content.Context;
+import android.media.MediaLibraryService2.MediaLibrarySession;
 import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.TestUtils.SyncHandler;
@@ -26,6 +27,8 @@
 import android.os.Handler;
 import android.os.Process;
 import android.support.annotation.GuardedBy;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 
 /**
  * Keeps the instance of currently running {@link MockMediaSessionService2}. And also provides
@@ -36,7 +39,7 @@
 public class TestServiceRegistry {
     /**
      * Proxy for both {@link MediaSession2.SessionCallback} and
-     * {@link MediaLibraryService2.MediaLibrarySessionCallback}.
+     * {@link MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback}.
      */
     public static abstract class SessionCallbackProxy {
         private final Context mContext;
@@ -56,7 +59,8 @@
          * @param controller
          * @return
          */
-        public CommandGroup onConnect(ControllerInfo controller) {
+        public CommandGroup onConnect(@NonNull MediaSession2 session,
+                @NonNull ControllerInfo controller) {
             if (Process.myUid() == controller.getUid()) {
                 CommandGroup commands = new CommandGroup(mContext);
                 commands.addAllPredefinedCommands();
@@ -75,8 +79,10 @@
          */
         public void onServiceDestroyed() { }
 
-        public void onSubscribe(ControllerInfo info, String parentId, Bundle extra) { }
-        public void onUnsubscribe(ControllerInfo info, String parentId) { }
+        public void onSubscribe(@NonNull MediaLibrarySession session, @NonNull ControllerInfo info,
+                @NonNull String parentId, @Nullable Bundle extra) { }
+        public void onUnsubscribe(@NonNull MediaLibrarySession session,
+                @NonNull ControllerInfo info, @NonNull String parentId) { }
     }
 
     @GuardedBy("TestServiceRegistry.class")
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
index bc12f39..e3bb5dc 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
@@ -364,7 +364,7 @@
     // queue, no onBufferReleased is called by the buffer queue.
     // Proactively trigger the callback to avoid buffer loss.
     if (queueOutput.bufferReplaced) {
-        onBufferReleasedByOutputLocked(output, surfaceId);
+        onBufferReplacedLocked(output, surfaceId);
     }
 
     return res;
@@ -582,7 +582,16 @@
 void Camera3StreamSplitter::onBufferReleasedByOutput(
         const sp<IGraphicBufferProducer>& from) {
     ATRACE_CALL();
+    sp<Fence> fence;
+
+    int slot = BufferItem::INVALID_BUFFER_SLOT;
+    auto res = from->dequeueBuffer(&slot, &fence, mWidth, mHeight, mFormat, mProducerUsage,
+            nullptr, nullptr);
     Mutex::Autolock lock(mMutex);
+    handleOutputDequeueStatusLocked(res, slot);
+    if (res != OK) {
+        return;
+    }
 
     size_t surfaceId = 0;
     bool found = false;
@@ -598,61 +607,47 @@
         return;
     }
 
-    onBufferReleasedByOutputLocked(from, surfaceId);
+    returnOutputBufferLocked(fence, from, surfaceId, slot);
 }
 
-void Camera3StreamSplitter::onBufferReleasedByOutputLocked(
+void Camera3StreamSplitter::onBufferReplacedLocked(
         const sp<IGraphicBufferProducer>& from, size_t surfaceId) {
     ATRACE_CALL();
-    sp<GraphicBuffer> buffer;
     sp<Fence> fence;
-    if (mOutputSlots[from] == nullptr) {
-        //Output surface got likely removed by client.
-        return;
-    }
-    auto outputSlots = *mOutputSlots[from];
 
     int slot = BufferItem::INVALID_BUFFER_SLOT;
     auto res = from->dequeueBuffer(&slot, &fence, mWidth, mHeight, mFormat, mProducerUsage,
             nullptr, nullptr);
-    if (res == NO_INIT) {
-        // If we just discovered that this output has been abandoned, note that,
-        // but we can't do anything else, since buffer is invalid
-        onAbandonedLocked();
-        return;
-    } else if (res == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
-        SP_LOGE("%s: Producer needs to re-allocate buffer!", __FUNCTION__);
-        SP_LOGE("%s: This should not happen with buffer allocation disabled!", __FUNCTION__);
-        return;
-    } else if (res == IGraphicBufferProducer::RELEASE_ALL_BUFFERS) {
-        SP_LOGE("%s: All slot->buffer mapping should be released!", __FUNCTION__);
-        SP_LOGE("%s: This should not happen with buffer allocation disabled!", __FUNCTION__);
-        return;
-    } else if (res == NO_MEMORY) {
-        SP_LOGE("%s: No free buffers", __FUNCTION__);
-        return;
-    } else if (res == WOULD_BLOCK) {
-        SP_LOGE("%s: Dequeue call will block", __FUNCTION__);
-        return;
-    } else if (res != OK || (slot == BufferItem::INVALID_BUFFER_SLOT)) {
-        SP_LOGE("%s: dequeue buffer from output failed (%d)", __FUNCTION__, res);
+    handleOutputDequeueStatusLocked(res, slot);
+    if (res != OK) {
         return;
     }
-    buffer = outputSlots[slot];
 
+    returnOutputBufferLocked(fence, from, surfaceId, slot);
+}
+
+void Camera3StreamSplitter::returnOutputBufferLocked(const sp<Fence>& fence,
+        const sp<IGraphicBufferProducer>& from, size_t surfaceId, int slot) {
+    sp<GraphicBuffer> buffer;
+
+    if (mOutputSlots[from] == nullptr) {
+        //Output surface got likely removed by client.
+        return;
+    }
+
+    auto outputSlots = *mOutputSlots[from];
+    buffer = outputSlots[slot];
     BufferTracker& tracker = *(mBuffers[buffer->getId()]);
     // Merge the release fence of the incoming buffer so that the fence we send
     // back to the input includes all of the outputs' fences
     if (fence != nullptr && fence->isValid()) {
         tracker.mergeFence(fence);
     }
-    SP_LOGV("%s: dequeued buffer %" PRId64 " %p from output %p", __FUNCTION__,
-            buffer->getId(), buffer.get(), from.get());
 
     auto detachBuffer = mDetachedBuffers.find(buffer->getId());
     bool detach = (detachBuffer != mDetachedBuffers.end());
     if (detach) {
-        res = from->detachBuffer(slot);
+        auto res = from->detachBuffer(slot);
         if (res == NO_ERROR) {
             outputSlots[slot] = nullptr;
         } else {
@@ -664,6 +659,26 @@
     decrementBufRefCountLocked(buffer->getId(), surfaceId);
 }
 
+void Camera3StreamSplitter::handleOutputDequeueStatusLocked(status_t res, int slot) {
+    if (res == NO_INIT) {
+        // If we just discovered that this output has been abandoned, note that,
+        // but we can't do anything else, since buffer is invalid
+        onAbandonedLocked();
+    } else if (res == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
+        SP_LOGE("%s: Producer needs to re-allocate buffer!", __FUNCTION__);
+        SP_LOGE("%s: This should not happen with buffer allocation disabled!", __FUNCTION__);
+    } else if (res == IGraphicBufferProducer::RELEASE_ALL_BUFFERS) {
+        SP_LOGE("%s: All slot->buffer mapping should be released!", __FUNCTION__);
+        SP_LOGE("%s: This should not happen with buffer allocation disabled!", __FUNCTION__);
+    } else if (res == NO_MEMORY) {
+        SP_LOGE("%s: No free buffers", __FUNCTION__);
+    } else if (res == WOULD_BLOCK) {
+        SP_LOGE("%s: Dequeue call will block", __FUNCTION__);
+    } else if (res != OK || (slot == BufferItem::INVALID_BUFFER_SLOT)) {
+        SP_LOGE("%s: dequeue buffer from output failed (%d)", __FUNCTION__, res);
+    }
+}
+
 void Camera3StreamSplitter::onAbandonedLocked() {
     // If this is called from binderDied callback, it means the app process
     // holding the binder has died. CameraService will be notified of the binder
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
index 76a5b7d..fea1bdb 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.h
@@ -122,10 +122,8 @@
     // onFrameAvailable call to proceed.
     void onBufferReleasedByOutput(const sp<IGraphicBufferProducer>& from);
 
-    // This is the implementation of onBufferReleasedByOutput without the mutex locked.
-    // It could either be called from onBufferReleasedByOutput or from
-    // onFrameAvailable when a buffer in the async buffer queue is overwritten.
-    void onBufferReleasedByOutputLocked(const sp<IGraphicBufferProducer>& from, size_t surfaceId);
+    // Called by outputBufferLocked when a buffer in the async buffer queue got replaced.
+    void onBufferReplacedLocked(const sp<IGraphicBufferProducer>& from, size_t surfaceId);
 
     // When this is called, the splitter disconnects from (i.e., abandons) its
     // input queue and signals any waiting onFrameAvailable calls to wake up.
@@ -138,6 +136,13 @@
     // 0, return the buffer back to the input BufferQueue.
     void decrementBufRefCountLocked(uint64_t id, size_t surfaceId);
 
+    // Check for and handle any output surface dequeue errors.
+    void handleOutputDequeueStatusLocked(status_t res, int slot);
+
+    // Handles released output surface buffers.
+    void returnOutputBufferLocked(const sp<Fence>& fence, const sp<IGraphicBufferProducer>& from,
+            size_t surfaceId, int slot);
+
     // This is a thin wrapper class that lets us determine which BufferQueue
     // the IProducerListener::onBufferReleased callback is associated with. We
     // create one of these per output BufferQueue, and then pass the producer
diff --git a/services/codec2/Android.bp b/services/codec2/Android.bp
deleted file mode 100644
index 1d5d205..0000000
--- a/services/codec2/Android.bp
+++ /dev/null
@@ -1,71 +0,0 @@
-cc_binary {
-    name: "vendor.google.media.c2@1.0-service",
-    defaults: ["hidl_defaults"],
-    soc_specific: true,
-    relative_install_path: "hw",
-    srcs: [
-        "vendor.cpp",
-    ],
-
-    init_rc: ["vendor.google.media.c2@1.0-service.rc"],
-
-    shared_libs: [
-        "vendor.google.media.c2@1.0",
-        "libavservices_minijail_vendor",
-        "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
-        "liblog",
-        "libstagefright_codec2_hidl@1.0",
-        "libstagefright_codec2_vndk",
-        "libutils",
-    ],
-
-    arch: {
-        arm: {
-            required: ["codec2.vendor.base.policy"],
-        },
-        x86: {
-            required: ["codec2.vendor.base.policy"],
-        },
-    },
-
-    compile_multilib: "32",
-}
-
-cc_binary {
-    name: "vendor.google.media.c2@1.0-service-system",
-    defaults: ["hidl_defaults"],
-    relative_install_path: "hw",
-    srcs: [
-        "system.cpp",
-    ],
-
-    init_rc: ["vendor.google.media.c2@1.0-service-system.rc"],
-
-    shared_libs: [
-        "vendor.google.media.c2@1.0",
-        "libavservices_minijail",
-        "libcutils",
-        "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
-        "liblog",
-        "libstagefright_codec2_hidl@1.0",
-        "libstagefright_codec2_vndk",
-        "libutils",
-        "libv4l2_c2componentstore",
-    ],
-
-    arch: {
-        arm: {
-            required: ["codec2.system.base.policy"],
-        },
-        x86: {
-            required: ["codec2.system.base.policy"],
-        },
-    },
-
-    compile_multilib: "32",
-}
-
diff --git a/services/codec2/Android.mk b/services/codec2/Android.mk
deleted file mode 100644
index fa49875..0000000
--- a/services/codec2/Android.mk
+++ /dev/null
@@ -1,43 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-# vendor service seccomp policy
-ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), x86 x86_64 arm arm64))
-include $(CLEAR_VARS)
-LOCAL_MODULE := codec2.vendor.base.policy
-LOCAL_VENDOR_MODULE := true
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/seccomp_policy
-LOCAL_REQUIRED_MODULES := crash_dump.policy
-ifdef TARGET_2ND_ARCH
-    ifneq ($(TARGET_TRANSLATE_2ND_ARCH),true)
-        LOCAL_SRC_FILES := seccomp_policy/codec2.vendor.base-$(TARGET_2ND_ARCH).policy
-    else
-        LOCAL_SRC_FILES := seccomp_policy/codec2.vendor.base-$(TARGET_ARCH).policy
-    endif
-else
-    LOCAL_SRC_FILES := seccomp_policy/codec2.vendor.base-$(TARGET_ARCH).policy
-endif
-include $(BUILD_PREBUILT)
-endif
-
-# system service seccomp policy
-ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), x86 x86_64 arm arm64))
-include $(CLEAR_VARS)
-LOCAL_MODULE := codec2.system.base.policy
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/seccomp_policy
-LOCAL_REQUIRED_MODULES := crash_dump.policy
-ifdef TARGET_2ND_ARCH
-    ifneq ($(TARGET_TRANSLATE_2ND_ARCH),true)
-        LOCAL_SRC_FILES := seccomp_policy/codec2.system.base-$(TARGET_2ND_ARCH).policy
-    else
-        LOCAL_SRC_FILES := seccomp_policy/codec2.system.base-$(TARGET_ARCH).policy
-    endif
-else
-    LOCAL_SRC_FILES := seccomp_policy/codec2.system.base-$(TARGET_ARCH).policy
-endif
-include $(BUILD_PREBUILT)
-endif
-
-include $(call all-makefiles-under, $(LOCAL_PATH))
-
diff --git a/services/codec2/seccomp_policy/codec2.system.base-arm.policy b/services/codec2/seccomp_policy/codec2.system.base-arm.policy
deleted file mode 100644
index d5871d1..0000000
--- a/services/codec2/seccomp_policy/codec2.system.base-arm.policy
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Organized by frequency of systemcall - in descending order for
-# best performance.
-futex: 1
-ioctl: 1
-write: 1
-prctl: 1
-clock_gettime: 1
-getpriority: 1
-read: 1
-close: 1
-writev: 1
-dup: 1
-ppoll: 1
-mmap2: 1
-getrandom: 1
-
-# mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail
-# parser support for '<' is in this needs to be modified to also prevent
-# |old_address| and |new_address| from touching the exception vector page, which
-# on ARM is statically loaded at 0xffff 0000. See
-# http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211h/Babfeega.html
-# for more details.
-mremap: arg3 == 3
-munmap: 1
-mprotect: 1
-madvise: 1
-openat: 1
-sigaltstack: 1
-clone: 1
-setpriority: 1
-getuid32: 1
-fstat64: 1
-fstatfs64: 1
-pread64: 1
-faccessat: 1
-readlinkat: 1
-exit: 1
-rt_sigprocmask: 1
-set_tid_address: 1
-restart_syscall: 1
-exit_group: 1
-rt_sigreturn: 1
-pipe2: 1
-gettimeofday: 1
-sched_yield: 1
-nanosleep: 1
-lseek: 1
-_llseek: 1
-sched_get_priority_max: 1
-sched_get_priority_min: 1
-statfs64: 1
-sched_setscheduler: 1
-fstatat64: 1
-ugetrlimit: 1
-getdents64: 1
-getrandom: 1
-
-@include /system/etc/seccomp_policy/crash_dump.arm.policy
-
diff --git a/services/codec2/seccomp_policy/codec2.system.base-x86.policy b/services/codec2/seccomp_policy/codec2.system.base-x86.policy
deleted file mode 100644
index 20c7625..0000000
--- a/services/codec2/seccomp_policy/codec2.system.base-x86.policy
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-read: 1
-mprotect: 1
-prctl: 1
-openat: 1
-getuid32: 1
-writev: 1
-ioctl: 1
-close: 1
-mmap2: 1
-fstat64: 1
-madvise: 1
-fstatat64: 1
-futex: 1
-munmap: 1
-faccessat: 1
-_llseek: 1
-lseek: 1
-clone: 1
-sigaltstack: 1
-setpriority: 1
-restart_syscall: 1
-exit: 1
-exit_group: 1
-rt_sigreturn: 1
-ugetrlimit: 1
-readlinkat: 1
-_llseek: 1
-fstatfs64: 1
-pread64: 1
-mremap: 1
-dup: 1
-set_tid_address: 1
-write: 1
-nanosleep: 1
-
-# Required by AddressSanitizer
-gettid: 1
-sched_yield: 1
-getpid: 1
-gettid: 1
-
-@include /system/etc/seccomp_policy/crash_dump.x86.policy
-
diff --git a/services/codec2/seccomp_policy/codec2.vendor.base-arm.policy b/services/codec2/seccomp_policy/codec2.vendor.base-arm.policy
deleted file mode 100644
index d5871d1..0000000
--- a/services/codec2/seccomp_policy/codec2.vendor.base-arm.policy
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Organized by frequency of systemcall - in descending order for
-# best performance.
-futex: 1
-ioctl: 1
-write: 1
-prctl: 1
-clock_gettime: 1
-getpriority: 1
-read: 1
-close: 1
-writev: 1
-dup: 1
-ppoll: 1
-mmap2: 1
-getrandom: 1
-
-# mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail
-# parser support for '<' is in this needs to be modified to also prevent
-# |old_address| and |new_address| from touching the exception vector page, which
-# on ARM is statically loaded at 0xffff 0000. See
-# http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211h/Babfeega.html
-# for more details.
-mremap: arg3 == 3
-munmap: 1
-mprotect: 1
-madvise: 1
-openat: 1
-sigaltstack: 1
-clone: 1
-setpriority: 1
-getuid32: 1
-fstat64: 1
-fstatfs64: 1
-pread64: 1
-faccessat: 1
-readlinkat: 1
-exit: 1
-rt_sigprocmask: 1
-set_tid_address: 1
-restart_syscall: 1
-exit_group: 1
-rt_sigreturn: 1
-pipe2: 1
-gettimeofday: 1
-sched_yield: 1
-nanosleep: 1
-lseek: 1
-_llseek: 1
-sched_get_priority_max: 1
-sched_get_priority_min: 1
-statfs64: 1
-sched_setscheduler: 1
-fstatat64: 1
-ugetrlimit: 1
-getdents64: 1
-getrandom: 1
-
-@include /system/etc/seccomp_policy/crash_dump.arm.policy
-
diff --git a/services/codec2/seccomp_policy/codec2.vendor.base-x86.policy b/services/codec2/seccomp_policy/codec2.vendor.base-x86.policy
deleted file mode 100644
index 20c7625..0000000
--- a/services/codec2/seccomp_policy/codec2.vendor.base-x86.policy
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-read: 1
-mprotect: 1
-prctl: 1
-openat: 1
-getuid32: 1
-writev: 1
-ioctl: 1
-close: 1
-mmap2: 1
-fstat64: 1
-madvise: 1
-fstatat64: 1
-futex: 1
-munmap: 1
-faccessat: 1
-_llseek: 1
-lseek: 1
-clone: 1
-sigaltstack: 1
-setpriority: 1
-restart_syscall: 1
-exit: 1
-exit_group: 1
-rt_sigreturn: 1
-ugetrlimit: 1
-readlinkat: 1
-_llseek: 1
-fstatfs64: 1
-pread64: 1
-mremap: 1
-dup: 1
-set_tid_address: 1
-write: 1
-nanosleep: 1
-
-# Required by AddressSanitizer
-gettid: 1
-sched_yield: 1
-getpid: 1
-gettid: 1
-
-@include /system/etc/seccomp_policy/crash_dump.x86.policy
-
diff --git a/services/codec2/system.cpp b/services/codec2/system.cpp
deleted file mode 100644
index d6ec644..0000000
--- a/services/codec2/system.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "vendor.google.media.c2@1.0-service"
-
-#include <C2PlatformSupport.h>
-#include <C2V4l2Support.h>
-#include <cutils/properties.h>
-
-#include <codec2/hidl/1.0/ComponentStore.h>
-#include <hidl/HidlTransportSupport.h>
-#include <minijail.h>
-
-// TODO: Remove this once "setenv()" call is removed.
-#include <stdlib.h>
-
-// This is created by module "codec2.system.base.policy". This can be modified.
-static constexpr char kBaseSeccompPolicyPath[] =
-        "/system/etc/seccomp_policy/codec2.system.base.policy";
-
-// Additional device-specific seccomp permissions can be added in this file.
-static constexpr char kExtSeccompPolicyPath[] =
-        "/system/etc/seccomp_policy/codec2.system.ext.policy";
-
-int main(int /* argc */, char** /* argv */) {
-    ALOGD("vendor.google.media.c2@1.0-service-system starting...");
-
-    // TODO: Remove this when all the build settings and sepolicies are in place.
-    setenv("TREBLE_TESTING_OVERRIDE", "true", true);
-
-    signal(SIGPIPE, SIG_IGN);
-    android::SetUpMinijail(kBaseSeccompPolicyPath, kExtSeccompPolicyPath);
-
-    // Extra threads may be needed to handle a stacked IPC sequence that
-    // contains alternating binder and hwbinder calls. (See b/35283480.)
-    android::hardware::configureRpcThreadpool(8, true /* callerWillJoin */);
-
-    // Create IComponentStore service.
-    {
-        using namespace ::vendor::google::media::c2::V1_0;
-        android::sp<IComponentStore> store =
-                new implementation::ComponentStore(
-                android::GetCodec2PlatformComponentStore());
-        if (store == nullptr) {
-            ALOGE("Cannot create Codec2's IComponentStore system service.");
-        } else {
-            if (store->registerAsService("system") != android::OK) {
-                ALOGE("Cannot register Codec2's "
-                        "IComponentStore system service.");
-            } else {
-                ALOGI("Codec2's IComponentStore system service created.");
-            }
-        }
-
-        // To enable the v4l2 service, set this sysprop and add "v4l2" instance
-        // to the system manifest file.
-        if (property_get_bool("debug.stagefright.ccodec_v4l2", false)) {
-            store = new implementation::ComponentStore(
-                    android::GetCodec2VDAComponentStore());
-            if (store == nullptr) {
-                ALOGE("Cannot create Codec2's IComponentStore V4L2 service.");
-            } else {
-                if (store->registerAsService("v4l2") != android::OK) {
-                    ALOGE("Cannot register Codec2's "
-                            "IComponentStore V4L2 service.");
-                } else {
-                    ALOGI("Codec2's IComponentStore V4L2 service created.");
-                }
-            }
-        }
-    }
-
-    android::hardware::joinRpcThreadpool();
-    return 0;
-}
-
diff --git a/services/codec2/vendor.cpp b/services/codec2/vendor.cpp
deleted file mode 100644
index 3993eba..0000000
--- a/services/codec2/vendor.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "vendor.google.media.c2@1.0-service"
-
-// TODO: Replace this with vendor's implementation
-#include <C2PlatformSupport.h>
-
-#include <codec2/hidl/1.0/ComponentStore.h>
-#include <hidl/HidlTransportSupport.h>
-#include <minijail.h>
-
-// TODO: Remove this once "setenv()" call is removed.
-#include <stdlib.h>
-
-// This is created by module "codec2.vendor.base.policy". This can be modified.
-static constexpr char kBaseSeccompPolicyPath[] =
-        "/vendor/etc/seccomp_policy/codec2.vendor.base.policy";
-
-// Additional device-specific seccomp permissions can be added in this file.
-static constexpr char kExtSeccompPolicyPath[] =
-        "/vendor/etc/seccomp_policy/codec2.vendor.ext.policy";
-
-int main(int /* argc */, char** /* argv */) {
-    ALOGD("vendor.google.media.c2@1.0-service starting...");
-
-    // TODO: Remove this when all the build settings and sepolicies are in place.
-    setenv("TREBLE_TESTING_OVERRIDE", "true", true);
-
-    signal(SIGPIPE, SIG_IGN);
-    android::SetUpMinijail(kBaseSeccompPolicyPath, kExtSeccompPolicyPath);
-
-    // Extra threads may be needed to handle a stacked IPC sequence that
-    // contains alternating binder and hwbinder calls. (See b/35283480.)
-    android::hardware::configureRpcThreadpool(8, true /* callerWillJoin */);
-
-    // Create IComponentStore service.
-    {
-        using namespace ::vendor::google::media::c2::V1_0;
-        android::sp<IComponentStore> store =
-                new implementation::ComponentStore(
-                android::GetCodec2PlatformComponentStore());
-        if (store == nullptr) {
-            ALOGE("Cannot create Codec2's IComponentStore service.");
-        } else {
-            if (store->registerAsService("default") != android::OK) {
-                ALOGE("Cannot register Codec2's "
-                        "IComponentStore service.");
-            } else {
-                ALOGI("Codec2's IComponentStore service created.");
-            }
-        }
-    }
-
-    android::hardware::joinRpcThreadpool();
-    return 0;
-}
-
diff --git a/services/codec2/vendor.google.media.c2@1.0-service-system.rc b/services/codec2/vendor.google.media.c2@1.0-service-system.rc
deleted file mode 100644
index 0577a1d..0000000
--- a/services/codec2/vendor.google.media.c2@1.0-service-system.rc
+++ /dev/null
@@ -1,7 +0,0 @@
-service vendor-google-media-c2-system-hal-1-0 /system/bin/hw/vendor.google.media.c2@1.0-service-system
-    class hal
-    user media
-    group mediadrm drmrpc
-    ioprio rt 4
-    writepid /dev/cpuset/foreground/tasks
-
diff --git a/services/codec2/vendor.google.media.c2@1.0-service.rc b/services/codec2/vendor.google.media.c2@1.0-service.rc
deleted file mode 100644
index 3e7e0a6..0000000
--- a/services/codec2/vendor.google.media.c2@1.0-service.rc
+++ /dev/null
@@ -1,7 +0,0 @@
-service vendor-google-media-c2-hal-1-0 /vendor/bin/hw/vendor.google.media.c2@1.0-service
-    class hal
-    user media
-    group mediadrm drmrpc
-    ioprio rt 4
-    writepid /dev/cpuset/foreground/tasks
-