Merge "NdkMediaExtractor: per-sample format metadata API"
diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp
index 5ea4614..66e4400 100644
--- a/drm/libmediadrm/Android.bp
+++ b/drm/libmediadrm/Android.bp
@@ -7,7 +7,6 @@
 cc_library {
     name: "libmediadrm",
 
-
     srcs: [
         "DrmPluginPath.cpp",
         "DrmSessionManager.cpp",
@@ -62,6 +61,7 @@
         "android.hardware.drm@1.1",
         "libbase",
         "libbinder",
+	"libhidlbase",
         "liblog",
         "libmediametrics",
         "libprotobuf-cpp-lite",
@@ -93,6 +93,7 @@
         "android.hardware.drm@1.1",
         "libbase",
         "libbinder",
+	"libhidlbase",
         "liblog",
         "libmediametrics",
         "libprotobuf-cpp-full",
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index 292895f..5d97188 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -48,6 +48,7 @@
 using drm::V1_0::SecureStopId;
 using drm::V1_1::SecurityLevel;
 using drm::V1_0::Status;
+using ::android::hardware::drm::V1_1::DrmMetricGroup;
 using ::android::hardware::hidl_array;
 using ::android::hardware::hidl_string;
 using ::android::hardware::hidl_vec;
@@ -1122,12 +1123,48 @@
     return status.isOk() ? toStatusT(status) : DEAD_OBJECT;
 }
 
-status_t DrmHal::getMetrics(PersistableBundle* item) {
-    if (item == nullptr) {
-      return UNEXPECTED_NULL;
+status_t DrmHal::getMetrics(PersistableBundle* metrics) {
+    if (metrics == nullptr) {
+        return UNEXPECTED_NULL;
+    }
+    mMetrics.Export(metrics);
+
+    // Append vendor metrics if they are supported.
+    if (mPluginV1_1 != NULL) {
+        String8 vendor;
+        String8 description;
+        if (getPropertyStringInternal(String8("vendor"), vendor) != OK
+            || vendor.isEmpty()) {
+          ALOGE("Get vendor failed or is empty");
+          vendor = "NONE";
+        }
+        if (getPropertyStringInternal(String8("description"), description) != OK
+            || description.isEmpty()) {
+          ALOGE("Get description failed or is empty.");
+          description = "NONE";
+        }
+        vendor += ".";
+        vendor += description;
+
+        hidl_vec<DrmMetricGroup> pluginMetrics;
+        status_t err = UNKNOWN_ERROR;
+
+        Return<void> status = mPluginV1_1->getMetrics(
+                [&](Status status, hidl_vec<DrmMetricGroup> pluginMetrics) {
+                    if (status != Status::OK) {
+                      ALOGV("Error getting plugin metrics: %d", status);
+                    } else {
+                        PersistableBundle pluginBundle;
+                        if (MediaDrmMetrics::HidlMetricsToBundle(
+                                pluginMetrics, &pluginBundle) == OK) {
+                            metrics->putPersistableBundle(String16(vendor), pluginBundle);
+                        }
+                    }
+                    err = toStatusT(status);
+                });
+        return status.isOk() ? err : DEAD_OBJECT;
     }
 
-    mMetrics.Export(item);
     return OK;
 }
 
diff --git a/drm/libmediadrm/DrmMetrics.cpp b/drm/libmediadrm/DrmMetrics.cpp
index 96cc2cd..fce1717 100644
--- a/drm/libmediadrm/DrmMetrics.cpp
+++ b/drm/libmediadrm/DrmMetrics.cpp
@@ -29,8 +29,10 @@
 using ::android::String16;
 using ::android::String8;
 using ::android::drm_metrics::DrmFrameworkMetrics;
+using ::android::hardware::hidl_vec;
 using ::android::hardware::drm::V1_0::EventType;
 using ::android::hardware::drm::V1_0::KeyStatusType;
+using ::android::hardware::drm::V1_1::DrmMetricGroup;
 using ::android::os::PersistableBundle;
 
 namespace {
@@ -172,6 +174,24 @@
     return out.str();
 }
 
+template <typename CT>
+void SetValue(const String16 &name, DrmMetricGroup::ValueType type,
+              const CT &value, PersistableBundle *bundle) {
+    switch (type) {
+    case DrmMetricGroup::ValueType::INT64_TYPE:
+        bundle->putLong(name, value.int64Value);
+        break;
+    case DrmMetricGroup::ValueType::DOUBLE_TYPE:
+        bundle->putDouble(name, value.doubleValue);
+        break;
+    case DrmMetricGroup::ValueType::STRING_TYPE:
+        bundle->putString(name, String16(value.stringValue.c_str()));
+        break;
+    default:
+        ALOGE("Unexpected value type: %hhu", type);
+    }
+}
+
 } // namespace
 
 namespace android {
@@ -339,4 +359,46 @@
     return ((int64_t)tv.tv_sec * 1000) + ((int64_t)tv.tv_usec / 1000);
 }
 
+status_t MediaDrmMetrics::HidlMetricsToBundle(
+    const hidl_vec<DrmMetricGroup> &hidlMetricGroups,
+    PersistableBundle *bundleMetricGroups) {
+    if (bundleMetricGroups == nullptr) {
+        return UNEXPECTED_NULL;
+    }
+    if (hidlMetricGroups.size() == 0) {
+        return OK;
+    }
+
+    int groupIndex = 0;
+    for (const auto &hidlMetricGroup : hidlMetricGroups) {
+        PersistableBundle bundleMetricGroup;
+        for (const auto &hidlMetric : hidlMetricGroup.metrics) {
+            PersistableBundle bundleMetric;
+            // Add metric component values.
+            for (const auto &value : hidlMetric.values) {
+                SetValue(String16(value.componentName.c_str()), value.type,
+                         value, &bundleMetric);
+            }
+            // Set metric attributes.
+            PersistableBundle bundleMetricAttributes;
+            for (const auto &attribute : hidlMetric.attributes) {
+                SetValue(String16(attribute.name.c_str()), attribute.type,
+                         attribute, &bundleMetricAttributes);
+            }
+            // Add attributes to the bundle metric.
+            bundleMetric.putPersistableBundle(String16("attributes"),
+                                              bundleMetricAttributes);
+            // Add the bundle metric to the group of metrics.
+            bundleMetricGroup.putPersistableBundle(
+                String16(hidlMetric.name.c_str()), bundleMetric);
+        }
+        // Add the bundle metric group to the collection of groups.
+        bundleMetricGroups->putPersistableBundle(
+            String16(std::to_string(groupIndex).c_str()), bundleMetricGroup);
+        groupIndex++;
+    }
+
+    return OK;
+}
+
 } // namespace android
diff --git a/drm/libmediadrm/tests/Android.bp b/drm/libmediadrm/tests/Android.bp
index 670d3b9..66c906f 100644
--- a/drm/libmediadrm/tests/Android.bp
+++ b/drm/libmediadrm/tests/Android.bp
@@ -16,7 +16,9 @@
     srcs: ["DrmMetrics_test.cpp"],
     shared_libs: [
       "android.hardware.drm@1.0",
+      "android.hardware.drm@1.1",
       "libbinder",
+      "libhidlbase",
       "liblog",
       "libmediadrmmetrics_full",
       "libmediametrics",
diff --git a/drm/libmediadrm/tests/DrmMetrics_test.cpp b/drm/libmediadrm/tests/DrmMetrics_test.cpp
index 7303806..1a20342 100644
--- a/drm/libmediadrm/tests/DrmMetrics_test.cpp
+++ b/drm/libmediadrm/tests/DrmMetrics_test.cpp
@@ -17,6 +17,8 @@
 #define LOG_TAG "DrmMetricsTest"
 #include "mediadrm/DrmMetrics.h"
 
+#include <android/hardware/drm/1.0/types.h>
+#include <android/hardware/drm/1.1/types.h>
 #include <binder/PersistableBundle.h>
 #include <google/protobuf/text_format.h>
 #include <google/protobuf/util/message_differencer.h>
@@ -26,8 +28,11 @@
 #include "protos/metrics.pb.h"
 
 using ::android::drm_metrics::DrmFrameworkMetrics;
+using ::android::hardware::hidl_vec;
 using ::android::hardware::drm::V1_0::EventType;
 using ::android::hardware::drm::V1_0::KeyStatusType;
+using ::android::hardware::drm::V1_0::Status;
+using ::android::hardware::drm::V1_1::DrmMetricGroup;
 using ::android::os::PersistableBundle;
 using ::google::protobuf::util::MessageDifferencer;
 using ::google::protobuf::TextFormat;
@@ -343,19 +348,19 @@
   ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics));
 
   std::string expectedMetrics =
-      "get_key_request_timing { "
+      "get_key_request_time_us { "
       "  min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
       "  attributes { error_code: -0x7FFFFFF8 } "
       "} "
-      "get_key_request_timing { "
+      "get_key_request_time_us { "
       "  min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
       "  attributes { error_code: 0 } "
       "} "
-      "provide_key_response_timing { "
+      "provide_key_response_time_us { "
       "  min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
       "  attributes { error_code: -0x7FFFFFF8 } "
       "} "
-      "provide_key_response_timing { "
+      "provide_key_response_time_us { "
       "  min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
       "  attributes { error_code: 0 } "
       "} ";
@@ -412,4 +417,55 @@
       << diffString;
 }
 
+TEST_F(MediaDrmMetricsTest, HidlToBundleMetricsEmpty) {
+  hidl_vec<DrmMetricGroup> hidlMetricGroups;
+  PersistableBundle bundleMetricGroups;
+
+  ASSERT_EQ(OK, MediaDrmMetrics::HidlMetricsToBundle(hidlMetricGroups, &bundleMetricGroups));
+  ASSERT_EQ(0U, bundleMetricGroups.size());
+}
+
+TEST_F(MediaDrmMetricsTest, HidlToBundleMetricsMultiple) {
+  DrmMetricGroup hidlMetricGroup =
+      { { {
+              "open_session_ok",
+              { { "status", DrmMetricGroup::ValueType::INT64_TYPE, (int64_t) Status::OK, 0.0, "" } },
+              { { "count", DrmMetricGroup::ValueType::INT64_TYPE, 3, 0.0, "" } }
+          },
+          {
+              "close_session_not_opened",
+              { { "status", DrmMetricGroup::ValueType::INT64_TYPE,
+                  (int64_t) Status::ERROR_DRM_SESSION_NOT_OPENED, 0.0, "" } },
+              { { "count", DrmMetricGroup::ValueType::INT64_TYPE, 7, 0.0, "" } }
+          } } };
+
+  PersistableBundle bundleMetricGroups;
+  ASSERT_EQ(OK, MediaDrmMetrics::HidlMetricsToBundle(hidl_vec<DrmMetricGroup>({hidlMetricGroup}),
+                                                     &bundleMetricGroups));
+  ASSERT_EQ(1U, bundleMetricGroups.size());
+  PersistableBundle bundleMetricGroup;
+  ASSERT_TRUE(bundleMetricGroups.getPersistableBundle(String16("0"), &bundleMetricGroup));
+  ASSERT_EQ(2U, bundleMetricGroup.size());
+
+  // Verify each metric.
+  PersistableBundle metric;
+  ASSERT_TRUE(bundleMetricGroup.getPersistableBundle(String16("open_session_ok"), &metric));
+  int64_t value = 0;
+  ASSERT_TRUE(metric.getLong(String16("count"), &value));
+  ASSERT_EQ(3, value);
+  PersistableBundle attributeBundle;
+  ASSERT_TRUE(metric.getPersistableBundle(String16("attributes"), &attributeBundle));
+  ASSERT_TRUE(attributeBundle.getLong(String16("status"), &value));
+  ASSERT_EQ((int64_t) Status::OK, value);
+
+  ASSERT_TRUE(bundleMetricGroup.getPersistableBundle(String16("close_session_not_opened"),
+                                                     &metric));
+  ASSERT_TRUE(metric.getLong(String16("count"), &value));
+  ASSERT_EQ(7, value);
+  ASSERT_TRUE(metric.getPersistableBundle(String16("attributes"), &attributeBundle));
+  value = 0;
+  ASSERT_TRUE(attributeBundle.getLong(String16("status"), &value));
+  ASSERT_EQ((int64_t) Status::ERROR_DRM_SESSION_NOT_OPENED, value);
+}
+
 }  // namespace android
diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
index e27631f..ed9534f 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
+++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
@@ -347,6 +347,9 @@
         return ERROR_CAS_CANNOT_HANDLE;
     }
 
+    scramblingControl = (DescramblerPlugin::ScramblingControl)
+        (scramblingControl & DescramblerPlugin::kScrambling_Mask_Key);
+
     AES_KEY contentKey;
 
     if (scramblingControl != DescramblerPlugin::kScrambling_Unscrambled) {
diff --git a/media/extractors/aac/AACExtractor.cpp b/media/extractors/aac/AACExtractor.cpp
index bbc1ff8..9fc5a76 100644
--- a/media/extractors/aac/AACExtractor.cpp
+++ b/media/extractors/aac/AACExtractor.cpp
@@ -373,6 +373,7 @@
         *confidence = 0.2;
 
         off64_t *offPtr = (off64_t*) malloc(sizeof(off64_t));
+        *offPtr = pos;
         *meta = offPtr;
         *freeMeta = ::free;
 
diff --git a/media/libmedia/include/media/DrmMetrics.h b/media/libmedia/include/media/DrmMetrics.h
index 5c2fdf2..261998f 100644
--- a/media/libmedia/include/media/DrmMetrics.h
+++ b/media/libmedia/include/media/DrmMetrics.h
@@ -20,6 +20,7 @@
 #include <map>
 
 #include <android/hardware/drm/1.0/types.h>
+#include <android/hardware/drm/1.1/types.h>
 #include <binder/PersistableBundle.h>
 #include <media/CounterMetric.h>
 #include <media/EventMetric.h>
@@ -78,6 +79,47 @@
   // caller and must not be null.
   status_t GetSerializedMetrics(std::string* serializedMetrics);
 
+  // Converts the DRM plugin metrics to a PersistableBundle. All of the metrics
+  // found in |pluginMetrics| are added to the |metricsBundle| parameter.
+  // |pluginBundle| is owned by the caller and must not be null.
+  //
+  // Each item in the pluginMetrics vector is added as a new PersistableBundle. E.g.
+  // DrmMetricGroup {
+  //   metrics[0] {
+  //     name: "buf_copy"
+  //     attributes[0] {
+  //       name: "size"
+  //       type: INT64_TYPE
+  //       int64Value: 1024
+  //     }
+  //     values[0] {
+  //       componentName: "operation_count"
+  //       type: INT64_TYPE
+  //       int64Value: 75
+  //     }
+  //     values[1] {
+  //       component_name: "average_time_seconds"
+  //       type: DOUBLE_TYPE
+  //       doubleValue: 0.00000042
+  //     }
+  //   }
+  // }
+  //
+  // becomes
+  //
+  // metricsBundle {
+  //   "0": (PersistableBundle) {
+  //     "attributes" : (PersistableBundle) {
+  //       "size" : (int64) 1024
+  //     }
+  //     "operation_count" : (int64) 75
+  //     "average_time_seconds" : (double) 0.00000042
+  //   }
+  //
+  static status_t HidlMetricsToBundle(
+          const hardware::hidl_vec<hardware::drm::V1_1::DrmMetricGroup>& pluginMetrics,
+          os::PersistableBundle* metricsBundle);
+
  protected:
   // This is visible for testing only.
   virtual int64_t GetCurrentTimeMs();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index abb8fb0..d252150 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -184,6 +184,7 @@
       mVideoDecoderGeneration(0),
       mRendererGeneration(0),
       mLastStartedPlayingTimeNs(0),
+      mLastStartedRebufferingTimeNs(0),
       mPreviousSeekTimeUs(0),
       mAudioEOS(false),
       mVideoEOS(false),
@@ -1311,8 +1312,8 @@
             ALOGV("kWhatReset");
 
             mResetting = true;
-            stopPlaybackTimer("kWhatReset");
-            stopRebufferingTimer(true);
+            updatePlaybackTimer(true /* stopping */, "kWhatReset");
+            updateRebufferingTimer(true /* stopping */, true /* exiting */);
 
             mDeferredActions.push_back(
                     new FlushDecoderAction(
@@ -1586,23 +1587,28 @@
     }
 }
 
-void NuPlayer::stopPlaybackTimer(const char *where) {
+void NuPlayer::updatePlaybackTimer(bool stopping, const char *where) {
     Mutex::Autolock autoLock(mPlayingTimeLock);
 
-    ALOGV("stopPlaybackTimer()  time %20" PRId64 " (%s)", mLastStartedPlayingTimeNs, where);
+    ALOGV("updatePlaybackTimer(%s)  time %20" PRId64 " (%s)",
+	  stopping ? "stop" : "snap", mLastStartedPlayingTimeNs, where);
 
     if (mLastStartedPlayingTimeNs != 0) {
         sp<NuPlayerDriver> driver = mDriver.promote();
+        int64_t now = systemTime();
         if (driver != NULL) {
-            int64_t now = systemTime();
             int64_t played = now - mLastStartedPlayingTimeNs;
-            ALOGV("stopPlaybackTimer()  log  %20" PRId64 "", played);
+            ALOGV("updatePlaybackTimer()  log  %20" PRId64 "", played);
 
             if (played > 0) {
                 driver->notifyMorePlayingTimeUs((played+500)/1000);
             }
         }
-        mLastStartedPlayingTimeNs = 0;
+	if (stopping) {
+            mLastStartedPlayingTimeNs = 0;
+	} else {
+            mLastStartedPlayingTimeNs = now;
+	}
     }
 }
 
@@ -1614,17 +1620,18 @@
     }
 }
 
-void NuPlayer::stopRebufferingTimer(bool exitingPlayback) {
+void NuPlayer::updateRebufferingTimer(bool stopping, bool exitingPlayback) {
     Mutex::Autolock autoLock(mPlayingTimeLock);
 
-    ALOGV("stopRebufferTimer()  time %20" PRId64 " (exiting %d)", mLastStartedRebufferingTimeNs, exitingPlayback);
+    ALOGV("updateRebufferingTimer(%s)  time %20" PRId64 " (exiting %d)",
+	  stopping ? "stop" : "snap", mLastStartedRebufferingTimeNs, exitingPlayback);
 
     if (mLastStartedRebufferingTimeNs != 0) {
         sp<NuPlayerDriver> driver = mDriver.promote();
+        int64_t now = systemTime();
         if (driver != NULL) {
-            int64_t now = systemTime();
             int64_t rebuffered = now - mLastStartedRebufferingTimeNs;
-            ALOGV("stopRebufferingTimer()  log  %20" PRId64 "", rebuffered);
+            ALOGV("updateRebufferingTimer()  log  %20" PRId64 "", rebuffered);
 
             if (rebuffered > 0) {
                 driver->notifyMoreRebufferingTimeUs((rebuffered+500)/1000);
@@ -1633,13 +1640,24 @@
                 }
             }
         }
-        mLastStartedRebufferingTimeNs = 0;
+	if (stopping) {
+            mLastStartedRebufferingTimeNs = 0;
+	} else {
+            mLastStartedRebufferingTimeNs = now;
+	}
     }
 }
 
+void NuPlayer::updateInternalTimers() {
+    // update values, but ticking clocks keep ticking
+    ALOGV("updateInternalTimers()");
+    updatePlaybackTimer(false /* stopping */, "updateInternalTimers");
+    updateRebufferingTimer(false /* stopping */, false /* exiting */);
+}
+
 void NuPlayer::onPause() {
 
-    stopPlaybackTimer("onPause");
+    updatePlaybackTimer(true /* stopping */, "onPause");
 
     if (mPaused) {
         return;
@@ -2282,8 +2300,8 @@
     CHECK(mAudioDecoder == NULL);
     CHECK(mVideoDecoder == NULL);
 
-    stopPlaybackTimer("performReset");
-    stopRebufferingTimer(true);
+    updatePlaybackTimer(true /* stopping */, "performReset");
+    updateRebufferingTimer(true /* stopping */, true /* exiting */);
 
     cancelPollDuration();
 
@@ -2551,7 +2569,7 @@
             if (mStarted) {
                 ALOGI("buffer ready, resuming...");
 
-                stopRebufferingTimer(false);
+                updateRebufferingTimer(true /* stopping */, false /* exiting */);
                 mPausedForBuffering = false;
 
                 // do not resume yet if client didn't unpause
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index fda69e8..9481234 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -99,6 +99,8 @@
 
     const char *getDataSourceType();
 
+    void updateInternalTimers();
+
 protected:
     virtual ~NuPlayer();
 
@@ -180,12 +182,12 @@
 
     Mutex mPlayingTimeLock;
     int64_t mLastStartedPlayingTimeNs;
-    void stopPlaybackTimer(const char *where);
+    void updatePlaybackTimer(bool stopping, const char *where);
     void startPlaybackTimer(const char *where);
 
     int64_t mLastStartedRebufferingTimeNs;
     void startRebufferingTimer();
-    void stopRebufferingTimer(bool exitingPlayback);
+    void updateRebufferingTimer(bool stopping, bool exitingPlayback);
 
     int64_t mPreviousSeekTimeUs;
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index c455951..731fdba 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -535,6 +535,7 @@
 }
 
 void NuPlayerDriver::updateMetrics(const char *where) {
+
     if (where == NULL) {
         where = "unknown";
     }
@@ -592,6 +593,8 @@
     getDuration(&duration_ms);
     mAnalyticsItem->setInt64(kPlayerDuration, duration_ms);
 
+    mPlayer->updateInternalTimers();
+
     mAnalyticsItem->setInt64(kPlayerPlaying, (mPlayingTimeUs+500)/1000 );
 
     if (mRebufferingEvents != 0) {
@@ -630,7 +633,7 @@
             mAnalyticsItem->setUid(mClientUid);
         }
     } else {
-        ALOGV("did not have anything to record");
+        ALOGV("nothing to record (only %d fields)", mAnalyticsItem->count());
     }
 }
 
diff --git a/media/libstagefright/codec2/vndk/C2Store.cpp b/media/libstagefright/codec2/vndk/C2Store.cpp
index f612b4f..216a897 100644
--- a/media/libstagefright/codec2/vndk/C2Store.cpp
+++ b/media/libstagefright/codec2/vndk/C2Store.cpp
@@ -433,9 +433,29 @@
     mComponents.emplace("c2.google.avc.encoder", "libstagefright_soft_c2avcenc.so");
     mComponents.emplace("c2.google.aac.decoder", "libstagefright_soft_c2aacdec.so");
     mComponents.emplace("c2.google.aac.encoder", "libstagefright_soft_c2aacenc.so");
-    mComponents.emplace("c2.google.mp3.decoder", "libstagefright_soft_c2mp3dec.so");
+    mComponents.emplace("c2.google.amrnb.decoder", "libstagefright_soft_c2amrnbdec.so");
+    mComponents.emplace("c2.google.amrnb.encoder", "libstagefright_soft_c2amrnbenc.so");
+    mComponents.emplace("c2.google.amrwb.decoder", "libstagefright_soft_c2amrwbdec.so");
+    mComponents.emplace("c2.google.amrwb.encoder", "libstagefright_soft_c2amrwbenc.so");
+    mComponents.emplace("c2.google.hevc.decoder", "libstagefright_soft_c2hevcdec.so");
     mComponents.emplace("c2.google.g711.alaw.decoder", "libstagefright_soft_c2g711alawdec.so");
     mComponents.emplace("c2.google.g711.mlaw.decoder", "libstagefright_soft_c2g711mlawdec.so");
+    mComponents.emplace("c2.google.mpeg2.decoder", "libstagefright_soft_c2mpeg2dec.so");
+    mComponents.emplace("c2.google.h263.decoder", "libstagefright_soft_c2h263dec.so");
+    mComponents.emplace("c2.google.h263.encoder", "libstagefright_soft_c2h263enc.so");
+    mComponents.emplace("c2.google.mpeg4.decoder", "libstagefright_soft_c2mpeg4dec.so");
+    mComponents.emplace("c2.google.mpeg4.encoder", "libstagefright_soft_c2mpeg4enc.so");
+    mComponents.emplace("c2.google.mp3.decoder", "libstagefright_soft_c2mp3dec.so");
+    mComponents.emplace("c2.google.vorbis.decoder", "libstagefright_soft_c2vorbisdec.so");
+    mComponents.emplace("c2.google.opus.decoder", "libstagefright_soft_c2opusdec.so");
+    mComponents.emplace("c2.google.vp8.decoder", "libstagefright_soft_c2vp8dec.so");
+    mComponents.emplace("c2.google.vp9.decoder", "libstagefright_soft_c2vp9dec.so");
+    mComponents.emplace("c2.google.vp8.encoder", "libstagefright_soft_c2vp8enc.so");
+    mComponents.emplace("c2.google.vp9.encoder", "libstagefright_soft_c2vp9enc.so");
+    mComponents.emplace("c2.google.raw.decoder", "libstagefright_soft_c2rawdec.so");
+    mComponents.emplace("c2.google.flac.decoder", "libstagefright_soft_c2flacdec.so");
+    mComponents.emplace("c2.google.flac.encoder", "libstagefright_soft_c2flacenc.so");
+    mComponents.emplace("c2.google.gsm.decoder", "libstagefright_soft_c2gsmdec.so");
 }
 
 c2_status_t C2PlatformComponentStore::copyBuffer(
diff --git a/media/libstagefright/codecs/amrnb/enc/Android.bp b/media/libstagefright/codecs/amrnb/enc/Android.bp
index f844459..4001d46 100644
--- a/media/libstagefright/codecs/amrnb/enc/Android.bp
+++ b/media/libstagefright/codecs/amrnb/enc/Android.bp
@@ -122,6 +122,50 @@
 
 //###############################################################################
 
+cc_library_shared {
+    name: "libstagefright_soft_c2amrnbenc",
+//    vendor_available: true,
+//    vndk: {
+//        enabled: true,
+//    },
+
+    srcs: ["C2SoftAmrNbEnc.cpp"],
+
+    local_include_dirs: ["src"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+            "unsigned-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+
+    static_libs: [
+        "libstagefright_amrnbenc",
+    ],
+
+    shared_libs: [
+        "libutils",
+        "liblog",
+        "libstagefright_codec2",
+        "libstagefright_codec2_vndk",
+        "libstagefright_foundation",
+        "libstagefright_simple_c2component",
+        "libstagefright_amrnb_common",
+    ],
+}
+
+//###############################################################################
+
 cc_test {
     name: "libstagefright_amrnbenc_test",
     gtest: false,
diff --git a/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.cpp b/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.cpp
new file mode 100644
index 0000000..4dd0309
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.cpp
@@ -0,0 +1,252 @@
+/*
+ * 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.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftAmrNbEnc"
+#include <utils/Log.h>
+
+#include "gsmamr_enc.h"
+
+#include "C2SoftAmrNbEnc.h"
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+namespace android {
+
+constexpr char kComponentName[] = "c2.google.amrnb.encoder";
+
+static std::shared_ptr<C2ComponentInterface> BuildIntf(
+        const char *name, c2_node_id_t id,
+        std::function<void(C2ComponentInterface*)> deleter =
+            std::default_delete<C2ComponentInterface>()) {
+    return SimpleC2Interface::Builder(name, id, deleter)
+            .inputFormat(C2FormatAudio)
+            .outputFormat(C2FormatCompressed)
+            .inputMediaType(MEDIA_MIMETYPE_AUDIO_RAW)
+            .outputMediaType(MEDIA_MIMETYPE_AUDIO_AMR_NB)
+            .build();
+}
+
+C2SoftAmrNbEnc::C2SoftAmrNbEnc(const char *name, c2_node_id_t id)
+    : SimpleC2Component(BuildIntf(name, id)),
+      mEncState(nullptr),
+      mSidState(nullptr) {
+}
+
+C2SoftAmrNbEnc::~C2SoftAmrNbEnc() {
+    onRelease();
+}
+
+c2_status_t C2SoftAmrNbEnc::onInit() {
+    bool dtx_enable = false;
+
+    if (AMREncodeInit(&mEncState, &mSidState, dtx_enable) != 0)
+        return C2_CORRUPTED;
+    mMode = MR795;
+    mIsFirst = true;
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+    mAnchorTimeStamp = 0;
+    mProcessedSamples = 0;
+    mFilledLen = 0;
+
+    return C2_OK;
+}
+
+void C2SoftAmrNbEnc::onRelease() {
+    if (mEncState) {
+        AMREncodeExit(&mEncState, &mSidState);
+        mEncState = mSidState = nullptr;
+    }
+}
+
+c2_status_t C2SoftAmrNbEnc::onStop() {
+    if (AMREncodeReset(mEncState, mSidState) != 0)
+        return C2_CORRUPTED;
+    mIsFirst = true;
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+    mAnchorTimeStamp = 0;
+    mProcessedSamples = 0;
+    mFilledLen = 0;
+
+    return C2_OK;
+}
+
+void C2SoftAmrNbEnc::onReset() {
+    (void) onStop();
+}
+
+c2_status_t C2SoftAmrNbEnc::onFlush_sm() {
+    return onStop();
+}
+
+static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+    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;
+}
+
+void C2SoftAmrNbEnc::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    work->result = C2_OK;
+    work->workletsProcessed = 0u;
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    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();
+    C2ReadView rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+    if (inSize && rView.error()) {
+        ALOGE("read view map failed %d", rView.error());
+        work->result = C2_CORRUPTED;
+        return;
+    }
+
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x",
+          inSize, (int)work->input.ordinal.timestamp.peeku(),
+          (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
+
+    size_t outCapacity = kNumBytesPerInputFrame;
+    outCapacity += mFilledLen + inSize;
+    std::shared_ptr<C2LinearBlock> outputBlock;
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    c2_status_t err = pool->fetchLinearBlock(outCapacity, usage, &outputBlock);
+    if (err != C2_OK) {
+        ALOGE("fetchLinearBlock for Output failed with status %d", err);
+        work->result = C2_NO_MEMORY;
+        return;
+    }
+    C2WriteView wView = outputBlock->map().get();
+    if (wView.error()) {
+        ALOGE("write view map failed %d", wView.error());
+        work->result = C2_CORRUPTED;
+        return;
+    }
+
+    uint64_t outTimeStamp = mProcessedSamples * 1000000ll / kSampleRate;
+    const uint8_t *inPtr = rView.data() + inOffset;
+    size_t inPos = 0;
+    size_t outPos = 0;
+    while (inPos < inSize) {
+        int validSamples = mFilledLen / sizeof(int16_t);
+        if ((inPos + (kNumBytesPerInputFrame - mFilledLen)) <= inSize) {
+            memcpy(mInputFrame + validSamples, inPtr + inPos,
+                   (kNumBytesPerInputFrame - mFilledLen));
+            inPos += (kNumBytesPerInputFrame - mFilledLen);
+        } else {
+            memcpy(mInputFrame + validSamples, inPtr + inPos, (inSize - inPos));
+            mFilledLen += (inSize - inPos);
+            inPos += (inSize - inPos);
+            if (eos) {
+                validSamples = mFilledLen / sizeof(int16_t);
+                memset(mInputFrame + validSamples, 0, (kNumBytesPerInputFrame - mFilledLen));
+            } else break;
+        }
+        Frame_Type_3GPP frameType;
+        int numEncBytes = AMREncode(mEncState, mSidState, mMode, mInputFrame,
+                                    wView.data() + outPos, &frameType,
+                                    AMR_TX_WMF);
+        if (numEncBytes < 0 || numEncBytes > ((int)outCapacity - (int)outPos)) {
+            ALOGE("encodeFrame call failed, state [%d %zu %zu]", numEncBytes, outPos, outCapacity);
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+        // Convert header byte from WMF to IETF format.
+        if (numEncBytes > 0)
+            wView.data()[outPos] = ((wView.data()[outPos] << 3) | 4) & 0x7c;
+        outPos += numEncBytes;
+        mProcessedSamples += kNumSamplesPerFrame;
+        mFilledLen = 0;
+    }
+    ALOGV("causal sample size %d", mFilledLen);
+    if (mIsFirst) {
+        mIsFirst = false;
+        mAnchorTimeStamp = work->input.ordinal.timestamp.peekull();
+    }
+    fillEmptyWork(work);
+    if (outPos != 0) {
+        work->worklets.front()->output.buffers.push_back(
+                createLinearBuffer(std::move(outputBlock), 0, outPos));
+        work->worklets.front()->output.ordinal.timestamp = mAnchorTimeStamp + outTimeStamp;
+    }
+    if (eos) {
+        mSignalledOutputEos = true;
+        ALOGV("signalled EOS");
+        if (mFilledLen) ALOGV("Discarding trailing %d bytes", mFilledLen);
+    }
+}
+
+c2_status_t C2SoftAmrNbEnc::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    (void) pool;
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    onFlush_sm();
+    return C2_OK;
+}
+
+class C2SoftAmrNbEncFactory : public C2ComponentFactory {
+public:
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(new C2SoftAmrNbEnc(kComponentName, id), deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = BuildIntf(kComponentName, id, deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftAmrNbEncFactory() override = default;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftAmrNbEncFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.h b/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.h
new file mode 100644
index 0000000..9ced921
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/enc/C2SoftAmrNbEnc.h
@@ -0,0 +1,65 @@
+/*
+ * 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 C2_SOFT_AMRNB_ENC_H_
+#define C2_SOFT_AMRNB_ENC_H_
+
+#include <SimpleC2Component.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+class C2SoftAmrNbEnc : public SimpleC2Component {
+public:
+    C2SoftAmrNbEnc(const char *name, c2_node_id_t id);
+    virtual ~C2SoftAmrNbEnc();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+
+private:
+    static const int32_t kNumSamplesPerFrame = L_FRAME;
+    static const int32_t kNumBytesPerInputFrame = kNumSamplesPerFrame * sizeof(int16_t);
+    static const int32_t kSampleRate = 8000;
+
+    void *mEncState;
+    void *mSidState;
+    Mode mMode;
+    bool mIsFirst;
+    bool mSignalledError;
+    bool mSignalledOutputEos;
+    uint64_t mAnchorTimeStamp;
+    uint64_t mProcessedSamples;
+    int32_t mFilledLen;
+    int16_t mInputFrame[kNumSamplesPerFrame];
+
+    DISALLOW_EVIL_CONSTRUCTORS(C2SoftAmrNbEnc);
+};
+
+}  // namespace android
+
+#endif  // C2_SOFT_AMRNB_ENC_H_
diff --git a/media/libstagefright/codecs/flac/dec/Android.bp b/media/libstagefright/codecs/flac/dec/Android.bp
index 9af086b..68fdd15 100644
--- a/media/libstagefright/codecs/flac/dec/Android.bp
+++ b/media/libstagefright/codecs/flac/dec/Android.bp
@@ -1,4 +1,48 @@
 cc_library_shared {
+    name: "libstagefright_soft_c2flacdec",
+//    vendor_available: true,
+//    vndk: {
+//        enabled: true,
+//    },
+
+    srcs: [
+        "C2SoftFlacDecoder.cpp",
+    ],
+
+    include_dirs: [
+        "external/flac/include",
+        "frameworks/av/media/libstagefright/flac/dec",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+            "unsigned-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+
+    shared_libs: [
+        "liblog",
+        "libutils",
+        "libstagefright_flacdec",
+        "libstagefright_codec2",
+        "libstagefright_codec2_vndk",
+        "libstagefright_foundation",
+        "libstagefright_simple_c2component",
+    ],
+}
+
+
+cc_library_shared {
     name: "libstagefright_soft_flacdec",
     vendor_available: true,
     vndk: {
diff --git a/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp
new file mode 100644
index 0000000..ce40d6b
--- /dev/null
+++ b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.cpp
@@ -0,0 +1,266 @@
+/*
+ * 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.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftFlacDecoder"
+#include <utils/Log.h>
+
+#include "C2SoftFlacDecoder.h"
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+namespace android {
+
+constexpr char kComponentName[] = "c2.google.flac.decoder";
+
+static std::shared_ptr<C2ComponentInterface> BuildIntf(
+        const char *name, c2_node_id_t id,
+        std::function<void(C2ComponentInterface*)> deleter =
+            std::default_delete<C2ComponentInterface>()) {
+    return SimpleC2Interface::Builder(name, id, deleter)
+            .inputFormat(C2FormatCompressed)
+            .outputFormat(C2FormatAudio)
+            .inputMediaType(MEDIA_MIMETYPE_AUDIO_FLAC)
+            .outputMediaType(MEDIA_MIMETYPE_AUDIO_RAW)
+            .build();
+}
+
+C2SoftFlacDecoder::C2SoftFlacDecoder(const char *name, c2_node_id_t id)
+    : SimpleC2Component(BuildIntf(name, id)),
+      mFLACDecoder(nullptr) {
+}
+
+C2SoftFlacDecoder::~C2SoftFlacDecoder() {
+}
+
+c2_status_t C2SoftFlacDecoder::onInit() {
+    status_t err = initDecoder();
+    return err == OK ? C2_OK : C2_NO_MEMORY;
+}
+
+c2_status_t C2SoftFlacDecoder::onStop() {
+    if (mFLACDecoder) mFLACDecoder->flush();
+    memset(&mStreamInfo, 0, sizeof(mStreamInfo));
+    mHasStreamInfo = false;
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+    mInputBufferCount = 0;
+    return C2_OK;
+}
+
+void C2SoftFlacDecoder::onReset() {
+    (void)onStop();
+}
+
+void C2SoftFlacDecoder::onRelease() {
+}
+
+c2_status_t C2SoftFlacDecoder::onFlush_sm() {
+    return onStop();
+}
+
+status_t C2SoftFlacDecoder::initDecoder() {
+    mFLACDecoder = FLACDecoder::Create();
+    if (!mFLACDecoder) {
+        ALOGE("initDecoder: failed to create FLACDecoder");
+        mSignalledError = true;
+        return NO_MEMORY;
+    }
+
+    memset(&mStreamInfo, 0, sizeof(mStreamInfo));
+    mHasStreamInfo = false;
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+    mInputBufferCount = 0;
+
+    return OK;
+}
+
+static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+    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;
+}
+
+// (TODO) add multiframe support, in plugin and FLACDecoder.cpp
+void C2SoftFlacDecoder::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    work->result = C2_OK;
+    work->workletsProcessed = 0u;
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    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();
+    if (inSize && rView.error()) {
+        ALOGE("read view map failed %d", rView.error());
+        work->result = C2_CORRUPTED;
+        return;
+    }
+    bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
+    bool codecConfig = (work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) != 0;
+
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d", inSize,
+          (int)work->input.ordinal.timestamp.peeku(), (int)work->input.ordinal.frameIndex.peeku());
+
+    if (inSize == 0) {
+        fillEmptyWork(work);
+        if (eos) {
+            mSignalledOutputEos = true;
+            ALOGV("signalled EOS");
+        }
+        return;
+    }
+
+    if (mInputBufferCount == 0 && !codecConfig) {
+        ALOGV("First frame has to include configuration, forcing config");
+        codecConfig = true;
+    }
+
+    uint8_t *input = const_cast<uint8_t *>(rView.data() + inOffset);
+    if (codecConfig) {
+        status_t decoderErr = mFLACDecoder->parseMetadata(input, inSize);
+        if (decoderErr != OK && decoderErr != WOULD_BLOCK) {
+            ALOGE("process: FLACDecoder parseMetaData returns error %d", decoderErr);
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+
+        mInputBufferCount++;
+        fillEmptyWork(work);
+        if (eos) {
+            mSignalledOutputEos = true;
+            ALOGV("signalled EOS");
+        }
+
+        if (decoderErr == WOULD_BLOCK) {
+            ALOGV("process: parseMetadata is Blocking, Continue %d", decoderErr);
+        } else {
+            mStreamInfo = mFLACDecoder->getStreamInfo();
+            if (mStreamInfo.max_blocksize && mStreamInfo.channels)
+                mHasStreamInfo = true;
+            ALOGD("process: decoder configuration : %d Hz, %d channels, %d samples,"
+                  " %d block size", mStreamInfo.sample_rate, mStreamInfo.channels,
+                  (int)mStreamInfo.total_samples, mStreamInfo.max_blocksize);
+        }
+        return;
+    }
+
+    size_t outSize;
+    if (mHasStreamInfo)
+        outSize = mStreamInfo.max_blocksize * mStreamInfo.channels * sizeof(short);
+    else
+        outSize = kMaxBlockSize * FLACDecoder::kMaxChannels * sizeof(short);
+
+    std::shared_ptr<C2LinearBlock> block;
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    c2_status_t err = pool->fetchLinearBlock(outSize, usage, &block);
+    if (err != C2_OK) {
+        ALOGE("fetchLinearBlock for Output failed with status %d", err);
+        work->result = C2_NO_MEMORY;
+        return;
+    }
+    C2WriteView wView = block->map().get();
+    if (wView.error()) {
+        ALOGE("write view map failed %d", wView.error());
+        work->result = C2_CORRUPTED;
+        return;
+    }
+
+    short *output = reinterpret_cast<short *>(wView.data());
+    status_t decoderErr = mFLACDecoder->decodeOneFrame(
+                            input, inSize, output, &outSize);
+    if (decoderErr != OK) {
+        ALOGE("process: FLACDecoder decodeOneFrame returns error %d", decoderErr);
+        mSignalledError = true;
+        work->result = C2_CORRUPTED;
+        return;
+    }
+
+    mInputBufferCount++;
+    ALOGV("out buffer attr. size %zu", outSize);
+    work->worklets.front()->output.flags = work->input.flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.buffers.push_back(createLinearBuffer(block, 0, outSize));
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+    if (eos) {
+        mSignalledOutputEos = true;
+        ALOGV("signalled EOS");
+    }
+}
+
+c2_status_t C2SoftFlacDecoder::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    (void) pool;
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    if (mFLACDecoder) mFLACDecoder->flush();
+
+    return C2_OK;
+}
+
+class C2SoftFlacDecFactory : public C2ComponentFactory {
+public:
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(new C2SoftFlacDecoder(kComponentName, id), deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = BuildIntf(kComponentName, id, deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftFlacDecFactory() override = default;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftFlacDecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.h b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.h
new file mode 100644
index 0000000..a5c01a9
--- /dev/null
+++ b/media/libstagefright/codecs/flac/dec/C2SoftFlacDecoder.h
@@ -0,0 +1,63 @@
+/*
+ * 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 C2_SOFT_FLAC_DECODER_H_
+#define C2_SOFT_FLAC_DECODER_H_
+
+#include <SimpleC2Component.h>
+
+#include "FLACDecoder.h"
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+struct C2SoftFlacDecoder : public SimpleC2Component {
+    C2SoftFlacDecoder(const char *name, c2_node_id_t id);
+    virtual ~C2SoftFlacDecoder();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+
+private:
+    enum {
+        kMaxBlockSize   = 4096
+    };
+
+    sp<FLACDecoder> mFLACDecoder;
+    FLAC__StreamMetadata_StreamInfo mStreamInfo;
+    bool mSignalledError;
+    bool mSignalledOutputEos;
+    bool mHasStreamInfo;
+    size_t mInputBufferCount;
+
+    status_t initDecoder();
+
+    DISALLOW_EVIL_CONSTRUCTORS(C2SoftFlacDecoder);
+};
+
+}  // namespace android
+
+#endif  // C2_SOFT_FLAC_DECODER_H_
diff --git a/media/libstagefright/codecs/flac/enc/Android.bp b/media/libstagefright/codecs/flac/enc/Android.bp
index 46b974d..bb705dd 100644
--- a/media/libstagefright/codecs/flac/enc/Android.bp
+++ b/media/libstagefright/codecs/flac/enc/Android.bp
@@ -1,4 +1,43 @@
 cc_library_shared {
+    name: "libstagefright_soft_c2flacenc",
+//    vendor_available: true,
+//    vndk: {
+//        enabled: true,
+//    },
+
+    srcs: ["C2SoftFlacEnc.cpp"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+            "unsigned-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+
+    static_libs: [
+        "libFLAC",
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libstagefright_codec2",
+        "libstagefright_codec2_vndk",
+        "libstagefright_foundation",
+        "libstagefright_simple_c2component",
+        "libutils",
+    ],
+}
+
+cc_library_shared {
 
     srcs: ["SoftFlacEncoder.cpp"],
 
diff --git a/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.cpp b/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.cpp
new file mode 100644
index 0000000..6c147ad
--- /dev/null
+++ b/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.cpp
@@ -0,0 +1,389 @@
+/*
+ * 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.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftFlacEnc"
+#include <utils/Log.h>
+
+#include "C2SoftFlacEnc.h"
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+namespace android {
+
+constexpr char kComponentName[] = "c2.google.flac.encoder";
+
+static std::shared_ptr<C2ComponentInterface> BuildIntf(
+        const char *name, c2_node_id_t id,
+        std::function<void(C2ComponentInterface*)> deleter =
+            std::default_delete<C2ComponentInterface>()) {
+    return SimpleC2Interface::Builder(name, id, deleter)
+            .inputFormat(C2FormatAudio)
+            .outputFormat(C2FormatCompressed)
+            .inputMediaType(MEDIA_MIMETYPE_AUDIO_RAW)
+            .outputMediaType(MEDIA_MIMETYPE_AUDIO_FLAC)
+            .build();
+}
+
+C2SoftFlacEnc::C2SoftFlacEnc(const char *name, c2_node_id_t id)
+    : SimpleC2Component(BuildIntf(name, id)),
+      mFlacStreamEncoder(nullptr),
+      mInputBufferPcm32(nullptr) {
+}
+
+C2SoftFlacEnc::~C2SoftFlacEnc() {
+    onRelease();
+}
+
+c2_status_t C2SoftFlacEnc::onInit() {
+    mFlacStreamEncoder = FLAC__stream_encoder_new();
+    if (!mFlacStreamEncoder) return C2_CORRUPTED;
+
+    mInputBufferPcm32 = (FLAC__int32*) malloc(
+            kInBlockSize * kMaxNumChannels * sizeof(FLAC__int32));
+    if (!mInputBufferPcm32) return C2_NO_MEMORY;
+
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+    mNumChannels = 1;
+    mSampleRate = 44100;
+    mCompressionLevel = FLAC_COMPRESSION_LEVEL_DEFAULT;
+    mIsFirstFrame = true;
+    mAnchorTimeStamp = 0ull;
+    mProcessedSamples = 0u;
+    mEncoderWriteData = false;
+    mEncoderReturnedNbBytes = 0;
+#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER
+    mHeaderOffset = 0;
+    mWroteHeader = false;
+#endif
+
+    status_t err = configureEncoder();
+    return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+void C2SoftFlacEnc::onRelease() {
+    if (mFlacStreamEncoder) {
+        FLAC__stream_encoder_delete(mFlacStreamEncoder);
+        mFlacStreamEncoder = nullptr;
+    }
+
+    if (mInputBufferPcm32) {
+        free(mInputBufferPcm32);
+        mInputBufferPcm32 = nullptr;
+    }
+}
+
+void C2SoftFlacEnc::onReset() {
+    mNumChannels = 1;
+    mSampleRate = 44100;
+    mCompressionLevel = FLAC_COMPRESSION_LEVEL_DEFAULT;
+    (void) onStop();
+}
+
+c2_status_t C2SoftFlacEnc::onStop() {
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+    mIsFirstFrame = true;
+    mAnchorTimeStamp = 0ull;
+    mProcessedSamples = 0u;
+    mEncoderWriteData = false;
+    mEncoderReturnedNbBytes = 0;
+#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER
+    mHeaderOffset = 0;
+    mWroteHeader = false;
+#endif
+
+    c2_status_t status = drain(DRAIN_COMPONENT_NO_EOS, nullptr);
+    if (C2_OK != status) return status;
+
+    status_t err = configureEncoder();
+    if (err != OK) mSignalledError = true;
+    return C2_OK;
+}
+
+c2_status_t C2SoftFlacEnc::onFlush_sm() {
+    return onStop();
+}
+
+static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+    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;
+}
+
+void C2SoftFlacEnc::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    work->result = C2_OK;
+    work->workletsProcessed = 0u;
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    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();
+    C2ReadView rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+    if (inSize && rView.error()) {
+        ALOGE("read view map failed %d", rView.error());
+        work->result = C2_CORRUPTED;
+        return;
+    }
+
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x",
+              inSize, (int)work->input.ordinal.timestamp.peeku(),
+              (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
+    if (mIsFirstFrame && inSize) {
+        mAnchorTimeStamp = work->input.ordinal.timestamp.peekull();
+        mIsFirstFrame = false;
+    }
+    uint64_t outTimeStamp = mProcessedSamples * 1000000ll / mSampleRate;
+
+    size_t outCapacity = inSize;
+    outCapacity += mBlockSize * mNumChannels * sizeof(int16_t);
+#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER
+    outCapacity += FLAC_HEADER_SIZE;
+#endif
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    c2_status_t err = pool->fetchLinearBlock(outCapacity, usage, &mOutputBlock);
+    if (err != C2_OK) {
+        ALOGE("fetchLinearBlock for Output failed with status %d", err);
+        work->result = C2_NO_MEMORY;
+        return;
+    }
+    C2WriteView wView = mOutputBlock->map().get();
+    if (wView.error()) {
+        ALOGE("write view map failed %d", wView.error());
+        work->result = C2_CORRUPTED;
+        return;
+    }
+
+    mEncoderWriteData = true;
+    mEncoderReturnedNbBytes = 0;
+    while (inOffset < inSize) {
+        size_t processSize = MIN(kInBlockSize * mNumChannels * sizeof(int16_t), (inSize - inOffset));
+        const unsigned nbInputFrames = processSize / (mNumChannels * sizeof(int16_t));
+        const unsigned nbInputSamples = processSize / sizeof(int16_t);
+        const int16_t *pcm16 = reinterpret_cast<const int16_t *>(rView.data() + inOffset);
+        ALOGV("about to encode %zu bytes", processSize);
+
+        for (unsigned i = 0; i < nbInputSamples; i++) {
+            mInputBufferPcm32[i] = (FLAC__int32) pcm16[i];
+        }
+
+        FLAC__bool ok = FLAC__stream_encoder_process_interleaved(
+                mFlacStreamEncoder, mInputBufferPcm32, nbInputFrames);
+        if (!ok) {
+            ALOGE("error encountered during encoding");
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            mOutputBlock.reset();
+            return;
+        }
+        inOffset += processSize;
+    }
+    if (eos && !drain(DRAIN_COMPONENT_WITH_EOS, pool)) {
+        ALOGE("error encountered during encoding");
+        mSignalledError = true;
+        work->result = C2_CORRUPTED;
+        mOutputBlock.reset();
+        return;
+    }
+    fillEmptyWork(work);
+    if (mEncoderReturnedNbBytes != 0) {
+        std::shared_ptr<C2Buffer> buffer = createLinearBuffer(std::move(mOutputBlock), 0, mEncoderReturnedNbBytes);
+        work->worklets.front()->output.buffers.push_back(buffer);
+        work->worklets.front()->output.ordinal.timestamp = mAnchorTimeStamp + outTimeStamp;
+    } else {
+        ALOGV("encoder process_interleaved returned without data to write");
+    }
+    mOutputBlock = nullptr;
+    if (eos) {
+        mSignalledOutputEos = true;
+        ALOGV("signalled EOS");
+    }
+    mEncoderWriteData = false;
+    mEncoderReturnedNbBytes = 0;
+}
+
+FLAC__StreamEncoderWriteStatus C2SoftFlacEnc::onEncodedFlacAvailable(
+        const FLAC__byte buffer[], size_t bytes, unsigned samples,
+        unsigned current_frame) {
+    (void) current_frame;
+    ALOGV("%s (bytes=%zu, samples=%u, curr_frame=%u)", __func__, bytes, samples,
+          current_frame);
+
+#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER
+    if (samples == 0) {
+        ALOGI("saving %zu bytes of header", bytes);
+        memcpy(mHeader + mHeaderOffset, buffer, bytes);
+        mHeaderOffset += bytes;// will contain header size when finished receiving header
+        return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
+    }
+#endif
+
+    if ((samples == 0) || !mEncoderWriteData) {
+        // called by the encoder because there's header data to save, but it's not the role
+        // of this component (unless WRITE_FLAC_HEADER_IN_FIRST_BUFFER is defined)
+        ALOGV("ignoring %zu bytes of header data (samples=%d)", bytes, samples);
+        return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
+    }
+
+    // write encoded data
+    C2WriteView wView = mOutputBlock->map().get();
+    uint8_t* outData = wView.data();
+#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER
+    if (!mWroteHeader) {
+        ALOGI("writing %d bytes of header on output", mHeaderOffset);
+        memcpy(outData + mEncoderReturnedNbBytes, mHeader, mHeaderOffset);
+        mEncoderReturnedNbBytes += mHeaderOffset;
+        mWroteHeader = true;
+    }
+#endif
+    ALOGV("writing %zu bytes of encoded data on output", bytes);
+    // increment mProcessedSamples to maintain audio synchronization during
+    // play back
+    mProcessedSamples += samples;
+    if (bytes + mEncoderReturnedNbBytes > mOutputBlock->capacity()) {
+        ALOGE("not enough space left to write encoded data, dropping %zu bytes", bytes);
+        // a fatal error would stop the encoding
+        return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
+    }
+    memcpy(outData + mEncoderReturnedNbBytes, buffer, bytes);
+    mEncoderReturnedNbBytes += bytes;
+    return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
+}
+
+
+status_t C2SoftFlacEnc::configureEncoder() {
+    ALOGV("%s numChannel=%d, sampleRate=%d", __func__, mNumChannels, mSampleRate);
+
+    if (mSignalledError || !mFlacStreamEncoder) {
+        ALOGE("can't configure encoder: no encoder or invalid state");
+        return UNKNOWN_ERROR;
+    }
+
+    FLAC__bool ok = true;
+    ok = ok && FLAC__stream_encoder_set_channels(mFlacStreamEncoder, mNumChannels);
+    ok = ok && FLAC__stream_encoder_set_sample_rate(mFlacStreamEncoder, mSampleRate);
+    ok = ok && FLAC__stream_encoder_set_bits_per_sample(mFlacStreamEncoder, 16);
+    ok = ok && FLAC__stream_encoder_set_compression_level(mFlacStreamEncoder, mCompressionLevel);
+    ok = ok && FLAC__stream_encoder_set_verify(mFlacStreamEncoder, false);
+    if (!ok) {
+        ALOGE("unknown error when configuring encoder");
+        return UNKNOWN_ERROR;
+    }
+
+    ok &= FLAC__STREAM_ENCODER_INIT_STATUS_OK ==
+            FLAC__stream_encoder_init_stream(mFlacStreamEncoder,
+                    flacEncoderWriteCallback    /*write_callback*/,
+                    nullptr /*seek_callback*/,
+                    nullptr /*tell_callback*/,
+                    nullptr /*metadata_callback*/,
+                    (void *) this /*client_data*/);
+
+    if (!ok) {
+        ALOGE("unknown error when configuring encoder");
+        return UNKNOWN_ERROR;
+    }
+
+    mBlockSize = FLAC__stream_encoder_get_blocksize(mFlacStreamEncoder);
+
+    ALOGV("encoder successfully configured");
+    return OK;
+}
+
+FLAC__StreamEncoderWriteStatus C2SoftFlacEnc::flacEncoderWriteCallback(
+            const FLAC__StreamEncoder *,
+            const FLAC__byte buffer[],
+            size_t bytes,
+            unsigned samples,
+            unsigned current_frame,
+            void *client_data) {
+    return ((C2SoftFlacEnc*) client_data)->onEncodedFlacAvailable(
+            buffer, bytes, samples, current_frame);
+}
+
+c2_status_t C2SoftFlacEnc::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    (void) pool;
+    switch (drainMode) {
+        case NO_DRAIN:
+            ALOGW("drain with NO_DRAIN: no-op");
+            return C2_OK;
+        case DRAIN_CHAIN:
+            ALOGW("DRAIN_CHAIN not supported");
+            return C2_OMITTED;
+        case DRAIN_COMPONENT_WITH_EOS:
+            // TODO: This flag is not being sent back to the client
+            // because there are no items in PendingWork queue as all the
+            // inputs are being sent back with emptywork or valid encoded data
+            // mSignalledOutputEos = true;
+        case DRAIN_COMPONENT_NO_EOS:
+            break;
+        default:
+            return C2_BAD_VALUE;
+    }
+    FLAC__bool ok = FLAC__stream_encoder_finish(mFlacStreamEncoder);
+    if (!ok) return C2_CORRUPTED;
+    mIsFirstFrame = true;
+    mAnchorTimeStamp = 0ull;
+    mProcessedSamples = 0u;
+
+    return C2_OK;
+}
+
+class C2SoftFlacEncFactory : public C2ComponentFactory {
+public:
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(new C2SoftFlacEnc(kComponentName, id), deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = BuildIntf(kComponentName, id, deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftFlacEncFactory() override = default;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftFlacEncFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.h b/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.h
new file mode 100644
index 0000000..cc8cb86
--- /dev/null
+++ b/media/libstagefright/codecs/flac/enc/C2SoftFlacEnc.h
@@ -0,0 +1,96 @@
+/*
+ * 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 C2_SOFT_FLAC_ENC_H_
+#define C2_SOFT_FLAC_ENC_H_
+
+#include <SimpleC2Component.h>
+
+#include "FLAC/stream_encoder.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+
+// use this symbol to have the first output buffer start with FLAC frame header so a dump of
+// all the output buffers can be opened as a .flac file
+//#define WRITE_FLAC_HEADER_IN_FIRST_BUFFER
+
+#define FLAC_COMPRESSION_LEVEL_MIN     0
+#define FLAC_COMPRESSION_LEVEL_DEFAULT 5
+#define FLAC_COMPRESSION_LEVEL_MAX     8
+
+#define FLAC_HEADER_SIZE               128
+
+#define MIN(a, b)                      (((a) < (b)) ? (a) : (b))
+
+namespace android {
+
+class C2SoftFlacEnc : public SimpleC2Component {
+public:
+    C2SoftFlacEnc(const char *name, c2_node_id_t id);
+    virtual ~C2SoftFlacEnc();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+
+private:
+    status_t configureEncoder();
+    static FLAC__StreamEncoderWriteStatus flacEncoderWriteCallback(
+            const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[],
+            size_t bytes, unsigned samples, unsigned current_frame,
+            void *client_data);
+    FLAC__StreamEncoderWriteStatus onEncodedFlacAvailable(
+            const FLAC__byte buffer[], size_t bytes, unsigned samples,
+            unsigned current_frame);
+
+    const unsigned int kInBlockSize = 1152;
+    const unsigned int kMaxNumChannels = 2;
+    FLAC__StreamEncoder* mFlacStreamEncoder;
+    FLAC__int32* mInputBufferPcm32;
+    std::shared_ptr<C2LinearBlock> mOutputBlock;
+    bool mSignalledError;
+    bool mSignalledOutputEos;
+    uint32_t mNumChannels;
+    uint32_t mSampleRate;
+    uint32_t mCompressionLevel;
+    uint32_t mBlockSize;
+    bool mIsFirstFrame;
+    uint64_t mAnchorTimeStamp;
+    uint64_t mProcessedSamples;
+    // should the data received by the callback be written to the output port
+    bool mEncoderWriteData;
+    size_t mEncoderReturnedNbBytes;
+#ifdef WRITE_FLAC_HEADER_IN_FIRST_BUFFER
+    unsigned mHeaderOffset;
+    bool mWroteHeader;
+    char mHeader[FLAC_HEADER_SIZE];
+#endif
+
+    DISALLOW_EVIL_CONSTRUCTORS(C2SoftFlacEnc);
+};
+
+}  // namespace android
+
+#endif  // C2_SOFT_FLAC_ENC_H_
diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.bp b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
index ca70cc2..d24a116 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/Android.bp
+++ b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
@@ -112,3 +112,91 @@
     },
     compile_multilib: "32",
 }
+
+//###############################################################################
+
+cc_library_shared {
+    name: "libstagefright_soft_c2mpeg4dec",
+//    vendor_available: true,
+//    vndk: {
+//        enabled: true,
+//    },
+
+    srcs: ["C2SoftMpeg4Dec.cpp"],
+
+    cflags: [
+        "-DOSCL_IMPORT_REF=",
+        "-DMPEG4",
+        "-Wall",
+        "-Werror",
+    ],
+
+    local_include_dirs: ["src"],
+    export_include_dirs: ["include"],
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+
+    static_libs: ["libstagefright_m4vh263dec"],
+
+    shared_libs: [
+        "liblog",
+        "libutils",
+        "libstagefright_codec2",
+        "libstagefright_codec2_vndk",
+        "libstagefright_foundation",
+        "libstagefright_simple_c2component",
+    ],
+
+    ldflags: ["-Wl,-Bsymbolic"],
+}
+
+//###############################################################################
+cc_library_shared {
+    name: "libstagefright_soft_c2h263dec",
+//    vendor_available: true,
+//    vndk: {
+//        enabled: true,
+//    },
+
+    srcs: ["C2SoftMpeg4Dec.cpp"],
+
+    cflags: [
+        "-DOSCL_IMPORT_REF=",
+        "-Wall",
+        "-Werror",
+    ],
+
+    local_include_dirs: ["src"],
+    export_include_dirs: ["include"],
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+
+    static_libs: ["libstagefright_m4vh263dec"],
+
+    shared_libs: [
+        "liblog",
+        "libutils",
+        "libstagefright_codec2",
+        "libstagefright_codec2_vndk",
+        "libstagefright_foundation",
+        "libstagefright_simple_c2component",
+    ],
+
+    ldflags: ["-Wl,-Bsymbolic"],
+}
diff --git a/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.cpp b/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.cpp
new file mode 100644
index 0000000..2a3239f
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.cpp
@@ -0,0 +1,502 @@
+/*
+ * 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.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftMpeg4Dec"
+#include <utils/Log.h>
+
+#include "C2SoftMpeg4Dec.h"
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include "mp4dec_api.h"
+
+namespace android {
+
+#ifdef MPEG4
+constexpr char kComponentName[] = "c2.google.mpeg4.decoder";
+#else
+constexpr char kComponentName[] = "c2.google.h263.decoder";
+#endif
+
+static std::shared_ptr<C2ComponentInterface> BuildIntf(
+        const char *name, c2_node_id_t id,
+        std::function<void(C2ComponentInterface*)> deleter =
+            std::default_delete<C2ComponentInterface>()) {
+    return SimpleC2Interface::Builder(name, id, deleter)
+            .inputFormat(C2FormatCompressed)
+            .outputFormat(C2FormatVideo)
+            .inputMediaType(
+#ifdef MPEG4
+                    MEDIA_MIMETYPE_VIDEO_MPEG4
+#else
+                    MEDIA_MIMETYPE_VIDEO_H263
+#endif
+            )
+            .outputMediaType(MEDIA_MIMETYPE_VIDEO_RAW)
+            .build();
+}
+
+C2SoftMpeg4Dec::C2SoftMpeg4Dec(const char *name, c2_node_id_t id)
+    : SimpleC2Component(BuildIntf(name, id)),
+      mDecHandle(nullptr) {
+}
+
+C2SoftMpeg4Dec::~C2SoftMpeg4Dec() {
+    onRelease();
+}
+
+c2_status_t C2SoftMpeg4Dec::onInit() {
+    status_t err = initDecoder();
+    return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+c2_status_t C2SoftMpeg4Dec::onStop() {
+    if (mInitialized) {
+        PVCleanUpVideoDecoder(mDecHandle);
+        mInitialized = false;
+    }
+    for (int32_t i = 0; i < kNumOutputBuffers; ++i) {
+        if (mOutputBuffer[i]) {
+            free(mOutputBuffer[i]);
+            mOutputBuffer[i] = nullptr;
+        }
+    }
+    mNumSamplesOutput = 0;
+    mFramesConfigured = false;
+    mSignalledOutputEos = false;
+    mSignalledError = false;
+
+    return C2_OK;
+}
+
+void C2SoftMpeg4Dec::onReset() {
+    (void) onStop();
+}
+
+void C2SoftMpeg4Dec::onRelease() {
+    if (mInitialized) {
+        PVCleanUpVideoDecoder(mDecHandle);
+    }
+    if (mOutBlock) {
+        mOutBlock.reset();
+    }
+    for (int32_t i = 0; i < kNumOutputBuffers; ++i) {
+        if (mOutputBuffer[i]) {
+            free(mOutputBuffer[i]);
+            mOutputBuffer[i] = nullptr;
+        }
+    }
+
+    delete mDecHandle;
+    mDecHandle = nullptr;
+}
+
+c2_status_t C2SoftMpeg4Dec::onFlush_sm() {
+    if (mInitialized) {
+        if (PV_TRUE != PVResetVideoDecoder(mDecHandle)) return C2_CORRUPTED;
+    }
+    mSignalledOutputEos = false;
+    mSignalledError = false;
+    return C2_OK;
+}
+
+status_t C2SoftMpeg4Dec::initDecoder() {
+#ifdef MPEG4
+    mIsMpeg4 = true;
+#else
+    mIsMpeg4 = false;
+#endif
+    if (!mDecHandle) {
+        mDecHandle = new tagvideoDecControls;
+    }
+    memset(mDecHandle, 0, sizeof(tagvideoDecControls));
+
+    for (int32_t i = 0; i < kNumOutputBuffers; ++i) {
+        mOutputBuffer[i] = nullptr;
+    }
+
+    /* TODO: bring these values to 352 and 288. It cannot be done as of now
+     * because, h263 doesn't seem to allow port reconfiguration. In OMX, the
+     * problem of larger width and height than default width and height is
+     * overcome by adaptivePlayBack() api call. This call gets width and height
+     * information from extractor. Such a thing is not possible here.
+     * So we are configuring to larger values.*/
+    mWidth = 1408;
+    mHeight = 1152;
+    mNumSamplesOutput = 0;
+    mInitialized = false;
+    mFramesConfigured = false;
+    mSignalledOutputEos = false;
+    mSignalledError = false;
+
+    return OK;
+}
+
+void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+    uint32_t flags = 0;
+    if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        flags |= C2FrameData::FLAG_END_OF_STREAM;
+        ALOGV("signalling eos");
+    }
+    work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+}
+
+void C2SoftMpeg4Dec::finishWork(uint64_t index, const std::unique_ptr<C2Work> &work) {
+    std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(std::move(mOutBlock),
+                                                           C2Rect(mWidth, mHeight));
+    mOutBlock = nullptr;
+    auto fillWork = [buffer, index](const std::unique_ptr<C2Work> &work) {
+        uint32_t flags = 0;
+        if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) &&
+                (c2_cntr64_t(index) == work->input.ordinal.frameIndex)) {
+            flags |= C2FrameData::FLAG_END_OF_STREAM;
+            ALOGV("signalling eos");
+        }
+        work->worklets.front()->output.flags = (C2FrameData::flags_t)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 && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
+        fillWork(work);
+    } else {
+        finish(index, fillWork);
+    }
+}
+
+c2_status_t C2SoftMpeg4Dec::ensureDecoderState(const std::shared_ptr<C2BlockPool> &pool) {
+    if (!mDecHandle) {
+        ALOGE("not supposed to be here, invalid decoder context");
+        return C2_CORRUPTED;
+    }
+
+    uint32_t outSize = align(mWidth, 16) * align(mHeight, 16) * 3 / 2;
+    for (int32_t i = 0; i < kNumOutputBuffers; ++i) {
+        if (!mOutputBuffer[i]) {
+            mOutputBuffer[i] = (uint8_t *)malloc(outSize * sizeof(uint8_t));
+            if (!mOutputBuffer[i]) return C2_NO_MEMORY;
+        }
+    }
+    if (mOutBlock &&
+            (mOutBlock->width() != align(mWidth, 16) || mOutBlock->height() != mHeight)) {
+        mOutBlock.reset();
+    }
+    if (!mOutBlock) {
+        uint32_t format = HAL_PIXEL_FORMAT_YV12;
+        C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+        c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 16), mHeight, format, usage, &mOutBlock);
+        if (err != C2_OK) {
+            ALOGE("fetchGraphicBlock for Output failed with status %d", err);
+            return err;
+        }
+        ALOGV("provided (%dx%d) required (%dx%d)",
+              mOutBlock->width(), mOutBlock->height(), mWidth, mHeight);
+    }
+    return C2_OK;
+}
+
+bool C2SoftMpeg4Dec::handleResChange(const std::unique_ptr<C2Work> &work) {
+    uint32_t disp_width, disp_height;
+    PVGetVideoDimensions(mDecHandle, (int32 *)&disp_width, (int32 *)&disp_height);
+
+    uint32_t buf_width, buf_height;
+    PVGetBufferDimensions(mDecHandle, (int32 *)&buf_width, (int32 *)&buf_height);
+
+    CHECK_LE(disp_width, buf_width);
+    CHECK_LE(disp_height, buf_height);
+
+    ALOGV("display size (%dx%d), buffer size (%dx%d)",
+           disp_width, disp_height, buf_width, buf_height);
+
+    bool resChanged = false;
+    if (disp_width != mWidth || disp_height != mHeight) {
+        mWidth = disp_width;
+        mHeight = disp_height;
+        resChanged = true;
+        for (int32_t i = 0; i < kNumOutputBuffers; ++i) {
+            if (mOutputBuffer[i]) {
+                free(mOutputBuffer[i]);
+                mOutputBuffer[i] = nullptr;
+            }
+        }
+
+        if (!mIsMpeg4) {
+            PVCleanUpVideoDecoder(mDecHandle);
+
+            uint8_t *vol_data[1]{};
+            int32_t vol_size = 0;
+
+            if (!PVInitVideoDecoder(
+                    mDecHandle, vol_data, &vol_size, 1, mWidth, mHeight, H263_MODE)) {
+                ALOGE("Error in PVInitVideoDecoder H263_MODE while resChanged was set to true");
+                work->result = C2_CORRUPTED;
+                mSignalledError = true;
+                return true;
+            }
+        }
+        mFramesConfigured = false;
+    }
+    return resChanged;
+}
+
+/* TODO: can remove temporary copy after library supports writing to display
+ * buffer Y, U and V plane pointers using stride info. */
+static void copyOutputBufferToYV12Frame(uint8_t *dst, uint8_t *src, size_t dstYStride,
+                                        size_t srcYStride, uint32_t width, uint32_t height) {
+    size_t dstUVStride = align(dstYStride / 2, 16);
+    size_t srcUVStride = srcYStride / 2;
+    uint8_t *srcStart = src;
+    uint8_t *dstStart = dst;
+    size_t vStride = align(height, 16);
+    for (size_t i = 0; i < height; ++i) {
+         memcpy(dst, src, width);
+         src += srcYStride;
+         dst += dstYStride;
+    }
+    /* U buffer */
+    src = srcStart + vStride * srcYStride;
+    dst = dstStart + (dstYStride * height) + (dstUVStride * height / 2);
+    for (size_t i = 0; i < height / 2; ++i) {
+         memcpy(dst, src, width / 2);
+         src += srcUVStride;
+         dst += dstUVStride;
+    }
+    /* V buffer */
+    src = srcStart + vStride * srcYStride * 5 / 4;
+    dst = dstStart + (dstYStride * height);
+    for (size_t i = 0; i < height / 2; ++i) {
+         memcpy(dst, src, width / 2);
+         src += srcUVStride;
+         dst += dstUVStride;
+    }
+}
+
+void C2SoftMpeg4Dec::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    work->result = C2_OK;
+    work->workletsProcessed = 0u;
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    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;
+    C2ReadView rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+    if (inSize && rView.error()) {
+        ALOGE("read view map failed %d", rView.error());
+        work->result = C2_CORRUPTED;
+        return;
+    }
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x",
+          inSize, (int)work->input.ordinal.timestamp.peeku(),
+          (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
+
+    bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+    if (inSize == 0) {
+        fillEmptyWork(work);
+        if (eos) {
+            mSignalledOutputEos = true;
+        }
+        return;
+    }
+
+    uint8_t *bitstream = const_cast<uint8_t *>(rView.data() + inOffset);
+    uint32_t *start_code = (uint32_t *)bitstream;
+    bool volHeader = *start_code == 0xB0010000;
+    if (volHeader) {
+        PVCleanUpVideoDecoder(mDecHandle);
+        mInitialized = false;
+    }
+
+    if (!mInitialized) {
+        uint8_t *vol_data[1]{};
+        int32_t vol_size = 0;
+
+        bool codecConfig = (work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) != 0;
+        if (codecConfig || volHeader) {
+            vol_data[0] = bitstream;
+            vol_size = inSize;
+        }
+        MP4DecodingMode mode = (mIsMpeg4) ? MPEG4_MODE : H263_MODE;
+
+        if (!PVInitVideoDecoder(
+                mDecHandle, vol_data, &vol_size, 1,
+                mWidth, mHeight, mode)) {
+            ALOGE("PVInitVideoDecoder failed. Unsupported content?");
+            work->result = C2_CORRUPTED;
+            mSignalledError = true;
+            return;
+        }
+        mInitialized = true;
+        MP4DecodingMode actualMode = PVGetDecBitstreamMode(mDecHandle);
+        if (mode != actualMode) {
+            ALOGE("Decoded mode not same as actual mode of the decoder");
+            work->result = C2_CORRUPTED;
+            mSignalledError = true;
+            return;
+        }
+
+        PVSetPostProcType(mDecHandle, 0);
+        (void) handleResChange(work);
+        if (codecConfig) {
+            fillEmptyWork(work);
+            return;
+        }
+    }
+
+    while (inOffset < inSize) {
+        c2_status_t err = ensureDecoderState(pool);
+        if (C2_OK != err) {
+            mSignalledError = true;
+            work->result = err;
+            return;
+        }
+        C2GraphicView wView = mOutBlock->map().get();
+        if (wView.error()) {
+            ALOGE("graphic view map failed %d", wView.error());
+            work->result = C2_CORRUPTED;
+            return;
+        }
+
+        uint32_t outSize = align(mWidth, 16) * align(mHeight, 16) * 3 / 2;
+        uint32_t yFrameSize = sizeof(uint8) * mDecHandle->size;
+        if (outSize < yFrameSize * 3 / 2){
+            ALOGE("Too small output buffer: %d bytes", outSize);
+            work->result = C2_NO_MEMORY;
+            mSignalledError = true;
+            return;
+        }
+
+        if (!mFramesConfigured) {
+            PVSetReferenceYUV(mDecHandle,mOutputBuffer[1]);
+            mFramesConfigured = true;
+        }
+
+        // Need to check if header contains new info, e.g., width/height, etc.
+        VopHeaderInfo header_info;
+        uint32_t useExtTimestamp = (inOffset == 0);
+        int32_t tmpInSize = (int32_t)inSize;
+        uint8_t *bitstreamTmp = bitstream;
+        uint32_t timestamp = workIndex;
+        if (PVDecodeVopHeader(
+                    mDecHandle, &bitstreamTmp, &timestamp, &tmpInSize,
+                    &header_info, &useExtTimestamp,
+                    mOutputBuffer[mNumSamplesOutput & 1]) != PV_TRUE) {
+            ALOGE("failed to decode vop header.");
+            work->result = C2_CORRUPTED;
+            mSignalledError = true;
+            return;
+        }
+
+        // H263 doesn't have VOL header, the frame size information is in short header, i.e. the
+        // decoder may detect size change after PVDecodeVopHeader.
+        bool resChange = handleResChange(work);
+        if (mIsMpeg4 && resChange) {
+            work->result = C2_CORRUPTED;
+            mSignalledError = true;
+            return;
+        } else if (resChange) {
+            continue;
+        }
+
+        if (PVDecodeVopBody(mDecHandle, &tmpInSize) != PV_TRUE) {
+            ALOGE("failed to decode video frame.");
+            work->result = C2_CORRUPTED;
+            mSignalledError = true;
+            return;
+        }
+        if (handleResChange(work)) {
+            work->result = C2_CORRUPTED;
+            mSignalledError = true;
+            return;
+        }
+
+        uint8_t *outputBufferY = wView.data()[C2PlanarLayout::PLANE_Y];
+        (void)copyOutputBufferToYV12Frame(outputBufferY, mOutputBuffer[mNumSamplesOutput & 1],
+                                          wView.width(), align(mWidth, 16), mWidth, mHeight);
+
+        inOffset += inSize - (size_t)tmpInSize;
+        finishWork(workIndex, work);
+        ++mNumSamplesOutput;
+        if (inSize - inOffset) {
+            ALOGD("decoded frame, ignoring further trailing bytes %zu",
+                   inSize - (size_t)tmpInSize);
+            break;
+        }
+    }
+}
+
+c2_status_t C2SoftMpeg4Dec::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    (void) pool;
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+    return C2_OK;
+}
+
+class C2SoftMpeg4DecFactory : public C2ComponentFactory {
+public:
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(new C2SoftMpeg4Dec(kComponentName, id), deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = BuildIntf(kComponentName, id, deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftMpeg4DecFactory() override = default;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftMpeg4DecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.h b/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.h
new file mode 100644
index 0000000..8eb316e
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/dec/C2SoftMpeg4Dec.h
@@ -0,0 +1,73 @@
+/*
+ * 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 C2_SOFT_MPEG4_DEC_H_
+#define C2_SOFT_MPEG4_DEC_H_
+
+#include <SimpleC2Component.h>
+
+#include <media/stagefright/foundation/ABase.h>
+
+struct tagvideoDecControls;
+
+namespace android {
+
+struct C2SoftMpeg4Dec : public SimpleC2Component {
+    C2SoftMpeg4Dec(const char *name, c2_node_id_t id);
+    virtual ~C2SoftMpeg4Dec();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+ private:
+    enum {
+        kNumOutputBuffers = 2,
+    };
+
+    status_t initDecoder();
+    c2_status_t ensureDecoderState(const std::shared_ptr<C2BlockPool> &pool);
+    void finishWork(uint64_t index, const std::unique_ptr<C2Work> &work);
+    bool handleResChange(const std::unique_ptr<C2Work> &work);
+
+    tagvideoDecControls *mDecHandle;
+    std::shared_ptr<C2GraphicBlock> mOutBlock;
+    uint8_t *mOutputBuffer[kNumOutputBuffers];
+
+    uint32_t mWidth;
+    uint32_t mHeight;
+    uint32_t mNumSamplesOutput;
+
+    bool mIsMpeg4;
+    bool mInitialized;
+    bool mFramesConfigured;
+    bool mSignalledOutputEos;
+    bool mSignalledError;
+
+    DISALLOW_EVIL_CONSTRUCTORS(C2SoftMpeg4Dec);
+};
+
+}  // namespace android
+
+#endif  // C2_SOFT_MPEG4_DEC_H_
diff --git a/media/libstagefright/codecs/mpeg2dec/Android.bp b/media/libstagefright/codecs/mpeg2dec/Android.bp
index fb0db8f..3123376 100644
--- a/media/libstagefright/codecs/mpeg2dec/Android.bp
+++ b/media/libstagefright/codecs/mpeg2dec/Android.bp
@@ -1,4 +1,47 @@
 cc_library_shared {
+    name: "libstagefright_soft_c2mpeg2dec",
+//    vendor_available: true,
+//    vndk: {
+//        enabled: true,
+//    },
+
+    srcs: ["C2SoftMpeg2Dec.cpp"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    include_dirs: [
+        "external/libmpeg2/decoder",
+        "external/libmpeg2/common",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+        ],
+        cfi: false, // true,
+        diag: {
+            cfi: false, // true,
+        },
+    },
+
+    static_libs: ["libmpeg2dec"],
+
+    shared_libs: [
+        "liblog",
+        "libutils",
+        "libstagefright_codec2",
+        "libstagefright_codec2_vndk",
+        "libstagefright_foundation",
+        "libstagefright_simple_c2component",
+    ],
+
+    ldflags: ["-Wl,-Bsymbolic"],
+}
+
+cc_library_shared {
     name: "libstagefright_soft_mpeg2dec",
     vendor_available: true,
     vndk: {
diff --git a/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.cpp b/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.cpp
new file mode 100644
index 0000000..0ebe7d6
--- /dev/null
+++ b/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.cpp
@@ -0,0 +1,795 @@
+/*
+ * 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.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftMpeg2Dec"
+#include <utils/Log.h>
+
+#include "iv_datatypedef.h"
+#include "iv.h"
+#include "ivd.h"
+#include "impeg2d.h"
+#include "C2SoftMpeg2Dec.h"
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+namespace android {
+
+constexpr char kComponentName[] = "c2.google.mpeg2.decoder";
+
+static std::shared_ptr<C2ComponentInterface> BuildIntf(
+        const char *name, c2_node_id_t id,
+        std::function<void(C2ComponentInterface*)> deleter =
+            std::default_delete<C2ComponentInterface>()) {
+    return SimpleC2Interface::Builder(name, id, deleter)
+            .inputFormat(C2FormatCompressed)
+            .outputFormat(C2FormatVideo)
+            .inputMediaType(MEDIA_MIMETYPE_VIDEO_MPEG2)
+            .outputMediaType(MEDIA_MIMETYPE_VIDEO_RAW)
+            .build();
+}
+
+static size_t getCpuCoreCount() {
+    long cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+    cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+    // _SC_NPROC_ONLN must be defined...
+    cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+    CHECK(cpuCoreCount >= 1);
+    ALOGV("Number of CPU cores: %ld", cpuCoreCount);
+    return (size_t)cpuCoreCount;
+}
+
+static void *ivd_aligned_malloc(WORD32 alignment, WORD32 size) {
+    return memalign(alignment, size);
+}
+
+static void ivd_aligned_free(void *mem) {
+    free(mem);
+}
+
+C2SoftMpeg2Dec::C2SoftMpeg2Dec(const char *name, c2_node_id_t id)
+    : SimpleC2Component(BuildIntf(name, id)),
+            mDecHandle(nullptr),
+            mMemRecords(nullptr),
+            mOutBufferDrain(nullptr),
+            mIvColorformat(IV_YUV_420P),
+            mWidth(320),
+            mHeight(240) {
+    // If input dump is enabled, then open create an empty file
+    GENERATE_FILE_NAMES();
+    CREATE_DUMP_FILE(mInFile);
+}
+
+C2SoftMpeg2Dec::~C2SoftMpeg2Dec() {
+    onRelease();
+}
+
+c2_status_t C2SoftMpeg2Dec::onInit() {
+    status_t err = initDecoder();
+    return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+c2_status_t C2SoftMpeg2Dec::onStop() {
+    if (OK != resetDecoder()) return C2_CORRUPTED;
+    resetPlugin();
+    return C2_OK;
+}
+
+void C2SoftMpeg2Dec::onReset() {
+    (void) onStop();
+}
+
+void C2SoftMpeg2Dec::onRelease() {
+    (void) deleteDecoder();
+    if (mOutBufferDrain) {
+        ivd_aligned_free(mOutBufferDrain);
+        mOutBufferDrain = nullptr;
+    }
+    if (mOutBlock) {
+        mOutBlock.reset();
+    }
+    if (mMemRecords) {
+        ivd_aligned_free(mMemRecords);
+        mMemRecords = nullptr;
+    }
+}
+
+c2_status_t C2SoftMpeg2Dec::onFlush_sm() {
+    if (OK != setFlushMode()) return C2_CORRUPTED;
+
+    uint32_t displayStride = mStride;
+    uint32_t displayHeight = mHeight;
+    uint32_t bufferSize = displayStride * displayHeight * 3 / 2;
+    mOutBufferDrain = (uint8_t *)ivd_aligned_malloc(128, bufferSize);
+    if (!mOutBufferDrain) {
+        ALOGE("could not allocate tmp output buffer (for flush) of size %u ", bufferSize);
+        return C2_NO_MEMORY;
+    }
+
+    while (true) {
+        ivd_video_decode_ip_t s_decode_ip;
+        ivd_video_decode_op_t s_decode_op;
+
+        setDecodeArgs(&s_decode_ip, &s_decode_op, nullptr, nullptr, 0, 0, 0);
+        (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
+        if (0 == s_decode_op.u4_output_present) {
+            resetPlugin();
+            break;
+        }
+    }
+
+    ivd_aligned_free(mOutBufferDrain);
+    mOutBufferDrain = nullptr;
+
+    return C2_OK;
+}
+
+status_t C2SoftMpeg2Dec::getNumMemRecords() {
+    iv_num_mem_rec_ip_t s_num_mem_rec_ip;
+    iv_num_mem_rec_op_t s_num_mem_rec_op;
+
+    s_num_mem_rec_ip.u4_size = sizeof(s_num_mem_rec_ip);
+    s_num_mem_rec_ip.e_cmd = IV_CMD_GET_NUM_MEM_REC;
+    s_num_mem_rec_op.u4_size = sizeof(s_num_mem_rec_op);
+
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_num_mem_rec_ip,
+                                                     &s_num_mem_rec_op);
+    if (IV_SUCCESS != status) {
+        ALOGE("Error in getting mem records: 0x%x", s_num_mem_rec_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+    mNumMemRecords = s_num_mem_rec_op.u4_num_mem_rec;
+
+    return OK;
+}
+
+status_t C2SoftMpeg2Dec::fillMemRecords() {
+    iv_mem_rec_t *ps_mem_rec = (iv_mem_rec_t *) ivd_aligned_malloc(
+            128, mNumMemRecords * sizeof(iv_mem_rec_t));
+    if (!ps_mem_rec) {
+        ALOGE("Allocation failure");
+        return NO_MEMORY;
+    }
+    memset(ps_mem_rec, 0, mNumMemRecords * sizeof(iv_mem_rec_t));
+    for (size_t i = 0; i < mNumMemRecords; i++)
+        ps_mem_rec[i].u4_size = sizeof(iv_mem_rec_t);
+    mMemRecords = ps_mem_rec;
+
+    ivdext_fill_mem_rec_ip_t s_fill_mem_ip;
+    ivdext_fill_mem_rec_op_t s_fill_mem_op;
+
+    s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_size = sizeof(ivdext_fill_mem_rec_ip_t);
+    s_fill_mem_ip.u4_share_disp_buf = 0;
+    s_fill_mem_ip.e_output_format = mIvColorformat;
+    s_fill_mem_ip.u4_deinterlace = 1;
+    s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.e_cmd = IV_CMD_FILL_NUM_MEM_REC;
+    s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.pv_mem_rec_location = mMemRecords;
+    s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_wd = mWidth;
+    s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_ht = mHeight;
+    s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_size = sizeof(ivdext_fill_mem_rec_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_fill_mem_ip,
+                                                     &s_fill_mem_op);
+    if (IV_SUCCESS != status) {
+        ALOGE("Error in filling mem records: 0x%x",
+              s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+
+    CHECK_EQ(mNumMemRecords, s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_num_mem_rec_filled);
+    for (size_t i = 0; i < mNumMemRecords; i++, ps_mem_rec++) {
+        ps_mem_rec->pv_base = ivd_aligned_malloc(
+                ps_mem_rec->u4_mem_alignment, ps_mem_rec->u4_mem_size);
+        if (!ps_mem_rec->pv_base) {
+            ALOGE("Allocation failure for memory record #%zu of size %u",
+                  i, ps_mem_rec->u4_mem_size);
+            return NO_MEMORY;
+        }
+    }
+
+    return OK;
+}
+
+status_t C2SoftMpeg2Dec::createDecoder() {
+    ivdext_init_ip_t s_init_ip;
+    ivdext_init_op_t s_init_op;
+
+    s_init_ip.s_ivd_init_ip_t.u4_size = sizeof(ivdext_init_ip_t);
+    s_init_ip.s_ivd_init_ip_t.e_cmd = (IVD_API_COMMAND_TYPE_T)IV_CMD_INIT;
+    s_init_ip.s_ivd_init_ip_t.pv_mem_rec_location = mMemRecords;
+    s_init_ip.s_ivd_init_ip_t.u4_frm_max_wd = mWidth;
+    s_init_ip.s_ivd_init_ip_t.u4_frm_max_ht = mHeight;
+    s_init_ip.u4_share_disp_buf = 0;
+    s_init_ip.u4_deinterlace = 1;
+    s_init_ip.s_ivd_init_ip_t.u4_num_mem_rec = mNumMemRecords;
+    s_init_ip.s_ivd_init_ip_t.e_output_format = mIvColorformat;
+    s_init_op.s_ivd_init_op_t.u4_size = sizeof(ivdext_init_op_t);
+
+    mDecHandle = (iv_obj_t *)mMemRecords[0].pv_base;
+    mDecHandle->pv_fxns = (void *)ivdec_api_function;
+    mDecHandle->u4_size = sizeof(iv_obj_t);
+
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_init_ip,
+                                                     &s_init_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("error in %s: 0x%x", __func__,
+              s_init_op.s_ivd_init_op_t.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+status_t C2SoftMpeg2Dec::setNumCores() {
+    ivdext_ctl_set_num_cores_ip_t s_set_num_cores_ip;
+    ivdext_ctl_set_num_cores_op_t s_set_num_cores_op;
+
+    s_set_num_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t);
+    s_set_num_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_set_num_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES;
+    s_set_num_cores_ip.u4_num_cores = mNumCores;
+    s_set_num_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_set_num_cores_ip,
+                                                     &s_set_num_cores_op);
+    if (status != IV_SUCCESS) {
+        ALOGD("error in %s: 0x%x", __func__, s_set_num_cores_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+status_t C2SoftMpeg2Dec::setParams(size_t stride) {
+    ivd_ctl_set_config_ip_t s_set_dyn_params_ip;
+    ivd_ctl_set_config_op_t s_set_dyn_params_op;
+
+    s_set_dyn_params_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t);
+    s_set_dyn_params_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_set_dyn_params_ip.e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
+    s_set_dyn_params_ip.u4_disp_wd = (UWORD32) stride;
+    s_set_dyn_params_ip.e_frm_skip_mode = IVD_SKIP_NONE;
+    s_set_dyn_params_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
+    s_set_dyn_params_ip.e_vid_dec_mode = IVD_DECODE_FRAME;
+    s_set_dyn_params_op.u4_size = sizeof(ivd_ctl_set_config_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_set_dyn_params_ip,
+                                                     &s_set_dyn_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("error in %s: 0x%x", __func__, s_set_dyn_params_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+status_t C2SoftMpeg2Dec::getVersion() {
+    ivd_ctl_getversioninfo_ip_t s_get_versioninfo_ip;
+    ivd_ctl_getversioninfo_op_t s_get_versioninfo_op;
+    UWORD8 au1_buf[512];
+
+    s_get_versioninfo_ip.u4_size = sizeof(ivd_ctl_getversioninfo_ip_t);
+    s_get_versioninfo_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_get_versioninfo_ip.e_sub_cmd = IVD_CMD_CTL_GETVERSION;
+    s_get_versioninfo_ip.pv_version_buffer = au1_buf;
+    s_get_versioninfo_ip.u4_version_buffer_size = sizeof(au1_buf);
+    s_get_versioninfo_op.u4_size = sizeof(ivd_ctl_getversioninfo_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_get_versioninfo_ip,
+                                                     &s_get_versioninfo_op);
+    if (status != IV_SUCCESS) {
+        ALOGD("error in %s: 0x%x", __func__,
+              s_get_versioninfo_op.u4_error_code);
+    } else {
+        ALOGV("ittiam decoder version number: %s",
+              (char *) s_get_versioninfo_ip.pv_version_buffer);
+    }
+
+    return OK;
+}
+
+status_t C2SoftMpeg2Dec::initDecoder() {
+    status_t ret = getNumMemRecords();
+    if (OK != ret) return ret;
+
+    ret = fillMemRecords();
+    if (OK != ret) return ret;
+
+    if (OK != createDecoder()) return UNKNOWN_ERROR;
+
+    mNumCores = MIN(getCpuCoreCount(), MAX_NUM_CORES);
+    mStride = ALIGN64(mWidth);
+    mSignalledError = false;
+    mPreference = kPreferBitstream;
+    memset(&mDefaultColorAspects, 0, sizeof(ColorAspects));
+    memset(&mBitstreamColorAspects, 0, sizeof(ColorAspects));
+    memset(&mFinalColorAspects, 0, sizeof(ColorAspects));
+    mUpdateColorAspects = false;
+    resetPlugin();
+    (void) setNumCores();
+    if (OK != setParams(mStride)) return UNKNOWN_ERROR;
+    (void) getVersion();
+
+    return OK;
+}
+
+bool C2SoftMpeg2Dec::setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
+                                   ivd_video_decode_op_t *ps_decode_op,
+                                   C2ReadView *inBuffer,
+                                   C2GraphicView *outBuffer,
+                                   size_t inOffset,
+                                   size_t inSize,
+                                   uint32_t tsMarker) {
+    uint32_t displayStride = mStride;
+    uint32_t displayHeight = mHeight;
+    size_t lumaSize = displayStride * displayHeight;
+    size_t chromaSize = lumaSize >> 2;
+
+    ps_decode_ip->u4_size = sizeof(ivd_video_decode_ip_t);
+    ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
+    if (inBuffer) {
+        ps_decode_ip->u4_ts = tsMarker;
+        ps_decode_ip->pv_stream_buffer = const_cast<uint8_t *>(inBuffer->data() + inOffset);
+        ps_decode_ip->u4_num_Bytes = inSize - inOffset;
+    } else {
+        ps_decode_ip->u4_ts = 0;
+        ps_decode_ip->pv_stream_buffer = nullptr;
+        ps_decode_ip->u4_num_Bytes = 0;
+    }
+    ps_decode_ip->s_out_buffer.u4_min_out_buf_size[0] = lumaSize;
+    ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize;
+    ps_decode_ip->s_out_buffer.u4_min_out_buf_size[2] = chromaSize;
+    if (outBuffer) {
+        if (outBuffer->width() < displayStride || outBuffer->height() < displayHeight) {
+            ALOGE("Output buffer too small: provided (%dx%d) required (%ux%u)",
+                  outBuffer->width(), outBuffer->height(), displayStride, displayHeight);
+            return false;
+        }
+        ps_decode_ip->s_out_buffer.pu1_bufs[0] = outBuffer->data()[C2PlanarLayout::PLANE_Y];
+        ps_decode_ip->s_out_buffer.pu1_bufs[1] = outBuffer->data()[C2PlanarLayout::PLANE_U];
+        ps_decode_ip->s_out_buffer.pu1_bufs[2] = outBuffer->data()[C2PlanarLayout::PLANE_V];
+    } else {
+        ps_decode_ip->s_out_buffer.pu1_bufs[0] = mOutBufferDrain;
+        ps_decode_ip->s_out_buffer.pu1_bufs[1] = mOutBufferDrain + lumaSize;
+        ps_decode_ip->s_out_buffer.pu1_bufs[2] = mOutBufferDrain + lumaSize + chromaSize;
+    }
+    ps_decode_ip->s_out_buffer.u4_num_bufs = 3;
+    ps_decode_op->u4_size = sizeof(ivd_video_decode_op_t);
+
+    return true;
+}
+
+bool C2SoftMpeg2Dec::colorAspectsDiffer(
+        const ColorAspects &a, const ColorAspects &b) {
+    if (a.mRange != b.mRange
+        || a.mPrimaries != b.mPrimaries
+        || a.mTransfer != b.mTransfer
+        || a.mMatrixCoeffs != b.mMatrixCoeffs) {
+        return true;
+    }
+    return false;
+}
+
+void C2SoftMpeg2Dec::updateFinalColorAspects(
+        const ColorAspects &otherAspects, const ColorAspects &preferredAspects) {
+    Mutex::Autolock autoLock(mColorAspectsLock);
+    ColorAspects newAspects;
+    newAspects.mRange = preferredAspects.mRange != ColorAspects::RangeUnspecified ?
+        preferredAspects.mRange : otherAspects.mRange;
+    newAspects.mPrimaries = preferredAspects.mPrimaries != ColorAspects::PrimariesUnspecified ?
+        preferredAspects.mPrimaries : otherAspects.mPrimaries;
+    newAspects.mTransfer = preferredAspects.mTransfer != ColorAspects::TransferUnspecified ?
+        preferredAspects.mTransfer : otherAspects.mTransfer;
+    newAspects.mMatrixCoeffs = preferredAspects.mMatrixCoeffs != ColorAspects::MatrixUnspecified ?
+        preferredAspects.mMatrixCoeffs : otherAspects.mMatrixCoeffs;
+
+    // Check to see if need update mFinalColorAspects.
+    if (colorAspectsDiffer(mFinalColorAspects, newAspects)) {
+        mFinalColorAspects = newAspects;
+        mUpdateColorAspects = true;
+    }
+}
+
+status_t C2SoftMpeg2Dec::handleColorAspectsChange() {
+    if (mPreference == kPreferBitstream) {
+        updateFinalColorAspects(mDefaultColorAspects, mBitstreamColorAspects);
+    } else if (mPreference == kPreferContainer) {
+        updateFinalColorAspects(mBitstreamColorAspects, mDefaultColorAspects);
+    } else {
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+bool C2SoftMpeg2Dec::getSeqInfo() {
+    ivdext_ctl_get_seq_info_ip_t s_ctl_get_seq_info_ip;
+    ivdext_ctl_get_seq_info_op_t s_ctl_get_seq_info_op;
+
+    s_ctl_get_seq_info_ip.u4_size = sizeof(ivdext_ctl_get_seq_info_ip_t);
+    s_ctl_get_seq_info_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_ctl_get_seq_info_ip.e_sub_cmd =
+        (IVD_CONTROL_API_COMMAND_TYPE_T)IMPEG2D_CMD_CTL_GET_SEQ_INFO;
+    s_ctl_get_seq_info_op.u4_size = sizeof(ivdext_ctl_get_seq_info_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_ctl_get_seq_info_ip,
+                                                     &s_ctl_get_seq_info_op);
+    if (status != IV_SUCCESS) {
+        ALOGW("Error in getting Sequence info: 0x%x", s_ctl_get_seq_info_op.u4_error_code);
+        return false;
+    }
+
+    int32_t primaries = s_ctl_get_seq_info_op.u1_colour_primaries;
+    int32_t transfer = s_ctl_get_seq_info_op.u1_transfer_characteristics;
+    int32_t coeffs = s_ctl_get_seq_info_op.u1_matrix_coefficients;
+    bool full_range =  false;  // mpeg2 video has limited range.
+
+    ColorAspects colorAspects;
+    ColorUtils::convertIsoColorAspectsToCodecAspects(
+            primaries, transfer, coeffs, full_range, colorAspects);
+    // Update color aspects if necessary.
+    if (colorAspectsDiffer(colorAspects, mBitstreamColorAspects)) {
+        mBitstreamColorAspects = colorAspects;
+        status_t err = handleColorAspectsChange();
+        CHECK(err == OK);
+    }
+
+    return true;
+}
+
+status_t C2SoftMpeg2Dec::setFlushMode() {
+    ivd_ctl_flush_ip_t s_set_flush_ip;
+    ivd_ctl_flush_op_t s_set_flush_op;
+
+    s_set_flush_ip.u4_size = sizeof(ivd_ctl_flush_ip_t);
+    s_set_flush_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_set_flush_ip.e_sub_cmd = IVD_CMD_CTL_FLUSH;
+    s_set_flush_op.u4_size = sizeof(ivd_ctl_flush_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_set_flush_ip,
+                                                     &s_set_flush_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("error in %s: 0x%x", __func__, s_set_flush_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+status_t C2SoftMpeg2Dec::resetDecoder() {
+    ivd_ctl_reset_ip_t s_reset_ip;
+    ivd_ctl_reset_op_t s_reset_op;
+
+    s_reset_ip.u4_size = sizeof(ivd_ctl_reset_ip_t);
+    s_reset_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+    s_reset_ip.e_sub_cmd = IVD_CMD_CTL_RESET;
+    s_reset_op.u4_size = sizeof(ivd_ctl_reset_op_t);
+    IV_API_CALL_STATUS_T status = ivdec_api_function(mDecHandle,
+                                                     &s_reset_ip,
+                                                     &s_reset_op);
+    if (IV_SUCCESS != status) {
+        ALOGE("error in %s: 0x%x", __func__, s_reset_op.u4_error_code);
+        return UNKNOWN_ERROR;
+    }
+    (void) setNumCores();
+    mStride = 0;
+    mSignalledError = false;
+
+    return OK;
+}
+
+void C2SoftMpeg2Dec::resetPlugin() {
+    mSignalledOutputEos = false;
+    gettimeofday(&mTimeStart, nullptr);
+    gettimeofday(&mTimeEnd, nullptr);
+}
+
+status_t C2SoftMpeg2Dec::deleteDecoder() {
+    if (mMemRecords) {
+        iv_mem_rec_t *ps_mem_rec = mMemRecords;
+
+        for (size_t i = 0; i < mNumMemRecords; i++, ps_mem_rec++) {
+            if (ps_mem_rec->pv_base) {
+                ivd_aligned_free(ps_mem_rec->pv_base);
+            }
+        }
+        ivd_aligned_free(mMemRecords);
+        mMemRecords = nullptr;
+    }
+    mDecHandle = nullptr;
+
+    return OK;
+}
+
+status_t C2SoftMpeg2Dec::reInitDecoder() {
+    deleteDecoder();
+
+    status_t ret = initDecoder();
+    if (OK != ret) {
+        ALOGE("Failed to initialize decoder");
+        deleteDecoder();
+        return ret;
+    }
+    return OK;
+}
+
+void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+    uint32_t flags = 0;
+    if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        flags |= C2FrameData::FLAG_END_OF_STREAM;
+        ALOGV("signalling eos");
+    }
+    work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+}
+
+void C2SoftMpeg2Dec::finishWork(uint64_t index, const std::unique_ptr<C2Work> &work) {
+    std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(std::move(mOutBlock),
+                                                           C2Rect(mWidth, mHeight));
+    mOutBlock = nullptr;
+    auto fillWork = [buffer, index](const std::unique_ptr<C2Work> &work) {
+        uint32_t flags = 0;
+        if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) &&
+                (c2_cntr64_t(index) == work->input.ordinal.frameIndex)) {
+            flags |= C2FrameData::FLAG_END_OF_STREAM;
+            ALOGV("signalling eos");
+        }
+        work->worklets.front()->output.flags = (C2FrameData::flags_t)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 && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
+        fillWork(work);
+    } else {
+        finish(index, fillWork);
+    }
+}
+
+c2_status_t C2SoftMpeg2Dec::ensureDecoderState(const std::shared_ptr<C2BlockPool> &pool) {
+    if (!mDecHandle) {
+        ALOGE("not supposed to be here, invalid decoder context");
+        return C2_CORRUPTED;
+    }
+    if (mStride != ALIGN64(mWidth)) {
+        mStride = ALIGN64(mWidth);
+        if (OK != setParams(mStride)) return C2_CORRUPTED;
+    }
+    if (mOutBlock &&
+            (mOutBlock->width() != mStride || mOutBlock->height() != mHeight)) {
+        mOutBlock.reset();
+    }
+    if (!mOutBlock) {
+        uint32_t format = HAL_PIXEL_FORMAT_YV12;
+        C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+        c2_status_t err = pool->fetchGraphicBlock(mStride, mHeight, format, usage, &mOutBlock);
+        if (err != C2_OK) {
+            ALOGE("fetchGraphicBlock for Output failed with status %d", err);
+            return err;
+        }
+        ALOGV("provided (%dx%d) required (%dx%d)",
+              mOutBlock->width(), mOutBlock->height(), mStride, mHeight);
+    }
+
+    return C2_OK;
+}
+
+// TODO: can overall error checking be improved?
+// TODO: allow configuration of color format and usage for graphic buffers instead
+//       of hard coding them to HAL_PIXEL_FORMAT_YV12
+// TODO: pass coloraspects information to surface
+// TODO: test support for dynamic change in resolution
+// TODO: verify if the decoder sent back all frames
+void C2SoftMpeg2Dec::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    work->result = C2_OK;
+    work->workletsProcessed = 0u;
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    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;
+    C2ReadView rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+    if (inSize && rView.error()) {
+        ALOGE("read view map failed %d", rView.error());
+        work->result = C2_CORRUPTED;
+        return;
+    }
+    bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+    bool hasPicture = false;
+
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x",
+          inSize, (int)work->input.ordinal.timestamp.peeku(),
+          (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
+    while (inOffset < inSize) {
+        if (C2_OK != ensureDecoderState(pool)) {
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+        C2GraphicView wView = mOutBlock->map().get();
+        if (wView.error()) {
+            ALOGE("graphic view map failed %d", wView.error());
+            work->result = C2_CORRUPTED;
+            return;
+        }
+
+        ivd_video_decode_ip_t s_decode_ip;
+        ivd_video_decode_op_t s_decode_op;
+        if (!setDecodeArgs(&s_decode_ip, &s_decode_op, &rView, &wView,
+                           inOffset, inSize, workIndex)) {
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+        // If input dump is enabled, then write to file
+        DUMP_TO_FILE(mInFile, s_decode_ip.pv_stream_buffer, s_decode_ip.u4_num_Bytes);
+        WORD32 delay;
+        GETTIME(&mTimeStart, NULL);
+        TIME_DIFF(mTimeEnd, mTimeStart, delay);
+        (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
+        WORD32 decodeTime;
+        GETTIME(&mTimeEnd, nullptr);
+        TIME_DIFF(mTimeStart, mTimeEnd, decodeTime);
+        ALOGV("decodeTime=%6d delay=%6d numBytes=%6d ", decodeTime, delay,
+              s_decode_op.u4_num_bytes_consumed);
+        if (IMPEG2D_UNSUPPORTED_DIMENSIONS == s_decode_op.u4_error_code) {
+            ALOGV("unsupported resolution : %dx%d", s_decode_op.u4_pic_wd, s_decode_op.u4_pic_ht);
+            drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work);
+            resetPlugin();
+            mWidth = s_decode_op.u4_pic_wd;
+            mHeight = s_decode_op.u4_pic_ht;
+            if (OK != reInitDecoder()) {
+                ALOGE("Failed to reinitialize decoder");
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+            continue;
+        } else if (IVD_RES_CHANGED == (s_decode_op.u4_error_code & 0xFF)) {
+            ALOGV("resolution changed");
+            drainInternal(DRAIN_COMPONENT_NO_EOS, pool, work);
+            resetDecoder();
+            resetPlugin();
+            mWidth = s_decode_op.u4_pic_wd;
+            mHeight = s_decode_op.u4_pic_ht;
+            continue;
+        }
+
+        (void) getSeqInfo();
+        if (mUpdateColorAspects) {
+            mUpdateColorAspects = false;
+        }
+        hasPicture |= (1 == s_decode_op.u4_frame_decoded_flag);
+        if (s_decode_op.u4_output_present) {
+            finishWork(s_decode_op.u4_ts, work);
+        }
+        inOffset += s_decode_op.u4_num_bytes_consumed;
+        if (hasPicture && (inSize - inOffset)) {
+            ALOGD("decoded frame in current access nal, ignoring further trailing bytes %d",
+                  (int)inSize - (int)inOffset);
+            break;
+        }
+    }
+
+    if (eos) {
+        drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
+        mSignalledOutputEos = true;
+    } else if (!hasPicture) {
+        fillEmptyWork(work);
+    }
+}
+
+c2_status_t C2SoftMpeg2Dec::drainInternal(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool,
+        const std::unique_ptr<C2Work> &work) {
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    if (OK != setFlushMode()) return C2_CORRUPTED;
+    while (true) {
+        if (C2_OK != ensureDecoderState(pool)) {
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return C2_CORRUPTED;
+        }
+        C2GraphicView wView = mOutBlock->map().get();
+        if (wView.error()) {
+            ALOGE("graphic view map failed %d", wView.error());
+            return C2_CORRUPTED;
+        }
+        ivd_video_decode_ip_t s_decode_ip;
+        ivd_video_decode_op_t s_decode_op;
+        if (!setDecodeArgs(&s_decode_ip, &s_decode_op, nullptr, &wView, 0, 0, 0)) {
+            mSignalledError = true;
+            return C2_CORRUPTED;
+        }
+        (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
+        if (s_decode_op.u4_output_present) {
+            finishWork(s_decode_op.u4_ts, work);
+        } else {
+            break;
+        }
+    }
+    if (drainMode == DRAIN_COMPONENT_WITH_EOS &&
+            work && work->workletsProcessed == 0u) {
+        fillEmptyWork(work);
+    }
+
+    return C2_OK;
+}
+
+c2_status_t C2SoftMpeg2Dec::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    return drainInternal(drainMode, pool, nullptr);
+}
+
+class C2SoftMpeg2DecFactory : public C2ComponentFactory {
+public:
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(new C2SoftMpeg2Dec(kComponentName, id), deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = BuildIntf(kComponentName, id, deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftMpeg2DecFactory() override = default;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftMpeg2DecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.h b/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.h
new file mode 100644
index 0000000..64e5b05
--- /dev/null
+++ b/media/libstagefright/codecs/mpeg2dec/C2SoftMpeg2Dec.h
@@ -0,0 +1,185 @@
+/*
+ * 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 C2_SOFT_MPEG2_DEC_H_
+#define C2_SOFT_MPEG2_DEC_H_
+
+#include <SimpleC2Component.h>
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/ColorUtils.h>
+
+namespace android {
+
+#define ivdec_api_function              impeg2d_api_function
+#define ivdext_init_ip_t                impeg2d_init_ip_t
+#define ivdext_init_op_t                impeg2d_init_op_t
+#define ivdext_fill_mem_rec_ip_t        impeg2d_fill_mem_rec_ip_t
+#define ivdext_fill_mem_rec_op_t        impeg2d_fill_mem_rec_op_t
+#define ivdext_ctl_set_num_cores_ip_t   impeg2d_ctl_set_num_cores_ip_t
+#define ivdext_ctl_set_num_cores_op_t   impeg2d_ctl_set_num_cores_op_t
+#define ivdext_ctl_get_seq_info_ip_t    impeg2d_ctl_get_seq_info_ip_t
+#define ivdext_ctl_get_seq_info_op_t    impeg2d_ctl_get_seq_info_op_t
+#define ALIGN64(x)                      ((((x) + 63) >> 6) << 6)
+#define MAX_NUM_CORES                   4
+#define IVDEXT_CMD_CTL_SET_NUM_CORES    \
+        (IVD_CONTROL_API_COMMAND_TYPE_T)IMPEG2D_CMD_CTL_SET_NUM_CORES
+#define MIN(a, b)                       (((a) < (b)) ? (a) : (b))
+#define GETTIME(a, b)                   gettimeofday(a, b);
+#define TIME_DIFF(start, end, diff)     \
+    diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \
+            ((end).tv_usec - (start).tv_usec);
+
+#ifdef FILE_DUMP_ENABLE
+    #define INPUT_DUMP_PATH     "/sdcard/clips/mpeg2d_input"
+    #define INPUT_DUMP_EXT      "m2v"
+    #define GENERATE_FILE_NAMES() {                         \
+        GETTIME(&mTimeStart, NULL);                         \
+        strcpy(mInFile, "");                                \
+        sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH,  \
+                mTimeStart.tv_sec, mTimeStart.tv_usec,      \
+                INPUT_DUMP_EXT);                            \
+    }
+    #define CREATE_DUMP_FILE(m_filename) {                  \
+        FILE *fp = fopen(m_filename, "wb");                 \
+        if (fp != NULL) {                                   \
+            fclose(fp);                                     \
+        } else {                                            \
+            ALOGD("Could not open file %s", m_filename);    \
+        }                                                   \
+    }
+    #define DUMP_TO_FILE(m_filename, m_buf, m_size)         \
+    {                                                       \
+        FILE *fp = fopen(m_filename, "ab");                 \
+        if (fp != NULL && m_buf != NULL) {                  \
+            uint32_t i;                                     \
+            i = fwrite(m_buf, 1, m_size, fp);               \
+            ALOGD("fwrite ret %d to write %d", i, m_size);  \
+            if (i != (uint32_t)m_size) {                    \
+                ALOGD("Error in fwrite, returned %d", i);   \
+                perror("Error in write to file");           \
+            }                                               \
+            fclose(fp);                                     \
+        } else {                                            \
+            ALOGD("Could not write to file %s", m_filename);\
+        }                                                   \
+    }
+#else /* FILE_DUMP_ENABLE */
+    #define INPUT_DUMP_PATH
+    #define INPUT_DUMP_EXT
+    #define OUTPUT_DUMP_PATH
+    #define OUTPUT_DUMP_EXT
+    #define GENERATE_FILE_NAMES()
+    #define CREATE_DUMP_FILE(m_filename)
+    #define DUMP_TO_FILE(m_filename, m_buf, m_size)
+#endif /* FILE_DUMP_ENABLE */
+
+struct C2SoftMpeg2Dec : public SimpleC2Component {
+    C2SoftMpeg2Dec(const char *name, c2_node_id_t id);
+    virtual ~C2SoftMpeg2Dec();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+ private:
+    status_t getNumMemRecords();
+    status_t fillMemRecords();
+    status_t createDecoder();
+    status_t setNumCores();
+    status_t setParams(size_t stride);
+    status_t getVersion();
+    status_t initDecoder();
+    bool setDecodeArgs(ivd_video_decode_ip_t *ps_decode_ip,
+                       ivd_video_decode_op_t *ps_decode_op,
+                       C2ReadView *inBuffer,
+                       C2GraphicView *outBuffer,
+                       size_t inOffset,
+                       size_t inSize,
+                       uint32_t tsMarker);
+    bool getSeqInfo();
+    // TODO:This is not the right place for colorAspects functions. These should
+    // be part of c2-vndk so that they can be accessed by all video plugins
+    // until then, make them feel at home
+    bool colorAspectsDiffer(const ColorAspects &a, const ColorAspects &b);
+    void updateFinalColorAspects(
+            const ColorAspects &otherAspects, const ColorAspects &preferredAspects);
+    status_t handleColorAspectsChange();
+    c2_status_t ensureDecoderState(const std::shared_ptr<C2BlockPool> &pool);
+    void finishWork(uint64_t index, const std::unique_ptr<C2Work> &work);
+    status_t setFlushMode();
+    c2_status_t drainInternal(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool,
+            const std::unique_ptr<C2Work> &work);
+    status_t resetDecoder();
+    void resetPlugin();
+    status_t deleteDecoder();
+    status_t reInitDecoder();
+
+    // TODO:This is not the right place for this enum. These should
+    // be part of c2-vndk so that they can be accessed by all video plugins
+    // until then, make them feel at home
+    enum {
+        kNotSupported,
+        kPreferBitstream,
+        kPreferContainer,
+    };
+
+    iv_obj_t *mDecHandle;
+    iv_mem_rec_t *mMemRecords;
+    size_t mNumMemRecords;
+    std::shared_ptr<C2GraphicBlock> mOutBlock;
+    uint8_t *mOutBufferDrain;
+
+    size_t mNumCores;
+    IV_COLOR_FORMAT_T mIvColorformat;
+
+    uint32_t mWidth;
+    uint32_t mHeight;
+    uint32_t mStride;
+    bool mSignalledOutputEos;
+    bool mSignalledError;
+
+    // ColorAspects
+    Mutex mColorAspectsLock;
+    int mPreference;
+    ColorAspects mDefaultColorAspects;
+    ColorAspects mBitstreamColorAspects;
+    ColorAspects mFinalColorAspects;
+    bool mUpdateColorAspects;
+
+    // profile
+    struct timeval mTimeStart;
+    struct timeval mTimeEnd;
+#ifdef FILE_DUMP_ENABLE
+    char mInFile[200];
+#endif /* FILE_DUMP_ENABLE */
+
+    DISALLOW_EVIL_CONSTRUCTORS(C2SoftMpeg2Dec);
+};
+
+}  // namespace android
+
+#endif  // C2_SOFT_MPEG2_DEC_H_
diff --git a/media/libstagefright/codecs/on2/dec/Android.bp b/media/libstagefright/codecs/on2/dec/Android.bp
index 8a9399a..03f0c05 100644
--- a/media/libstagefright/codecs/on2/dec/Android.bp
+++ b/media/libstagefright/codecs/on2/dec/Android.bp
@@ -37,3 +37,82 @@
     },
     compile_multilib: "32",
 }
+
+cc_library_shared {
+    name: "libstagefright_soft_c2vp9dec",
+//    vendor_available: true,
+//    vndk: {
+//        enabled: true,
+//    },
+
+    srcs: ["C2SoftVpx.cpp"],
+
+    static_libs: ["libvpx"],
+
+    shared_libs: [
+        "liblog",
+        "libutils",
+        "libstagefright_codec2",
+        "libstagefright_codec2_vndk",
+        "libstagefright_foundation",
+        "libstagefright_simple_c2component",
+    ],
+
+    cflags: [
+        "-DVP9",
+        "-Wall",
+        "-Werror",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+            "unsigned-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+
+    ldflags: ["-Wl,-Bsymbolic"],
+}
+
+cc_library_shared {
+    name: "libstagefright_soft_c2vp8dec",
+//    vendor_available: true,
+//    vndk: {
+//        enabled: true,
+//    },
+
+    srcs: ["C2SoftVpx.cpp"],
+
+    static_libs: ["libvpx"],
+
+    shared_libs: [
+        "liblog",
+        "libutils",
+        "libstagefright_codec2",
+        "libstagefright_codec2_vndk",
+        "libstagefright_foundation",
+        "libstagefright_simple_c2component",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+            "unsigned-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+
+    ldflags: ["-Wl,-Bsymbolic"],
+}
diff --git a/media/libstagefright/codecs/on2/dec/C2SoftVpx.cpp b/media/libstagefright/codecs/on2/dec/C2SoftVpx.cpp
new file mode 100644
index 0000000..96b303c
--- /dev/null
+++ b/media/libstagefright/codecs/on2/dec/C2SoftVpx.cpp
@@ -0,0 +1,411 @@
+/*
+ * 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.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftVpx"
+#include <utils/Log.h>
+
+#include "C2SoftVpx.h"
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+namespace android {
+
+#ifdef VP9
+constexpr char kComponentName[] = "c2.google.vp9.decoder";
+#else
+constexpr char kComponentName[] = "c2.google.vp8.decoder";
+#endif
+
+static std::shared_ptr<C2ComponentInterface> BuildIntf(
+        const char *name, c2_node_id_t id,
+        std::function<void(C2ComponentInterface*)> deleter =
+            std::default_delete<C2ComponentInterface>()) {
+    return SimpleC2Interface::Builder(name, id, deleter)
+            .inputFormat(C2FormatCompressed)
+            .outputFormat(C2FormatVideo)
+            .inputMediaType(
+#ifdef VP9
+                    MEDIA_MIMETYPE_VIDEO_VP9
+#else
+                    MEDIA_MIMETYPE_VIDEO_VP8
+#endif
+            )
+            .outputMediaType(MEDIA_MIMETYPE_VIDEO_RAW)
+            .build();
+}
+
+C2SoftVpx::C2SoftVpx(const char *name, c2_node_id_t id)
+    : SimpleC2Component(BuildIntf(name, id)),
+      mCodecCtx(nullptr) {
+}
+
+C2SoftVpx::~C2SoftVpx() {
+    onRelease();
+}
+
+c2_status_t C2SoftVpx::onInit() {
+    status_t err = initDecoder();
+    return err == OK ? C2_OK : C2_CORRUPTED;
+}
+
+c2_status_t C2SoftVpx::onStop() {
+    (void) onFlush_sm();
+    destroyDecoder();
+
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+
+    return C2_OK;
+}
+
+void C2SoftVpx::onReset() {
+    (void) onStop();
+    (void) initDecoder();
+}
+
+void C2SoftVpx::onRelease() {
+    destroyDecoder();
+}
+
+c2_status_t C2SoftVpx::onFlush_sm() {
+    if (mFrameParallelMode) {
+        // Flush decoder by passing nullptr data ptr and 0 size.
+        // Ideally, this should never fail.
+        if (vpx_codec_decode(mCodecCtx, nullptr, 0, nullptr, 0)) {
+            ALOGE("Failed to flush on2 decoder.");
+            return C2_CORRUPTED;
+        }
+    }
+
+    // Drop all the decoded frames in decoder.
+    vpx_codec_iter_t iter = nullptr;
+    while (vpx_codec_get_frame(mCodecCtx, &iter)) {
+    }
+
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+    return C2_OK;
+}
+
+static int GetCPUCoreCount() {
+    int cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+    cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+    // _SC_NPROC_ONLN must be defined...
+    cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+    CHECK(cpuCoreCount >= 1);
+    ALOGV("Number of CPU cores: %d", cpuCoreCount);
+    return cpuCoreCount;
+}
+
+status_t C2SoftVpx::initDecoder() {
+#ifdef VP9
+    mMode = MODE_VP9;
+#else
+    mMode = MODE_VP8;
+#endif
+
+    mWidth = 320;
+    mHeight = 240;
+    mFrameParallelMode = false;
+    mSignalledOutputEos = false;
+    mSignalledError = false;
+
+    mCodecCtx = new vpx_codec_ctx_t;
+
+    vpx_codec_dec_cfg_t cfg;
+    memset(&cfg, 0, sizeof(vpx_codec_dec_cfg_t));
+    cfg.threads = GetCPUCoreCount();
+
+    vpx_codec_flags_t flags;
+    memset(&flags, 0, sizeof(vpx_codec_flags_t));
+    if (mFrameParallelMode) flags |= VPX_CODEC_USE_FRAME_THREADING;
+
+    vpx_codec_err_t vpx_err;
+    if ((vpx_err = vpx_codec_dec_init(
+                 mCodecCtx, mMode == MODE_VP8 ? &vpx_codec_vp8_dx_algo : &vpx_codec_vp9_dx_algo,
+                 &cfg, flags))) {
+        ALOGE("on2 decoder failed to initialize. (%d)", vpx_err);
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+status_t C2SoftVpx::destroyDecoder() {
+    if  (mCodecCtx) {
+        vpx_codec_destroy(mCodecCtx);
+        delete mCodecCtx;
+        mCodecCtx = nullptr;
+    }
+
+    return OK;
+}
+
+void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+    uint32_t flags = 0;
+    if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        flags |= C2FrameData::FLAG_END_OF_STREAM;
+        ALOGV("signalling eos");
+    }
+    work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
+    work->worklets.front()->output.buffers.clear();
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->workletsProcessed = 1u;
+}
+
+void C2SoftVpx::finishWork(uint64_t index, const std::unique_ptr<C2Work> &work,
+                           const std::shared_ptr<C2GraphicBlock> &block) {
+    std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(block,
+                                                           C2Rect(mWidth, mHeight));
+    auto fillWork = [buffer, index](const std::unique_ptr<C2Work> &work) {
+        uint32_t flags = 0;
+        if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) &&
+                (c2_cntr64_t(index) == work->input.ordinal.frameIndex)) {
+            flags |= C2FrameData::FLAG_END_OF_STREAM;
+            ALOGV("signalling eos");
+        }
+        work->worklets.front()->output.flags = (C2FrameData::flags_t)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 && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
+        fillWork(work);
+    } else {
+        finish(index, fillWork);
+    }
+}
+
+void C2SoftVpx::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    work->result = C2_OK;
+    work->workletsProcessed = 0u;
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    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();
+    if (inSize && rView.error()) {
+        ALOGE("read view map failed %d", rView.error());
+        work->result = C2_CORRUPTED;
+        return;
+    }
+
+    bool codecConfig = ((work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) !=0);
+    bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
+
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x",
+          inSize, (int)work->input.ordinal.timestamp.peeku(),
+          (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
+
+    // Software VP9 Decoder does not need the Codec Specific Data (CSD)
+    // (specified in http://www.webmproject.org/vp9/profiles/). Ignore it if
+    // it was passed.
+    if (codecConfig) {
+        // Ignore CSD buffer for VP9.
+        if (mMode == MODE_VP9) {
+            fillEmptyWork(work);
+            return;
+        } else {
+            // Tolerate the CSD buffer for VP8. This is a workaround
+            // for b/28689536. continue
+            ALOGW("WARNING: Got CSD buffer for VP8. Continue");
+        }
+    }
+
+    uint8_t *bitstream = const_cast<uint8_t *>(rView.data() + inOffset);
+    int64_t frameIndex = work->input.ordinal.frameIndex.peekll();
+
+    if (inSize) {
+        vpx_codec_err_t err = vpx_codec_decode(
+                mCodecCtx, bitstream, inSize, &frameIndex, 0);
+        if (err != VPX_CODEC_OK) {
+            ALOGE("on2 decoder failed to decode frame. err: %d", err);
+            work->result = C2_CORRUPTED;
+            mSignalledError = true;
+            return;
+        }
+    }
+
+    (void)outputBuffer(pool, work);
+
+    if (eos) {
+        drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
+        mSignalledOutputEos = true;
+    } else if (!inSize) {
+        fillEmptyWork(work);
+    }
+}
+
+static void copyOutputBufferToYV12Frame(uint8_t *dst,
+        const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV,
+        size_t srcYStride, size_t srcUStride, size_t srcVStride,
+        uint32_t width, uint32_t height, int32_t bpp) {
+    size_t dstYStride = align(width, 16) * bpp ;
+    size_t dstUVStride = align(dstYStride / 2, 16);
+    uint8_t *dstStart = dst;
+
+    for (size_t i = 0; i < height; ++i) {
+         memcpy(dst, srcY, width * bpp);
+         srcY += srcYStride;
+         dst += dstYStride;
+    }
+
+    dst = dstStart + dstYStride * height;
+    for (size_t i = 0; i < height / 2; ++i) {
+         memcpy(dst, srcV, width / 2 * bpp);
+         srcV += srcVStride;
+         dst += dstUVStride;
+    }
+
+    dst = dstStart + (dstYStride * height) + (dstUVStride * height / 2);
+    for (size_t i = 0; i < height / 2; ++i) {
+         memcpy(dst, srcU, width / 2 * bpp);
+         srcU += srcUStride;
+         dst += dstUVStride;
+    }
+}
+
+bool C2SoftVpx::outputBuffer(
+        const std::shared_ptr<C2BlockPool> &pool,
+        const std::unique_ptr<C2Work> &work)
+{
+    if (!(work && pool)) return false;
+
+    vpx_codec_iter_t iter = nullptr;
+    vpx_image_t *img = vpx_codec_get_frame(mCodecCtx, &iter);
+
+    if (!img) return false;
+
+    mWidth = img->d_w;
+    mHeight = img->d_h;
+
+    CHECK(img->fmt == VPX_IMG_FMT_I420 || img->fmt == VPX_IMG_FMT_I42016);
+    int32_t bpp = 1;
+    if (img->fmt == VPX_IMG_FMT_I42016) {
+        bpp = 2;
+    }
+
+    std::shared_ptr<C2GraphicBlock> block;
+    uint32_t format = HAL_PIXEL_FORMAT_YV12;
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 16) * bpp, mHeight, format, usage, &block);
+    if (err != C2_OK) {
+        ALOGE("fetchGraphicBlock for Output failed with status %d", err);
+        work->result = err;
+        return false;
+    }
+
+    C2GraphicView wView = block->map().get();
+    if (wView.error()) {
+        ALOGE("graphic view map failed %d", wView.error());
+        work->result = C2_CORRUPTED;
+        return false;
+    }
+
+    ALOGV("provided (%dx%d) required (%dx%d), out frameindex %d",
+           block->width(), block->height(), mWidth, mHeight, (int)*(int64_t *)img->user_priv);
+
+    uint8_t *dst = const_cast<uint8_t *>(wView.data()[C2PlanarLayout::PLANE_Y]);
+    size_t srcYStride = img->stride[VPX_PLANE_Y];
+    size_t srcUStride = img->stride[VPX_PLANE_U];
+    size_t srcVStride = img->stride[VPX_PLANE_V];
+    const uint8_t *srcY = (const uint8_t *)img->planes[VPX_PLANE_Y];
+    const uint8_t *srcU = (const uint8_t *)img->planes[VPX_PLANE_U];
+    const uint8_t *srcV = (const uint8_t *)img->planes[VPX_PLANE_V];
+    copyOutputBufferToYV12Frame(dst, srcY, srcU, srcV,
+                                srcYStride, srcUStride, srcVStride, mWidth, mHeight, bpp);
+
+    finishWork(*(int64_t *)img->user_priv, work, std::move(block));
+    return true;
+}
+
+c2_status_t C2SoftVpx::drainInternal(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool,
+        const std::unique_ptr<C2Work> &work) {
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    while ((outputBuffer(pool, work))) {
+    }
+
+    if (drainMode == DRAIN_COMPONENT_WITH_EOS &&
+            work && work->workletsProcessed == 0u) {
+        fillEmptyWork(work);
+    }
+
+    return C2_OK;
+}
+c2_status_t C2SoftVpx::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    return drainInternal(drainMode, pool, nullptr);
+}
+
+class C2SoftVpxFactory : public C2ComponentFactory {
+public:
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(new C2SoftVpx(kComponentName, id), deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = BuildIntf(kComponentName, id, deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftVpxFactory() override = default;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftVpxFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/libstagefright/codecs/on2/dec/C2SoftVpx.h b/media/libstagefright/codecs/on2/dec/C2SoftVpx.h
new file mode 100644
index 0000000..b5d4e21
--- /dev/null
+++ b/media/libstagefright/codecs/on2/dec/C2SoftVpx.h
@@ -0,0 +1,76 @@
+/*
+ * 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 C2_SOFT_VPX_H_
+#define C2_SOFT_VPX_H_
+
+#include <SimpleC2Component.h>
+
+#include <media/stagefright/foundation/ABase.h>
+
+#include "vpx/vpx_decoder.h"
+#include "vpx/vp8dx.h"
+
+namespace android {
+
+struct C2SoftVpx : public SimpleC2Component {
+    C2SoftVpx(const char *name, c2_node_id_t id);
+    virtual ~C2SoftVpx();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+ private:
+    enum {
+        MODE_VP8,
+        MODE_VP9,
+    } mMode;
+
+    vpx_codec_ctx_t *mCodecCtx;
+    bool mFrameParallelMode;  // Frame parallel is only supported by VP9 decoder.
+
+    uint32_t mWidth;
+    uint32_t mHeight;
+    bool mSignalledOutputEos;
+    bool mSignalledError;
+
+    status_t initDecoder();
+    status_t destroyDecoder();
+    void finishWork(uint64_t index, const std::unique_ptr<C2Work> &work,
+                    const std::shared_ptr<C2GraphicBlock> &block);
+    bool outputBuffer(
+            const std::shared_ptr<C2BlockPool> &pool,
+            const std::unique_ptr<C2Work> &work);
+    c2_status_t drainInternal(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool,
+            const std::unique_ptr<C2Work> &work);
+
+    DISALLOW_EVIL_CONSTRUCTORS(C2SoftVpx);
+};
+
+}  // namespace android
+
+#endif  // C2_SOFT_VPX_H_
diff --git a/media/libstagefright/codecs/opus/dec/Android.bp b/media/libstagefright/codecs/opus/dec/Android.bp
index 43318f2..38d72e6 100644
--- a/media/libstagefright/codecs/opus/dec/Android.bp
+++ b/media/libstagefright/codecs/opus/dec/Android.bp
@@ -1,4 +1,40 @@
 cc_library_shared {
+    name: "libstagefright_soft_c2opusdec",
+//    vendor_available: true,
+//    vndk: {
+//        enabled: true,
+//    },
+
+    srcs: ["C2SoftOpus.cpp"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+            "unsigned-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+
+    shared_libs: [
+        "liblog",
+        "libutils",
+        "libstagefright_codec2",
+        "libstagefright_codec2_vndk",
+        "libstagefright_foundation",
+        "libstagefright_simple_c2component",
+        "libopus",
+    ],
+}
+
+cc_library_shared {
     name: "libstagefright_soft_opusdec",
     vendor_available: true,
     vndk: {
diff --git a/media/libstagefright/codecs/opus/dec/C2SoftOpus.cpp b/media/libstagefright/codecs/opus/dec/C2SoftOpus.cpp
new file mode 100644
index 0000000..4eec362
--- /dev/null
+++ b/media/libstagefright/codecs/opus/dec/C2SoftOpus.cpp
@@ -0,0 +1,448 @@
+/*
+ * 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.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftOpus"
+#include <utils/Log.h>
+
+#include "C2SoftOpus.h"
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+extern "C" {
+    #include <opus.h>
+    #include <opus_multistream.h>
+}
+
+namespace android {
+
+constexpr char kComponentName[] = "c2.google.opus.decoder";
+
+static std::shared_ptr<C2ComponentInterface> BuildIntf(
+        const char *name, c2_node_id_t id,
+        std::function<void(C2ComponentInterface*)> deleter =
+            std::default_delete<C2ComponentInterface>()) {
+    return SimpleC2Interface::Builder(name, id, deleter)
+            .inputFormat(C2FormatCompressed)
+            .outputFormat(C2FormatAudio)
+            .inputMediaType(MEDIA_MIMETYPE_AUDIO_OPUS)
+            .outputMediaType(MEDIA_MIMETYPE_AUDIO_RAW)
+            .build();
+}
+
+C2SoftOpus::C2SoftOpus(const char *name, c2_node_id_t id)
+    : SimpleC2Component(BuildIntf(name, id)),
+      mDecoder(nullptr) {
+}
+
+C2SoftOpus::~C2SoftOpus() {
+    onRelease();
+}
+
+c2_status_t C2SoftOpus::onInit() {
+    status_t err = initDecoder();
+    return err == OK ? C2_OK : C2_NO_MEMORY;
+}
+
+c2_status_t C2SoftOpus::onStop() {
+    if (mDecoder) {
+        opus_multistream_decoder_destroy(mDecoder);
+        mDecoder = nullptr;
+    }
+    memset(&mHeader, 0, sizeof(mHeader));
+    mCodecDelay = 0;
+    mSeekPreRoll = 0;
+    mSamplesToDiscard = 0;
+    mInputBufferCount = 0;
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+
+    return C2_OK;
+}
+
+void C2SoftOpus::onReset() {
+    (void)onStop();
+}
+
+void C2SoftOpus::onRelease() {
+    if (mDecoder) {
+        opus_multistream_decoder_destroy(mDecoder);
+        mDecoder = nullptr;
+    }
+}
+
+status_t C2SoftOpus::initDecoder() {
+    memset(&mHeader, 0, sizeof(mHeader));
+    mCodecDelay = 0;
+    mSeekPreRoll = 0;
+    mSamplesToDiscard = 0;
+    mInputBufferCount = 0;
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+
+    return OK;
+}
+
+c2_status_t C2SoftOpus::onFlush_sm() {
+    if (mDecoder) {
+        opus_multistream_decoder_ctl(mDecoder, OPUS_RESET_STATE);
+        mSamplesToDiscard = mSeekPreRoll;
+        mSignalledOutputEos = false;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftOpus::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    (void) pool;
+    if (drainMode == NO_DRAIN) {
+        ALOGW("drain with NO_DRAIN: no-op");
+        return C2_OK;
+    }
+    if (drainMode == DRAIN_CHAIN) {
+        ALOGW("DRAIN_CHAIN not supported");
+        return C2_OMITTED;
+    }
+
+    return C2_OK;
+}
+
+static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
+    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;
+}
+
+static uint16_t ReadLE16(const uint8_t *data, size_t data_size,
+                         uint32_t read_offset) {
+    if (read_offset + 1 > data_size)
+        return 0;
+    uint16_t val;
+    val = data[read_offset];
+    val |= data[read_offset + 1] << 8;
+    return val;
+}
+
+static const int kRate = 48000;
+
+// Opus uses Vorbis channel mapping, and Vorbis channel mapping specifies
+// mappings for up to 8 channels. This information is part of the Vorbis I
+// Specification:
+// http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html
+static const int kMaxChannels = 8;
+
+// Maximum packet size used in Xiph's opusdec.
+static const int kMaxOpusOutputPacketSizeSamples = 960 * 6;
+
+// Default audio output channel layout. Used to initialize |stream_map| in
+// OpusHeader, and passed to opus_multistream_decoder_create() when the header
+// does not contain mapping information. The values are valid only for mono and
+// stereo output: Opus streams with more than 2 channels require a stream map.
+static const int kMaxChannelsWithDefaultLayout = 2;
+static const uint8_t kDefaultOpusChannelLayout[kMaxChannelsWithDefaultLayout] = { 0, 1 };
+
+// Parses Opus Header. Header spec: http://wiki.xiph.org/OggOpus#ID_Header
+static bool ParseOpusHeader(const uint8_t *data, size_t data_size,
+                            OpusHeader* header) {
+    // Size of the Opus header excluding optional mapping information.
+    const size_t kOpusHeaderSize = 19;
+
+    // Offset to the channel count byte in the Opus header.
+    const size_t kOpusHeaderChannelsOffset = 9;
+
+    // Offset to the pre-skip value in the Opus header.
+    const size_t kOpusHeaderSkipSamplesOffset = 10;
+
+    // Offset to the gain value in the Opus header.
+    const size_t kOpusHeaderGainOffset = 16;
+
+    // Offset to the channel mapping byte in the Opus header.
+    const size_t kOpusHeaderChannelMappingOffset = 18;
+
+    // Opus Header contains a stream map. The mapping values are in the header
+    // beyond the always present |kOpusHeaderSize| bytes of data. The mapping
+    // data contains stream count, coupling information, and per channel mapping
+    // values:
+    //   - Byte 0: Number of streams.
+    //   - Byte 1: Number coupled.
+    //   - Byte 2: Starting at byte 2 are |header->channels| uint8 mapping
+    //             values.
+    const size_t kOpusHeaderNumStreamsOffset = kOpusHeaderSize;
+    const size_t kOpusHeaderNumCoupledOffset = kOpusHeaderNumStreamsOffset + 1;
+    const size_t kOpusHeaderStreamMapOffset = kOpusHeaderNumStreamsOffset + 2;
+
+    if (data_size < kOpusHeaderSize) {
+        ALOGE("Header size is too small.");
+        return false;
+    }
+    header->channels = *(data + kOpusHeaderChannelsOffset);
+    if (header->channels <= 0 || header->channels > kMaxChannels) {
+        ALOGE("Invalid Header, wrong channel count: %d", header->channels);
+        return false;
+    }
+
+    header->skip_samples = ReadLE16(data,
+                                    data_size,
+                                    kOpusHeaderSkipSamplesOffset);
+
+    header->gain_db = static_cast<int16_t>(ReadLE16(data,
+                                                    data_size,
+                                                    kOpusHeaderGainOffset));
+
+    header->channel_mapping = *(data + kOpusHeaderChannelMappingOffset);
+    if (!header->channel_mapping) {
+        if (header->channels > kMaxChannelsWithDefaultLayout) {
+            ALOGE("Invalid Header, missing stream map.");
+            return false;
+        }
+        header->num_streams = 1;
+        header->num_coupled = header->channels > 1;
+        header->stream_map[0] = 0;
+        header->stream_map[1] = 1;
+        return true;
+    }
+    if (data_size < kOpusHeaderStreamMapOffset + header->channels) {
+        ALOGE("Invalid stream map; insufficient data for current channel "
+              "count: %d", header->channels);
+        return false;
+    }
+    header->num_streams = *(data + kOpusHeaderNumStreamsOffset);
+    header->num_coupled = *(data + kOpusHeaderNumCoupledOffset);
+    if (header->num_streams + header->num_coupled != header->channels) {
+        ALOGE("Inconsistent channel mapping.");
+        return false;
+    }
+    for (int i = 0; i < header->channels; ++i)
+        header->stream_map[i] = *(data + kOpusHeaderStreamMapOffset + i);
+    return true;
+}
+
+// Convert nanoseconds to number of samples.
+static uint64_t ns_to_samples(uint64_t ns, int rate) {
+    return static_cast<double>(ns) * rate / 1000000000;
+}
+
+void C2SoftOpus::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    work->result = C2_OK;
+    work->workletsProcessed = 0u;
+    if (mSignalledError || mSignalledOutputEos) {
+        work->result = C2_BAD_VALUE;
+        return;
+    }
+
+    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();
+    C2ReadView rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
+    if (inSize && rView.error()) {
+        ALOGE("read view map failed %d", rView.error());
+        work->result = C2_CORRUPTED;
+        return;
+    }
+    if (inSize == 0) {
+        fillEmptyWork(work);
+        if (eos) {
+            mSignalledOutputEos = true;
+            ALOGV("signalled EOS");
+        }
+        return;
+    }
+
+    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d", inSize,
+          (int)work->input.ordinal.timestamp.peeku(), (int)work->input.ordinal.frameIndex.peeku());
+    const uint8_t *data = rView.data() + inOffset;
+    if (mInputBufferCount < 3) {
+        if (mInputBufferCount == 0) {
+            if (!ParseOpusHeader(data, inSize, &mHeader)) {
+                ALOGE("Encountered error while Parsing Opus Header.");
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+            uint8_t channel_mapping[kMaxChannels] = {0};
+            if (mHeader.channels <= kMaxChannelsWithDefaultLayout) {
+                memcpy(&channel_mapping,
+                       kDefaultOpusChannelLayout,
+                       kMaxChannelsWithDefaultLayout);
+            } else {
+                memcpy(&channel_mapping,
+                       mHeader.stream_map,
+                       mHeader.channels);
+            }
+            int status = OPUS_INVALID_STATE;
+            mDecoder = opus_multistream_decoder_create(kRate,
+                                                       mHeader.channels,
+                                                       mHeader.num_streams,
+                                                       mHeader.num_coupled,
+                                                       channel_mapping,
+                                                       &status);
+            if (!mDecoder || status != OPUS_OK) {
+                ALOGE("opus_multistream_decoder_create failed status = %s",
+                      opus_strerror(status));
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+            status = opus_multistream_decoder_ctl(mDecoder,
+                                                  OPUS_SET_GAIN(mHeader.gain_db));
+            if (status != OPUS_OK) {
+                ALOGE("Failed to set OPUS header gain; status = %s",
+                      opus_strerror(status));
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+        } else {
+            if (inSize < 8) {
+                ALOGE("Input sample size is too small.");
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+            int64_t samples = ns_to_samples( *(reinterpret_cast<int64_t*>
+                              (const_cast<uint8_t *> (data))), kRate);
+            if (mInputBufferCount == 1) {
+                mCodecDelay = samples;
+                mSamplesToDiscard = mCodecDelay;
+            }
+            else {
+                mSeekPreRoll = samples;
+            }
+        }
+
+        ++mInputBufferCount;
+        fillEmptyWork(work);
+        if (eos) {
+            mSignalledOutputEos = true;
+            ALOGV("signalled EOS");
+        }
+        return;
+    }
+
+    // Ignore CSD re-submissions.
+    if ((work->input.flags & C2FrameData::FLAG_CODEC_CONFIG)) {
+        fillEmptyWork(work);
+        return;
+    }
+
+    // When seeking to zero, |mCodecDelay| samples has to be discarded
+    // instead of |mSeekPreRoll| samples (as we would when seeking to any
+    // other timestamp).
+    if (work->input.ordinal.timestamp.peeku() == 0) mSamplesToDiscard = mCodecDelay;
+
+    std::shared_ptr<C2LinearBlock> block;
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    c2_status_t err = pool->fetchLinearBlock(
+                          kMaxNumSamplesPerBuffer * kMaxChannels * sizeof(int16_t),
+                          usage, &block);
+    if (err != C2_OK) {
+        ALOGE("fetchLinearBlock for Output failed with status %d", err);
+        work->result = C2_NO_MEMORY;
+        return;
+    }
+    C2WriteView wView = block->map().get();
+    if (wView.error()) {
+        ALOGE("write view map failed %d", wView.error());
+        work->result = C2_CORRUPTED;
+        return;
+    }
+
+    int numSamples = opus_multistream_decode(mDecoder,
+                                             data,
+                                             inSize,
+                                             reinterpret_cast<int16_t *> (wView.data()),
+                                             kMaxOpusOutputPacketSizeSamples,
+                                             0);
+    if (numSamples < 0) {
+        ALOGE("opus_multistream_decode returned numSamples %d", numSamples);
+        numSamples = 0;
+        mSignalledError = true;
+        work->result = C2_CORRUPTED;
+        return;
+    }
+
+    int outOffset = 0;
+    if (mSamplesToDiscard > 0) {
+        if (mSamplesToDiscard > numSamples) {
+            mSamplesToDiscard -= numSamples;
+            numSamples = 0;
+        } else {
+            numSamples -= mSamplesToDiscard;
+            outOffset = mSamplesToDiscard * sizeof(int16_t) * mHeader.channels;
+            mSamplesToDiscard = 0;
+        }
+    }
+
+    if (numSamples) {
+        int outSize = numSamples * sizeof(int16_t) * mHeader.channels;
+        ALOGV("out buffer attr. offset %d size %d ", outOffset, outSize);
+
+        work->worklets.front()->output.flags = work->input.flags;
+        work->worklets.front()->output.buffers.clear();
+        work->worklets.front()->output.buffers.push_back(createLinearBuffer(block, outOffset, outSize));
+        work->worklets.front()->output.ordinal = work->input.ordinal;
+        work->workletsProcessed = 1u;
+    } else {
+        fillEmptyWork(work);
+        block.reset();
+    }
+    if (eos) {
+        mSignalledOutputEos = true;
+        ALOGV("signalled EOS");
+    }
+}
+
+class C2SoftOpusDecFactory : public C2ComponentFactory {
+public:
+    virtual c2_status_t createComponent(
+            c2_node_id_t id,
+            std::shared_ptr<C2Component>* const component,
+            std::function<void(C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(new C2SoftOpus(kComponentName, id), deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id,
+            std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(C2ComponentInterface*)> deleter) override {
+        *interface = BuildIntf(kComponentName, id, deleter);
+        return C2_OK;
+    }
+
+    virtual ~C2SoftOpusDecFactory() override = default;
+};
+
+}  // namespace android
+
+extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftOpusDecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/libstagefright/codecs/opus/dec/C2SoftOpus.h b/media/libstagefright/codecs/opus/dec/C2SoftOpus.h
new file mode 100644
index 0000000..70ad2de
--- /dev/null
+++ b/media/libstagefright/codecs/opus/dec/C2SoftOpus.h
@@ -0,0 +1,76 @@
+/*
+ * 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 C2_SOFT_OPUS_H_
+#define C2_SOFT_OPUS_H_
+
+#include <SimpleC2Component.h>
+
+#include <media/stagefright/foundation/ABase.h>
+
+struct OpusMSDecoder;
+
+namespace android {
+
+struct OpusHeader {
+  int channels;
+  int skip_samples;
+  int channel_mapping;
+  int num_streams;
+  int num_coupled;
+  int16_t gain_db;
+  uint8_t stream_map[8];
+};
+
+struct C2SoftOpus : public SimpleC2Component {
+    C2SoftOpus(const char *name, c2_node_id_t id);
+    virtual ~C2SoftOpus();
+
+    // From SimpleC2Component
+    c2_status_t onInit() override;
+    c2_status_t onStop() override;
+    void onReset() override;
+    void onRelease() override;
+    c2_status_t onFlush_sm() override;
+    void process(
+            const std::unique_ptr<C2Work> &work,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+    c2_status_t drain(
+            uint32_t drainMode,
+            const std::shared_ptr<C2BlockPool> &pool) override;
+private:
+    enum {
+        kMaxNumSamplesPerBuffer = 960 * 6
+    };
+
+    OpusMSDecoder *mDecoder;
+    OpusHeader mHeader;
+
+    int64_t mCodecDelay;
+    int64_t mSeekPreRoll;
+    int64_t mSamplesToDiscard;
+    size_t mInputBufferCount;
+    bool mSignalledError;
+    bool mSignalledOutputEos;
+
+    status_t initDecoder();
+
+    DISALLOW_EVIL_CONSTRUCTORS(C2SoftOpus);
+};
+
+}  // namespace android
+
+#endif  // C2_SOFT_OPUS_H_
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index bef2db4..5cc5093 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -27,6 +27,7 @@
 #include <binder/MemoryDealer.h>
 #include <cutils/native_handle.h>
 #include <hidlmemory/FrameworkUtils.h>
+#include <media/cas/DescramblerAPI.h>
 #include <media/stagefright/foundation/ABitReader.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -1387,6 +1388,9 @@
 
     uint32_t sctrl = tsScramblingControl != 0 ?
             tsScramblingControl : pesScramblingControl;
+    if (mQueue->isScrambled()) {
+        sctrl |= DescramblerPlugin::kScrambling_Flag_PesHeader;
+    }
 
     // Perform the 1st pass descrambling if needed
     if (descrambleBytes > 0) {
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index 13f9208..4e29d37 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -471,7 +471,7 @@
         Bundle args = new Bundle();
         args.putInt(MediaSession2Stub.ARGUMENT_KEY_ITEM_INDEX, item);
         sendTransportControlCommand(
-                MediaSession2.COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM, args);
+                MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_TO_PLAYLIST_ITEM, args);
         */
     }
 
@@ -483,16 +483,21 @@
     }
 
     @Override
-    public void removePlaylistItem_impl(MediaItem2 index) {
+    public void addPlaylistItem_impl(int index, MediaItem2 item) {
         // TODO(jaewan): Implement
     }
 
     @Override
-    public void addPlaylistItem_impl(int index, MediaItem2 item) {
+    public void removePlaylistItem_impl(MediaItem2 item) {
         // TODO(jaewan): Implement
     }
 
     @Override
+    public void replacePlaylistItem_impl(int index, MediaItem2 item) {
+        // TODO: Implement this
+    }
+
+    @Override
     public PlaylistParams getPlaylistParams_impl() {
         synchronized (mLock) {
             return mPlaylistParams;
diff --git a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
index 18ff1b9..a21fda6 100644
--- a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
@@ -131,9 +131,8 @@
     public static class BuilderImpl
             extends BuilderBaseImpl<MediaLibrarySession, MediaLibrarySessionCallback> {
         public BuilderImpl(MediaLibraryService2 service, Builder instance,
-                MediaPlayerBase player, Executor callbackExecutor,
-                MediaLibrarySessionCallback callback) {
-            super(service, player);
+                Executor callbackExecutor, MediaLibrarySessionCallback callback) {
+            super(service);
             setSessionCallback_impl(callbackExecutor, callback);
         }
 
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index ec4ce8d..5e7af3b 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -38,7 +38,8 @@
 import android.media.MediaLibraryService2;
 import android.media.MediaMetadata2;
 import android.media.MediaPlayerBase;
-import android.media.MediaPlayerBase.EventCallback;
+import android.media.MediaPlayerBase.PlayerEventCallback;
+import android.media.MediaPlaylistController;
 import android.media.MediaSession2;
 import android.media.MediaSession2.Builder;
 import android.media.MediaSession2.Command;
@@ -85,7 +86,7 @@
     private final MediaSession2Stub mSessionStub;
     private final SessionToken2 mSessionToken;
     private final AudioManager mAudioManager;
-    private final ArrayMap<EventCallback, Executor> mCallbacks = new ArrayMap<>();
+    private final ArrayMap<PlayerEventCallback, Executor> mCallbacks = new ArrayMap<>();
     private final PendingIntent mSessionActivity;
 
     // mPlayer is set to null when the session is closed, and we shouldn't throw an exception
@@ -202,24 +203,13 @@
     }
 
     @Override
-    public void setPlayer_impl(MediaPlayerBase player) {
+    public void setPlayer_impl(MediaPlayerBase player, MediaPlaylistController mpcl,
+            VolumeProvider2 volumeProvider) throws IllegalArgumentException {
         ensureCallingThread();
         if (player == null) {
             throw new IllegalArgumentException("player shouldn't be null");
         }
-        setPlayer(player, null);
-    }
 
-    @Override
-    public void setPlayer_impl(MediaPlayerBase player, VolumeProvider2 volumeProvider)
-            throws IllegalArgumentException {
-        ensureCallingThread();
-        if (player == null) {
-            throw new IllegalArgumentException("player shouldn't be null");
-        }
-        if (volumeProvider == null) {
-            throw new IllegalArgumentException("volumeProvider shouldn't be null");
-        }
         setPlayer(player, volumeProvider);
     }
 
@@ -228,11 +218,11 @@
         synchronized (mLock) {
             if (mPlayer != null && mEventCallback != null) {
                 // This might not work for a poorly implemented player.
-                mPlayer.unregisterEventCallback(mEventCallback);
+                mPlayer.unregisterPlayerEventCallback(mEventCallback);
             }
             mPlayer = player;
             mEventCallback = new MyEventCallback(this, player);
-            player.registerEventCallback(mCallbackExecutor, mEventCallback);
+            player.registerPlayerEventCallback(mCallbackExecutor, mEventCallback);
             mVolumeProvider = volumeProvider;
             mPlaybackInfo = info;
         }
@@ -293,7 +283,7 @@
         synchronized (mLock) {
             if (mPlayer != null) {
                 // close can be called multiple times
-                mPlayer.unregisterEventCallback(mEventCallback);
+                mPlayer.unregisterPlayerEventCallback(mEventCallback);
                 mPlayer = null;
             }
         }
@@ -347,7 +337,9 @@
         ensureCallingThread();
         final MediaPlayerBase player = mPlayer;
         if (player != null) {
-            player.stop();
+            // TODO: Uncomment or remove
+            //player.stop();
+            player.pause();
         } else if (DEBUG) {
             Log.d(TAG, "API calls after the close()", new IllegalStateException());
         }
@@ -356,12 +348,16 @@
     @Override
     public void skipToPrevious_impl() {
         ensureCallingThread();
+        // TODO: Uncomment or remove
+        /*
         final MediaPlayerBase player = mPlayer;
         if (player != null) {
-            player.skipToPrevious();
+            // TODO implement
+            //player.skipToPrevious();
         } else if (DEBUG) {
             Log.d(TAG, "API calls after the close()", new IllegalStateException());
         }
+        */
     }
 
     @Override
@@ -393,23 +389,32 @@
             throw new IllegalArgumentException("params shouldn't be null");
         }
         ensureCallingThread();
+        // TODO: Uncomment or remove
+        /*
         final MediaPlayerBase player = mPlayer;
         if (player != null) {
-            player.setPlaylistParams(params);
+            // TODO implement
+            //player.setPlaylistParams(params);
             mSessionStub.notifyPlaylistParamsChanged(params);
         }
+        */
     }
 
     @Override
     public PlaylistParams getPlaylistParams_impl() {
+        // TODO: Uncomment or remove
+        /*
         final MediaPlayerBase player = mPlayer;
         if (player != null) {
             // TODO(jaewan): Is it safe to be called on any thread?
             //               Otherwise MediaSession2 should cache parameter of setPlaylistParams.
-            return player.getPlaylistParams();
+            // TODO implement
+            //return player.getPlaylistParams();
+            return null;
         } else if (DEBUG) {
             Log.d(TAG, "API calls after the close()", new IllegalStateException());
         }
+        */
         return null;
     }
 
@@ -439,13 +444,17 @@
             throw new IllegalArgumentException("playlist shouldn't be null");
         }
         ensureCallingThread();
+        // TODO: Uncomment or remove
+        /*
         final MediaPlayerBase player = mPlayer;
         if (player != null) {
-            player.setPlaylist(playlist);
+            // TODO implement, use the SessionPlaylistController itf
+            //player.setPlaylist(playlist);
             mSessionStub.notifyPlaylistChanged(playlist);
         } else if (DEBUG) {
             Log.d(TAG, "API calls after the close()", new IllegalStateException());
         }
+        */
     }
 
     @Override
@@ -464,15 +473,25 @@
     }
 
     @Override
+    public void replacePlaylistItem_impl(int index, MediaItem2 item) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
     public List<MediaItem2> getPlaylist_impl() {
+        // TODO: Uncomment or remove
+        /*
         final MediaPlayerBase player = mPlayer;
         if (player != null) {
             // TODO(jaewan): Is it safe to be called on any thread?
             //               Otherwise MediaSession2 should cache parameter of setPlaylist.
-            return player.getPlaylist();
+            // TODO implement
+            //return player.getPlaylist();
+            return null;
         } else if (DEBUG) {
             Log.d(TAG, "API calls after the close()", new IllegalStateException());
         }
+        */
         return null;
     }
 
@@ -496,23 +515,31 @@
     @Override
     public void fastForward_impl() {
         ensureCallingThread();
+        // TODO: Uncomment or remove
+        /*
         final MediaPlayerBase player = mPlayer;
         if (player != null) {
-            player.fastForward();
+            // TODO implement
+            //player.fastForward();
         } else if (DEBUG) {
             Log.d(TAG, "API calls after the close()", new IllegalStateException());
         }
+        */
     }
 
     @Override
     public void rewind_impl() {
         ensureCallingThread();
+        // TODO: Uncomment or remove
+        /*
         final MediaPlayerBase player = mPlayer;
         if (player != null) {
-            player.rewind();
+            // TODO implement
+            //player.rewind();
         } else if (DEBUG) {
             Log.d(TAG, "API calls after the close()", new IllegalStateException());
         }
+        */
     }
 
     @Override
@@ -529,16 +556,20 @@
     @Override
     public void skipToPlaylistItem_impl(MediaItem2 item) {
         ensureCallingThread();
+        // TODO: Uncomment or remove
+        /*
         final MediaPlayerBase player = mPlayer;
         if (player != null) {
-            player.setCurrentPlaylistItem(item);
+            // TODO implement
+            //player.setCurrentPlaylistItem(item);
         } else if (DEBUG) {
             Log.d(TAG, "API calls after the close()", new IllegalStateException());
         }
+        */
     }
 
     @Override
-    public void registerPlayerEventCallback_impl(Executor executor, EventCallback callback) {
+    public void registerPlayerEventCallback_impl(Executor executor, PlayerEventCallback callback) {
         if (executor == null) {
             throw new IllegalArgumentException("executor shouldn't be null");
         }
@@ -551,13 +582,16 @@
             return;
         }
         mCallbacks.put(callback, executor);
+        // TODO: Uncomment or remove
+        /*
         // TODO(jaewan): Double check if we need this.
         final PlaybackState2 state = getInstance().getPlaybackState();
         executor.execute(() -> callback.onPlaybackStateChanged(state));
+        */
     }
 
     @Override
-    public void unregisterPlayerEventCallback_impl(EventCallback callback) {
+    public void unregisterPlayerEventCallback_impl(PlayerEventCallback callback) {
         if (callback == null) {
             throw new IllegalArgumentException("callback shouldn't be null");
         }
@@ -568,19 +602,24 @@
     @Override
     public PlaybackState2 getPlaybackState_impl() {
         ensureCallingThread();
+        // TODO: Uncomment or remove
+        /*
         final MediaPlayerBase player = mPlayer;
         if (player != null) {
-            // TODO(jaewan): Is it safe to be called on any thread?
+           // TODO(jaewan): Is it safe to be called on any thread?
             //               Otherwise MediaSession2 should cache the result from listener.
-            return player.getPlaybackState();
+            // TODO implement
+            //return player.getPlaybackState();
+            return null;
         } else if (DEBUG) {
             Log.d(TAG, "API calls after the close()", new IllegalStateException());
         }
+        */
         return null;
     }
 
     @Override
-    public void notifyError_impl(int errorCode, int extra) {
+    public void notifyError_impl(int errorCode, Bundle extras) {
         // TODO(jaewan): Implement
     }
 
@@ -609,30 +648,32 @@
     }
 
     private void notifyPlaybackStateChangedNotLocked(final PlaybackState2 state) {
-        ArrayMap<EventCallback, Executor> callbacks = new ArrayMap<>();
+        ArrayMap<PlayerEventCallback, Executor> callbacks = new ArrayMap<>();
         synchronized (mLock) {
             callbacks.putAll(mCallbacks);
         }
         // Notify to callbacks added directly to this session
         for (int i = 0; i < callbacks.size(); i++) {
-            final EventCallback callback = callbacks.keyAt(i);
+            final PlayerEventCallback callback = callbacks.keyAt(i);
             final Executor executor = callbacks.valueAt(i);
-            executor.execute(() -> callback.onPlaybackStateChanged(state));
+            // TODO: Uncomment or remove
+            //executor.execute(() -> callback.onPlaybackStateChanged(state));
         }
         // Notify to controllers as well.
         mSessionStub.notifyPlaybackStateChangedNotLocked(state);
     }
 
     private void notifyErrorNotLocked(String mediaId, int what, int extra) {
-        ArrayMap<EventCallback, Executor> callbacks = new ArrayMap<>();
+        ArrayMap<PlayerEventCallback, Executor> callbacks = new ArrayMap<>();
         synchronized (mLock) {
             callbacks.putAll(mCallbacks);
         }
         // Notify to callbacks added directly to this session
         for (int i = 0; i < callbacks.size(); i++) {
-            final EventCallback callback = callbacks.keyAt(i);
+            final PlayerEventCallback callback = callbacks.keyAt(i);
             final Executor executor = callbacks.valueAt(i);
-            executor.execute(() -> callback.onError(mediaId, what, extra));
+            // TODO: Uncomment or remove
+            //executor.execute(() -> callback.onError(mediaId, what, extra));
         }
         // TODO(jaewan): Notify to controllers as well.
     }
@@ -675,7 +716,7 @@
         return mSessionActivity;
     }
 
-    private static class MyEventCallback extends EventCallback {
+    private static class MyEventCallback extends PlayerEventCallback {
         private final WeakReference<MediaSession2Impl> mSession;
         private final MediaPlayerBase mPlayer;
 
@@ -684,6 +725,8 @@
             mPlayer = player;
         }
 
+        // TODO: Uncomment or remove
+        /*
         @Override
         public void onPlaybackStateChanged(PlaybackState2 state) {
             MediaSession2Impl session = mSession.get();
@@ -697,7 +740,10 @@
             }
             session.notifyPlaybackStateChangedNotLocked(state);
         }
+        */
 
+        // TODO: Uncomment or remove
+        /*
         @Override
         public void onError(String mediaId, int what, int extra) {
             MediaSession2Impl session = mSession.get();
@@ -712,6 +758,9 @@
             }
             session.notifyErrorNotLocked(mediaId, what, extra);
         }
+        */
+
+        //TODO implement the real PlayerEventCallback methods
     }
 
     public static final class CommandImpl implements CommandProvider {
@@ -719,30 +768,30 @@
                 = "android.media.media_session2.command.command_code";
         private static final String KEY_COMMAND_CUSTOM_COMMAND
                 = "android.media.media_session2.command.custom_command";
-        private static final String KEY_COMMAND_EXTRA
-                = "android.media.media_session2.command.extra";
+        private static final String KEY_COMMAND_EXTRAS
+                = "android.media.media_session2.command.extras";
 
         private final Command mInstance;
         private final int mCommandCode;
         // Nonnull if it's custom command
         private final String mCustomCommand;
-        private final Bundle mExtra;
+        private final Bundle mExtras;
 
         public CommandImpl(Command instance, int commandCode) {
             mInstance = instance;
             mCommandCode = commandCode;
             mCustomCommand = null;
-            mExtra = null;
+            mExtras = null;
         }
 
-        public CommandImpl(Command instance, @NonNull String action, @Nullable Bundle extra) {
+        public CommandImpl(Command instance, @NonNull String action, @Nullable Bundle extras) {
             if (action == null) {
                 throw new IllegalArgumentException("action shouldn't be null");
             }
             mInstance = instance;
             mCommandCode = COMMAND_CODE_CUSTOM;
             mCustomCommand = action;
-            mExtra = extra;
+            mExtras = extras;
         }
 
         public int getCommandCode_impl() {
@@ -753,8 +802,8 @@
             return mCustomCommand;
         }
 
-        public @Nullable Bundle getExtra_impl() {
-            return mExtra;
+        public @Nullable Bundle getExtras_impl() {
+            return mExtras;
         }
 
         /**
@@ -764,7 +813,7 @@
             Bundle bundle = new Bundle();
             bundle.putInt(KEY_COMMAND_CODE, mCommandCode);
             bundle.putString(KEY_COMMAND_CUSTOM_COMMAND, mCustomCommand);
-            bundle.putBundle(KEY_COMMAND_EXTRA, mExtra);
+            bundle.putBundle(KEY_COMMAND_EXTRAS, mExtras);
             return bundle;
         }
 
@@ -780,7 +829,7 @@
                 if (customCommand == null) {
                     return null;
                 }
-                return new Command(context, customCommand, command.getBundle(KEY_COMMAND_EXTRA));
+                return new Command(context, customCommand, command.getBundle(KEY_COMMAND_EXTRAS));
             }
         }
 
@@ -1073,8 +1122,8 @@
                 = "android.media.media_session2.command_button.icon_res_id";
         private static final String KEY_DISPLAY_NAME
                 = "android.media.media_session2.command_button.display_name";
-        private static final String KEY_EXTRA
-                = "android.media.media_session2.command_button.extra";
+        private static final String KEY_EXTRAS
+                = "android.media.media_session2.command_button.extras";
         private static final String KEY_ENABLED
                 = "android.media.media_session2.command_button.enabled";
 
@@ -1082,15 +1131,15 @@
         private Command mCommand;
         private int mIconResId;
         private String mDisplayName;
-        private Bundle mExtra;
+        private Bundle mExtras;
         private boolean mEnabled;
 
         public CommandButtonImpl(Context context, @Nullable Command command, int iconResId,
-                @Nullable String displayName, Bundle extra, boolean enabled) {
+                @Nullable String displayName, Bundle extras, boolean enabled) {
             mCommand = command;
             mIconResId = iconResId;
             mDisplayName = displayName;
-            mExtra = extra;
+            mExtras = extras;
             mEnabled = enabled;
             mInstance = new CommandButton(this);
         }
@@ -1111,8 +1160,8 @@
         }
 
         @Override
-        public @Nullable Bundle getExtra_impl() {
-            return mExtra;
+        public @Nullable Bundle getExtras_impl() {
+            return mExtras;
         }
 
         @Override
@@ -1125,7 +1174,7 @@
             bundle.putBundle(KEY_COMMAND, mCommand.toBundle());
             bundle.putInt(KEY_ICON_RES_ID, mIconResId);
             bundle.putString(KEY_DISPLAY_NAME, mDisplayName);
-            bundle.putBundle(KEY_EXTRA, mExtra);
+            bundle.putBundle(KEY_EXTRAS, mExtras);
             bundle.putBoolean(KEY_ENABLED, mEnabled);
             return bundle;
         }
@@ -1138,7 +1187,7 @@
             builder.setCommand(Command.fromBundle(context, bundle.getBundle(KEY_COMMAND)));
             builder.setIconResId(bundle.getInt(KEY_ICON_RES_ID, 0));
             builder.setDisplayName(bundle.getString(KEY_DISPLAY_NAME));
-            builder.setExtra(bundle.getBundle(KEY_EXTRA));
+            builder.setExtras(bundle.getBundle(KEY_EXTRAS));
             builder.setEnabled(bundle.getBoolean(KEY_ENABLED));
             try {
                 return builder.build();
@@ -1157,7 +1206,7 @@
             private Command mCommand;
             private int mIconResId;
             private String mDisplayName;
-            private Bundle mExtra;
+            private Bundle mExtras;
             private boolean mEnabled;
 
             public BuilderImpl(Context context, CommandButton.Builder instance) {
@@ -1191,8 +1240,8 @@
             }
 
             @Override
-            public CommandButton.Builder setExtra_impl(Bundle extra) {
-                mExtra = extra;
+            public CommandButton.Builder setExtras_impl(Bundle extras) {
+                mExtras = extras;
                 return mInstance;
             }
 
@@ -1208,7 +1257,7 @@
                             + " and name to display");
                 }
                 return new CommandButtonImpl(
-                        mContext, mCommand, mIconResId, mDisplayName, mExtra, mEnabled).mInstance;
+                        mContext, mCommand, mIconResId, mDisplayName, mExtras, mEnabled).mInstance;
             }
         }
     }
@@ -1216,7 +1265,7 @@
     public static abstract class BuilderBaseImpl<T extends MediaSession2, C extends SessionCallback>
             implements BuilderBaseProvider<T, C> {
         final Context mContext;
-        final MediaPlayerBase mPlayer;
+        MediaPlayerBase mPlayer;
         String mId;
         Executor mCallbackExecutor;
         C mCallback;
@@ -1232,17 +1281,23 @@
          *      {@link MediaSession2} or {@link MediaController2}.
          */
         // TODO(jaewan): Also need executor
-        public BuilderBaseImpl(Context context, MediaPlayerBase player) {
+        public BuilderBaseImpl(Context context) {
             if (context == null) {
                 throw new IllegalArgumentException("context shouldn't be null");
             }
+            mContext = context;
+            // Ensure non-null
+            mId = "";
+        }
+
+        public void setPlayer_impl(MediaPlayerBase player, MediaPlaylistController mplc,
+                VolumeProvider2 volumeProvider) {
+            // TODO: Use MediaPlaylistController
             if (player == null) {
                 throw new IllegalArgumentException("player shouldn't be null");
             }
-            mContext = context;
             mPlayer = player;
-            // Ensure non-null
-            mId = "";
+            mVolumeProvider = volumeProvider;
         }
 
         public void setVolumeProvider_impl(VolumeProvider2 volumeProvider) {
@@ -1275,8 +1330,8 @@
     }
 
     public static class BuilderImpl extends BuilderBaseImpl<MediaSession2, SessionCallback> {
-        public BuilderImpl(Context context, Builder instance, MediaPlayerBase player) {
-            super(context, player);
+        public BuilderImpl(Context context, Builder instance) {
+            super(context);
         }
 
         @Override
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index 7fb6f0f..b1e71e2 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -356,7 +356,7 @@
                 case MediaSession2.COMMAND_CODE_PLAYBACK_SEEK_TO:
                     session.getInstance().seekTo(args.getLong(ARGUMENT_KEY_POSITION));
                     break;
-                case MediaSession2.COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM:
+                case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_TO_PLAYLIST_ITEM:
                     // TODO(jaewan): Implement
                     /*
                     session.getInstance().skipToPlaylistItem(
diff --git a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
index e640f4c..5bf67d2 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
@@ -22,7 +22,8 @@
 import android.app.NotificationManager;
 import android.content.Context;
 import android.content.Intent;
-import android.media.MediaPlayerBase.EventCallback;
+import android.media.MediaPlayerBase;
+import android.media.MediaPlayerBase.PlayerEventCallback;
 import android.media.MediaSession2;
 import android.media.MediaSessionService2;
 import android.media.MediaSessionService2.MediaNotification;
@@ -42,7 +43,7 @@
     private static final boolean DEBUG = true; // TODO(jaewan): Change this.
 
     private final MediaSessionService2 mInstance;
-    private final EventCallback mCallback = new SessionServiceEventCallback();
+    private final PlayerEventCallback mCallback = new SessionServiceEventCallback();
 
     private final Object mLock = new Object();
     @GuardedBy("mLock")
@@ -135,16 +136,21 @@
                 mediaNotification.getNotification());
     }
 
-    private class SessionServiceEventCallback extends EventCallback {
+    private class SessionServiceEventCallback extends PlayerEventCallback {
         @Override
-        public void onPlaybackStateChanged(PlaybackState2 state) {
-            if (state == null) {
-                Log.w(TAG, "Ignoring null playback state");
-                return;
-            }
-            MediaSession2Impl impl = (MediaSession2Impl) mSession.getProvider();
-            updateNotification(impl.getInstance().getPlaybackState());
+        public void onPlayerStateChanged(MediaPlayerBase mpb, int state) {
+            // TODO: Implement this
+            return;
         }
+        // TODO: Uncomment or remove
+        //public void onPlaybackStateChanged(PlaybackState2 state) {
+        //    if (state == null) {
+        //        Log.w(TAG, "Ignoring null playback state");
+        //        return;
+        //    }
+        //    MediaSession2Impl impl = (MediaSession2Impl) mSession.getProvider();
+        //    updateNotification(impl.getInstance().getPlaybackState());
+        //}
     }
 
     public static class MediaNotificationImpl implements MediaNotificationProvider {
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
index 577173d..b0d435d 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
@@ -162,8 +162,8 @@
     }
 
     public BuilderBaseProvider<MediaSession2, SessionCallback> createMediaSession2Builder(
-            Context context, MediaSession2.Builder instance, MediaPlayerBase player) {
-        return new MediaSession2Impl.BuilderImpl(context, instance, player);
+            Context context, MediaSession2.Builder instance) {
+        return new MediaSession2Impl.BuilderImpl(context, instance);
     }
 
     @Override
@@ -188,9 +188,9 @@
     @Override
     public BuilderBaseProvider<MediaLibrarySession, MediaLibrarySessionCallback>
         createMediaLibraryService2Builder(MediaLibraryService2 service,
-            MediaLibrarySession.Builder instance, MediaPlayerBase player,
-            Executor callbackExecutor, MediaLibrarySessionCallback callback) {
-        return new MediaLibraryService2Impl.BuilderImpl(service, instance, player, callbackExecutor,
+            MediaLibrarySession.Builder instance, Executor callbackExecutor,
+            MediaLibrarySessionCallback callback) {
+        return new MediaLibraryService2Impl.BuilderImpl(service, instance, callbackExecutor,
                 callback);
     }
 
diff --git a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
index f440ad6..c4787d1 100644
--- a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
@@ -20,6 +20,7 @@
 import android.media.MediaMetadata;
 import android.media.session.MediaController;
 import android.media.session.PlaybackState;
+import android.media.SessionToken2;
 import android.media.update.MediaControlView2Provider;
 import android.media.update.ViewGroupProvider;
 import android.os.Bundle;
@@ -150,6 +151,16 @@
     }
 
     @Override
+    public void setMediaSessionToken_impl(SessionToken2 token) {
+        // TODO: implement this
+    }
+
+    @Override
+    public void setOnFullScreenListener_impl(MediaControlView2.OnFullScreenListener l) {
+        // TODO: implement this
+    }
+
+    @Override
     public void setController_impl(MediaController controller) {
         mController = controller;
         if (controller != null) {
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index 928b2a5..cdb1470 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
@@ -22,11 +22,13 @@
 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.Cea708CaptionRenderer;
 import android.media.ClosedCaptionRenderer;
+import android.media.MediaItem2;
 import android.media.MediaMetadata2;
 import android.media.Metadata;
 import android.media.PlaybackParams;
@@ -39,6 +41,7 @@
 import android.media.session.MediaController.PlaybackInfo;
 import android.media.session.MediaSession;
 import android.media.session.PlaybackState;
+import android.media.SessionToken2;
 import android.media.update.VideoView2Provider;
 import android.media.update.ViewGroupProvider;
 import android.net.Uri;
@@ -234,6 +237,12 @@
     }
 
     @Override
+    public SessionToken2 getMediaSessionToken_impl() {
+        // TODO: implement this
+        return null;
+    }
+
+    @Override
     public MediaControlView2 getMediaControlView2_impl() {
         return mMediaControlView;
     }
@@ -352,6 +361,16 @@
     }
 
     @Override
+    public void setMediaItem_impl(MediaItem2 mediaItem) {
+        // TODO: implement this
+    }
+
+    @Override
+    public void setDataSource_impl(DataSourceDesc dsd) {
+        // TODO: implement this
+    }
+
+    @Override
     public void setViewType_impl(int viewType) {
         if (viewType == mCurrentView.getViewType()) {
             return;
diff --git a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
index 1abb9b4..27822e6 100644
--- a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
@@ -41,6 +41,7 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -227,6 +228,7 @@
         assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
     }
 
+    @Ignore
     @Test
     public void testSearch() throws InterruptedException {
         final String query = MockMediaLibraryService2.SEARCH_QUERY;
@@ -352,6 +354,7 @@
         assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
     }
 
+    @Ignore
     @Test
     public void testUnsubscribe() throws InterruptedException {
         final String testParentId = "testUnsubscribeId";
diff --git a/packages/MediaComponents/test/src/android/media/MediaController2Test.java b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
index 69769bf..0efb84a 100644
--- a/packages/MediaComponents/test/src/android/media/MediaController2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
@@ -19,7 +19,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
-import android.media.MediaPlayerBase.EventCallback;
+import android.media.MediaPlayerBase.PlayerEventCallback;
 import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
@@ -76,7 +76,8 @@
         mIntent = PendingIntent.getActivity(mContext, 0, sessionActivity, 0);
 
         mPlayer = new MockPlayer(1);
-        mSession = new MediaSession2.Builder(mContext, mPlayer)
+        mSession = new MediaSession2.Builder(mContext)
+                .setPlayer(mPlayer)
                 .setSessionCallback(sHandlerExecutor, new SessionCallback(mContext) {})
                 .setSessionActivity(mIntent)
                 .setId(TAG).build();
@@ -116,6 +117,7 @@
         assertTrue(mPlayer.mPauseCalled);
     }
 
+    @Ignore
     @Test
     public void testSkipToPrevious() throws InterruptedException {
         mController.skipToPrevious();
@@ -138,6 +140,7 @@
         assertTrue(mPlayer.mSkipToNextCalled);
     }
 
+    @Ignore
     @Test
     public void testStop() throws InterruptedException {
         mController.stop();
@@ -160,6 +163,7 @@
         assertTrue(mPlayer.mPrepareCalled);
     }
 
+    @Ignore
     @Test
     public void testFastForward() throws InterruptedException {
         mController.fastForward();
@@ -171,6 +175,7 @@
         assertTrue(mPlayer.mFastForwardCalled);
     }
 
+    @Ignore
     @Test
     public void testRewind() throws InterruptedException {
         mController.rewind();
@@ -219,6 +224,7 @@
         assertEquals(Process.myUid(), sessionActivity.getCreatorUid());
     }
 
+    @Ignore
     @Test
     public void testGetSetPlaylistParams() throws Exception {
         final PlaylistParams params = new PlaylistParams(mContext,
@@ -251,7 +257,7 @@
         TestVolumeProvider volumeProvider =
                 new TestVolumeProvider(mContext, volumeControlType, maxVolume, currentVolume);
 
-        mSession.setPlayer(new MockPlayer(0), volumeProvider);
+        mSession.updatePlayer(new MockPlayer(0), null, volumeProvider);
         final MediaController2 controller = createController(mSession.getToken(), true, null);
 
         final int targetVolume = 50;
@@ -269,7 +275,7 @@
         TestVolumeProvider volumeProvider =
                 new TestVolumeProvider(mContext, volumeControlType, maxVolume, currentVolume);
 
-        mSession.setPlayer(new MockPlayer(0), volumeProvider);
+        mSession.updatePlayer(new MockPlayer(0), null, volumeProvider);
         final MediaController2 controller = createController(mSession.getToken(), true, null);
 
         final int direction = AudioManager.ADJUST_RAISE;
@@ -285,6 +291,7 @@
     }
 
     // This also tests getPlaybackState().
+    @Ignore
     @Test
     public void testControllerCallback_onPlaybackStateChanged() throws InterruptedException {
         final CountDownLatch latch = new CountDownLatch(1);
@@ -326,7 +333,7 @@
             }
         };
         mSession.close();
-        mSession = new MediaSession2.Builder(mContext, mPlayer)
+        mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
                 .setSessionCallback(sHandlerExecutor, callback).setId(TAG).build();
         final MediaController2 controller = createController(mSession.getToken());
         controller.sendCustomCommand(testCommand, testArgs, null);
@@ -351,7 +358,7 @@
         };
         sHandler.postAndSync(() -> {
             mSession.close();
-            mSession = new MediaSession2.Builder(mContext, mPlayer)
+            mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
                     .setSessionCallback(sHandlerExecutor, sessionCallback).build();
         });
         MediaController2 controller =
@@ -390,7 +397,8 @@
                 latch.countDown();
             }
         };
-        try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
+        try (MediaSession2 session = new MediaSession2.Builder(mContext)
+                .setPlayer(mPlayer)
                 .setSessionCallback(sHandlerExecutor, callback)
                 .setId("testPlayFromSearch").build()) {
             MediaController2 controller = createController(session.getToken());
@@ -414,7 +422,8 @@
                 latch.countDown();
             }
         };
-        try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
+        try (MediaSession2 session = new MediaSession2.Builder(mContext)
+                .setPlayer(mPlayer)
                 .setSessionCallback(sHandlerExecutor, callback)
                 .setId("testPlayFromUri").build()) {
             MediaController2 controller = createController(session.getToken());
@@ -438,7 +447,8 @@
                 latch.countDown();
             }
         };
-        try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
+        try (MediaSession2 session = new MediaSession2.Builder(mContext)
+                .setPlayer(mPlayer)
                 .setSessionCallback(sHandlerExecutor, callback)
                 .setId("testPlayFromMediaId").build()) {
             MediaController2 controller = createController(session.getToken());
@@ -464,7 +474,8 @@
                 latch.countDown();
             }
         };
-        try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
+        try (MediaSession2 session = new MediaSession2.Builder(mContext)
+                .setPlayer(mPlayer)
                 .setSessionCallback(sHandlerExecutor, callback)
                 .setId("testPrepareFromSearch").build()) {
             MediaController2 controller = createController(session.getToken());
@@ -488,7 +499,8 @@
                 latch.countDown();
             }
         };
-        try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
+        try (MediaSession2 session = new MediaSession2.Builder(mContext)
+                .setPlayer(mPlayer)
                 .setSessionCallback(sHandlerExecutor, callback)
                 .setId("testPrepareFromUri").build()) {
             MediaController2 controller = createController(session.getToken());
@@ -512,7 +524,8 @@
                 latch.countDown();
             }
         };
-        try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
+        try (MediaSession2 session = new MediaSession2.Builder(mContext)
+                .setPlayer(mPlayer)
                 .setSessionCallback(sHandlerExecutor, callback)
                 .setId("testPrepareFromMediaId").build()) {
             MediaController2 controller = createController(session.getToken());
@@ -540,7 +553,8 @@
             }
         };
 
-        try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
+        try (MediaSession2 session = new MediaSession2.Builder(mContext)
+                .setPlayer(mPlayer)
                 .setSessionCallback(sHandlerExecutor, callback)
                 .setId("testSetRating").build()) {
             MediaController2 controller = createController(session.getToken());
@@ -582,7 +596,8 @@
         try {
             final MockPlayer player = new MockPlayer(0);
             sessionHandler.postAndSync(() -> {
-                mSession = new MediaSession2.Builder(mContext, mPlayer)
+                mSession = new MediaSession2.Builder(mContext)
+                        .setPlayer(mPlayer)
                         .setSessionCallback(sHandlerExecutor, new SessionCallback(mContext) {})
                         .setId("testDeadlock").build();
             });
@@ -649,6 +664,7 @@
         testConnectToService(MockMediaSessionService2.ID);
     }
 
+    @Ignore
     @Test
     public void testConnectToService_libraryService() throws InterruptedException {
         testConnectToService(MockMediaLibraryService2.ID);
@@ -696,6 +712,7 @@
         testControllerAfterSessionIsGone(mSession.getToken().getId());
     }
 
+    @Ignore
     @Test
     public void testControllerAfterSessionIsGone_sessionService() throws InterruptedException {
         connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
@@ -772,7 +789,8 @@
         sHandler.postAndSync(() -> {
             // Recreated session has different session stub, so previously created controller
             // shouldn't be available.
-            mSession = new MediaSession2.Builder(mContext, mPlayer)
+            mSession = new MediaSession2.Builder(mContext)
+                    .setPlayer(mPlayer)
                     .setSessionCallback(sHandlerExecutor, new SessionCallback(mContext) {})
                     .setId(id).build();
         });
@@ -780,14 +798,18 @@
     }
 
     private void testNoInteraction() throws InterruptedException {
+        // TODO: Uncomment
+        /*
         final CountDownLatch latch = new CountDownLatch(1);
-        final EventCallback callback = new EventCallback() {
+        final PlayerEventCallback callback = new PlayerEventCallback() {
             @Override
             public void onPlaybackStateChanged(PlaybackState2 state) {
                 fail("Controller shouldn't be notified about change in session after the close.");
                 latch.countDown();
             }
         };
+        */
+
         // TODO(jaewan): Add equivalent tests again
         /*
         mController.registerPlayerEventCallback(playbackListener, sHandler);
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
index 441d38c..3c72e7d 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
@@ -29,7 +29,7 @@
 
 import android.content.Context;
 import android.media.MediaController2.PlaybackInfo;
-import android.media.MediaPlayerBase.EventCallback;
+import android.media.MediaPlayerBase.PlayerEventCallback;
 import android.media.MediaSession2.Builder;
 import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandButton;
@@ -48,6 +48,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -72,7 +73,7 @@
     public void setUp() throws Exception {
         super.setUp();
         mPlayer = new MockPlayer(0);
-        mSession = new MediaSession2.Builder(mContext, mPlayer)
+        mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
                 .setSessionCallback(sHandlerExecutor, new SessionCallback(mContext) {}).build();
     }
 
@@ -83,15 +84,16 @@
         mSession.close();
     }
 
+    @Ignore
     @Test
     public void testBuilder() throws Exception {
         try {
-            MediaSession2.Builder builder = new Builder(mContext, null);
+            MediaSession2.Builder builder = new Builder(mContext);
             fail("null player shouldn't be allowed");
         } catch (IllegalArgumentException e) {
             // expected. pass-through
         }
-        MediaSession2.Builder builder = new Builder(mContext, mPlayer);
+        MediaSession2.Builder builder = new Builder(mContext).setPlayer(mPlayer);
         try {
             builder.setId(null);
             fail("null id shouldn't be allowed");
@@ -101,11 +103,11 @@
     }
 
     @Test
-    public void testSetPlayer() throws Exception {
+    public void testUpdatePlayer() throws Exception {
         MockPlayer player = new MockPlayer(0);
         // Test if setPlayer doesn't crash with various situations.
-        mSession.setPlayer(mPlayer);
-        mSession.setPlayer(player);
+        mSession.updatePlayer(mPlayer, null, null);
+        mSession.updatePlayer(player, null, null);
         mSession.close();
     }
 
@@ -137,7 +139,7 @@
             }
         };
 
-        mSession.setPlayer(player);
+        mSession.updatePlayer(player, null, null);
 
         final MediaController2 controller = createController(mSession.getToken(), true, callback);
         PlaybackInfo info = controller.getPlaybackInfo();
@@ -151,7 +153,7 @@
         assertEquals(manager.getStreamMaxVolume(AudioManager.STREAM_MUSIC), info.getMaxVolume());
         assertEquals(manager.getStreamVolume(AudioManager.STREAM_MUSIC), info.getCurrentVolume());
 
-        mSession.setPlayer(player, volumeProvider);
+        mSession.updatePlayer(player, null, volumeProvider);
         assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
 
         info = controller.getPlaybackInfo();
@@ -179,6 +181,7 @@
         });
     }
 
+    @Ignore
     @Test
     public void testStop() throws Exception {
         sHandler.postAndSync(() -> {
@@ -195,6 +198,7 @@
         });
     }
 
+    @Ignore
     @Test
     public void testSkipToPrevious() throws Exception {
         sHandler.postAndSync(() -> {
@@ -203,6 +207,7 @@
         });
     }
 
+    @Ignore
     @Test
     public void testSetPlaylist() throws Exception {
         final List<MediaItem2> playlist = new ArrayList<>();
@@ -227,6 +232,7 @@
         assertMediaItemListEquals(playlist, controller.getPlaylist());
     }
 
+    @Ignore
     @Test
     public void testSetPlaylistParams() throws Exception {
         final PlaylistParams params = new PlaylistParams(mContext,
@@ -251,13 +257,16 @@
         assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
     }
 
+    @Ignore
     @Test
     public void testRegisterEventCallback() throws InterruptedException {
         final int testWhat = 1001;
         final MockPlayer player = new MockPlayer(0);
         final CountDownLatch playbackLatch = new CountDownLatch(3);
         final CountDownLatch errorLatch = new CountDownLatch(1);
-        final EventCallback callback = new EventCallback() {
+        // TODO: Uncomment or remove
+        /*
+        final PlayerEventCallback callback = new PlayerEventCallback() {
             @Override
             public void onPlaybackStateChanged(PlaybackState2 state) {
                 assertEquals(sHandler.getLooper(), Looper.myLooper());
@@ -285,11 +294,13 @@
                 errorLatch.countDown();
             }
         };
+        */
         player.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PLAYING));
         // EventCallback will be notified with the mPlayer's playback state (null)
-        mSession.registerPlayerEventCallback(sHandlerExecutor, callback);
+        // TODO: Uncomment or remove
+        //mSession.registerPlayerEventCallback(sHandlerExecutor, callback);
         // When the player is set, EventCallback will be notified about the new player's state.
-        mSession.setPlayer(player);
+        mSession.updatePlayer(player, null, null);
         // When the player is set, EventCallback will be notified about the new player's state.
         player.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PAUSED));
         assertTrue(playbackLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
@@ -302,7 +313,9 @@
         // TODO(jaewan): Add equivalent tests again
         final CountDownLatch latch = new CountDownLatch(4); // expected call + 1
         final BadPlayer player = new BadPlayer(0);
-        mSession.registerPlayerEventCallback(sHandlerExecutor, new EventCallback() {
+        // TODO: Uncomment or remove
+        /*
+        mSession.registerPlayerEventCallback(sHandlerExecutor, new PlayerEventCallback() {
             @Override
             public void onPlaybackStateChanged(PlaybackState2 state) {
                 // This will be called for every setPlayer() calls, but no more.
@@ -310,8 +323,9 @@
                 latch.countDown();
             }
         });
-        mSession.setPlayer(player);
-        mSession.setPlayer(mPlayer);
+        */
+        mSession.updatePlayer(player, null, null);
+        mSession.updatePlayer(mPlayer, null, null);
         player.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PAUSED));
         assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
     }
@@ -322,7 +336,7 @@
         }
 
         @Override
-        public void unregisterEventCallback(@NonNull EventCallback listener) {
+        public void unregisterPlayerEventCallback(@NonNull PlayerEventCallback listener) {
             // No-op. This bad player will keep push notification to the listener that is previously
             // registered by session.setPlayer().
         }
@@ -334,7 +348,7 @@
         sHandler.postAndSync(() -> {
             mSession.close();
             mPlayer = new MockPlayer(1);
-            mSession = new MediaSession2.Builder(mContext, mPlayer)
+            mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
                     .setSessionCallback(sHandlerExecutor, callback).build();
         });
         MediaController2 controller = createController(mSession.getToken());
@@ -358,7 +372,7 @@
         final MockOnConnectCallback sessionCallback = new MockOnConnectCallback();
         sHandler.postAndSync(() -> {
             mSession.close();
-            mSession = new MediaSession2.Builder(mContext, mPlayer)
+            mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
                     .setSessionCallback(sHandlerExecutor, sessionCallback).build();
         });
         MediaController2 controller =
@@ -385,7 +399,8 @@
             }
         };
 
-        try (final MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
+        try (final MediaSession2 session = new MediaSession2.Builder(mContext)
+                .setPlayer(mPlayer)
                 .setId("testSetCustomLayout")
                 .setSessionCallback(sHandlerExecutor, sessionCallback)
                 .build()) {
diff --git a/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
index 50599b6..4cdd140 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
@@ -26,6 +26,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -54,7 +55,8 @@
         // Specify TAG here so {@link MediaSession2.getInstance()} doesn't complaint about
         // per test thread differs across the {@link MediaSession2} with the same TAG.
         final MockPlayer player = new MockPlayer(1);
-        mSession = new MediaSession2.Builder(mContext, player)
+        mSession = new MediaSession2.Builder(mContext)
+                .setPlayer(player)
                 .setSessionCallback(sHandlerExecutor, new SessionCallback(mContext) { })
                 .setId(TAG)
                 .build();
@@ -70,6 +72,7 @@
     }
 
     // TODO(jaewan): Make this host-side test to see per-user behavior.
+    @Ignore
     @Test
     public void testGetMediaSession2Tokens_hasMediaController() throws InterruptedException {
         final MockPlayer player = (MockPlayer) mSession.getPlayer();
@@ -105,8 +108,8 @@
     public void testGetSessionTokens_sessionRejected() throws InterruptedException {
         sHandler.postAndSync(() -> {
             mSession.close();
-            mSession = new MediaSession2.Builder(mContext, new MockPlayer(0)).setId(TAG)
-                    .setSessionCallback(sHandlerExecutor, new SessionCallback(mContext) {
+            mSession = new MediaSession2.Builder(mContext).setPlayer(new MockPlayer(0))
+                    .setId(TAG).setSessionCallback(sHandlerExecutor, new SessionCallback(mContext) {
                         @Override
                         public MediaSession2.CommandGroup onConnect(ControllerInfo controller) {
                             // Reject all connection request.
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
index b0fbde0..20ea3d2 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
@@ -114,8 +114,8 @@
         }
         TestLibrarySessionCallback callback =
                 new TestLibrarySessionCallback(sessionCallbackProxy);
-        mSession = new MediaLibrarySession.Builder(MockMediaLibraryService2.this, player,
-                executor, callback).setId(sessionId).build();
+        mSession = new MediaLibrarySession.Builder(MockMediaLibraryService2.this, executor,
+                callback).setPlayer(player).setId(sessionId).build();
         return mSession;
     }
 
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java b/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
index b63104a..1c6534d 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
@@ -64,7 +64,8 @@
         }
         TestSessionServiceCallback callback =
                 new TestSessionServiceCallback(sessionCallbackProxy);
-        mSession = new MediaSession2.Builder(this, player)
+        mSession = new MediaSession2.Builder(this)
+                .setPlayer(player)
                 .setSessionCallback(executor, callback)
                 .setId(sessionId).build();
         return mSession;
diff --git a/packages/MediaComponents/test/src/android/media/MockPlayer.java b/packages/MediaComponents/test/src/android/media/MockPlayer.java
index 1b3da73..da4acb5 100644
--- a/packages/MediaComponents/test/src/android/media/MockPlayer.java
+++ b/packages/MediaComponents/test/src/android/media/MockPlayer.java
@@ -46,7 +46,7 @@
     public boolean mSetPlaylistCalled;
     public boolean mSetPlaylistParamsCalled;
 
-    public ArrayMap<EventCallback, Executor> mCallbacks = new ArrayMap<>();
+    public ArrayMap<PlayerEventCallback, Executor> mCallbacks = new ArrayMap<>();
     public List<MediaItem2> mPlaylist;
     public PlaylistParams mPlaylistParams;
 
@@ -78,6 +78,8 @@
         }
     }
 
+    // TODO: Uncomment or remove
+    /*
     @Override
     public void stop() {
         mStopCalled = true;
@@ -85,7 +87,10 @@
             mCountDownLatch.countDown();
         }
     }
+    */
 
+    // TODO: Uncomment or remove
+    /*
     @Override
     public void skipToPrevious() {
         mSkipToPreviousCalled = true;
@@ -93,6 +98,7 @@
             mCountDownLatch.countDown();
         }
     }
+    */
 
     @Override
     public void skipToNext() {
@@ -110,6 +116,8 @@
         }
     }
 
+    // TODO: Uncomment or remove
+    /*
     @Override
     public void fastForward() {
         mFastForwardCalled = true;
@@ -117,7 +125,10 @@
             mCountDownLatch.countDown();
         }
     }
+    */
 
+    // TODO: Uncomment or remove
+    /*
     @Override
     public void rewind() {
         mRewindCalled = true;
@@ -125,6 +136,7 @@
             mCountDownLatch.countDown();
         }
     }
+    */
 
     @Override
     public void seekTo(long pos) {
@@ -135,6 +147,8 @@
         }
     }
 
+    // TODO: Uncomment or remove
+    /*
     @Override
     public void setCurrentPlaylistItem(MediaItem2 item) {
         mSetCurrentPlaylistItemCalled = true;
@@ -143,12 +157,16 @@
             mCountDownLatch.countDown();
         }
     }
+    */
 
+    // TODO: Uncomment or remove
+    /*
     @Nullable
     @Override
     public PlaybackState2 getPlaybackState() {
         return mLastPlaybackState;
     }
+    */
 
     @Override
     public int getPlayerState() {
@@ -156,51 +174,71 @@
     }
 
     @Override
-    public void registerEventCallback(@NonNull Executor executor,
-            @NonNull EventCallback callback) {
+    public int getBufferingState() {
+        // TODO: implement this
+        return -1;
+    }
+
+    @Override
+    public void registerPlayerEventCallback(@NonNull Executor executor,
+            @NonNull PlayerEventCallback callback) {
         mCallbacks.put(callback, executor);
     }
 
     @Override
-    public void unregisterEventCallback(@NonNull EventCallback callback) {
+    public void unregisterPlayerEventCallback(@NonNull PlayerEventCallback callback) {
         mCallbacks.remove(callback);
     }
 
     public void notifyPlaybackState(final PlaybackState2 state) {
         mLastPlaybackState = state;
         for (int i = 0; i < mCallbacks.size(); i++) {
-            final EventCallback callback = mCallbacks.keyAt(i);
+            final PlayerEventCallback callback = mCallbacks.keyAt(i);
             final Executor executor = mCallbacks.valueAt(i);
-            executor.execute(() -> callback.onPlaybackStateChanged(state));
+            // TODO: Uncomment or remove
+            //executor.execute(() -> callback.onPlaybackStateChanged(state));
         }
     }
 
     public void notifyError(int what) {
         for (int i = 0; i < mCallbacks.size(); i++) {
-            final EventCallback callback = mCallbacks.keyAt(i);
+            final PlayerEventCallback callback = mCallbacks.keyAt(i);
             final Executor executor = mCallbacks.valueAt(i);
-            executor.execute(() -> callback.onError(null, what, 0));
+            // TODO: Uncomment or remove
+            //executor.execute(() -> callback.onError(null, what, 0));
         }
     }
 
+    // TODO: Uncomment or remove
+    /*
     @Override
     public void setPlaylistParams(PlaylistParams params) {
         mSetPlaylistParamsCalled = true;
         mPlaylistParams = params;
     }
+    */
 
+    // TODO: Uncomment or remove
+    /*
     @Override
     public void addPlaylistItem(int index, MediaItem2 item) {
     }
+    */
 
+    // TODO: Uncomment or remove
+    /*
     @Override
     public void removePlaylistItem(MediaItem2 item) {
     }
+    */
 
+    // TODO: Uncomment or remove
+    /*
     @Override
     public PlaylistParams getPlaylistParams() {
         return mPlaylistParams;
     }
+    */
 
     @Override
     public void setAudioAttributes(AudioAttributes attributes) {
@@ -212,14 +250,62 @@
         return mAudioAttributes;
     }
 
+    // TODO: Uncomment or remove
+    /*
     @Override
     public void setPlaylist(List<MediaItem2> playlist) {
         mSetPlaylistCalled = true;
         mPlaylist = playlist;
     }
+    */
 
+    // TODO: Uncomment or remove
+    /*
     @Override
     public List<MediaItem2> getPlaylist() {
         return mPlaylist;
     }
+    */
+
+    @Override
+    public void setDataSource(@NonNull DataSourceDesc dsd) {
+        // TODO: Implement this
+    }
+
+    @Override
+    public void setNextDataSource(@NonNull DataSourceDesc dsd) {
+        // TODO: Implement this
+    }
+
+    @Override
+    public void setNextDataSources(@NonNull List<DataSourceDesc> dsds) {
+        // TODO: Implement this
+    }
+
+    @Override
+    public DataSourceDesc getCurrentDataSource() {
+        // TODO: Implement this
+        return null;
+    }
+
+    @Override
+    public void loopCurrent(boolean loop) {
+        // TODO: implement this
+    }
+
+    @Override
+    public void setPlaybackSpeed(float speed) {
+        // TODO: implement this
+    }
+
+    @Override
+    public void setPlayerVolume(float volume) {
+        // TODO: implement this
+    }
+
+    @Override
+    public float getPlayerVolume() {
+        // TODO: implement this
+        return -1;
+    }
 }
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 229e08e..8033382 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -3051,6 +3051,7 @@
 
         // check recording permission for visualizer
         if ((memcmp(&desc.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0) &&
+            // TODO: Do we need to start/stop op - i.e. is there recording being performed?
             !recordingAllowed(opPackageName, pid, IPCThreadState::self()->getCallingUid())) {
             lStatus = PERMISSION_DENIED;
             goto Exit;
diff --git a/services/audioflinger/ServiceUtilities.cpp b/services/audioflinger/ServiceUtilities.cpp
index f08698e..84b43ce 100644
--- a/services/audioflinger/ServiceUtilities.cpp
+++ b/services/audioflinger/ServiceUtilities.cpp
@@ -30,6 +30,8 @@
 
 namespace android {
 
+static const String16 sAndroidPermissionRecordAudio("android.permission.RECORD_AUDIO");
+
 // Not valid until initialized by AudioFlinger constructor.  It would have to be
 // re-initialized if the process containing AudioFlinger service forks (which it doesn't).
 // This is often used to validate binder interface calls within audioserver
@@ -49,25 +51,72 @@
 }
 
 bool recordingAllowed(const String16& opPackageName, pid_t pid, uid_t uid) {
-    // we're always OK.
-    if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
+    return checkRecordingInternal(opPackageName, pid, uid, false);
+}
 
-    static const String16 sRecordAudio("android.permission.RECORD_AUDIO");
+bool startRecording(const String16& opPackageName, pid_t pid, uid_t uid) {
+     return checkRecordingInternal(opPackageName, pid, uid, true);
+}
 
-    // We specify a pid and uid here as mediaserver (aka MediaRecorder or StageFrightRecorder)
-    // may open a record track on behalf of a client.  Note that pid may be a tid.
-    // IMPORTANT: Don't use PermissionCache - a runtime permission and may change.
-    const bool ok = checkPermission(sRecordAudio, pid, uid);
-    if (!ok) {
-        ALOGE("Request requires android.permission.RECORD_AUDIO");
-        return false;
+bool checkRecordingInternal(const String16& opPackageName, pid_t pid, uid_t uid, bool start) {
+     // we're always OK.
+     if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
+
+     // To permit command-line native tests
+     if (uid == AID_ROOT) return true;
+
+     // We specify a pid and uid here as mediaserver (aka MediaRecorder or StageFrightRecorder)
+     // may open a record track on behalf of a client.  Note that pid may be a tid.
+     // IMPORTANT: DON'T USE PermissionCache - RUNTIME PERMISSIONS CHANGE.
+     PermissionController permissionController;
+     const bool ok = permissionController.checkPermission(sAndroidPermissionRecordAudio, pid, uid);
+     if (!ok) {
+         ALOGE("Request requires %s", String8(sAndroidPermissionRecordAudio).c_str());
+         return false;
+     }
+
+     const String16 resolvedOpPackageName = resolveCallingPackage(
+             permissionController, opPackageName, uid);
+     if (opPackageName.size() <= 0) {
+         return false;
+     }
+
+     AppOpsManager appOps;
+     const int32_t op = appOps.permissionToOpCode(sAndroidPermissionRecordAudio);
+     if (start) {
+         if (appOps.startOpNoThrow(op, uid, resolvedOpPackageName, /*startIfModeDefault*/ false)
+                 != AppOpsManager::MODE_ALLOWED) {
+             ALOGE("Request denied by app op: %d", op);
+             return false;
+         }
+     } else {
+         if (appOps.noteOp(op, uid, resolvedOpPackageName) != AppOpsManager::MODE_ALLOWED) {
+             ALOGE("Request denied by app op: %d", op);
+             return false;
+         }
+     }
+
+     return true;
+}
+
+void finishRecording(const String16& opPackageName, uid_t uid) {
+    PermissionController permissionController;
+    const String16 resolvedOpPackageName = resolveCallingPackage(
+            permissionController, opPackageName, uid);
+    if (opPackageName.size() <= 0) {
+        return;
     }
 
-    // To permit command-line native tests
-    if (uid == AID_ROOT) return true;
+    AppOpsManager appOps;
+    const int32_t op = appOps.permissionToOpCode(sAndroidPermissionRecordAudio);
+    appOps.finishOp(op, uid, resolvedOpPackageName);
+}
 
-    String16 checkedOpPackageName = opPackageName;
-
+const String16 resolveCallingPackage(PermissionController& permissionController,
+        const String16& opPackageName, uid_t uid) {
+    if (opPackageName.size() > 0) {
+        return opPackageName;
+    }
     // In some cases the calling code has no access to the package it runs under.
     // For example, code using the wilhelm framework's OpenSL-ES APIs. In this
     // case we will get the packages for the calling UID and pick the first one
@@ -75,40 +124,19 @@
     // as for legacy apps we will toggle the app op for all packages in the UID.
     // The caveat is that the operation may be attributed to the wrong package and
     // stats based on app ops may be slightly off.
-    if (checkedOpPackageName.size() <= 0) {
-        sp<IServiceManager> sm = defaultServiceManager();
-        sp<IBinder> binder = sm->getService(String16("permission"));
-        if (binder == 0) {
-            ALOGE("Cannot get permission service");
-            return false;
-        }
-
-        sp<IPermissionController> permCtrl = interface_cast<IPermissionController>(binder);
-        Vector<String16> packages;
-
-        permCtrl->getPackagesForUid(uid, packages);
-
-        if (packages.isEmpty()) {
-            ALOGE("No packages for calling UID");
-            return false;
-        }
-        checkedOpPackageName = packages[0];
+    Vector<String16> packages;
+    permissionController.getPackagesForUid(uid, packages);
+    if (packages.isEmpty()) {
+        ALOGE("No packages for uid %d", uid);
+        return opPackageName;
     }
-
-    AppOpsManager appOps;
-    if (appOps.noteOp(AppOpsManager::OP_RECORD_AUDIO, uid, checkedOpPackageName)
-            != AppOpsManager::MODE_ALLOWED) {
-        ALOGE("Request denied by app op OP_RECORD_AUDIO");
-        return false;
-    }
-
-    return true;
+    return packages[0];
 }
 
 bool captureAudioOutputAllowed(pid_t pid, uid_t uid) {
     if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
     static const String16 sCaptureAudioOutput("android.permission.CAPTURE_AUDIO_OUTPUT");
-    bool ok = checkPermission(sCaptureAudioOutput, pid, uid);
+    bool ok = PermissionCache::checkPermission(sCaptureAudioOutput, pid, uid);
     if (!ok) ALOGE("Request requires android.permission.CAPTURE_AUDIO_OUTPUT");
     return ok;
 }
@@ -155,7 +183,7 @@
 
 bool modifyPhoneStateAllowed(pid_t pid, uid_t uid) {
     static const String16 sModifyPhoneState("android.permission.MODIFY_PHONE_STATE");
-    bool ok = checkPermission(sModifyPhoneState, pid, uid);
+    bool ok = PermissionCache::checkPermission(sModifyPhoneState, pid, uid);
     if (!ok) ALOGE("Request requires android.permission.MODIFY_PHONE_STATE");
     return ok;
 }
diff --git a/services/audioflinger/ServiceUtilities.h b/services/audioflinger/ServiceUtilities.h
index 83533dd..8f96282 100644
--- a/services/audioflinger/ServiceUtilities.h
+++ b/services/audioflinger/ServiceUtilities.h
@@ -16,11 +16,19 @@
 
 #include <unistd.h>
 
+#include <binder/PermissionController.h>
+
 namespace android {
 
 extern pid_t getpid_cached;
 bool isTrustedCallingUid(uid_t uid);
 bool recordingAllowed(const String16& opPackageName, pid_t pid, uid_t uid);
+bool startRecording(const String16& opPackageName, pid_t pid, uid_t uid);
+void finishRecording(const String16& opPackageName, uid_t uid);
+// DON'T USE THIS INTERNAL METHOD
+bool checkRecordingInternal(const String16& opPackageName, pid_t pid, uid_t uid, bool start);
+const String16 resolveCallingPackage(PermissionController& permissionController,
+        const String16& opPackageName, uid_t uid);
 bool captureAudioOutputAllowed(pid_t pid, uid_t uid);
 bool captureHotwordAllowed(pid_t pid, uid_t uid);
 bool settingsAllowed();
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 3134323..1301998 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1837,10 +1837,6 @@
 void AudioFlinger::PlaybackThread::preExit()
 {
     ALOGV("  preExit()");
-    // FIXME this is using hard-coded strings but in the future, this functionality will be
-    //       converted to use audio HAL extensions required to support tunneling
-    status_t result = mOutput->stream->setParameters(String8("exiting=1"));
-    ALOGE_IF(result != OK, "Error when setting parameters on exit: %d", result);
 }
 
 // PlaybackThread::createTrack_l() must be called with AudioFlinger::mLock held
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 306de3f..d04b21e 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -412,7 +412,7 @@
     }
 
     // check calling permissions
-    if (!recordingAllowed(client->opPackageName, client->pid, client->uid)) {
+    if (!startRecording(client->opPackageName, client->pid, client->uid)) {
         ALOGE("%s permission denied: recording not allowed for uid %d pid %d",
                 __func__, client->uid, client->pid);
         return PERMISSION_DENIED;
@@ -457,6 +457,9 @@
     }
     sp<AudioRecordClient> client = mAudioRecordClients.valueAt(index);
 
+    // finish the recording app op
+    finishRecording(client->opPackageName, client->uid);
+
     return mAudioPolicyManager->stopInput(client->input, client->session);
 }
 
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index dcc7ad2..ad63899 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -2218,8 +2218,8 @@
 
     mAppOpsManager.startWatchingMode(AppOpsManager::OP_CAMERA,
             mClientPackageName, mOpsCallback);
-    res = mAppOpsManager.startOp(AppOpsManager::OP_CAMERA,
-            mClientUid, mClientPackageName);
+    res = mAppOpsManager.startOpNoThrow(AppOpsManager::OP_CAMERA,
+            mClientUid, mClientPackageName, /*startIfModeDefault*/ false);
 
     if (res == AppOpsManager::MODE_ERRORED) {
         ALOGI("Camera %s: Access for \"%s\" has been revoked",
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index efe3ca1..9ab2d88 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -1083,9 +1083,10 @@
     }
 
     // FIXME: remove this override since the default format should be
-    //       IMPLEMENTATION_DEFINED. b/9487482
-    if (format >= HAL_PIXEL_FORMAT_RGBA_8888 &&
-        format <= HAL_PIXEL_FORMAT_BGRA_8888) {
+    //       IMPLEMENTATION_DEFINED. b/9487482 & b/35317944
+    if ((format >= HAL_PIXEL_FORMAT_RGBA_8888 && format <= HAL_PIXEL_FORMAT_BGRA_8888) &&
+            ((consumerUsage & GRALLOC_USAGE_HW_MASK) &&
+             ((consumerUsage & GRALLOC_USAGE_SW_READ_MASK) == 0))) {
         ALOGW("%s: Camera %s: Overriding format %#x to IMPLEMENTATION_DEFINED",
                 __FUNCTION__, mCameraIdStr.string(), format);
         format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;