Merge "aaudio: fix sessionId conversion"
diff --git a/camera/CaptureResult.cpp b/camera/CaptureResult.cpp
index e6c0d00..928a6bc 100644
--- a/camera/CaptureResult.cpp
+++ b/camera/CaptureResult.cpp
@@ -60,6 +60,39 @@
     return OK;
 }
 
+status_t PhysicalCaptureResultInfo::readFromParcel(const android::Parcel* parcel) {
+    status_t res;
+
+    mPhysicalCameraId.remove(mPhysicalCameraId.size());
+    mPhysicalCameraMetadata.clear();
+
+    if ((res = parcel->readString16(&mPhysicalCameraId)) != OK) {
+        ALOGE("%s: Failed to read camera id: %d", __FUNCTION__, res);
+        return res;
+    }
+
+    if ((res = mPhysicalCameraMetadata.readFromParcel(parcel)) != OK) {
+        ALOGE("%s: Failed to read metadata from parcel: %d", __FUNCTION__, res);
+        return res;
+    }
+    return OK;
+}
+
+status_t PhysicalCaptureResultInfo::writeToParcel(android::Parcel* parcel) const {
+    status_t res;
+    if ((res = parcel->writeString16(mPhysicalCameraId)) != OK) {
+        ALOGE("%s: Failed to write physical camera ID to parcel: %d",
+                __FUNCTION__, res);
+        return res;
+    }
+    if ((res = mPhysicalCameraMetadata.writeToParcel(parcel)) != OK) {
+        ALOGE("%s: Failed to write physical camera metadata to parcel: %d",
+                __FUNCTION__, res);
+        return res;
+    }
+    return OK;
+}
+
 CaptureResult::CaptureResult() :
         mMetadata(), mResultExtras() {
 }
@@ -67,6 +100,7 @@
 CaptureResult::CaptureResult(const CaptureResult &otherResult) {
     mResultExtras = otherResult.mResultExtras;
     mMetadata = otherResult.mMetadata;
+    mPhysicalMetadatas = otherResult.mPhysicalMetadatas;
 }
 
 status_t CaptureResult::readFromParcel(android::Parcel *parcel) {
@@ -79,6 +113,7 @@
     }
 
     mMetadata.clear();
+    mPhysicalMetadatas.clear();
 
     status_t res = OK;
     res = mMetadata.readFromParcel(parcel);
@@ -89,6 +124,34 @@
     }
     ALOGV("%s: Read metadata from parcel", __FUNCTION__);
 
+    int32_t physicalMetadataCount;
+    if ((res = parcel->readInt32(&physicalMetadataCount)) != OK) {
+        ALOGE("%s: Failed to read the physical metadata count from parcel: %d", __FUNCTION__, res);
+        return res;
+    }
+    if (physicalMetadataCount < 0) {
+        ALOGE("%s: Invalid physical metadata count from parcel: %d",
+                __FUNCTION__, physicalMetadataCount);
+        return BAD_VALUE;
+    }
+
+    for (int32_t i = 0; i < physicalMetadataCount; i++) {
+        String16 cameraId;
+        if ((res = parcel->readString16(&cameraId)) != OK) {
+            ALOGE("%s: Failed to read camera id: %d", __FUNCTION__, res);
+            return res;
+        }
+
+        CameraMetadata physicalMetadata;
+        if ((res = physicalMetadata.readFromParcel(parcel)) != OK) {
+            ALOGE("%s: Failed to read metadata from parcel: %d", __FUNCTION__, res);
+            return res;
+        }
+
+        mPhysicalMetadatas.emplace(mPhysicalMetadatas.end(), cameraId, physicalMetadata);
+    }
+    ALOGV("%s: Read physical metadata from parcel", __FUNCTION__);
+
     res = mResultExtras.readFromParcel(parcel);
     if (res != OK) {
         ALOGE("%s: Failed to read result extras from parcel.",
@@ -118,6 +181,27 @@
     }
     ALOGV("%s: Wrote metadata to parcel", __FUNCTION__);
 
+    int32_t physicalMetadataCount = static_cast<int32_t>(mPhysicalMetadatas.size());
+    res = parcel->writeInt32(physicalMetadataCount);
+    if (res != OK) {
+        ALOGE("%s: Failed to write physical metadata count to parcel: %d",
+                __FUNCTION__, res);
+        return BAD_VALUE;
+    }
+    for (const auto& physicalMetadata : mPhysicalMetadatas) {
+        if ((res = parcel->writeString16(physicalMetadata.mPhysicalCameraId)) != OK) {
+            ALOGE("%s: Failed to write physical camera ID to parcel: %d",
+                    __FUNCTION__, res);
+            return res;
+        }
+        if ((res = physicalMetadata.mPhysicalCameraMetadata.writeToParcel(parcel)) != OK) {
+            ALOGE("%s: Failed to write physical camera metadata to parcel: %d",
+                    __FUNCTION__, res);
+            return res;
+        }
+    }
+    ALOGV("%s: Wrote physical camera metadata to parcel", __FUNCTION__);
+
     res = mResultExtras.writeToParcel(parcel);
     if (res != OK) {
         ALOGE("%s: Failed to write result extras to parcel", __FUNCTION__);
diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl
index 4db7f85..58b19a3 100644
--- a/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl
+++ b/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl
@@ -18,6 +18,7 @@
 
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.impl.CaptureResultExtras;
+import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
 
 /** @hide */
 interface ICameraDeviceCallbacks
@@ -36,7 +37,8 @@
     oneway void onDeviceIdle();
     oneway void onCaptureStarted(in CaptureResultExtras resultExtras, long timestamp);
     oneway void onResultReceived(in CameraMetadataNative result,
-                                 in CaptureResultExtras resultExtras);
+                                 in CaptureResultExtras resultExtras,
+                                 in PhysicalCaptureResultInfo[] physicalCaptureResultInfos);
     oneway void onPrepared(int streamId);
 
     /**
diff --git a/camera/aidl/android/hardware/camera2/impl/PhysicalCaptureResultInfo.aidl b/camera/aidl/android/hardware/camera2/impl/PhysicalCaptureResultInfo.aidl
new file mode 100644
index 0000000..78d9b7b
--- /dev/null
+++ b/camera/aidl/android/hardware/camera2/impl/PhysicalCaptureResultInfo.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+package android.hardware.camera2.impl;
+
+/** @hide */
+parcelable PhysicalCaptureResultInfo cpp_header "camera/CaptureResult.h";
diff --git a/camera/include/camera/CaptureResult.h b/camera/include/camera/CaptureResult.h
index 917d953..56fa178 100644
--- a/camera/include/camera/CaptureResult.h
+++ b/camera/include/camera/CaptureResult.h
@@ -91,14 +91,36 @@
     virtual status_t                readFromParcel(const android::Parcel* parcel) override;
     virtual status_t                writeToParcel(android::Parcel* parcel) const override;
 };
+
+struct PhysicalCaptureResultInfo : public android::Parcelable {
+
+    PhysicalCaptureResultInfo()
+        : mPhysicalCameraId(),
+          mPhysicalCameraMetadata() {
+    }
+    PhysicalCaptureResultInfo(const String16& cameraId,
+            const CameraMetadata& cameraMetadata)
+            : mPhysicalCameraId(cameraId),
+              mPhysicalCameraMetadata(cameraMetadata) {
+    }
+
+    String16  mPhysicalCameraId;
+    CameraMetadata mPhysicalCameraMetadata;
+
+    virtual status_t                readFromParcel(const android::Parcel* parcel) override;
+    virtual status_t                writeToParcel(android::Parcel* parcel) const override;
+};
+
 } // namespace impl
 } // namespace camera2
 } // namespace hardware
 
 using hardware::camera2::impl::CaptureResultExtras;
+using hardware::camera2::impl::PhysicalCaptureResultInfo;
 
 struct CaptureResult : public virtual LightRefBase<CaptureResult> {
     CameraMetadata          mMetadata;
+    std::vector<PhysicalCaptureResultInfo> mPhysicalMetadatas;
     CaptureResultExtras     mResultExtras;
 
     CaptureResult();
diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp
index ef1c61f..907debc 100644
--- a/camera/ndk/impl/ACameraDevice.cpp
+++ b/camera/ndk/impl/ACameraDevice.cpp
@@ -1406,7 +1406,9 @@
 binder::Status
 CameraDevice::ServiceCallback::onResultReceived(
         const CameraMetadata& metadata,
-        const CaptureResultExtras& resultExtras) {
+        const CaptureResultExtras& resultExtras,
+        const std::vector<PhysicalCaptureResultInfo>& physicalResultInfos) {
+    (void) physicalResultInfos;
     binder::Status ret = binder::Status::ok();
 
     sp<CameraDevice> dev = mDevice.promote();
diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h
index 1db3dfb..1369148 100644
--- a/camera/ndk/impl/ACameraDevice.h
+++ b/camera/ndk/impl/ACameraDevice.h
@@ -74,7 +74,8 @@
         binder::Status onCaptureStarted(const CaptureResultExtras& resultExtras,
                               int64_t timestamp) override;
         binder::Status onResultReceived(const CameraMetadata& metadata,
-                              const CaptureResultExtras& resultExtras) override;
+                              const CaptureResultExtras& resultExtras,
+                              const std::vector<PhysicalCaptureResultInfo>& physicalResultInfos) override;
         binder::Status onPrepared(int streamId) override;
         binder::Status onRequestQueueEmpty() override;
         binder::Status onRepeatingRequestError(int64_t lastFrameNumber,
diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp
index 24c0c51..1de7013 100644
--- a/camera/tests/CameraBinderTests.cpp
+++ b/camera/tests/CameraBinderTests.cpp
@@ -198,9 +198,11 @@
 
 
     virtual binder::Status onResultReceived(const CameraMetadata& metadata,
-            const CaptureResultExtras& resultExtras) {
+            const CaptureResultExtras& resultExtras,
+            const std::vector<PhysicalCaptureResultInfo>& physicalResultInfos) {
         (void) metadata;
         (void) resultExtras;
+        (void) physicalResultInfos;
         Mutex::Autolock l(mLock);
         mLastStatus = SENT_RESULT;
         mStatusesHit.push_back(mLastStatus);
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index f9e4639..46bd8f0 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -73,6 +73,7 @@
 static enum {
     FORMAT_MP4, FORMAT_H264, FORMAT_FRAMES, FORMAT_RAW_FRAMES
 } gOutputFormat = FORMAT_MP4;           // data format for output
+static AString gCodecName = "";         // codec name override
 static bool gSizeSpecified = false;     // was size explicitly requested?
 static bool gWantInfoScreen = false;    // do we want initial info screen?
 static bool gWantFrameTime = false;     // do we want times on each frame?
@@ -154,6 +155,7 @@
     if (gVerbose) {
         printf("Configuring recorder for %dx%d %s at %.2fMbps\n",
                 gVideoWidth, gVideoHeight, kMimeTypeAvc, gBitRate / 1000000.0);
+        fflush(stdout);
     }
 
     sp<AMessage> format = new AMessage;
@@ -169,11 +171,21 @@
     looper->setName("screenrecord_looper");
     looper->start();
     ALOGV("Creating codec");
-    sp<MediaCodec> codec = MediaCodec::CreateByType(looper, kMimeTypeAvc, true);
-    if (codec == NULL) {
-        fprintf(stderr, "ERROR: unable to create %s codec instance\n",
-                kMimeTypeAvc);
-        return UNKNOWN_ERROR;
+    sp<MediaCodec> codec;
+    if (gCodecName.empty()) {
+        codec = MediaCodec::CreateByType(looper, kMimeTypeAvc, true);
+        if (codec == NULL) {
+            fprintf(stderr, "ERROR: unable to create %s codec instance\n",
+                    kMimeTypeAvc);
+            return UNKNOWN_ERROR;
+        }
+    } else {
+        codec = MediaCodec::CreateByComponentName(looper, gCodecName);
+        if (codec == NULL) {
+            fprintf(stderr, "ERROR: unable to create %s codec instance\n",
+                    gCodecName.c_str());
+            return UNKNOWN_ERROR;
+        }
     }
 
     err = codec->configure(format, NULL, NULL,
@@ -275,9 +287,11 @@
         if (gRotate) {
             printf("Rotated content area is %ux%u at offset x=%d y=%d\n",
                     outHeight, outWidth, offY, offX);
+            fflush(stdout);
         } else {
             printf("Content area is %ux%u at offset x=%d y=%d\n",
                     outWidth, outHeight, offX, offY);
+            fflush(stdout);
         }
     }
 
@@ -346,6 +360,7 @@
         if (systemTime(CLOCK_MONOTONIC) > endWhenNsec) {
             if (gVerbose) {
                 printf("Time limit reached\n");
+                fflush(stdout);
             }
             break;
         }
@@ -483,6 +498,7 @@
         printf("Encoder stopping; recorded %u frames in %" PRId64 " seconds\n",
                 debugNumFrames, nanoseconds_to_seconds(
                         systemTime(CLOCK_MONOTONIC) - startWhenNsec));
+        fflush(stdout);
     }
     return NO_ERROR;
 }
@@ -556,6 +572,7 @@
         printf("Main display is %dx%d @%.2ffps (orientation=%u)\n",
                 mainDpyInfo.w, mainDpyInfo.h, mainDpyInfo.fps,
                 mainDpyInfo.orientation);
+        fflush(stdout);
     }
 
     bool rotated = isDeviceRotated(mainDpyInfo.orientation);
@@ -623,6 +640,7 @@
         }
         if (gVerbose) {
             printf("Bugreport overlay created\n");
+            fflush(stdout);
         }
     } else {
         // Use the encoder's input surface as the virtual display surface.
@@ -715,6 +733,7 @@
 
         if (gVerbose) {
             printf("Stopping encoder and muxer\n");
+            fflush(stdout);
         }
     }
 
@@ -761,6 +780,7 @@
             printf(" %s", argv[i]);
         }
         putchar('\n');
+        fflush(stdout);
     }
 
     pid_t pid = fork();
@@ -898,6 +918,7 @@
         { "show-frame-time",    no_argument,        NULL, 'f' },
         { "rotate",             no_argument,        NULL, 'r' },
         { "output-format",      required_argument,  NULL, 'o' },
+        { "codec-name",         required_argument,  NULL, 'N' },
         { "monotonic-time",     no_argument,        NULL, 'm' },
         { NULL,                 0,                  NULL, 0 }
     };
@@ -978,6 +999,9 @@
                 return 2;
             }
             break;
+        case 'N':
+            gCodecName = optarg;
+            break;
         case 'm':
             gMonotonicTime = true;
             break;
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index c62833d..c7619af 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -111,7 +111,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
         libstagefright liblog libutils libbinder libui libgui \
-        libstagefright_foundation libmedia libcutils
+        libstagefright_foundation libmedia libcutils libmediaextractor
 
 LOCAL_C_INCLUDES:= \
         frameworks/av/media/libstagefright \
@@ -204,7 +204,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
         libstagefright liblog libutils libbinder libstagefright_foundation \
-        libcutils libc
+        libcutils libc libmediaextractor
 
 LOCAL_C_INCLUDES:= \
         frameworks/av/media/libstagefright \
diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp
index 2e8da4b..ea239c5 100644
--- a/drm/libmediadrm/Android.bp
+++ b/drm/libmediadrm/Android.bp
@@ -18,6 +18,7 @@
         "PluginMetricsReporting.cpp",
         "SharedLibrary.cpp",
         "DrmHal.cpp",
+	"DrmMetrics.cpp",
         "CryptoHal.cpp",
         "protos/plugin_metrics.proto",
     ],
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index 982b117..07cec01 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -30,6 +30,7 @@
 #include <media/DrmHal.h>
 #include <media/DrmSessionClientInterface.h>
 #include <media/DrmSessionManager.h>
+#include <media/EventMetric.h>
 #include <media/PluginMetricsReporting.h>
 #include <media/drm/DrmAPI.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -43,6 +44,8 @@
 using drm::V1_0::KeyValue;
 using drm::V1_1::HdcpLevel;;
 using drm::V1_0::SecureStop;
+using drm::V1_1::SecureStopRelease;
+using drm::V1_0::SecureStopId;
 using drm::V1_1::SecurityLevel;
 using drm::V1_0::Status;
 using ::android::hardware::hidl_array;
@@ -53,6 +56,14 @@
 using ::android::hidl::manager::V1_0::IServiceManager;
 using ::android::sp;
 
+namespace {
+
+// This constant corresponds to the PROPERTY_DEVICE_UNIQUE_ID constant
+// in the MediaDrm API.
+constexpr char kPropertyDeviceUniqueId[] = "deviceUniqueId";
+
+}
+
 namespace android {
 
 #define INIT_CHECK() {if (mInitCheck != OK) return mInitCheck;}
@@ -156,6 +167,15 @@
     return secureStops;
 }
 
+static List<Vector<uint8_t>> toSecureStopIds(const hidl_vec<SecureStopId>&
+        hSecureStopIds) {
+    List<Vector<uint8_t>> secureStopIds;
+    for (size_t i = 0; i < hSecureStopIds.size(); i++) {
+        secureStopIds.push_back(toVector(hSecureStopIds[i]));
+    }
+    return secureStopIds;
+}
+
 static status_t toStatusT(Status status) {
     switch (status) {
     case Status::OK:
@@ -319,6 +339,7 @@
 
 Return<void> DrmHal::sendEvent(EventType hEventType,
         const hidl_vec<uint8_t>& sessionId, const hidl_vec<uint8_t>& data) {
+    mMetrics.mEventCounter.Increment(hEventType);
 
     mEventLock.lock();
     sp<IDrmClient> listener = mListener;
@@ -409,12 +430,21 @@
                 break;
             }
             obj.writeInt32(type);
+            mMetrics.mKeyStatusChangeCounter.Increment(keyStatus.type);
         }
         obj.writeInt32(hasNewUsableKey);
 
         Mutex::Autolock lock(mNotifyLock);
         listener->notify(DrmPlugin::kDrmPluginEventKeysChange, 0, &obj);
+    } else {
+        // There's no listener. But we still want to count the key change
+        // events.
+        size_t nKeys = keyStatusList.size();
+        for (size_t i = 0; i < nKeys; i++) {
+            mMetrics.mKeyStatusChangeCounter.Increment(keyStatusList[i].type);
+        }
     }
+
     return Void();
 }
 
@@ -442,7 +472,9 @@
     for (size_t i = 0; i < mFactories.size(); i++) {
         if (mFactories[i]->isCryptoSchemeSupported(uuid)) {
             mPlugin = makeDrmPlugin(mFactories[i], uuid, appPackageName);
-            mPluginV1_1 = drm::V1_1::IDrmPlugin::castFrom(mPlugin);
+            if (mPlugin != NULL) {
+                mPluginV1_1 = drm::V1_1::IDrmPlugin::castFrom(mPlugin);
+            }
         }
     }
 
@@ -474,6 +506,7 @@
         }
     }
     mPlugin.clear();
+    mPluginV1_1.clear();
     return OK;
 }
 
@@ -517,6 +550,8 @@
                 mDrmSessionClient, sessionId);
         mOpenSessions.push(sessionId);
     }
+
+    mMetrics.mOpenSessionCounter.Increment(err);
     return err;
 }
 
@@ -536,8 +571,11 @@
             }
         }
         reportMetrics();
-        return toStatusT(status);
+        status_t response = toStatusT(status);
+        mMetrics.mCloseSessionCounter.Increment(response);
+        return response;
     }
+    mMetrics.mCloseSessionCounter.Increment(DEAD_OBJECT);
     return DEAD_OBJECT;
 }
 
@@ -548,6 +586,7 @@
         String8 &defaultUrl, DrmPlugin::KeyRequestType *keyRequestType) {
     Mutex::Autolock autoLock(mLock);
     INIT_CHECK();
+    EventTimer<status_t> keyRequestTimer(&mMetrics.mGetKeyRequestTiming);
 
     DrmSessionManager::Instance()->useSession(sessionId);
 
@@ -559,6 +598,7 @@
     } else if (keyType == DrmPlugin::kKeyType_Release) {
         hKeyType = KeyType::RELEASE;
     } else {
+        keyRequestTimer.SetAttribute(BAD_VALUE);
         return BAD_VALUE;
     }
 
@@ -633,12 +673,16 @@
                 }
             });
 
-    return hResult.isOk() ? err : DEAD_OBJECT;
+    err = hResult.isOk() ? err : DEAD_OBJECT;
+    keyRequestTimer.SetAttribute(err);
+    return err;
 }
 
 status_t DrmHal::provideKeyResponse(Vector<uint8_t> const &sessionId,
         Vector<uint8_t> const &response, Vector<uint8_t> &keySetId) {
     Mutex::Autolock autoLock(mLock);
+    EventTimer<status_t> keyResponseTimer(&mMetrics.mProvideKeyResponseTiming);
+
     INIT_CHECK();
 
     DrmSessionManager::Instance()->useSession(sessionId);
@@ -654,8 +698,9 @@
                 err = toStatusT(status);
             }
         );
-
-    return hResult.isOk() ? err : DEAD_OBJECT;
+    err = hResult.isOk() ? err : DEAD_OBJECT;
+    keyResponseTimer.SetAttribute(err);
+    return err;
 }
 
 status_t DrmHal::removeKeys(Vector<uint8_t> const &keySetId) {
@@ -719,7 +764,9 @@
             }
         );
 
-    return hResult.isOk() ? err : DEAD_OBJECT;
+    err = hResult.isOk() ? err : DEAD_OBJECT;
+    mMetrics.mGetProvisionRequestCounter.Increment(err);
+    return err;
 }
 
 status_t DrmHal::provideProvisionResponse(Vector<uint8_t> const &response,
@@ -740,7 +787,9 @@
             }
         );
 
-    return hResult.isOk() ? err : DEAD_OBJECT;
+    err = hResult.isOk() ? err : DEAD_OBJECT;
+    mMetrics.mProvideProvisionResponseCounter.Increment(err);
+    return err;
 }
 
 status_t DrmHal::getSecureStops(List<Vector<uint8_t>> &secureStops) {
@@ -762,6 +811,32 @@
 }
 
 
+status_t DrmHal::getSecureStopIds(List<Vector<uint8_t>> &secureStopIds) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPluginV1_1 == NULL) {
+        return ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    status_t err = UNKNOWN_ERROR;
+
+    Return<void> hResult = mPluginV1_1->getSecureStopIds(
+            [&](Status status, const hidl_vec<SecureStopId>& hSecureStopIds) {
+                if (status == Status::OK) {
+                    secureStopIds = toSecureStopIds(hSecureStopIds);
+                }
+                err = toStatusT(status);
+            }
+    );
+
+    return hResult.isOk() ? err : DEAD_OBJECT;
+}
+
+
 status_t DrmHal::getSecureStop(Vector<uint8_t> const &ssid, Vector<uint8_t> &secureStop) {
     Mutex::Autolock autoLock(mLock);
     INIT_CHECK();
@@ -784,13 +859,36 @@
     Mutex::Autolock autoLock(mLock);
     INIT_CHECK();
 
+    if (mPluginV1_1 != NULL) {
+        SecureStopRelease secureStopRelease;
+        secureStopRelease.opaqueData = toHidlVec(ssRelease);
+        return toStatusT(mPluginV1_1->releaseSecureStops(secureStopRelease));
+    }
+
     return toStatusT(mPlugin->releaseSecureStop(toHidlVec(ssRelease)));
 }
 
-status_t DrmHal::releaseAllSecureStops() {
+status_t DrmHal::removeSecureStop(Vector<uint8_t> const &ssid) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPluginV1_1 == NULL) {
+        return ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    return toStatusT(mPluginV1_1->removeSecureStop(toHidlVec(ssid)));
+}
+
+status_t DrmHal::removeAllSecureStops() {
     Mutex::Autolock autoLock(mLock);
     INIT_CHECK();
 
+    if (mPluginV1_1 != NULL) {
+        return toStatusT(mPluginV1_1->removeAllSecureStops());
+    }
     return toStatusT(mPlugin->releaseAllSecureStops());
 }
 
@@ -962,7 +1060,11 @@
             }
     );
 
-    return hResult.isOk() ? err : DEAD_OBJECT;
+    err = hResult.isOk() ? err : DEAD_OBJECT;
+    if (name == kPropertyDeviceUniqueId) {
+        mMetrics.mGetDeviceUniqueIdCounter.Increment(err);
+    }
+    return err;
 }
 
 status_t DrmHal::setPropertyString(String8 const &name, String8 const &value ) const {
@@ -984,9 +1086,12 @@
     return toStatusT(status);
 }
 
-status_t DrmHal::getMetrics(MediaAnalyticsItem* metrics) {
-    // TODO: Replace this with real metrics.
-    metrics->setCString("/drm/mediadrm/dummymetric", "dummy");
+status_t DrmHal::getMetrics(MediaAnalyticsItem* item) {
+    if (item == nullptr) {
+      return UNEXPECTED_NULL;
+    }
+
+    mMetrics.Export(item);
     return OK;
 }
 
diff --git a/drm/libmediadrm/DrmMetrics.cpp b/drm/libmediadrm/DrmMetrics.cpp
new file mode 100644
index 0000000..258c4b0
--- /dev/null
+++ b/drm/libmediadrm/DrmMetrics.cpp
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+#include <android-base/macros.h>
+#include <media/DrmMetrics.h>
+
+using ::android::hardware::drm::V1_0::EventType;
+using ::android::hardware::drm::V1_0::KeyStatusType;
+
+namespace {
+
+template<typename T>
+std::string GetAttributeName(T type);
+
+template<>
+std::string GetAttributeName<KeyStatusType>(KeyStatusType type) {
+  static const char* type_names[] = {
+      "USABLE", "EXPIRED", "OUTPUT_NOT_ALLOWED",
+      "STATUS_PENDING", "INTERNAL_ERROR" };
+  if (((size_t) type) > arraysize(type_names)) {
+    return "UNKNOWN_TYPE";
+  }
+  return type_names[(size_t) type];
+}
+
+template<>
+std::string GetAttributeName<EventType>(EventType type) {
+  static const char* type_names[] = {
+      "PROVISION_REQUIRED", "KEY_NEEDED", "KEY_EXPIRED",
+      "VENDOR_DEFINED", "SESSION_RECLAIMED" };
+  if (((size_t) type) > arraysize(type_names)) {
+    return "UNKNOWN_TYPE";
+  }
+  return type_names[(size_t) type];
+}
+
+template<typename T>
+void ExportCounterMetric(const android::CounterMetric<T>& counter,
+                         android::MediaAnalyticsItem* item) {
+  if (!item) {
+    ALOGE("item was unexpectedly null.");
+    return;
+  }
+  std::string success_count_name = counter.metric_name() + ".ok.count";
+  std::string error_count_name = counter.metric_name() + ".error.count";
+  counter.ExportValues(
+      [&] (const android::status_t status, const int64_t value) {
+          if (status == android::OK) {
+              item->setInt64(success_count_name.c_str(), value);
+          } else {
+              int64_t total_errors(0);
+              item->getInt64(error_count_name.c_str(), &total_errors);
+              item->setInt64(error_count_name.c_str(), total_errors + value);
+              // TODO: Add support for exporting the list of error values.
+              // This probably needs to be added to MediaAnalyticsItem.
+          }
+      });
+}
+
+template<typename T>
+void ExportCounterMetricWithAttributeNames(
+    const android::CounterMetric<T>& counter,
+    android::MediaAnalyticsItem* item) {
+  if (!item) {
+    ALOGE("item was unexpectedly null.");
+    return;
+  }
+  counter.ExportValues(
+      [&] (const T& attribute, const int64_t value) {
+          std::string name = counter.metric_name()
+              + "." + GetAttributeName(attribute) + ".count";
+          item->setInt64(name.c_str(), value);
+      });
+}
+
+template<typename T>
+void ExportEventMetric(const android::EventMetric<T>& event,
+                       android::MediaAnalyticsItem* item) {
+  if (!item) {
+    ALOGE("item was unexpectedly null.");
+    return;
+  }
+  std::string success_count_name = event.metric_name() + ".ok.count";
+  std::string error_count_name = event.metric_name() + ".error.count";
+  std::string timing_name = event.metric_name() + ".ok.average_time_micros";
+  event.ExportValues(
+      [&] (const android::status_t& status,
+           const android::EventStatistics& value) {
+          if (status == android::OK) {
+              item->setInt64(success_count_name.c_str(), value.count);
+              item->setInt64(timing_name.c_str(), value.mean);
+          } else {
+              int64_t total_errors(0);
+              item->getInt64(error_count_name.c_str(), &total_errors);
+              item->setInt64(error_count_name.c_str(),
+                             total_errors + value.count);
+              // TODO: Add support for exporting the list of error values.
+              // This probably needs to be added to MediaAnalyticsItem.
+          }
+      });
+}
+
+}  // namespace anonymous
+
+namespace android {
+
+MediaDrmMetrics::MediaDrmMetrics()
+    : mOpenSessionCounter("drm.mediadrm.open_session", "status"),
+      mCloseSessionCounter("drm.mediadrm.close_session", "status"),
+      mGetKeyRequestTiming("drm.mediadrm.get_key_request", "status"),
+      mProvideKeyResponseTiming("drm.mediadrm.provide_key_response", "status"),
+      mGetProvisionRequestCounter(
+          "drm.mediadrm.get_provision_request", "status"),
+      mProvideProvisionResponseCounter(
+          "drm.mediadrm.provide_provision_response", "status"),
+      mKeyStatusChangeCounter(
+          "drm.mediadrm.key_status_change", "key_status_type"),
+      mEventCounter("drm.mediadrm.event", "event_type"),
+      mGetDeviceUniqueIdCounter(
+          "drm.mediadrm.get_device_unique_id", "status") {
+}
+
+void MediaDrmMetrics::Export(MediaAnalyticsItem* item) {
+  if (!item) {
+    ALOGE("item was unexpectedly null.");
+    return;
+  }
+  ExportCounterMetric(mOpenSessionCounter, item);
+  ExportCounterMetric(mCloseSessionCounter, item);
+  ExportEventMetric(mGetKeyRequestTiming, item);
+  ExportEventMetric(mProvideKeyResponseTiming, item);
+  ExportCounterMetric(mGetProvisionRequestCounter, item);
+  ExportCounterMetric(mProvideProvisionResponseCounter, item);
+  ExportCounterMetricWithAttributeNames(mKeyStatusChangeCounter, item);
+  ExportCounterMetricWithAttributeNames(mEventCounter, item);
+  ExportCounterMetric(mGetDeviceUniqueIdCounter, item);
+}
+
+}  // namespace android
diff --git a/drm/libmediadrm/IDrm.cpp b/drm/libmediadrm/IDrm.cpp
index e7417cc..63a9562 100644
--- a/drm/libmediadrm/IDrm.cpp
+++ b/drm/libmediadrm/IDrm.cpp
@@ -56,11 +56,13 @@
     VERIFY,
     SET_LISTENER,
     GET_SECURE_STOP,
-    RELEASE_ALL_SECURE_STOPS,
+    REMOVE_ALL_SECURE_STOPS,
     GET_HDCP_LEVELS,
     GET_NUMBER_OF_SESSIONS,
     GET_SECURITY_LEVEL,
     SET_SECURITY_LEVEL,
+    REMOVE_SECURE_STOP,
+    GET_SECURE_STOP_IDS
 };
 
 struct BpDrm : public BpInterface<IDrm> {
@@ -302,6 +304,25 @@
         return reply.readInt32();
     }
 
+    virtual status_t getSecureStopIds(List<Vector<uint8_t> > &secureStopIds) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        status_t status = remote()->transact(GET_SECURE_STOP_IDS, data, &reply);
+        if (status != OK) {
+            return status;
+        }
+
+        secureStopIds.clear();
+        uint32_t count = reply.readInt32();
+        for (size_t i = 0; i < count; i++) {
+            Vector<uint8_t> secureStopId;
+            readVector(reply, secureStopId);
+            secureStopIds.push_back(secureStopId);
+        }
+        return reply.readInt32();
+    }
+
     virtual status_t getSecureStop(Vector<uint8_t> const &ssid, Vector<uint8_t> &secureStop) {
         Parcel data, reply;
         data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
@@ -329,11 +350,24 @@
         return reply.readInt32();
     }
 
-    virtual status_t releaseAllSecureStops() {
+    virtual status_t removeSecureStop(Vector<uint8_t> const &ssid) {
         Parcel data, reply;
         data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
 
-        status_t status = remote()->transact(RELEASE_ALL_SECURE_STOPS, data, &reply);
+        writeVector(data, ssid);
+        status_t status = remote()->transact(REMOVE_SECURE_STOP, data, &reply);
+        if (status != OK) {
+            return status;
+        }
+
+        return reply.readInt32();
+    }
+
+    virtual status_t removeAllSecureStops() {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+
+        status_t status = remote()->transact(REMOVE_ALL_SECURE_STOPS, data, &reply);
         if (status != OK) {
             return status;
         }
@@ -854,6 +888,24 @@
             return OK;
         }
 
+        case GET_SECURE_STOP_IDS:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            List<Vector<uint8_t> > secureStopIds;
+            status_t result = getSecureStopIds(secureStopIds);
+            size_t count = secureStopIds.size();
+            reply->writeInt32(count);
+            List<Vector<uint8_t> >::iterator iter = secureStopIds.begin();
+            while(iter != secureStopIds.end()) {
+                size_t size = iter->size();
+                reply->writeInt32(size);
+                reply->write(iter->array(), iter->size());
+                iter++;
+            }
+            reply->writeInt32(result);
+            return OK;
+        }
+
         case GET_SECURE_STOP:
         {
             CHECK_INTERFACE(IDrm, data, reply);
@@ -874,10 +926,19 @@
             return OK;
         }
 
-        case RELEASE_ALL_SECURE_STOPS:
+        case REMOVE_SECURE_STOP:
         {
             CHECK_INTERFACE(IDrm, data, reply);
-            reply->writeInt32(releaseAllSecureStops());
+            Vector<uint8_t> ssid;
+            readVector(data, ssid);
+            reply->writeInt32(removeSecureStop(ssid));
+            return OK;
+        }
+
+        case REMOVE_ALL_SECURE_STOPS:
+        {
+            CHECK_INTERFACE(IDrm, data, reply);
+            reply->writeInt32(removeAllSecureStops());
             return OK;
         }
 
diff --git a/drm/libmediadrm/PluginMetricsReporting.cpp b/drm/libmediadrm/PluginMetricsReporting.cpp
index cc7fb72..26c8427 100644
--- a/drm/libmediadrm/PluginMetricsReporting.cpp
+++ b/drm/libmediadrm/PluginMetricsReporting.cpp
@@ -80,7 +80,6 @@
         }
     }
 
-    analyticsItem.setFinalized(true);
     if (!analyticsItem.selfrecord()) {
       ALOGE("selfrecord() returned false. sessioId %" PRId64, sessionId);
     }
diff --git a/drm/libmediadrm/tests/Android.bp b/drm/libmediadrm/tests/Android.bp
new file mode 100644
index 0000000..fdc982d
--- /dev/null
+++ b/drm/libmediadrm/tests/Android.bp
@@ -0,0 +1,44 @@
+// Build definitions for unit tests.
+
+cc_test {
+    name: "CounterMetric_test",
+    srcs: ["CounterMetric_test.cpp"],
+    shared_libs: ["libmediadrm"],
+    include_dirs: ["frameworks/av/include/media"],
+    cflags: [
+      "-Werror",
+      "-Wall",
+    ],
+}
+
+cc_test {
+    name: "DrmMetrics_test",
+    srcs: ["DrmMetrics_test.cpp"],
+    shared_libs: [
+      "android.hardware.drm@1.0",
+      "liblog",
+      "libmediadrm",
+      "libmediametrics",
+      "libutils",
+    ],
+    include_dirs: ["frameworks/av/include/media"],
+    cflags: [
+      "-Werror",
+      "-Wall",
+    ],
+}
+
+cc_test {
+    name: "EventMetric_test",
+    srcs: ["EventMetric_test.cpp"],
+    shared_libs: [
+      "liblog",
+      "libmediadrm",
+      "libutils",
+    ],
+    include_dirs: ["frameworks/av/include/media"],
+    cflags: [
+      "-Werror",
+      "-Wall",
+    ],
+}
diff --git a/drm/libmediadrm/tests/CounterMetric_test.cpp b/drm/libmediadrm/tests/CounterMetric_test.cpp
new file mode 100644
index 0000000..6bca0da
--- /dev/null
+++ b/drm/libmediadrm/tests/CounterMetric_test.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "CounterMetric.h"
+
+namespace android {
+
+/**
+ * Unit tests for the CounterMetric class.
+ */
+class CounterMetricTest : public ::testing::Test {
+};
+
+TEST_F(CounterMetricTest, IntDataTypeEmpty) {
+  CounterMetric<int> metric("MyMetricName", "MetricAttributeName");
+
+  std::map<int, int64_t> values;
+
+  metric.ExportValues(
+      [&] (int attribute_value, int64_t value) {
+          values[attribute_value] = value;
+      });
+
+  EXPECT_TRUE(values.empty());
+}
+
+TEST_F(CounterMetricTest, IntDataType) {
+  CounterMetric<int> metric("MyMetricName", "MetricAttributeName");
+
+  std::map<int, int64_t> values;
+
+  metric.Increment(7);
+  metric.Increment(8);
+  metric.Increment(8);
+
+  metric.ExportValues(
+      [&] (int attribute_value, int64_t value) {
+          values[attribute_value] = value;
+      });
+
+  ASSERT_EQ(2u, values.size());
+  EXPECT_EQ(1, values[7]);
+  EXPECT_EQ(2, values[8]);
+}
+
+TEST_F(CounterMetricTest, StringDataType) {
+  CounterMetric<std::string> metric("MyMetricName", "MetricAttributeName");
+
+  std::map<std::string, int64_t> values;
+
+  metric.Increment("a");
+  metric.Increment("b");
+  metric.Increment("b");
+
+  metric.ExportValues(
+      [&] (std::string attribute_value, int64_t value) {
+          values[attribute_value] = value;
+      });
+
+  ASSERT_EQ(2u, values.size());
+  EXPECT_EQ(1, values["a"]);
+  EXPECT_EQ(2, values["b"]);
+}
+
+}  // namespace android
diff --git a/drm/libmediadrm/tests/DrmMetrics_test.cpp b/drm/libmediadrm/tests/DrmMetrics_test.cpp
new file mode 100644
index 0000000..d1948b4
--- /dev/null
+++ b/drm/libmediadrm/tests/DrmMetrics_test.cpp
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "DrmMetrics.h"
+
+using ::android::hardware::drm::V1_0::EventType;
+using ::android::hardware::drm::V1_0::KeyStatusType;
+
+namespace android {
+
+/**
+ * Unit tests for the MediaDrmMetrics class.
+ */
+class MediaDrmMetricsTest : public ::testing::Test {
+};
+
+TEST_F(MediaDrmMetricsTest, EmptySuccess) {
+  MediaDrmMetrics metrics;
+  MediaAnalyticsItem item;
+
+  metrics.Export(&item);
+  EXPECT_EQ(0, item.count());
+}
+
+TEST_F(MediaDrmMetricsTest, AllValuesSuccessCounts) {
+  MediaDrmMetrics metrics;
+
+  metrics.mOpenSessionCounter.Increment(OK);
+  metrics.mCloseSessionCounter.Increment(OK);
+
+  {
+    EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTiming);
+    EventTimer<status_t> provide_key_response_timer(
+        &metrics.mProvideKeyResponseTiming);
+    get_key_request_timer.SetAttribute(OK);
+    provide_key_response_timer.SetAttribute(OK);
+  }
+
+  metrics.mGetProvisionRequestCounter.Increment(OK);
+  metrics.mProvideProvisionResponseCounter.Increment(OK);
+  metrics.mGetDeviceUniqueIdCounter.Increment(OK);
+
+  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::USABLE);
+  metrics.mEventCounter.Increment(EventType::PROVISION_REQUIRED);
+
+  MediaAnalyticsItem item;
+
+  metrics.Export(&item);
+  EXPECT_EQ(11, item.count());
+
+  // Verify the list of pairs of int64 metrics.
+  std::vector<std::pair<std::string, int64_t>> expected_values = {
+      { "drm.mediadrm.open_session.ok.count", 1 },
+      { "drm.mediadrm.close_session.ok.count", 1 },
+      { "drm.mediadrm.get_key_request.ok.count", 1 },
+      { "drm.mediadrm.provide_key_response.ok.count", 1 },
+      { "drm.mediadrm.get_provision_request.ok.count", 1 },
+      { "drm.mediadrm.provide_provision_response.ok.count", 1 },
+      { "drm.mediadrm.key_status_change.USABLE.count", 1 },
+      { "drm.mediadrm.event.PROVISION_REQUIRED.count", 1 },
+      { "drm.mediadrm.get_device_unique_id.ok.count", 1 }};
+  for (const auto& expected_pair : expected_values) {
+    int64_t value = -1;
+    EXPECT_TRUE(item.getInt64(expected_pair.first.c_str(), &value))
+        << "Failed to get " << expected_pair.first;
+    EXPECT_EQ(expected_pair.second, value)
+        << "Unexpected value for " << expected_pair.first;
+  }
+
+  // Validate timing values exist.
+  int64_t value = -1;
+  EXPECT_TRUE(
+      item.getInt64("drm.mediadrm.get_key_request.ok.average_time_micros",
+                    &value));
+  EXPECT_GE(value, 0);
+
+  value = -1;
+  EXPECT_TRUE(
+      item.getInt64("drm.mediadrm.provide_key_response.ok.average_time_micros",
+                    &value));
+  EXPECT_GE(value, 0);
+}
+
+TEST_F(MediaDrmMetricsTest, AllValuesFull) {
+  MediaDrmMetrics metrics;
+
+  metrics.mOpenSessionCounter.Increment(OK);
+  metrics.mOpenSessionCounter.Increment(UNEXPECTED_NULL);
+
+  metrics.mCloseSessionCounter.Increment(OK);
+  metrics.mCloseSessionCounter.Increment(UNEXPECTED_NULL);
+
+  for (status_t s : {OK, UNEXPECTED_NULL}) {
+    {
+      EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTiming);
+      EventTimer<status_t> provide_key_response_timer(
+          &metrics.mProvideKeyResponseTiming);
+      get_key_request_timer.SetAttribute(s);
+      provide_key_response_timer.SetAttribute(s);
+    }
+  }
+
+  metrics.mGetProvisionRequestCounter.Increment(OK);
+  metrics.mGetProvisionRequestCounter.Increment(UNEXPECTED_NULL);
+  metrics.mProvideProvisionResponseCounter.Increment(OK);
+  metrics.mProvideProvisionResponseCounter.Increment(UNEXPECTED_NULL);
+  metrics.mGetDeviceUniqueIdCounter.Increment(OK);
+  metrics.mGetDeviceUniqueIdCounter.Increment(UNEXPECTED_NULL);
+
+  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::USABLE);
+  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::EXPIRED);
+  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::OUTPUTNOTALLOWED);
+  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::STATUSPENDING);
+  metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::INTERNALERROR);
+  metrics.mEventCounter.Increment(EventType::PROVISION_REQUIRED);
+  metrics.mEventCounter.Increment(EventType::KEY_NEEDED);
+  metrics.mEventCounter.Increment(EventType::KEY_EXPIRED);
+  metrics.mEventCounter.Increment(EventType::VENDOR_DEFINED);
+  metrics.mEventCounter.Increment(EventType::SESSION_RECLAIMED);
+
+  MediaAnalyticsItem item;
+
+  metrics.Export(&item);
+  EXPECT_EQ(26, item.count());
+
+  // Verify the list of pairs of int64 metrics.
+  std::vector<std::pair<std::string, int64_t>> expected_values = {
+      { "drm.mediadrm.open_session.ok.count", 1 },
+      { "drm.mediadrm.close_session.ok.count", 1 },
+      { "drm.mediadrm.get_key_request.ok.count", 1 },
+      { "drm.mediadrm.provide_key_response.ok.count", 1 },
+      { "drm.mediadrm.get_provision_request.ok.count", 1 },
+      { "drm.mediadrm.provide_provision_response.ok.count", 1 },
+      { "drm.mediadrm.get_device_unique_id.ok.count", 1 },
+      { "drm.mediadrm.open_session.error.count", 1 },
+      { "drm.mediadrm.close_session.error.count", 1 },
+      { "drm.mediadrm.get_key_request.error.count", 1 },
+      { "drm.mediadrm.provide_key_response.error.count", 1 },
+      { "drm.mediadrm.get_provision_request.error.count", 1 },
+      { "drm.mediadrm.provide_provision_response.error.count", 1 },
+      { "drm.mediadrm.get_device_unique_id.error.count", 1 },
+      { "drm.mediadrm.key_status_change.USABLE.count", 1 },
+      { "drm.mediadrm.key_status_change.EXPIRED.count", 1 },
+      { "drm.mediadrm.key_status_change.OUTPUT_NOT_ALLOWED.count", 1 },
+      { "drm.mediadrm.key_status_change.STATUS_PENDING.count", 1 },
+      { "drm.mediadrm.key_status_change.INTERNAL_ERROR.count", 1 },
+      { "drm.mediadrm.event.PROVISION_REQUIRED.count", 1 },
+      { "drm.mediadrm.event.KEY_NEEDED.count", 1 },
+      { "drm.mediadrm.event.KEY_EXPIRED.count", 1 },
+      { "drm.mediadrm.event.VENDOR_DEFINED.count", 1 },
+      { "drm.mediadrm.event.SESSION_RECLAIMED.count", 1 }};
+  for (const auto& expected_pair : expected_values) {
+    int64_t value = -1;
+    EXPECT_TRUE(item.getInt64(expected_pair.first.c_str(), &value))
+        << "Failed to get " << expected_pair.first;
+    EXPECT_EQ(expected_pair.second, value)
+        << "Unexpected value for " << expected_pair.first;
+  }
+
+  // Validate timing values exist.
+  int64_t value = -1;
+  EXPECT_TRUE(
+      item.getInt64("drm.mediadrm.get_key_request.ok.average_time_micros",
+                    &value));
+  EXPECT_GE(value, 0);
+
+  value = -1;
+  EXPECT_TRUE(
+      item.getInt64("drm.mediadrm.provide_key_response.ok.average_time_micros",
+                    &value));
+  EXPECT_GE(value, 0);
+}
+
+}  // namespace android
diff --git a/drm/libmediadrm/tests/EventMetric_test.cpp b/drm/libmediadrm/tests/EventMetric_test.cpp
new file mode 100644
index 0000000..eb6c4f6
--- /dev/null
+++ b/drm/libmediadrm/tests/EventMetric_test.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "EventMetric.h"
+
+namespace android {
+
+/**
+ * Unit tests for the EventMetric class.
+ */
+
+TEST(EventMetricTest, IntDataTypeEmpty) {
+  EventMetric<int> metric("MyMetricName", "MetricAttributeName");
+
+  std::map<int, EventStatistics> values;
+
+  metric.ExportValues(
+      [&] (int attribute_value, const EventStatistics& value) {
+          values[attribute_value] = value;
+      });
+
+  EXPECT_TRUE(values.empty());
+}
+
+TEST(EventMetricTest, IntDataType) {
+  EventMetric<int> metric("MyMetricName", "MetricAttributeName");
+
+  std::map<int, EventStatistics> values;
+
+  metric.Record(4, 7);
+  metric.Record(5, 8);
+  metric.Record(5, 8);
+  metric.Record(5, 8);
+  metric.Record(6, 8);
+  metric.Record(6, 8);
+  metric.Record(6, 8);
+
+  metric.ExportValues(
+      [&] (int attribute_value, const EventStatistics& value) {
+          values[attribute_value] = value;
+      });
+
+  ASSERT_EQ(2u, values.size());
+  EXPECT_EQ(4, values[7].min);
+  EXPECT_EQ(4, values[7].max);
+  EXPECT_EQ(4, values[7].mean);
+  EXPECT_EQ(1, values[7].count);
+
+  EXPECT_EQ(5, values[8].min);
+  EXPECT_EQ(6, values[8].max);
+  // This is an approximate value because of the technique we're using.
+  EXPECT_NEAR(5.5, values[8].mean, 0.2);
+  EXPECT_EQ(6, values[8].count);
+}
+
+TEST(EventMetricTest, StringDataType) {
+  EventMetric<std::string> metric("MyMetricName", "MetricAttributeName");
+
+  std::map<std::string, EventStatistics> values;
+
+  metric.Record(1, "a");
+  metric.Record(2, "b");
+  metric.Record(2, "b");
+  metric.Record(3, "b");
+  metric.Record(3, "b");
+
+  metric.ExportValues(
+      [&] (std::string attribute_value, const EventStatistics& value) {
+          values[attribute_value] = value;
+      });
+
+  ASSERT_EQ(2u, values.size());
+  EXPECT_EQ(1, values["a"].min);
+  EXPECT_EQ(1, values["a"].max);
+  EXPECT_EQ(1, values["a"].mean);
+  EXPECT_EQ(1, values["a"].count);
+
+  EXPECT_EQ(2, values["b"].min);
+  EXPECT_EQ(3, values["b"].max);
+  EXPECT_NEAR(2.5, values["b"].mean, 0.2);
+  EXPECT_EQ(4, values["b"].count);
+}
+
+// Helper class that allows us to mock the clock.
+template<typename AttributeType>
+class MockEventTimer : public EventTimer<AttributeType> {
+ public:
+  explicit MockEventTimer(nsecs_t time_delta_ns,
+                          EventMetric<AttributeType>* metric)
+      : EventTimer<AttributeType>(metric) {
+    // Pretend the event started earlier.
+    this->start_time_ = systemTime() - time_delta_ns;
+  }
+};
+
+TEST(EventTimerTest, IntDataType) {
+  EventMetric<int> metric("MyMetricName", "MetricAttributeName");
+
+  for (int i = 0; i < 5; i++) {
+    {
+      // Add a mock time delta.
+      MockEventTimer<int> metric_timer(i * 1000000, &metric);
+      metric_timer.SetAttribute(i % 2);
+    }
+  }
+
+  std::map<int, EventStatistics> values;
+  metric.ExportValues(
+      [&] (int attribute_value, const EventStatistics& value) {
+          values[attribute_value] = value;
+      });
+
+  ASSERT_EQ(2u, values.size());
+  EXPECT_LT(values[0].min, values[0].max);
+  EXPECT_GE(4000, values[0].max);
+  EXPECT_GT(values[0].mean, values[0].min);
+  EXPECT_LE(values[0].mean, values[0].max);
+  EXPECT_EQ(3, values[0].count);
+
+  EXPECT_LT(values[1].min, values[1].max);
+  EXPECT_GE(3000, values[1].max);
+  EXPECT_GT(values[1].mean, values[1].min);
+  EXPECT_LE(values[1].mean, values[1].max);
+  EXPECT_EQ(2, values[1].count);
+}
+
+}  // namespace android
diff --git a/include/media/AudioPresentationInfo.h b/include/media/AudioPresentationInfo.h
new file mode 100644
index 0000000..e91a992
--- /dev/null
+++ b/include/media/AudioPresentationInfo.h
@@ -0,0 +1,79 @@
+/*
+ * 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 AUDIO_PRESENTATION_INFO_H_
+#define AUDIO_PRESENTATION_INFO_H_
+
+#include <sstream>
+#include <stdint.h>
+
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+enum mastering_indication {
+    MASTERING_NOT_INDICATED,
+    MASTERED_FOR_STEREO,
+    MASTERED_FOR_SURROUND,
+    MASTERED_FOR_3D,
+    MASTERED_FOR_HEADPHONE,
+};
+
+struct AudioPresentation : public RefBase {
+    int32_t mPresentationId;
+    int32_t mProgramId;
+    KeyedVector<String8, String8> mLabels;
+    String8 mLanguage;
+    int32_t mMasteringIndication;
+    bool mAudioDescriptionAvailable;
+    bool mSpokenSubtitlesAvailable;
+    bool mDialogueEnhancementAvailable;
+
+    AudioPresentation() {
+        mPresentationId = -1;
+        mProgramId = -1;
+        mLanguage = "";
+        mMasteringIndication = MASTERING_NOT_INDICATED;
+        mAudioDescriptionAvailable = false;
+        mSpokenSubtitlesAvailable = false;
+        mDialogueEnhancementAvailable = false;
+    }
+};
+
+typedef Vector<sp<AudioPresentation>> AudioPresentations;
+
+class AudioPresentationInfo : public RefBase {
+ public:
+    AudioPresentationInfo();
+
+    ~AudioPresentationInfo();
+
+    void addPresentation(sp<AudioPresentation> presentation);
+
+    size_t countPresentations() const;
+
+    const sp<AudioPresentation> getPresentation(size_t index) const;
+
+ private:
+    AudioPresentations mPresentations;
+};
+
+}  // namespace android
+
+#endif  // AUDIO_PRESENTATION_INFO_H_
diff --git a/include/media/CounterMetric.h b/include/media/CounterMetric.h
new file mode 120000
index 0000000..baba043
--- /dev/null
+++ b/include/media/CounterMetric.h
@@ -0,0 +1 @@
+../../media/libmedia/include/media/CounterMetric.h
\ No newline at end of file
diff --git a/include/media/DrmMetrics.h b/include/media/DrmMetrics.h
new file mode 120000
index 0000000..abc966b
--- /dev/null
+++ b/include/media/DrmMetrics.h
@@ -0,0 +1 @@
+../../media/libmedia/include/media/DrmMetrics.h
\ No newline at end of file
diff --git a/include/media/EventMetric.h b/include/media/EventMetric.h
new file mode 120000
index 0000000..5707d9a
--- /dev/null
+++ b/include/media/EventMetric.h
@@ -0,0 +1 @@
+../../media/libmedia/include/media/EventMetric.h
\ No newline at end of file
diff --git a/include/media/MediaSourceBase.h b/include/media/MediaSourceBase.h
new file mode 120000
index 0000000..fe227b1
--- /dev/null
+++ b/include/media/MediaSourceBase.h
@@ -0,0 +1 @@
+../../media/libmediaextractor/include/media/MediaSourceBase.h
\ No newline at end of file
diff --git a/include/media/MicrophoneInfo.h b/include/media/MicrophoneInfo.h
new file mode 100644
index 0000000..b0199d4
--- /dev/null
+++ b/include/media/MicrophoneInfo.h
@@ -0,0 +1,249 @@
+/*
+ * 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 ANDROID_MICROPHONE_INFO_H
+#define ANDROID_MICROPHONE_INFO_H
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <system/audio.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+namespace android {
+namespace media {
+
+#define RETURN_IF_FAILED(calledOnce)                                     \
+    {                                                                    \
+        status_t returnStatus = calledOnce;                              \
+        if (returnStatus) {                                              \
+            ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
+            return returnStatus;                                         \
+         }                                                               \
+    }
+
+class MicrophoneInfo : public Parcelable {
+public:
+    MicrophoneInfo() = default;
+    MicrophoneInfo(const MicrophoneInfo& microphoneInfo) = default;
+    MicrophoneInfo(audio_microphone_characteristic_t& characteristic) {
+        mDeviceId = String16(&characteristic.device_id[0]);
+        mPortId = characteristic.id;
+        mType = characteristic.type;
+        mAddress = String16(&characteristic.address[0]);
+        mDeviceLocation = characteristic.location;
+        mDeviceGroup = characteristic.group;
+        mIndexInTheGroup = characteristic.index_in_the_group;
+        mGeometricLocation.push_back(characteristic.geometric_location.x);
+        mGeometricLocation.push_back(characteristic.geometric_location.y);
+        mGeometricLocation.push_back(characteristic.geometric_location.z);
+        mOrientation.push_back(characteristic.orientation.x);
+        mOrientation.push_back(characteristic.orientation.y);
+        mOrientation.push_back(characteristic.orientation.z);
+        Vector<float> frequencies;
+        Vector<float> responses;
+        for (size_t i = 0; i < characteristic.num_frequency_responses; i++) {
+            frequencies.push_back(characteristic.frequency_responses[0][i]);
+            responses.push_back(characteristic.frequency_responses[1][i]);
+        }
+        mFrequencyResponses.push_back(frequencies);
+        mFrequencyResponses.push_back(responses);
+        for (size_t i = 0; i < AUDIO_CHANNEL_COUNT_MAX; i++) {
+            mChannelMapping.push_back(characteristic.channel_mapping[i]);
+        }
+        mSensitivity = characteristic.sensitivity;
+        mMaxSpl = characteristic.max_spl;
+        mMinSpl = characteristic.min_spl;
+        mDirectionality = characteristic.directionality;
+    }
+
+    virtual ~MicrophoneInfo() = default;
+
+    virtual status_t writeToParcel(Parcel* parcel) const {
+        RETURN_IF_FAILED(parcel->writeString16(mDeviceId));
+        RETURN_IF_FAILED(parcel->writeInt32(mPortId));
+        RETURN_IF_FAILED(parcel->writeUint32(mType));
+        RETURN_IF_FAILED(parcel->writeString16(mAddress));
+        RETURN_IF_FAILED(parcel->writeInt32(mDeviceLocation));
+        RETURN_IF_FAILED(parcel->writeInt32(mDeviceGroup));
+        RETURN_IF_FAILED(parcel->writeInt32(mIndexInTheGroup));
+        RETURN_IF_FAILED(writeFloatVector(parcel, mGeometricLocation));
+        RETURN_IF_FAILED(writeFloatVector(parcel, mOrientation));
+        if (mFrequencyResponses.size() != 2) {
+            return BAD_VALUE;
+        }
+        for (size_t i = 0; i < mFrequencyResponses.size(); i++) {
+            RETURN_IF_FAILED(parcel->writeInt32(mFrequencyResponses[i].size()));
+            RETURN_IF_FAILED(writeFloatVector(parcel, mFrequencyResponses[i]));
+        }
+        std::vector<int> channelMapping;
+        for (size_t i = 0; i < mChannelMapping.size(); ++i) {
+            channelMapping.push_back(mChannelMapping[i]);
+        }
+        RETURN_IF_FAILED(parcel->writeInt32Vector(channelMapping));
+        RETURN_IF_FAILED(parcel->writeFloat(mSensitivity));
+        RETURN_IF_FAILED(parcel->writeFloat(mMaxSpl));
+        RETURN_IF_FAILED(parcel->writeFloat(mMinSpl));
+        RETURN_IF_FAILED(parcel->writeInt32(mDirectionality));
+        return OK;
+    }
+
+    virtual status_t readFromParcel(const Parcel* parcel) {
+        RETURN_IF_FAILED(parcel->readString16(&mDeviceId));
+        RETURN_IF_FAILED(parcel->readInt32(&mPortId));
+        RETURN_IF_FAILED(parcel->readUint32(&mType));
+        RETURN_IF_FAILED(parcel->readString16(&mAddress));
+        RETURN_IF_FAILED(parcel->readInt32(&mDeviceLocation));
+        RETURN_IF_FAILED(parcel->readInt32(&mDeviceGroup));
+        RETURN_IF_FAILED(parcel->readInt32(&mIndexInTheGroup));
+        RETURN_IF_FAILED(readFloatVector(parcel, &mGeometricLocation, 3));
+        RETURN_IF_FAILED(readFloatVector(parcel, &mOrientation, 3));
+        int32_t frequenciesNum;
+        RETURN_IF_FAILED(parcel->readInt32(&frequenciesNum));
+        Vector<float> frequencies;
+        RETURN_IF_FAILED(readFloatVector(parcel, &frequencies, frequenciesNum));
+        int32_t responsesNum;
+        RETURN_IF_FAILED(parcel->readInt32(&responsesNum));
+        Vector<float> responses;
+        RETURN_IF_FAILED(readFloatVector(parcel, &responses, responsesNum));
+        if (frequencies.size() != responses.size()) {
+            return BAD_VALUE;
+        }
+        mFrequencyResponses.push_back(frequencies);
+        mFrequencyResponses.push_back(responses);
+        std::vector<int> channelMapping;
+        status_t result = parcel->readInt32Vector(&channelMapping);
+        if (result != OK) {
+            return result;
+        }
+        if (channelMapping.size() != AUDIO_CHANNEL_COUNT_MAX) {
+            return BAD_VALUE;
+        }
+        for (size_t i = 0; i < channelMapping.size(); i++) {
+            mChannelMapping.push_back(channelMapping[i]);
+        }
+        RETURN_IF_FAILED(parcel->readFloat(&mSensitivity));
+        RETURN_IF_FAILED(parcel->readFloat(&mMaxSpl));
+        RETURN_IF_FAILED(parcel->readFloat(&mMinSpl));
+        RETURN_IF_FAILED(parcel->readInt32(&mDirectionality));
+        return OK;
+    }
+
+    String16 getDeviceId() const {
+        return mDeviceId;
+    }
+
+    int getPortId() const {
+        return mPortId;
+    }
+
+    unsigned int getType() const {
+        return mType;
+    }
+
+    String16 getAddress() const {
+        return mAddress;
+    }
+
+    int getDeviceLocation() const {
+        return mDeviceLocation;
+    }
+
+    int getDeviceGroup() const {
+        return mDeviceGroup;
+    }
+
+    int getIndexInTheGroup() const {
+        return mIndexInTheGroup;
+    }
+
+    const Vector<float>& getGeometricLocation() const {
+        return mGeometricLocation;
+    }
+
+    const Vector<float>& getOrientation() const {
+        return mOrientation;
+    }
+
+    const Vector<Vector<float>>& getFrequencyResponses() const {
+        return mFrequencyResponses;
+    }
+
+    const Vector<int>& getChannelMapping() const {
+        return mChannelMapping;
+    }
+
+    float getSensitivity() const {
+        return mSensitivity;
+    }
+
+    float getMaxSpl() const {
+        return mMaxSpl;
+    }
+
+    float getMinSpl() const {
+        return mMinSpl;
+    }
+
+    int getDirectionality() const {
+        return mDirectionality;
+    }
+
+private:
+    status_t readFloatVector(
+            const Parcel* parcel, Vector<float> *vectorPtr, size_t defaultLength) {
+        std::unique_ptr<std::vector<float>> v;
+        status_t result = parcel->readFloatVector(&v);
+        if (result != OK) return result;
+        vectorPtr->clear();
+        if (v.get() != nullptr) {
+            for (const auto& iter : *v) {
+                vectorPtr->push_back(iter);
+            }
+        } else {
+            vectorPtr->resize(defaultLength);
+        }
+        return OK;
+    }
+    status_t writeFloatVector(Parcel* parcel, const Vector<float>& vector) const {
+        std::vector<float> v;
+        for (size_t i = 0; i < vector.size(); i++) {
+            v.push_back(vector[i]);
+        }
+        return parcel->writeFloatVector(v);
+    }
+
+    String16 mDeviceId;
+    int32_t mPortId;
+    uint32_t mType;
+    String16 mAddress;
+    int32_t mDeviceLocation;
+    int32_t mDeviceGroup;
+    int32_t mIndexInTheGroup;
+    Vector<float> mGeometricLocation;
+    Vector<float> mOrientation;
+    Vector<Vector<float>> mFrequencyResponses;
+    Vector<int> mChannelMapping;
+    float mSensitivity;
+    float mMaxSpl;
+    float mMinSpl;
+    int32_t mDirectionality;
+};
+
+} // namespace media
+} // namespace android
+
+#endif
diff --git a/media/extractors/aac/AACExtractor.cpp b/media/extractors/aac/AACExtractor.cpp
index dfb54e2..6f28374 100644
--- a/media/extractors/aac/AACExtractor.cpp
+++ b/media/extractors/aac/AACExtractor.cpp
@@ -20,8 +20,7 @@
 
 #include "AACExtractor.h"
 #include <media/DataSource.h>
-#include <media/MediaSource.h>
-#include <media/stagefright/foundation/avc_utils.h>
+#include <media/MediaSourceBase.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -29,11 +28,12 @@
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
+#include <media/stagefright/MetaDataUtils.h>
 #include <utils/String8.h>
 
 namespace android {
 
-class AACSource : public MediaSource {
+class AACSource : public MediaSourceBase {
 public:
     AACSource(const sp<DataSource> &source,
               const sp<MetaData> &meta,
@@ -207,7 +207,7 @@
     return mInitCheck == OK ? 1 : 0;
 }
 
-sp<MediaSource> AACExtractor::getTrack(size_t index) {
+MediaSourceBase *AACExtractor::getTrack(size_t index) {
     if (mInitCheck != OK || index != 0) {
         return NULL;
     }
diff --git a/media/extractors/aac/AACExtractor.h b/media/extractors/aac/AACExtractor.h
index aede185..33fbba7 100644
--- a/media/extractors/aac/AACExtractor.h
+++ b/media/extractors/aac/AACExtractor.h
@@ -32,7 +32,7 @@
     AACExtractor(const sp<DataSource> &source, const sp<AMessage> &meta);
 
     virtual size_t countTracks();
-    virtual sp<MediaSource> getTrack(size_t index);
+    virtual MediaSourceBase *getTrack(size_t index);
     virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
 
     virtual sp<MetaData> getMetaData();
diff --git a/media/extractors/aac/Android.bp b/media/extractors/aac/Android.bp
index 7937a29..92575f2 100644
--- a/media/extractors/aac/Android.bp
+++ b/media/extractors/aac/Android.bp
@@ -13,6 +13,10 @@
         "libutils",
     ],
 
+    static_libs: [
+        "libstagefright_metadatautils",
+    ],
+
     name: "libaacextractor",
     relative_install_path: "extractors",
 
diff --git a/media/extractors/amr/AMRExtractor.cpp b/media/extractors/amr/AMRExtractor.cpp
index b8967bd..10be50c 100644
--- a/media/extractors/amr/AMRExtractor.cpp
+++ b/media/extractors/amr/AMRExtractor.cpp
@@ -21,7 +21,7 @@
 #include "AMRExtractor.h"
 
 #include <media/DataSource.h>
-#include <media/MediaSource.h>
+#include <media/MediaSourceBase.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaBufferGroup.h>
 #include <media/stagefright/MediaDefs.h>
@@ -31,7 +31,7 @@
 
 namespace android {
 
-class AMRSource : public MediaSource {
+class AMRSource : public MediaSourceBase {
 public:
     AMRSource(const sp<DataSource> &source,
               const sp<MetaData> &meta,
@@ -186,7 +186,7 @@
     return mInitCheck == OK ? 1 : 0;
 }
 
-sp<MediaSource> AMRExtractor::getTrack(size_t index) {
+MediaSourceBase *AMRExtractor::getTrack(size_t index) {
     if (mInitCheck != OK || index != 0) {
         return NULL;
     }
diff --git a/media/extractors/amr/AMRExtractor.h b/media/extractors/amr/AMRExtractor.h
index 79b22d6..56883e3 100644
--- a/media/extractors/amr/AMRExtractor.h
+++ b/media/extractors/amr/AMRExtractor.h
@@ -32,7 +32,7 @@
     explicit AMRExtractor(const sp<DataSource> &source);
 
     virtual size_t countTracks();
-    virtual sp<MediaSource> getTrack(size_t index);
+    virtual MediaSourceBase *getTrack(size_t index);
     virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
 
     virtual sp<MetaData> getMetaData();
diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp
index 0c88246..ba28e86 100644
--- a/media/extractors/flac/FLACExtractor.cpp
+++ b/media/extractors/flac/FLACExtractor.cpp
@@ -23,7 +23,7 @@
 #include "FLAC/stream_decoder.h"
 
 #include <media/DataSource.h>
-#include <media/MediaSource.h>
+#include <media/MediaSourceBase.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/base64.h>
@@ -161,7 +161,7 @@
 
 class FLACParser;
 
-class FLACSource : public MediaSource {
+class FLACSource : public MediaSourceBase {
 
 public:
     FLACSource(
@@ -936,7 +936,7 @@
     return mInitCheck == OK ? 1 : 0;
 }
 
-sp<MediaSource> FLACExtractor::getTrack(size_t index)
+MediaSourceBase *FLACExtractor::getTrack(size_t index)
 {
     if (mInitCheck != OK || index > 0) {
         return NULL;
diff --git a/media/extractors/flac/FLACExtractor.h b/media/extractors/flac/FLACExtractor.h
index 6907ceb..2e7ee3b 100644
--- a/media/extractors/flac/FLACExtractor.h
+++ b/media/extractors/flac/FLACExtractor.h
@@ -32,7 +32,7 @@
     explicit FLACExtractor(const sp<DataSource> &source);
 
     virtual size_t countTracks();
-    virtual sp<MediaSource> getTrack(size_t index);
+    virtual MediaSourceBase *getTrack(size_t index);
     virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
 
     virtual sp<MetaData> getMetaData();
diff --git a/media/extractors/midi/MidiExtractor.cpp b/media/extractors/midi/MidiExtractor.cpp
index a8509fc..18b1d23 100644
--- a/media/extractors/midi/MidiExtractor.cpp
+++ b/media/extractors/midi/MidiExtractor.cpp
@@ -25,7 +25,7 @@
 #include <media/stagefright/MediaBufferGroup.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MetaData.h>
-#include <media/MediaSource.h>
+#include <media/MediaSourceBase.h>
 #include <libsonivox/eas_reverb.h>
 
 namespace android {
@@ -33,7 +33,7 @@
 // how many Sonivox output buffers to aggregate into one MediaBuffer
 static const int NUM_COMBINE_BUFFERS = 4;
 
-class MidiSource : public MediaSource {
+class MidiSource : public MediaSourceBase {
 
 public:
     MidiSource(
@@ -282,7 +282,7 @@
     return mInitCheck == OK ? 1 : 0;
 }
 
-sp<MediaSource> MidiExtractor::getTrack(size_t index)
+MediaSourceBase *MidiExtractor::getTrack(size_t index)
 {
     if (mInitCheck != OK || index > 0) {
         return NULL;
diff --git a/media/extractors/midi/MidiExtractor.h b/media/extractors/midi/MidiExtractor.h
index 0fae94a..87e4654 100644
--- a/media/extractors/midi/MidiExtractor.h
+++ b/media/extractors/midi/MidiExtractor.h
@@ -56,7 +56,7 @@
     explicit MidiExtractor(const sp<DataSource> &source);
 
     virtual size_t countTracks();
-    virtual sp<MediaSource> getTrack(size_t index);
+    virtual MediaSourceBase *getTrack(size_t index);
     virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
 
     virtual sp<MetaData> getMetaData();
diff --git a/media/extractors/mkv/Android.bp b/media/extractors/mkv/Android.bp
index 0301ffa..c6cd753 100644
--- a/media/extractors/mkv/Android.bp
+++ b/media/extractors/mkv/Android.bp
@@ -18,6 +18,7 @@
 
     static_libs: [
         "libstagefright_flacdec",
+        "libstagefright_metadatautils",
         "libwebm",
     ],
 
diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp
index e199f03..6df0012 100644
--- a/media/extractors/mkv/MatroskaExtractor.cpp
+++ b/media/extractors/mkv/MatroskaExtractor.cpp
@@ -22,18 +22,18 @@
 #include "MatroskaExtractor.h"
 
 #include <media/DataSource.h>
-#include <media/MediaSource.h>
+#include <media/MediaSourceBase.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AUtils.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ByteUtils.h>
 #include <media/stagefright/foundation/ColorUtils.h>
-#include <media/stagefright/foundation/avc_utils.h>
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
+#include <media/stagefright/MetaDataUtils.h>
 #include <utils/String8.h>
 
 #include <inttypes.h>
@@ -121,9 +121,8 @@
     BlockIterator &operator=(const BlockIterator &);
 };
 
-struct MatroskaSource : public MediaSource {
-    MatroskaSource(
-            const sp<MatroskaExtractor> &extractor, size_t index);
+struct MatroskaSource : public MediaSourceBase {
+    MatroskaSource(MatroskaExtractor *extractor, size_t index);
 
     virtual status_t start(MetaData *params);
     virtual status_t stop();
@@ -144,7 +143,7 @@
         OTHER
     };
 
-    sp<MatroskaExtractor> mExtractor;
+    MatroskaExtractor *mExtractor;
     size_t mTrackIndex;
     Type mType;
     bool mIsAudio;
@@ -211,12 +210,12 @@
 }
 
 MatroskaSource::MatroskaSource(
-        const sp<MatroskaExtractor> &extractor, size_t index)
+        MatroskaExtractor *extractor, size_t index)
     : mExtractor(extractor),
       mTrackIndex(index),
       mType(OTHER),
       mIsAudio(false),
-      mBlockIter(mExtractor.get(),
+      mBlockIter(mExtractor,
                  mExtractor->mTracks.itemAt(index).mTrackNum,
                  index),
       mNALSizeLen(-1) {
@@ -928,7 +927,7 @@
     return mTracks.size();
 }
 
-sp<MediaSource> MatroskaExtractor::getTrack(size_t index) {
+MediaSourceBase *MatroskaExtractor::getTrack(size_t index) {
     if (index >= mTracks.size()) {
         return NULL;
     }
diff --git a/media/extractors/mkv/MatroskaExtractor.h b/media/extractors/mkv/MatroskaExtractor.h
index 26f8d19..54419bf 100644
--- a/media/extractors/mkv/MatroskaExtractor.h
+++ b/media/extractors/mkv/MatroskaExtractor.h
@@ -38,7 +38,7 @@
 
     virtual size_t countTracks();
 
-    virtual sp<MediaSource> getTrack(size_t index);
+    virtual MediaSourceBase *getTrack(size_t index);
 
     virtual sp<MetaData> getTrackMetaData(
             size_t index, uint32_t flags);
diff --git a/media/extractors/mp3/MP3Extractor.cpp b/media/extractors/mp3/MP3Extractor.cpp
index f26ed25..7e27fd8 100644
--- a/media/extractors/mp3/MP3Extractor.cpp
+++ b/media/extractors/mp3/MP3Extractor.cpp
@@ -25,7 +25,7 @@
 #include "XINGSeeker.h"
 
 #include <media/DataSource.h>
-#include <media/MediaSource.h>
+#include <media/MediaSourceBase.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/foundation/avc_utils.h>
@@ -209,7 +209,7 @@
     return valid;
 }
 
-class MP3Source : public MediaSource {
+class MP3Source : public MediaSourceBase {
 public:
     MP3Source(
             const sp<MetaData> &meta, const sp<DataSource> &source,
@@ -407,7 +407,7 @@
     return mInitCheck != OK ? 0 : 1;
 }
 
-sp<MediaSource> MP3Extractor::getTrack(size_t index) {
+MediaSourceBase *MP3Extractor::getTrack(size_t index) {
     if (mInitCheck != OK || index != 0) {
         return NULL;
     }
diff --git a/media/extractors/mp3/MP3Extractor.h b/media/extractors/mp3/MP3Extractor.h
index f0ab6b0..3b3387d 100644
--- a/media/extractors/mp3/MP3Extractor.h
+++ b/media/extractors/mp3/MP3Extractor.h
@@ -34,7 +34,7 @@
     MP3Extractor(const sp<DataSource> &source, const sp<AMessage> &meta);
 
     virtual size_t countTracks();
-    virtual sp<MediaSource> getTrack(size_t index);
+    virtual MediaSourceBase *getTrack(size_t index);
     virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
 
     virtual sp<MetaData> getMetaData();
diff --git a/media/extractors/mp4/Android.bp b/media/extractors/mp4/Android.bp
index fce8dd6..3fe2336 100644
--- a/media/extractors/mp4/Android.bp
+++ b/media/extractors/mp4/Android.bp
@@ -1,4 +1,5 @@
-cc_library_shared {
+cc_defaults {
+    name: "libmp4extractor_defaults",
 
     srcs: [
         "ItemTable.cpp",
@@ -23,17 +24,21 @@
         "libstagefright_id3",
     ],
 
-    name: "libmp4extractor",
-    relative_install_path: "extractors",
-
-    compile_multilib: "first",
-
     cflags: [
         "-Werror",
         "-Wall",
         "-fvisibility=hidden",
     ],
     version_script: "exports.lds",
+    relative_install_path: "extractors",
+    compile_multilib: "first",
+}
+
+cc_library_shared {
+
+
+    name: "libmp4extractor",
+    defaults: ["libmp4extractor_defaults"],
 
     sanitize: {
         cfi: true,
@@ -47,3 +52,9 @@
     },
 
 }
+
+cc_library_static {
+    name: "libmp4extractor_fuzzing",
+
+    defaults: ["libmp4extractor_defaults"],
+}
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 938bd5d..9f21db6 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -31,7 +31,7 @@
 #include "ItemTable.h"
 #include "include/ESDS.h"
 
-#include <media/MediaSource.h>
+#include <media/MediaSourceBase.h>
 #include <media/stagefright/foundation/ABitReader.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -65,11 +65,10 @@
     kMaxAtomSize = 64 * 1024 * 1024,
 };
 
-class MPEG4Source : public MediaSource {
+class MPEG4Source : public MediaSourceBase {
 public:
     // Caller retains ownership of both "dataSource" and "sampleTable".
-    MPEG4Source(const sp<MPEG4Extractor> &owner,
-                const sp<MetaData> &format,
+    MPEG4Source(const sp<MetaData> &format,
                 const sp<DataSource> &dataSource,
                 int32_t timeScale,
                 const sp<SampleTable> &sampleTable,
@@ -88,14 +87,11 @@
     virtual bool supportNonblockingRead() { return true; }
     virtual status_t fragmentedRead(MediaBuffer **buffer, const ReadOptions *options = NULL);
 
-protected:
     virtual ~MPEG4Source();
 
 private:
     Mutex mLock;
 
-    // keep the MPEG4Extractor around, since we're referencing its data
-    sp<MPEG4Extractor> mOwner;
     sp<MetaData> mFormat;
     sp<DataSource> mDataSource;
     int32_t mTimescale;
@@ -606,6 +602,14 @@
                 ALOGE("heif image %u has no meta!", imageIndex);
                 continue;
             }
+            // Some heif files advertise image sequence brands (eg. 'hevc') in
+            // ftyp box, but don't have any valid tracks in them. Instead of
+            // reporting the entire file as malformed, we override the error
+            // to allow still images to be extracted.
+            if (err != OK) {
+                ALOGW("Extracting still images only");
+                err = OK;
+            }
 
             ALOGV("adding HEIF image track %u", imageIndex);
             Track *track = new Track;
@@ -3412,7 +3416,7 @@
     }
 }
 
-sp<MediaSource> MPEG4Extractor::getTrack(size_t index) {
+MediaSourceBase *MPEG4Extractor::getTrack(size_t index) {
     status_t err;
     if ((err = readMetaData()) != OK) {
         return NULL;
@@ -3488,10 +3492,11 @@
         }
     }
 
-    sp<MPEG4Source> source =  new MPEG4Source(this,
+    MPEG4Source *source =  new MPEG4Source(
             track->meta, mDataSource, track->timescale, track->sampleTable,
             mSidxEntries, trex, mMoofOffset, itemTable);
     if (source->init() != OK) {
+        delete source;
         return NULL;
     }
     return source;
@@ -3884,7 +3889,6 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 MPEG4Source::MPEG4Source(
-        const sp<MPEG4Extractor> &owner,
         const sp<MetaData> &format,
         const sp<DataSource> &dataSource,
         int32_t timeScale,
@@ -3893,8 +3897,7 @@
         const Trex *trex,
         off64_t firstMoofOffset,
         const sp<ItemTable> &itemTable)
-    : mOwner(owner),
-      mFormat(format),
+    : mFormat(format),
       mDataSource(dataSource),
       mTimescale(timeScale),
       mSampleTable(sampleTable),
diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h
index 8bfecaa..e947b87 100644
--- a/media/extractors/mp4/MPEG4Extractor.h
+++ b/media/extractors/mp4/MPEG4Extractor.h
@@ -22,6 +22,7 @@
 
 #include <media/DataSource.h>
 #include <media/MediaExtractor.h>
+#include <media/stagefright/foundation/AString.h>
 #include <utils/List.h>
 #include <utils/Vector.h>
 #include <utils/String8.h>
@@ -55,7 +56,7 @@
     explicit MPEG4Extractor(const sp<DataSource> &source, const char *mime = NULL);
 
     virtual size_t countTracks();
-    virtual sp<MediaSource> getTrack(size_t index);
+    virtual MediaSourceBase *getTrack(size_t index);
     virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
 
     virtual sp<MetaData> getMetaData();
diff --git a/media/extractors/mpeg2/MPEG2PSExtractor.cpp b/media/extractors/mpeg2/MPEG2PSExtractor.cpp
index c519caf..0978387 100644
--- a/media/extractors/mpeg2/MPEG2PSExtractor.cpp
+++ b/media/extractors/mpeg2/MPEG2PSExtractor.cpp
@@ -24,7 +24,7 @@
 #include "mpeg2ts/ESQueue.h"
 
 #include <media/DataSource.h>
-#include <media/MediaSource.h>
+#include <media/MediaSourceBase.h>
 #include <media/stagefright/foundation/ABitReader.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -40,7 +40,7 @@
 
 namespace android {
 
-struct MPEG2PSExtractor::Track : public MediaSource {
+struct MPEG2PSExtractor::Track : public MediaSourceBase, public RefBase {
     Track(MPEG2PSExtractor *extractor,
           unsigned stream_id, unsigned stream_type);
 
@@ -72,8 +72,8 @@
     DISALLOW_EVIL_CONSTRUCTORS(Track);
 };
 
-struct MPEG2PSExtractor::WrappedTrack : public MediaSource {
-    WrappedTrack(const sp<MPEG2PSExtractor> &extractor, const sp<Track> &track);
+struct MPEG2PSExtractor::WrappedTrack : public MediaSourceBase {
+    WrappedTrack(MPEG2PSExtractor *extractor, const sp<Track> &track);
 
     virtual status_t start(MetaData *params);
     virtual status_t stop();
@@ -86,7 +86,7 @@
     virtual ~WrappedTrack();
 
 private:
-    sp<MPEG2PSExtractor> mExtractor;
+    MPEG2PSExtractor *mExtractor;
     sp<MPEG2PSExtractor::Track> mTrack;
 
     DISALLOW_EVIL_CONSTRUCTORS(WrappedTrack);
@@ -125,7 +125,7 @@
     return mTracks.size();
 }
 
-sp<MediaSource> MPEG2PSExtractor::getTrack(size_t index) {
+MediaSourceBase *MPEG2PSExtractor::getTrack(size_t index) {
     if (index >= mTracks.size()) {
         return NULL;
     }
@@ -723,7 +723,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 MPEG2PSExtractor::WrappedTrack::WrappedTrack(
-        const sp<MPEG2PSExtractor> &extractor, const sp<Track> &track)
+        MPEG2PSExtractor *extractor, const sp<Track> &track)
     : mExtractor(extractor),
       mTrack(track) {
 }
diff --git a/media/extractors/mpeg2/MPEG2PSExtractor.h b/media/extractors/mpeg2/MPEG2PSExtractor.h
index ab3ab05..f8a97ef 100644
--- a/media/extractors/mpeg2/MPEG2PSExtractor.h
+++ b/media/extractors/mpeg2/MPEG2PSExtractor.h
@@ -34,7 +34,7 @@
     explicit MPEG2PSExtractor(const sp<DataSource> &source);
 
     virtual size_t countTracks();
-    virtual sp<MediaSource> getTrack(size_t index);
+    virtual MediaSourceBase *getTrack(size_t index);
     virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
 
     virtual sp<MetaData> getMetaData();
diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.cpp b/media/extractors/mpeg2/MPEG2TSExtractor.cpp
index fef4d30..b24e8db 100644
--- a/media/extractors/mpeg2/MPEG2TSExtractor.cpp
+++ b/media/extractors/mpeg2/MPEG2TSExtractor.cpp
@@ -24,7 +24,7 @@
 
 #include <media/DataSource.h>
 #include <media/IStreamSource.h>
-#include <media/MediaSource.h>
+#include <media/MediaSourceBase.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/ALooper.h>
@@ -49,11 +49,12 @@
 static const int kMaxDurationReadSize = 250000LL;
 static const int kMaxDurationRetry = 6;
 
-struct MPEG2TSSource : public MediaSource {
+struct MPEG2TSSource : public MediaSourceBase {
     MPEG2TSSource(
-            const sp<MPEG2TSExtractor> &extractor,
+            MPEG2TSExtractor *extractor,
             const sp<AnotherPacketSource> &impl,
             bool doesSeek);
+    virtual ~MPEG2TSSource();
 
     virtual status_t start(MetaData *params = NULL);
     virtual status_t stop();
@@ -63,7 +64,7 @@
             MediaBuffer **buffer, const ReadOptions *options = NULL);
 
 private:
-    sp<MPEG2TSExtractor> mExtractor;
+    MPEG2TSExtractor *mExtractor;
     sp<AnotherPacketSource> mImpl;
 
     // If there are both audio and video streams, only the video stream
@@ -74,7 +75,7 @@
 };
 
 MPEG2TSSource::MPEG2TSSource(
-        const sp<MPEG2TSExtractor> &extractor,
+        MPEG2TSExtractor *extractor,
         const sp<AnotherPacketSource> &impl,
         bool doesSeek)
     : mExtractor(extractor),
@@ -82,6 +83,9 @@
       mDoesSeek(doesSeek) {
 }
 
+MPEG2TSSource::~MPEG2TSSource() {
+}
+
 status_t MPEG2TSSource::start(MetaData *params) {
     return mImpl->start(params);
 }
@@ -129,7 +133,7 @@
     return mSourceImpls.size();
 }
 
-sp<MediaSource> MPEG2TSExtractor::getTrack(size_t index) {
+MediaSourceBase *MPEG2TSExtractor::getTrack(size_t index) {
     if (index >= mSourceImpls.size()) {
         return NULL;
     }
@@ -202,9 +206,7 @@
             break;
         }
         if (!haveVideo) {
-            sp<AnotherPacketSource> impl =
-                (AnotherPacketSource *)mParser->getSource(
-                        ATSParser::VIDEO).get();
+            sp<AnotherPacketSource> impl = mParser->getSource(ATSParser::VIDEO);
 
             if (impl != NULL) {
                 sp<MetaData> format = impl->getFormat();
@@ -220,9 +222,7 @@
         }
 
         if (!haveAudio) {
-            sp<AnotherPacketSource> impl =
-                (AnotherPacketSource *)mParser->getSource(
-                        ATSParser::AUDIO).get();
+            sp<AnotherPacketSource> impl = mParser->getSource(ATSParser::AUDIO);
 
             if (impl != NULL) {
                 sp<MetaData> format = impl->getFormat();
@@ -261,10 +261,8 @@
     off64_t size;
     if (mDataSource->getSize(&size) == OK && (haveAudio || haveVideo)) {
         sp<AnotherPacketSource> impl = haveVideo
-                ? (AnotherPacketSource *)mParser->getSource(
-                        ATSParser::VIDEO).get()
-                : (AnotherPacketSource *)mParser->getSource(
-                        ATSParser::AUDIO).get();
+                ? mParser->getSource(ATSParser::VIDEO)
+                : mParser->getSource(ATSParser::AUDIO);
         size_t prevSyncSize = 1;
         int64_t durationUs = -1;
         List<int64_t> durations;
@@ -420,8 +418,7 @@
                 ev.reset();
 
                 int64_t firstTimeUs;
-                sp<AnotherPacketSource> src =
-                    (AnotherPacketSource *)mParser->getSource(type).get();
+                sp<AnotherPacketSource> src = mParser->getSource(type);
                 if (src == NULL || src->nextBufferTime(&firstTimeUs) != OK) {
                     continue;
                 }
@@ -449,7 +446,7 @@
         if (!allDurationsFound) {
             allDurationsFound = true;
             for (auto t: {ATSParser::VIDEO, ATSParser::AUDIO}) {
-                sp<AnotherPacketSource> src = (AnotherPacketSource *)mParser->getSource(t).get();
+                sp<AnotherPacketSource> src = mParser->getSource(t);
                 if (src == NULL) {
                     continue;
                 }
@@ -473,7 +470,7 @@
 }
 
 status_t MPEG2TSExtractor::seek(int64_t seekTimeUs,
-        const MediaSource::ReadOptions::SeekMode &seekMode) {
+        const MediaSourceBase::ReadOptions::SeekMode &seekMode) {
     if (mSeekSyncPoints == NULL || mSeekSyncPoints->isEmpty()) {
         ALOGW("No sync point to seek to.");
         // ... and therefore we have nothing useful to do here.
@@ -494,18 +491,18 @@
     }
 
     switch (seekMode) {
-        case MediaSource::ReadOptions::SEEK_NEXT_SYNC:
+        case MediaSourceBase::ReadOptions::SEEK_NEXT_SYNC:
             if (index == mSeekSyncPoints->size()) {
                 ALOGW("Next sync not found; starting from the latest sync.");
                 --index;
             }
             break;
-        case MediaSource::ReadOptions::SEEK_CLOSEST_SYNC:
-        case MediaSource::ReadOptions::SEEK_CLOSEST:
+        case MediaSourceBase::ReadOptions::SEEK_CLOSEST_SYNC:
+        case MediaSourceBase::ReadOptions::SEEK_CLOSEST:
             ALOGW("seekMode not supported: %d; falling back to PREVIOUS_SYNC",
                     seekMode);
             // fall-through
-        case MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC:
+        case MediaSourceBase::ReadOptions::SEEK_PREVIOUS_SYNC:
             if (index == 0) {
                 ALOGW("Previous sync not found; starting from the earliest "
                         "sync.");
diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.h b/media/extractors/mpeg2/MPEG2TSExtractor.h
index 55356bf..362f016 100644
--- a/media/extractors/mpeg2/MPEG2TSExtractor.h
+++ b/media/extractors/mpeg2/MPEG2TSExtractor.h
@@ -20,7 +20,7 @@
 
 #include <media/stagefright/foundation/ABase.h>
 #include <media/MediaExtractor.h>
-#include <media/MediaSource.h>
+#include <media/MediaSourceBase.h>
 #include <utils/threads.h>
 #include <utils/KeyedVector.h>
 #include <utils/Vector.h>
@@ -40,7 +40,7 @@
     explicit MPEG2TSExtractor(const sp<DataSource> &source);
 
     virtual size_t countTracks();
-    virtual sp<MediaSource> getTrack(size_t index);
+    virtual MediaSourceBase *getTrack(size_t index);
     virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
 
     virtual sp<MetaData> getMetaData();
diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp
index f42a6a8..f62ec47 100644
--- a/media/extractors/ogg/OggExtractor.cpp
+++ b/media/extractors/ogg/OggExtractor.cpp
@@ -22,7 +22,7 @@
 
 #include <cutils/properties.h>
 #include <media/DataSource.h>
-#include <media/MediaSource.h>
+#include <media/MediaSourceBase.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/base64.h>
@@ -45,8 +45,8 @@
 
 namespace android {
 
-struct OggSource : public MediaSource {
-    explicit OggSource(const sp<OggExtractor> &extractor);
+struct OggSource : public MediaSourceBase {
+    explicit OggSource(OggExtractor *extractor);
 
     virtual sp<MetaData> getFormat();
 
@@ -60,7 +60,7 @@
     virtual ~OggSource();
 
 private:
-    sp<OggExtractor> mExtractor;
+    OggExtractor *mExtractor;
     bool mStarted;
 
     OggSource(const OggSource &);
@@ -224,7 +224,7 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-OggSource::OggSource(const sp<OggExtractor> &extractor)
+OggSource::OggSource(OggExtractor *extractor)
     : mExtractor(extractor),
       mStarted(false) {
 }
@@ -1348,7 +1348,7 @@
     return mInitCheck != OK ? 0 : 1;
 }
 
-sp<MediaSource> OggExtractor::getTrack(size_t index) {
+MediaSourceBase *OggExtractor::getTrack(size_t index) {
     if (index >= 1) {
         return NULL;
     }
diff --git a/media/extractors/ogg/OggExtractor.h b/media/extractors/ogg/OggExtractor.h
index 0f7fe5f..126428c 100644
--- a/media/extractors/ogg/OggExtractor.h
+++ b/media/extractors/ogg/OggExtractor.h
@@ -34,7 +34,7 @@
     explicit OggExtractor(const sp<DataSource> &source);
 
     virtual size_t countTracks();
-    virtual sp<MediaSource> getTrack(size_t index);
+    virtual MediaSourceBase *getTrack(size_t index);
     virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
 
     virtual sp<MetaData> getMetaData();
diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp
index 6c5f893..cf22c66 100644
--- a/media/extractors/wav/WAVExtractor.cpp
+++ b/media/extractors/wav/WAVExtractor.cpp
@@ -22,7 +22,7 @@
 
 #include <audio_utils/primitives.h>
 #include <media/DataSource.h>
-#include <media/MediaSource.h>
+#include <media/MediaSourceBase.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaBufferGroup.h>
 #include <media/stagefright/MediaDefs.h>
@@ -55,7 +55,7 @@
     return ptr[1] << 8 | ptr[0];
 }
 
-struct WAVSource : public MediaSource {
+struct WAVSource : public MediaSourceBase {
     WAVSource(
             const sp<DataSource> &dataSource,
             const sp<MetaData> &meta,
@@ -120,7 +120,7 @@
     return mInitCheck == OK ? 1 : 0;
 }
 
-sp<MediaSource> WAVExtractor::getTrack(size_t index) {
+MediaSourceBase *WAVExtractor::getTrack(size_t index) {
     if (mInitCheck != OK || index > 0) {
         return NULL;
     }
@@ -564,8 +564,10 @@
         return NULL;
     }
 
-    sp<MediaExtractor> extractor = new WAVExtractor(source);
-    if (extractor->countTracks() == 0) {
+    MediaExtractor *extractor = new WAVExtractor(source);
+    int numTracks = extractor->countTracks();
+    delete extractor;
+    if (numTracks == 0) {
         return NULL;
     }
 
diff --git a/media/extractors/wav/WAVExtractor.h b/media/extractors/wav/WAVExtractor.h
index 98a2dfa..47c3c40 100644
--- a/media/extractors/wav/WAVExtractor.h
+++ b/media/extractors/wav/WAVExtractor.h
@@ -33,13 +33,12 @@
     explicit WAVExtractor(const sp<DataSource> &source);
 
     virtual size_t countTracks();
-    virtual sp<MediaSource> getTrack(size_t index);
+    virtual MediaSourceBase *getTrack(size_t index);
     virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
 
     virtual sp<MetaData> getMetaData();
     virtual const char * name() { return "WAVExtractor"; }
 
-protected:
     virtual ~WAVExtractor();
 
 private:
diff --git a/media/libaaudio/examples/loopback/src/loopback.cpp b/media/libaaudio/examples/loopback/src/loopback.cpp
index d23d907..f2254ce 100644
--- a/media/libaaudio/examples/loopback/src/loopback.cpp
+++ b/media/libaaudio/examples/loopback/src/loopback.cpp
@@ -160,16 +160,9 @@
 
 static void usage() {
     printf("Usage: aaudio_loopback [OPTION]...\n\n");
-    printf("          -c{channels}      number of output channels\n");
+    AAudioArgsParser::usage();
     printf("          -C{channels}      number of input channels\n");
     printf("          -g{gain}          recirculating loopback gain\n");
-    printf("          -m{0|1|2|3}       set MMAP policy\n");
-    printf("              0 = _UNSPECIFIED\n");
-    printf("              1 = _NEVER\n");
-    printf("              2 = _AUTO, also if -m is used with no number\n");
-    printf("              3 = _ALWAYS\n");
-    printf("          -n{numBursts}     buffer size, for example 2 for double buffered\n");
-    printf("          -p{outPerf}       set output AAUDIO_PERFORMANCE_MODE*\n");
     printf("          -P{inPerf}        set input AAUDIO_PERFORMANCE_MODE*\n");
     printf("              n for _NONE\n");
     printf("              l for _LATENCY\n");
@@ -178,8 +171,7 @@
     printf("              m for sine magnitude\n");
     printf("              e for echo latency (default)\n");
     printf("              f for file latency, analyzes %s\n\n", FILENAME_ECHOS);
-    printf("          -x                use EXCLUSIVE mode for output\n");
-    printf("          -X                use EXCLUSIVE mode for input\n");
+    printf("          -X  use EXCLUSIVE mode for input\n");
     printf("Example:  aaudio_loopback -n2 -pl -Pl -x\n");
 }
 
diff --git a/media/libaaudio/examples/utils/AAudioArgsParser.h b/media/libaaudio/examples/utils/AAudioArgsParser.h
index 142b295..4fc5b9f 100644
--- a/media/libaaudio/examples/utils/AAudioArgsParser.h
+++ b/media/libaaudio/examples/utils/AAudioArgsParser.h
@@ -88,6 +88,30 @@
         mPerformanceMode = performanceMode;
     }
 
+    aaudio_usage_t getUsage() const {
+        return mUsage;
+    }
+
+    void setUsage(aaudio_usage_t usage) {
+        mUsage = usage;
+    }
+
+    aaudio_content_type_t getContentType() const {
+        return mContentType;
+    }
+
+    void setContentType(aaudio_content_type_t contentType) {
+        mContentType = contentType;
+    }
+
+    aaudio_input_preset_t getInputPreset() const {
+        return mInputPreset;
+    }
+
+    void setInputPreset(aaudio_input_preset_t inputPreset) {
+        mInputPreset = inputPreset;
+    }
+
     int32_t getDeviceId() const {
         return mDeviceId;
     }
@@ -116,6 +140,9 @@
         AAudioStreamBuilder_setDeviceId(builder, mDeviceId);
         AAudioStreamBuilder_setSharingMode(builder, mSharingMode);
         AAudioStreamBuilder_setPerformanceMode(builder, mPerformanceMode);
+        AAudioStreamBuilder_setUsage(builder, mUsage);
+        AAudioStreamBuilder_setContentType(builder, mContentType);
+        AAudioStreamBuilder_setInputPreset(builder, mInputPreset);
     }
 
 private:
@@ -128,6 +155,10 @@
     aaudio_sharing_mode_t      mSharingMode     = AAUDIO_SHARING_MODE_SHARED;
     aaudio_performance_mode_t  mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
 
+    aaudio_usage_t             mUsage           = AAUDIO_UNSPECIFIED;
+    aaudio_content_type_t      mContentType     = AAUDIO_UNSPECIFIED;
+    aaudio_input_preset_t      mInputPreset     = AAUDIO_UNSPECIFIED;
+
     int32_t                    mNumberOfBursts  = AAUDIO_UNSPECIFIED;
 };
 
@@ -158,8 +189,8 @@
                 case 'd':
                     setDeviceId(atoi(&arg[2]));
                     break;
-                case 's':
-                    mDurationSeconds = atoi(&arg[2]);
+                case 'i':
+                    setInputPreset(atoi(&arg[2]));
                     break;
                 case 'm': {
                     aaudio_policy_t policy = AAUDIO_POLICY_AUTO;
@@ -177,9 +208,18 @@
                 case 'r':
                     setSampleRate(atoi(&arg[2]));
                     break;
+                case 's':
+                    mDurationSeconds = atoi(&arg[2]);
+                    break;
+                case 'u':
+                    setUsage(atoi(&arg[2]));
+                    break;
                 case 'x':
                     setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
                     break;
+                case 'y':
+                    setContentType(atoi(&arg[2]));
+                    break;
                 default:
                     unrecognized = true;
                     break;
@@ -207,24 +247,28 @@
     }
 
     static void usage() {
-        printf("-c{channels} -d{duration} -m -n{burstsPerBuffer} -p{perfMode} -r{rate} -x\n");
+        printf("-c{channels} -d{deviceId} -m{mmapPolicy} -n{burstsPerBuffer} -p{perfMode}");
+        printf(" -r{rate} -s{seconds} -x\n");
         printf("      Default values are UNSPECIFIED unless otherwise stated.\n");
         printf("      -b{bufferCapacity} frames\n");
         printf("      -c{channels} for example 2 for stereo\n");
         printf("      -d{deviceId} default is %d\n", AAUDIO_UNSPECIFIED);
-        printf("      -s{duration} in seconds, default is %d\n", DEFAULT_DURATION_SECONDS);
+        printf("      -i{inputPreset} eg. 5 for AAUDIO_INPUT_PRESET_CAMCORDER\n");
         printf("      -m{0|1|2|3} set MMAP policy\n");
-        printf("          0 = _UNSPECIFIED, default\n");
-        printf("          1 = _NEVER\n");
-        printf("          2 = _AUTO, also if -m is used with no number\n");
-        printf("          3 = _ALWAYS\n");
+        printf("          0 = _UNSPECIFIED, use aaudio.mmap_policy system property, default\n");
+        printf("          1 = _NEVER, never use MMAP\n");
+        printf("          2 = _AUTO, use MMAP if available, default for -m with no number\n");
+        printf("          3 = _ALWAYS, use MMAP or fail\n");
         printf("      -n{numberOfBursts} for setBufferSize\n");
         printf("      -p{performanceMode} set output AAUDIO_PERFORMANCE_MODE*, default NONE\n");
         printf("          n for _NONE\n");
         printf("          l for _LATENCY\n");
         printf("          p for _POWER_SAVING;\n");
         printf("      -r{sampleRate} for example 44100\n");
+        printf("      -s{duration} in seconds, default is %d\n", DEFAULT_DURATION_SECONDS);
+        printf("      -u{usage} eg. 14 for AAUDIO_USAGE_GAME\n");
         printf("      -x to use EXCLUSIVE mode\n");
+        printf("      -y{contentType} eg. 1 for AAUDIO_CONTENT_TYPE_SPEECH\n");
     }
 
     static aaudio_performance_mode_t parsePerformanceMode(char c) {
@@ -287,6 +331,17 @@
 
         printf("  PerformanceMode: requested = %d, actual = %d\n",
                getPerformanceMode(), AAudioStream_getPerformanceMode(stream));
+
+        printf("  Usage:        requested = %d, actual = %d\n",
+               getUsage(), AAudioStream_getUsage(stream));
+        printf("  ContentType:  requested = %d, actual = %d\n",
+               getContentType(), AAudioStream_getContentType(stream));
+
+        if (AAudioStream_getDirection(stream) == AAUDIO_DIRECTION_INPUT) {
+            printf("  InputPreset:  requested = %d, actual = %d\n",
+                   getInputPreset(), AAudioStream_getInputPreset(stream));
+        }
+
         printf("  Is MMAP used? %s\n", AAudioStream_isMMapUsed(stream)
                ? "yes" : "no");
 
diff --git a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
index 5d41fd0..e167773 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
@@ -204,7 +204,7 @@
     AAudioArgsParser::usage();
     printf("      -l{count} loopCount start/stop, every other one is silent\n");
     printf("      -t{msec}  play a high pitched tone at the beginning\n");
-    printf("      -u        force periodic Underruns by sleeping in callback\n");
+    printf("      -f        force periodic underruns by sleeping in callback\n");
 }
 
 int main(int argc, const char **argv)
@@ -234,7 +234,7 @@
                     case 't':
                         prefixToneMsec = atoi(&arg[2]);
                         break;
-                    case 'u':
+                    case 'f':
                         forceUnderruns = true;
                         break;
                     default:
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index bc294c5..6b3a8f0 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -77,22 +77,47 @@
     return rawbuffer;
 }
 
+static std::string audioSourceString(audio_source_t value) {
+    std::string source;
+    if (SourceTypeConverter::toString(value, source)) {
+        return source;
+    }
+    char rawbuffer[16];  // room for "%d"
+    snprintf(rawbuffer, sizeof(rawbuffer), "%d", value);
+    return rawbuffer;
+}
+
 void AudioRecord::MediaMetrics::gather(const AudioRecord *record)
 {
     // key for media statistics is defined in the header
     // attrs for media statistics
     static constexpr char kAudioRecordChannelCount[] = "android.media.audiorecord.channels";
-    static constexpr char kAudioRecordFormat[] = "android.media.audiorecord.format";
+    static constexpr char kAudioRecordEncoding[] = "android.media.audiorecord.encoding";
     static constexpr char kAudioRecordLatency[] = "android.media.audiorecord.latency";
     static constexpr char kAudioRecordSampleRate[] = "android.media.audiorecord.samplerate";
+    static constexpr char kAudioRecordSource[] = "android.media.audiotrack.source";
 
     // constructor guarantees mAnalyticsItem is valid
 
     mAnalyticsItem->setInt32(kAudioRecordLatency, record->mLatency);
     mAnalyticsItem->setInt32(kAudioRecordSampleRate, record->mSampleRate);
     mAnalyticsItem->setInt32(kAudioRecordChannelCount, record->mChannelCount);
-    mAnalyticsItem->setCString(kAudioRecordFormat,
+    mAnalyticsItem->setCString(kAudioRecordEncoding,
                                audioFormatTypeString(record->mFormat).c_str());
+    mAnalyticsItem->setCString(kAudioRecordSource,
+                               audioSourceString(record->mAttributes.source).c_str());
+}
+
+// hand the user a snapshot of the metrics.
+status_t AudioRecord::getMetrics(MediaAnalyticsItem * &item)
+{
+    mMediaMetrics.gather(this);
+    MediaAnalyticsItem *tmp = mMediaMetrics.dup();
+    if (tmp == nullptr) {
+        return BAD_VALUE;
+    }
+    item = tmp;
+    return NO_ERROR;
 }
 
 AudioRecord::AudioRecord(const String16 &opPackageName)
@@ -1291,6 +1316,14 @@
     }
 }
 
+// -------------------------------------------------------------------------
+
+status_t AudioRecord::getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones)
+{
+    AutoMutex lock(mLock);
+    return mAudioRecord->getActiveMicrophones(activeMicrophones).transactionError();
+}
+
 // =========================================================================
 
 void AudioRecord::DeathNotifier::binderDied(const wp<IBinder>& who __unused)
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 7969ed0..9dfb514 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -1059,11 +1059,11 @@
     return af->getPrimaryOutputFrameCount();
 }
 
-status_t AudioSystem::setLowRamDevice(bool isLowRamDevice)
+status_t AudioSystem::setLowRamDevice(bool isLowRamDevice, int64_t totalMemory)
 {
     const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
     if (af == 0) return PERMISSION_DENIED;
-    return af->setLowRamDevice(isLowRamDevice);
+    return af->setLowRamDevice(isLowRamDevice, totalMemory);
 }
 
 void AudioSystem::clearAudioConfigCache()
@@ -1278,6 +1278,13 @@
     return aps->getStreamVolumeDB(stream, index, device);
 }
 
+status_t AudioSystem::getMicrophones(std::vector<media::MicrophoneInfo> *microphones)
+{
+    const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+    if (af == 0) return PERMISSION_DENIED;
+    return af->getMicrophones(microphones);
+}
+
 // ---------------------------------------------------------------------------
 
 int AudioSystem::AudioPolicyServiceClient::addAudioPortCallback(
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index a3c66fe..55954f2 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -29,6 +29,7 @@
 #include <utils/Log.h>
 #include <private/media/AudioTrackShared.h>
 #include <media/IAudioFlinger.h>
+#include <media/AudioParameter.h>
 #include <media/AudioPolicyHelper.h>
 #include <media/AudioResamplerPublic.h>
 #include <media/MediaAnalyticsItem.h>
@@ -189,22 +190,23 @@
     static constexpr char kAudioTrackUsage[] = "android.media.audiotrack.usage";
     static constexpr char kAudioTrackSampleRate[] = "android.media.audiotrack.samplerate";
     static constexpr char kAudioTrackChannelMask[] = "android.media.audiotrack.channelmask";
-#if 0
-    // XXX: disabled temporarily for b/72027185
     static constexpr char kAudioTrackUnderrunFrames[] = "android.media.audiotrack.underrunframes";
-#endif
     static constexpr char kAudioTrackStartupGlitch[] = "android.media.audiotrack.glitch.startup";
 
+    // only if we're in a good state...
+    // XXX: shall we gather alternative info if failing?
+    const status_t lstatus = track->initCheck();
+    if (lstatus != NO_ERROR) {
+        ALOGD("no metrics gathered, track status=%d", (int) lstatus);
+        return;
+    }
+
     // constructor guarantees mAnalyticsItem is valid
 
-#if 0
-    // XXX: disabled temporarily for b/72027185
-    // must gather underrun info before cleaning mProxy information.
     const int32_t underrunFrames = track->getUnderrunFrames();
     if (underrunFrames != 0) {
         mAnalyticsItem->setInt32(kAudioTrackUnderrunFrames, underrunFrames);
     }
-#endif
 
     if (track->mTimestampStartupGlitchReported) {
         mAnalyticsItem->setInt32(kAudioTrackStartupGlitch, 1);
@@ -223,6 +225,17 @@
     mAnalyticsItem->setInt64(kAudioTrackChannelMask, track->mChannelMask);
 }
 
+// hand the user a snapshot of the metrics.
+status_t AudioTrack::getMetrics(MediaAnalyticsItem * &item)
+{
+    mMediaMetrics.gather(this);
+    MediaAnalyticsItem *tmp = mMediaMetrics.dup();
+    if (tmp == nullptr) {
+        return BAD_VALUE;
+    }
+    item = tmp;
+    return NO_ERROR;
+}
 
 AudioTrack::AudioTrack()
     : mStatus(NO_INIT),
@@ -2342,6 +2355,17 @@
     return mAudioTrack->setParameters(keyValuePairs);
 }
 
+status_t AudioTrack::selectPresentation(int presentationId, int programId)
+{
+    AutoMutex lock(mLock);
+    AudioParameter param = AudioParameter();
+    param.addInt(String8(AudioParameter::keyPresentationId), presentationId);
+    param.addInt(String8(AudioParameter::keyProgramId), programId);
+    ALOGV("PresentationId/ProgramId[%s]",param.toString().string());
+
+    return mAudioTrack->setParameters(param.toString());
+}
+
 VolumeShaper::Status AudioTrack::applyVolumeShaper(
         const sp<VolumeShaper::Configuration>& configuration,
         const sp<VolumeShaper::Operation>& operation)
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 6507a5c..3358e35 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -24,6 +24,7 @@
 
 #include <binder/IPCThreadState.h>
 #include <binder/Parcel.h>
+#include <private/android_filesystem_config.h>
 
 #include "IAudioFlinger.h"
 
@@ -85,6 +86,7 @@
     GET_AUDIO_HW_SYNC_FOR_SESSION,
     SYSTEM_READY,
     FRAME_COUNT_HAL,
+    LIST_MICROPHONES,
 };
 
 #define MAX_ITEMS_PER_LIST 1024
@@ -707,14 +709,18 @@
         return reply.readInt64();
     }
 
-    virtual status_t setLowRamDevice(bool isLowRamDevice)
+    virtual status_t setLowRamDevice(bool isLowRamDevice, int64_t totalMemory) override
     {
         Parcel data, reply;
-        data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
-        data.writeInt32((int) isLowRamDevice);
-        remote()->transact(SET_LOW_RAM_DEVICE, data, &reply);
-        return reply.readInt32();
+
+        static_assert(NO_ERROR == 0, "NO_ERROR must be 0");
+        return data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor())
+                ?: data.writeInt32((int) isLowRamDevice)
+                ?: data.writeInt64(totalMemory)
+                ?: remote()->transact(SET_LOW_RAM_DEVICE, data, &reply)
+                ?: reply.readInt32();
     }
+
     virtual status_t listAudioPorts(unsigned int *num_ports,
                                     struct audio_port *ports)
     {
@@ -838,6 +844,18 @@
         }
         return reply.readInt64();
     }
+    virtual status_t getMicrophones(std::vector<media::MicrophoneInfo> *microphones)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+        status_t status = remote()->transact(LIST_MICROPHONES, data, &reply);
+        if (status != NO_ERROR ||
+                (status = (status_t)reply.readInt32()) != NO_ERROR) {
+            return status;
+        }
+        status = reply.readParcelableVector(microphones);
+        return status;
+    }
 };
 
 IMPLEMENT_META_INTERFACE(AudioFlinger, "android.media.IAudioFlinger");
@@ -877,6 +895,24 @@
             break;
     }
 
+    // make sure the following transactions come from system components
+    switch (code) {
+        case SET_MASTER_VOLUME:
+        case SET_MASTER_MUTE:
+        case SET_MODE:
+        case SET_MIC_MUTE:
+        case SET_LOW_RAM_DEVICE:
+        case SYSTEM_READY:
+            if (IPCThreadState::self()->getCallingUid() >= AID_APP_START) {
+                ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
+                      __func__, code, IPCThreadState::self()->getCallingPid(),
+                      IPCThreadState::self()->getCallingUid());
+                return INVALID_OPERATION;
+            }
+        default:
+            break;
+    }
+
     // Whitelist of relevant events to trigger log merging.
     // Log merging should activate during audio activity of any kind. This are considered the
     // most relevant events.
@@ -1281,8 +1317,13 @@
         } break;
         case SET_LOW_RAM_DEVICE: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
-            bool isLowRamDevice = data.readInt32() != 0;
-            reply->writeInt32(setLowRamDevice(isLowRamDevice));
+            int32_t isLowRamDevice;
+            int64_t totalMemory;
+            const status_t status =
+                    data.readInt32(&isLowRamDevice) ?:
+                    data.readInt64(&totalMemory) ?:
+                    setLowRamDevice(isLowRamDevice != 0, totalMemory);
+            (void)reply->writeInt32(status);
             return NO_ERROR;
         } break;
         case LIST_AUDIO_PORTS: {
@@ -1398,6 +1439,16 @@
             reply->writeInt64( frameCountHAL((audio_io_handle_t) data.readInt32()) );
             return NO_ERROR;
         } break;
+        case LIST_MICROPHONES: {
+            CHECK_INTERFACE(IAudioFlinger, data, reply);
+            std::vector<media::MicrophoneInfo> microphones;
+            status_t status = getMicrophones(&microphones);
+            reply->writeInt32(status);
+            if (status == NO_ERROR) {
+                reply->writeParcelableVector(microphones);
+            }
+            return NO_ERROR;
+        }
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index 6478975..a24a099 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -27,7 +27,7 @@
 
 #include <media/AudioEffect.h>
 #include <media/IAudioPolicyService.h>
-
+#include <private/android_filesystem_config.h>
 #include <system/audio.h>
 
 namespace android {
@@ -869,6 +869,27 @@
             break;
     }
 
+    // make sure the following transactions come from system components
+    switch (code) {
+        case SET_DEVICE_CONNECTION_STATE:
+        case HANDLE_DEVICE_CONFIG_CHANGE:
+        case SET_PHONE_STATE:
+        case SET_RINGER_MODE:
+        case SET_FORCE_USE:
+        case INIT_STREAM_VOLUME:
+        case SET_STREAM_VOLUME:
+        case REGISTER_POLICY_MIXES:
+        case SET_MASTER_MONO:
+            if (IPCThreadState::self()->getCallingUid() >= AID_APP_START) {
+                ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
+                      __func__, code, IPCThreadState::self()->getCallingPid(),
+                      IPCThreadState::self()->getCallingUid());
+                return INVALID_OPERATION;
+            }
+        default:
+            break;
+    }
+
     switch (code) {
         case SET_DEVICE_CONNECTION_STATE: {
             CHECK_INTERFACE(IAudioPolicyService, data, reply);
diff --git a/media/libaudioclient/aidl/android/media/IAudioRecord.aidl b/media/libaudioclient/aidl/android/media/IAudioRecord.aidl
index 7572671..01e0a71 100644
--- a/media/libaudioclient/aidl/android/media/IAudioRecord.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioRecord.aidl
@@ -16,6 +16,8 @@
 
 package android.media;
 
+import android.media.MicrophoneInfo;
+
 /* Native code must specify namespace media (media::IAudioRecord) when referring to this class */
 interface IAudioRecord {
 
@@ -30,4 +32,8 @@
    * will be processed, unless flush() is called.
    */
   void stop();
+
+  /* Get a list of current active microphones.
+   */
+  void getActiveMicrophones(out MicrophoneInfo[] activeMicrophones);
 }
diff --git a/media/libaudioclient/aidl/android/media/MicrophoneInfo.aidl b/media/libaudioclient/aidl/android/media/MicrophoneInfo.aidl
new file mode 100644
index 0000000..d6e46cb
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/MicrophoneInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+parcelable MicrophoneInfo cpp_header "media/MicrophoneInfo.h";
diff --git a/media/libaudioclient/include/media/AudioMixer.h b/media/libaudioclient/include/media/AudioMixer.h
index d4ce417..2e29316 100644
--- a/media/libaudioclient/include/media/AudioMixer.h
+++ b/media/libaudioclient/include/media/AudioMixer.h
@@ -18,8 +18,11 @@
 #ifndef ANDROID_AUDIO_MIXER_H
 #define ANDROID_AUDIO_MIXER_H
 
+#include <pthread.h>
+#include <sstream>
 #include <stdint.h>
 #include <sys/types.h>
+#include <unordered_map>
 
 #include <media/AudioBufferProvider.h>
 #include <media/AudioResampler.h>
@@ -43,20 +46,14 @@
 class AudioMixer
 {
 public:
-                            AudioMixer(size_t frameCount, uint32_t sampleRate,
-                                       uint32_t maxNumTracks = MAX_NUM_TRACKS);
+    // This mixer has a hard-coded upper limit of active track inputs;
+    // the value is arbitrary but should be less than TRACK0 to avoid confusion.
+    static constexpr int32_t MAX_NUM_TRACKS = 256;
 
-    /*virtual*/             ~AudioMixer();  // non-virtual saves a v-table, restore if sub-classed
-
-
-    // This mixer has a hard-coded upper limit of 32 active track inputs.
-    // Adding support for > 32 tracks would require more than simply changing this value.
-    static const uint32_t MAX_NUM_TRACKS = 32;
-    // maximum number of channels supported by the mixer
-
+    // Do not change these unless underlying code changes.
     // This mixer has a hard-coded upper limit of 8 channels for output.
-    static const uint32_t MAX_NUM_CHANNELS = 8;
-    static const uint32_t MAX_NUM_VOLUMES = 2; // stereo volume only
+    static constexpr uint32_t MAX_NUM_CHANNELS = FCC_8;
+    static constexpr uint32_t MAX_NUM_VOLUMES = FCC_2; // stereo volume only
     // maximum number of channels supported for the content
     static const uint32_t MAX_NUM_CHANNELS_TO_DOWNMIX = AUDIO_CHANNEL_COUNT_MAX;
 
@@ -108,6 +105,12 @@
                                   // parameter 'value' is a pointer to the new playback rate.
     };
 
+    AudioMixer(size_t frameCount, uint32_t sampleRate, int32_t maxNumTracks = MAX_NUM_TRACKS)
+        : mMaxNumTracks(maxNumTracks)
+        , mSampleRate(sampleRate)
+        , mFrameCount(frameCount) {
+        pthread_once(&sOnceControl, &sInitRoutine);
+    }
 
     // For all APIs with "name": TRACK0 <= name < TRACK0 + MAX_NUM_TRACKS
 
@@ -127,27 +130,38 @@
     void        setParameter(int name, int target, int param, void *value);
 
     void        setBufferProvider(int name, AudioBufferProvider* bufferProvider);
-    void        process();
 
-    uint32_t    trackNames() const { return mTrackNames; }
+    void        process() {
+        (this->*mHook)();
+    }
 
     size_t      getUnreleasedFrames(int name) const;
 
-    static inline bool isValidPcmTrackFormat(audio_format_t format) {
-        switch (format) {
-        case AUDIO_FORMAT_PCM_8_BIT:
-        case AUDIO_FORMAT_PCM_16_BIT:
-        case AUDIO_FORMAT_PCM_24_BIT_PACKED:
-        case AUDIO_FORMAT_PCM_32_BIT:
-        case AUDIO_FORMAT_PCM_FLOAT:
-            return true;
-        default:
-            return false;
+    std::string trackNames() const {
+        std::stringstream ss;
+        for (const auto &pair : mTracks) {
+            ss << pair.first << " ";
         }
+        return ss.str();
+    }
+
+    void        setNBLogWriter(NBLog::Writer *logWriter) {
+        mNBLogWriter = logWriter;
     }
 
 private:
 
+    /* For multi-format functions (calls template functions
+     * in AudioMixerOps.h).  The template parameters are as follows:
+     *
+     *   MIXTYPE     (see AudioMixerOps.h MIXTYPE_* enumeration)
+     *   USEFLOATVOL (set to true if float volume is used)
+     *   ADJUSTVOL   (set to true if volume ramp parameters needs adjustment afterwards)
+     *   TO: int32_t (Q4.27) or float
+     *   TI: int32_t (Q4.27) or int16_t (Q0.15) or float
+     *   TA: int32_t (Q4.27)
+     */
+
     enum {
         // FIXME this representation permits up to 8 channels
         NEEDS_CHANNEL_COUNT__MASK   = 0x00000007,
@@ -164,14 +178,67 @@
         NEEDS_AUX                   = 0x00010000,
     };
 
-    struct state_t;
-    struct track_t;
+    // hook types
+    enum {
+        PROCESSTYPE_NORESAMPLEONETRACK, // others set elsewhere
+    };
 
-    typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp,
-                           int32_t* aux);
-    static const int BLOCKSIZE = 16; // 4 cache lines
+    enum {
+        TRACKTYPE_NOP,
+        TRACKTYPE_RESAMPLE,
+        TRACKTYPE_NORESAMPLE,
+        TRACKTYPE_NORESAMPLEMONO,
+    };
 
-    struct track_t {
+    // process hook functionality
+    using process_hook_t = void(AudioMixer::*)();
+
+    struct Track;
+    using hook_t = void(Track::*)(int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux);
+
+    struct Track {
+        Track()
+            : bufferProvider(nullptr)
+        {
+            // TODO: move additional initialization here.
+        }
+
+        ~Track()
+        {
+            // bufferProvider, mInputBufferProvider need not be deleted.
+            mResampler.reset(nullptr);
+            // Ensure the order of destruction of buffer providers as they
+            // release the upstream provider in the destructor.
+            mTimestretchBufferProvider.reset(nullptr);
+            mPostDownmixReformatBufferProvider.reset(nullptr);
+            mDownmixerBufferProvider.reset(nullptr);
+            mReformatBufferProvider.reset(nullptr);
+        }
+
+        bool        needsRamp() { return (volumeInc[0] | volumeInc[1] | auxInc) != 0; }
+        bool        setResampler(uint32_t trackSampleRate, uint32_t devSampleRate);
+        bool        doesResample() const { return mResampler.get() != nullptr; }
+        void        resetResampler() { if (mResampler.get() != nullptr) mResampler->reset(); }
+        void        adjustVolumeRamp(bool aux, bool useFloat = false);
+        size_t      getUnreleasedFrames() const { return mResampler.get() != nullptr ?
+                                                    mResampler->getUnreleasedFrames() : 0; };
+
+        status_t    prepareForDownmix();
+        void        unprepareForDownmix();
+        status_t    prepareForReformat();
+        void        unprepareForReformat();
+        bool        setPlaybackRate(const AudioPlaybackRate &playbackRate);
+        void        reconfigureBufferProviders();
+
+        static hook_t getTrackHook(int trackType, uint32_t channelCount,
+                audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
+
+        void track__nop(int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
+
+        template <int MIXTYPE, bool USEFLOATVOL, bool ADJUSTVOL,
+            typename TO, typename TI, typename TA>
+        void volumeMix(TO *out, size_t outFrames, const TI *in, TA *aux, bool ramp);
+
         uint32_t    needs;
 
         // TODO: Eventually remove legacy integer volume settings
@@ -181,16 +248,11 @@
         };
 
         int32_t     prevVolume[MAX_NUM_VOLUMES];
-
-        // 16-byte boundary
-
         int32_t     volumeInc[MAX_NUM_VOLUMES];
         int32_t     auxInc;
         int32_t     prevAuxLevel;
-
-        // 16-byte boundary
-
         int16_t     auxLevel;       // 0 <= auxLevel <= MAX_GAIN_INT, but signed for mul performance
+
         uint16_t    frameCount;
 
         uint8_t     channelCount;   // 1 or 2, redundant with (needs & NEEDS_CHANNEL_COUNT__MASK)
@@ -202,22 +264,16 @@
         //  for how the Track buffer provider is wrapped by another one when dowmixing is required
         AudioBufferProvider*                bufferProvider;
 
-        // 16-byte boundary
-
         mutable AudioBufferProvider::Buffer buffer; // 8 bytes
 
         hook_t      hook;
-        const void* in;             // current location in buffer
+        const void  *mIn;             // current location in buffer
 
-        // 16-byte boundary
-
-        AudioResampler*     resampler;
+        std::unique_ptr<AudioResampler> mResampler;
         uint32_t            sampleRate;
         int32_t*           mainBuffer;
         int32_t*           auxBuffer;
 
-        // 16-byte boundary
-
         /* Buffer providers are constructed to translate the track input data as needed.
          *
          * TODO: perhaps make a single PlaybackConverterProvider class to move
@@ -228,17 +284,17 @@
          *    match either mMixerInFormat or mDownmixRequiresFormat, if the downmixer
          *    requires reformat. For example, it may convert floating point input to
          *    PCM_16_bit if that's required by the downmixer.
-         * 3) downmixerBufferProvider: If not NULL, performs the channel remixing to match
+         * 3) mDownmixerBufferProvider: If not NULL, performs the channel remixing to match
          *    the number of channels required by the mixer sink.
          * 4) mPostDownmixReformatBufferProvider: If not NULL, performs reformatting from
          *    the downmixer requirements to the mixer engine input requirements.
          * 5) mTimestretchBufferProvider: Adds timestretching for playback rate
          */
         AudioBufferProvider*     mInputBufferProvider;    // externally provided buffer provider.
-        PassthruBufferProvider*  mReformatBufferProvider; // provider wrapper for reformatting.
-        PassthruBufferProvider*  downmixerBufferProvider; // wrapper for channel conversion.
-        PassthruBufferProvider*  mPostDownmixReformatBufferProvider;
-        PassthruBufferProvider*  mTimestretchBufferProvider;
+        std::unique_ptr<PassthruBufferProvider> mReformatBufferProvider;
+        std::unique_ptr<PassthruBufferProvider> mDownmixerBufferProvider;
+        std::unique_ptr<PassthruBufferProvider> mPostDownmixReformatBufferProvider;
+        std::unique_ptr<PassthruBufferProvider> mTimestretchBufferProvider;
 
         int32_t     sessionId;
 
@@ -263,129 +319,94 @@
 
         AudioPlaybackRate    mPlaybackRate;
 
-        bool        needsRamp() { return (volumeInc[0] | volumeInc[1] | auxInc) != 0; }
-        bool        setResampler(uint32_t trackSampleRate, uint32_t devSampleRate);
-        bool        doesResample() const { return resampler != NULL; }
-        void        resetResampler() { if (resampler != NULL) resampler->reset(); }
-        void        adjustVolumeRamp(bool aux, bool useFloat = false);
-        size_t      getUnreleasedFrames() const { return resampler != NULL ?
-                                                    resampler->getUnreleasedFrames() : 0; };
+    private:
+        // hooks
+        void track__genericResample(int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
+        void track__16BitsStereo(int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
+        void track__16BitsMono(int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
 
-        status_t    prepareForDownmix();
-        void        unprepareForDownmix();
-        status_t    prepareForReformat();
-        void        unprepareForReformat();
-        bool        setPlaybackRate(const AudioPlaybackRate &playbackRate);
-        void        reconfigureBufferProviders();
+        void volumeRampStereo(int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux);
+        void volumeStereo(int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux);
+
+        // multi-format track hooks
+        template <int MIXTYPE, typename TO, typename TI, typename TA>
+        void track__Resample(TO* out, size_t frameCount, TO* temp __unused, TA* aux);
+        template <int MIXTYPE, typename TO, typename TI, typename TA>
+        void track__NoResample(TO* out, size_t frameCount, TO* temp __unused, TA* aux);
     };
 
-    typedef void (*process_hook_t)(state_t* state);
-
-    // pad to 32-bytes to fill cache line
-    struct state_t {
-        uint32_t        enabledTracks;
-        uint32_t        needsChanged;
-        size_t          frameCount;
-        process_hook_t  hook;   // one of process__*, never NULL
-        int32_t         *outputTemp;
-        int32_t         *resampleTemp;
-        NBLog::Writer*  mNBLogWriter;   // associated NBLog::Writer or &mDummyLog
-        int32_t         reserved[1];
-        // FIXME allocate dynamically to save some memory when maxNumTracks < MAX_NUM_TRACKS
-        track_t         tracks[MAX_NUM_TRACKS] __attribute__((aligned(32)));
-    };
-
-    // bitmask of allocated track names, where bit 0 corresponds to TRACK0 etc.
-    uint32_t        mTrackNames;
-
-    // bitmask of configured track names; ~0 if maxNumTracks == MAX_NUM_TRACKS,
-    // but will have fewer bits set if maxNumTracks < MAX_NUM_TRACKS
-    const uint32_t  mConfiguredNames;
-
-    const uint32_t  mSampleRate;
-
-    NBLog::Writer   mDummyLogWriter;
-public:
-    // Called by FastMixer to inform AudioMixer of it's associated NBLog::Writer.
-    // FIXME It would be safer to use TLS for this, so we don't accidentally use wrong one.
-    void            setNBLogWriter(NBLog::Writer* log);
-private:
-    state_t         mState __attribute__((aligned(32)));
-
-    // Call after changing either the enabled status of a track, or parameters of an enabled track.
-    // OK to call more often than that, but unnecessary.
-    void invalidateState(uint32_t mask);
+    // TODO: remove BLOCKSIZE unit of processing - it isn't needed anymore.
+    static constexpr int BLOCKSIZE = 16;
 
     bool setChannelMasks(int name,
             audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask);
 
-    static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp,
-            int32_t* aux);
-    static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
-    static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp,
-            int32_t* aux);
-    static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp,
-            int32_t* aux);
-    static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp,
-            int32_t* aux);
-    static void volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp,
-            int32_t* aux);
+    // Called when track info changes and a new process hook should be determined.
+    void invalidate() {
+        mHook = &AudioMixer::process__validate;
+    }
 
-    static void process__validate(state_t* state);
-    static void process__nop(state_t* state);
-    static void process__genericNoResampling(state_t* state);
-    static void process__genericResampling(state_t* state);
-    static void process__OneTrack16BitsStereoNoResampling(state_t* state);
+    void process__validate();
+    void process__nop();
+    void process__genericNoResampling();
+    void process__genericResampling();
+    void process__oneTrack16BitsStereoNoResampling();
 
-    static pthread_once_t   sOnceControl;
-    static void             sInitRoutine();
-
-    /* multi-format volume mixing function (calls template functions
-     * in AudioMixerOps.h).  The template parameters are as follows:
-     *
-     *   MIXTYPE     (see AudioMixerOps.h MIXTYPE_* enumeration)
-     *   USEFLOATVOL (set to true if float volume is used)
-     *   ADJUSTVOL   (set to true if volume ramp parameters needs adjustment afterwards)
-     *   TO: int32_t (Q4.27) or float
-     *   TI: int32_t (Q4.27) or int16_t (Q0.15) or float
-     *   TA: int32_t (Q4.27)
-     */
-    template <int MIXTYPE, bool USEFLOATVOL, bool ADJUSTVOL,
-        typename TO, typename TI, typename TA>
-    static void volumeMix(TO *out, size_t outFrames,
-            const TI *in, TA *aux, bool ramp, AudioMixer::track_t *t);
-
-    // multi-format process hooks
     template <int MIXTYPE, typename TO, typename TI, typename TA>
-    static void process_NoResampleOneTrack(state_t* state);
+    void process__noResampleOneTrack();
 
-    // multi-format track hooks
-    template <int MIXTYPE, typename TO, typename TI, typename TA>
-    static void track__Resample(track_t* t, TO* out, size_t frameCount,
-            TO* temp __unused, TA* aux);
-    template <int MIXTYPE, typename TO, typename TI, typename TA>
-    static void track__NoResample(track_t* t, TO* out, size_t frameCount,
-            TO* temp __unused, TA* aux);
+    static process_hook_t getProcessHook(int processType, uint32_t channelCount,
+            audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
 
     static void convertMixerFormat(void *out, audio_format_t mixerOutFormat,
             void *in, audio_format_t mixerInFormat, size_t sampleCount);
 
-    // hook types
-    enum {
-        PROCESSTYPE_NORESAMPLEONETRACK,
-    };
-    enum {
-        TRACKTYPE_NOP,
-        TRACKTYPE_RESAMPLE,
-        TRACKTYPE_NORESAMPLE,
-        TRACKTYPE_NORESAMPLEMONO,
-    };
+    static inline bool isValidPcmTrackFormat(audio_format_t format) {
+        switch (format) {
+        case AUDIO_FORMAT_PCM_8_BIT:
+        case AUDIO_FORMAT_PCM_16_BIT:
+        case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+        case AUDIO_FORMAT_PCM_32_BIT:
+        case AUDIO_FORMAT_PCM_FLOAT:
+            return true;
+        default:
+            return false;
+        }
+    }
 
-    // functions for determining the proper process and track hooks.
-    static process_hook_t getProcessHook(int processType, uint32_t channelCount,
-            audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
-    static hook_t getTrackHook(int trackType, uint32_t channelCount,
-            audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
+    static void sInitRoutine();
+
+    // initialization constants
+    const int mMaxNumTracks;
+    const uint32_t mSampleRate;
+    const size_t mFrameCount;
+
+    NBLog::Writer *mNBLogWriter = nullptr;   // associated NBLog::Writer
+
+    process_hook_t mHook = &AudioMixer::process__nop;   // one of process__*, never nullptr
+
+    // the size of the type (int32_t) should be the largest of all types supported
+    // by the mixer.
+    std::unique_ptr<int32_t[]> mOutputTemp;
+    std::unique_ptr<int32_t[]> mResampleTemp;
+
+    // fast lookup of previously deleted track names for reuse.
+    // the AudioMixer tries to return the smallest unused name -
+    // this is an arbitrary decision (actually any non-negative
+    // integer that isn't in mTracks could be used).
+    std::set<int /* name */> mUnusedNames;    // set of unused track names (may be empty)
+
+    // track names grouped by main buffer, in no particular order of main buffer.
+    // however names for a particular main buffer are in order (by construction).
+    std::unordered_map<void * /* mainBuffer */, std::vector<int /* name */>> mGroups;
+
+    // track names that are enabled, in increasing order (by construction).
+    std::vector<int /* name */> mEnabled;
+
+    // track smart pointers, by name, in increasing order of name.
+    std::map<int /* name */, std::shared_ptr<Track>> mTracks;
+
+    static pthread_once_t sOnceControl; // initialized in constructor by first new
 };
 
 // ----------------------------------------------------------------------------
diff --git a/media/libaudioclient/include/media/AudioParameter.h b/media/libaudioclient/include/media/AudioParameter.h
index 1ace607..59ac1db 100644
--- a/media/libaudioclient/include/media/AudioParameter.h
+++ b/media/libaudioclient/include/media/AudioParameter.h
@@ -58,6 +58,12 @@
     static const char * const keyMonoOutput;
     static const char * const keyStreamHwAvSync;
 
+    //  keys for presentation selection
+    //  keyPresentationId: Audio presentation identifier
+    //  keyProgramId: Audio presentation program identifier
+    static const char * const keyPresentationId;
+    static const char * const keyProgramId;
+
     //  keyStreamConnect / Disconnect: value is an int in audio_devices_t
     static const char * const keyStreamConnect;
     static const char * const keyStreamDisconnect;
diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h
index fea973a..c07c397 100644
--- a/media/libaudioclient/include/media/AudioRecord.h
+++ b/media/libaudioclient/include/media/AudioRecord.h
@@ -23,8 +23,10 @@
 #include <media/AudioTimestamp.h>
 #include <media/MediaAnalyticsItem.h>
 #include <media/Modulo.h>
+#include <media/MicrophoneInfo.h>
 #include <utils/RefBase.h>
 #include <utils/threads.h>
+#include <vector>
 
 #include "android/media/IAudioRecord.h"
 
@@ -256,6 +258,11 @@
      */
             uint32_t    getNotificationPeriodInFrames() const { return mNotificationFramesAct; }
 
+    /*
+     * return metrics information for the current instance.
+     */
+            status_t getMetrics(MediaAnalyticsItem * &item);
+
     /* After it's created the track is not active. Call start() to
      * make it active. If set, the callback will start being called.
      * If event is not AudioSystem::SYNC_EVENT_NONE, the capture start will be delayed until
@@ -522,6 +529,11 @@
     /* Get the flags */
             audio_input_flags_t getFlags() const { AutoMutex _l(mLock); return mFlags; }
 
+    /* Get active microphones. A empty vector of MicrophoneInfo will be passed as a parameter,
+     * the data will be filled when querying the hal.
+     */
+            status_t    getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones);
+
     /*
      * Dumps the state of an audio record.
      */
@@ -698,11 +710,11 @@
             // mAnalyticsItem alloc failure will be flagged in the constructor
             // don't log empty records
             if (mAnalyticsItem->count() > 0) {
-                mAnalyticsItem->setFinalized(true);
                 mAnalyticsItem->selfrecord();
             }
         }
         void gather(const AudioRecord *record);
+        MediaAnalyticsItem *dup() { return mAnalyticsItem->dup(); }
       private:
         std::unique_ptr<MediaAnalyticsItem> mAnalyticsItem;
     };
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index e452837..3c8e7bc 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -23,11 +23,13 @@
 #include <media/AudioIoDescriptor.h>
 #include <media/IAudioFlingerClient.h>
 #include <media/IAudioPolicyServiceClient.h>
+#include <media/MicrophoneInfo.h>
 #include <system/audio.h>
 #include <system/audio_effect.h>
 #include <system/audio_policy.h>
 #include <utils/Errors.h>
 #include <utils/Mutex.h>
+#include <vector>
 
 namespace android {
 
@@ -285,7 +287,7 @@
     static uint32_t getPrimaryOutputSamplingRate();
     static size_t getPrimaryOutputFrameCount();
 
-    static status_t setLowRamDevice(bool isLowRamDevice);
+    static status_t setLowRamDevice(bool isLowRamDevice, int64_t totalMemory);
 
     // Check if hw offload is possible for given format, stream type, sample rate,
     // bit rate, duration, video and streaming or offload property is enabled
@@ -340,6 +342,8 @@
     static float    getStreamVolumeDB(
             audio_stream_type_t stream, int index, audio_devices_t device);
 
+    static status_t getMicrophones(std::vector<media::MicrophoneInfo> *microphones);
+
     // ----------------------------------------------------------------------------
 
     class AudioPortCallback : public RefBase
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index c146db9..3eb627d 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -386,6 +386,11 @@
     /* Return the static buffer specified in constructor or set(), or 0 for streaming mode */
             sp<IMemory> sharedBuffer() const { return mSharedBuffer; }
 
+    /*
+     * return metrics information for the current track.
+     */
+            status_t getMetrics(MediaAnalyticsItem * &item);
+
     /* After it's created the track is not active. Call start() to
      * make it active. If set, the callback will start being called.
      * If the track was previously paused, volume is ramped up over the first mix buffer.
@@ -760,6 +765,9 @@
     /* Gets the volume shaper state */
             sp<media::VolumeShaper::State> getVolumeShaperState(int id);
 
+    /* Selects the presentation (if available) */
+            status_t    selectPresentation(int presentationId, int programId);
+
     /* Get parameters */
             String8     getParameters(const String8& keys);
 
@@ -1193,11 +1201,11 @@
             // mAnalyticsItem alloc failure will be flagged in the constructor
             // don't log empty records
             if (mAnalyticsItem->count() > 0) {
-                mAnalyticsItem->setFinalized(true);
                 mAnalyticsItem->selfrecord();
             }
         }
         void gather(const AudioTrack *track);
+        MediaAnalyticsItem *dup() { return mAnalyticsItem->dup(); }
       private:
         std::unique_ptr<MediaAnalyticsItem> mAnalyticsItem;
     };
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index 472a3da..e6bf72f 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -35,6 +35,8 @@
 #include <media/IEffect.h>
 #include <media/IEffectClient.h>
 #include <utils/String8.h>
+#include <media/MicrophoneInfo.h>
+#include <vector>
 
 #include "android/media/IAudioRecord.h"
 
@@ -454,8 +456,9 @@
 
     // Intended for AudioService to inform AudioFlinger of device's low RAM attribute,
     // and should be called at most once.  For a definition of what "low RAM" means, see
-    // android.app.ActivityManager.isLowRamDevice().
-    virtual status_t setLowRamDevice(bool isLowRamDevice) = 0;
+    // android.app.ActivityManager.isLowRamDevice().  The totalMemory parameter
+    // is obtained from android.app.ActivityManager.MemoryInfo.totalMem.
+    virtual status_t setLowRamDevice(bool isLowRamDevice, int64_t totalMemory) = 0;
 
     /* List available audio ports and their attributes */
     virtual status_t listAudioPorts(unsigned int *num_ports,
@@ -485,6 +488,9 @@
 
     // Returns the number of frames per audio HAL buffer.
     virtual size_t frameCountHAL(audio_io_handle_t ioHandle) const = 0;
+
+    /* List available microphones and their characteristics */
+    virtual status_t getMicrophones(std::vector<media::MicrophoneInfo> *microphones) = 0;
 };
 
 
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index 5fafb8a..f1daeb4 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -62,13 +62,6 @@
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
 #endif
 
-// TODO: Move these macro/inlines to a header file.
-template <typename T>
-static inline
-T max(const T& x, const T& y) {
-    return x > y ? x : y;
-}
-
 // Set kUseNewMixer to true to use the new mixer engine always. Otherwise the
 // original code will be used for stereo sinks, the new mixer for multichannel.
 static constexpr bool kUseNewMixer = true;
@@ -93,88 +86,41 @@
 
 // ----------------------------------------------------------------------------
 
-template <typename T>
-T min(const T& a, const T& b)
-{
-    return a < b ? a : b;
-}
-
-// ----------------------------------------------------------------------------
-
-// Ensure mConfiguredNames bitmask is initialized properly on all architectures.
-// The value of 1 << x is undefined in C when x >= 32.
-
-AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate, uint32_t maxNumTracks)
-    :   mTrackNames(0), mConfiguredNames((maxNumTracks >= 32 ? 0 : 1 << maxNumTracks) - 1),
-        mSampleRate(sampleRate)
-{
-    ALOG_ASSERT(maxNumTracks <= MAX_NUM_TRACKS, "maxNumTracks %u > MAX_NUM_TRACKS %u",
-            maxNumTracks, MAX_NUM_TRACKS);
-
-    // AudioMixer is not yet capable of more than 32 active track inputs
-    ALOG_ASSERT(32 >= MAX_NUM_TRACKS, "bad MAX_NUM_TRACKS %d", MAX_NUM_TRACKS);
-
-    pthread_once(&sOnceControl, &sInitRoutine);
-
-    mState.enabledTracks= 0;
-    mState.needsChanged = 0;
-    mState.frameCount   = frameCount;
-    mState.hook         = process__nop;
-    mState.outputTemp   = NULL;
-    mState.resampleTemp = NULL;
-    mState.mNBLogWriter = &mDummyLogWriter;
-    // mState.reserved
-
-    // FIXME Most of the following initialization is probably redundant since
-    // tracks[i] should only be referenced if (mTrackNames & (1 << i)) != 0
-    // and mTrackNames is initially 0.  However, leave it here until that's verified.
-    track_t* t = mState.tracks;
-    for (unsigned i=0 ; i < MAX_NUM_TRACKS ; i++) {
-        t->resampler = NULL;
-        t->downmixerBufferProvider = NULL;
-        t->mReformatBufferProvider = NULL;
-        t->mTimestretchBufferProvider = NULL;
-        t++;
-    }
-
-}
-
-AudioMixer::~AudioMixer()
-{
-    track_t* t = mState.tracks;
-    for (unsigned i=0 ; i < MAX_NUM_TRACKS ; i++) {
-        delete t->resampler;
-        delete t->downmixerBufferProvider;
-        delete t->mReformatBufferProvider;
-        delete t->mTimestretchBufferProvider;
-        t++;
-    }
-    delete [] mState.outputTemp;
-    delete [] mState.resampleTemp;
-}
-
-void AudioMixer::setNBLogWriter(NBLog::Writer *logWriter)
-{
-    mState.mNBLogWriter = logWriter;
-}
-
 static inline audio_format_t selectMixerInFormat(audio_format_t inputFormat __unused) {
     return kUseFloat && kUseNewMixer ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
 }
 
-int AudioMixer::getTrackName(audio_channel_mask_t channelMask,
-        audio_format_t format, int sessionId)
+int AudioMixer::getTrackName(
+        audio_channel_mask_t channelMask, audio_format_t format, int sessionId)
 {
     if (!isValidPcmTrackFormat(format)) {
         ALOGE("AudioMixer::getTrackName invalid format (%#x)", format);
         return -1;
     }
-    uint32_t names = (~mTrackNames) & mConfiguredNames;
-    if (names != 0) {
-        int n = __builtin_ctz(names);
-        ALOGV("add track (%d)", n);
+    if (mTracks.size() >= (size_t)mMaxNumTracks) {
+        ALOGE("%s: out of track names (max = %d)", __func__, mMaxNumTracks);
+        return -1;
+    }
+
+    // get a new name for the track.
+    int name;
+    if (mUnusedNames.size() != 0) {
+        // reuse first name for deleted track.
+        auto it = mUnusedNames.begin();
+        name = *it;
+        (void)mUnusedNames.erase(it);
+    } else {
+        // we're fully populated, so create a new name.
+        name = mTracks.size();
+    }
+    ALOGV("add track (%d)", name);
+
+    auto t = std::make_shared<Track>();
+    mTracks[name] = t;
+
+    {
+        // TODO: move initialization to the Track constructor.
         // assume default parameters for the track, except where noted below
-        track_t* t = &mState.tracks[n];
         t->needs = 0;
 
         // Integer volume.
@@ -215,17 +161,12 @@
         // no initialization needed
         // t->buffer.frameCount
         t->hook = NULL;
-        t->in = NULL;
-        t->resampler = NULL;
+        t->mIn = NULL;
         t->sampleRate = mSampleRate;
         // setParameter(name, TRACK, MAIN_BUFFER, mixBuffer) is required before enable(name)
         t->mainBuffer = NULL;
         t->auxBuffer = NULL;
         t->mInputBufferProvider = NULL;
-        t->mReformatBufferProvider = NULL;
-        t->downmixerBufferProvider = NULL;
-        t->mPostDownmixReformatBufferProvider = NULL;
-        t->mTimestretchBufferProvider = NULL;
         t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT;
         t->mFormat = format;
         t->mMixerInFormat = selectMixerInFormat(format);
@@ -243,91 +184,78 @@
         // prepareForDownmix() may change mDownmixRequiresFormat
         ALOGVV("mMixerFormat:%#x  mMixerInFormat:%#x\n", t->mMixerFormat, t->mMixerInFormat);
         t->prepareForReformat();
-        mTrackNames |= 1 << n;
-        return TRACK0 + n;
+        return TRACK0 + name;
     }
-    ALOGE("AudioMixer::getTrackName out of available tracks");
-    return -1;
 }
 
-void AudioMixer::invalidateState(uint32_t mask)
-{
-    if (mask != 0) {
-        mState.needsChanged |= mask;
-        mState.hook = process__validate;
-    }
- }
-
 // Called when channel masks have changed for a track name
 // TODO: Fix DownmixerBufferProvider not to (possibly) change mixer input format,
 // which will simplify this logic.
 bool AudioMixer::setChannelMasks(int name,
         audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask) {
-    track_t &track = mState.tracks[name];
+    LOG_ALWAYS_FATAL_IF(mTracks.find(name) == mTracks.end(), "invalid name: %d", name);
+    const std::shared_ptr<Track> &track = mTracks[name];
 
-    if (trackChannelMask == track.channelMask
-            && mixerChannelMask == track.mMixerChannelMask) {
+    if (trackChannelMask == track->channelMask
+            && mixerChannelMask == track->mMixerChannelMask) {
         return false;  // no need to change
     }
     // always recompute for both channel masks even if only one has changed.
     const uint32_t trackChannelCount = audio_channel_count_from_out_mask(trackChannelMask);
     const uint32_t mixerChannelCount = audio_channel_count_from_out_mask(mixerChannelMask);
-    const bool mixerChannelCountChanged = track.mMixerChannelCount != mixerChannelCount;
+    const bool mixerChannelCountChanged = track->mMixerChannelCount != mixerChannelCount;
 
     ALOG_ASSERT((trackChannelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX)
             && trackChannelCount
             && mixerChannelCount);
-    track.channelMask = trackChannelMask;
-    track.channelCount = trackChannelCount;
-    track.mMixerChannelMask = mixerChannelMask;
-    track.mMixerChannelCount = mixerChannelCount;
+    track->channelMask = trackChannelMask;
+    track->channelCount = trackChannelCount;
+    track->mMixerChannelMask = mixerChannelMask;
+    track->mMixerChannelCount = mixerChannelCount;
 
     // channel masks have changed, does this track need a downmixer?
     // update to try using our desired format (if we aren't already using it)
-    const audio_format_t prevDownmixerFormat = track.mDownmixRequiresFormat;
-    const status_t status = mState.tracks[name].prepareForDownmix();
+    const audio_format_t prevDownmixerFormat = track->mDownmixRequiresFormat;
+    const status_t status = track->prepareForDownmix();
     ALOGE_IF(status != OK,
             "prepareForDownmix error %d, track channel mask %#x, mixer channel mask %#x",
-            status, track.channelMask, track.mMixerChannelMask);
+            status, track->channelMask, track->mMixerChannelMask);
 
-    if (prevDownmixerFormat != track.mDownmixRequiresFormat) {
-        track.prepareForReformat(); // because of downmixer, track format may change!
+    if (prevDownmixerFormat != track->mDownmixRequiresFormat) {
+        track->prepareForReformat(); // because of downmixer, track format may change!
     }
 
-    if (track.resampler && mixerChannelCountChanged) {
+    if (track->mResampler.get() != nullptr && mixerChannelCountChanged) {
         // resampler channels may have changed.
-        const uint32_t resetToSampleRate = track.sampleRate;
-        delete track.resampler;
-        track.resampler = NULL;
-        track.sampleRate = mSampleRate; // without resampler, track rate is device sample rate.
+        const uint32_t resetToSampleRate = track->sampleRate;
+        track->mResampler.reset(nullptr);
+        track->sampleRate = mSampleRate; // without resampler, track rate is device sample rate.
         // recreate the resampler with updated format, channels, saved sampleRate.
-        track.setResampler(resetToSampleRate /*trackSampleRate*/, mSampleRate /*devSampleRate*/);
+        track->setResampler(resetToSampleRate /*trackSampleRate*/, mSampleRate /*devSampleRate*/);
     }
     return true;
 }
 
-void AudioMixer::track_t::unprepareForDownmix() {
+void AudioMixer::Track::unprepareForDownmix() {
     ALOGV("AudioMixer::unprepareForDownmix(%p)", this);
 
-    if (mPostDownmixReformatBufferProvider != nullptr) {
+    if (mPostDownmixReformatBufferProvider.get() != nullptr) {
         // release any buffers held by the mPostDownmixReformatBufferProvider
-        // before deallocating the downmixerBufferProvider.
+        // before deallocating the mDownmixerBufferProvider.
         mPostDownmixReformatBufferProvider->reset();
     }
 
     mDownmixRequiresFormat = AUDIO_FORMAT_INVALID;
-    if (downmixerBufferProvider != NULL) {
+    if (mDownmixerBufferProvider.get() != nullptr) {
         // this track had previously been configured with a downmixer, delete it
-        ALOGV(" deleting old downmixer");
-        delete downmixerBufferProvider;
-        downmixerBufferProvider = NULL;
+        mDownmixerBufferProvider.reset(nullptr);
         reconfigureBufferProviders();
     } else {
         ALOGV(" nothing to do, no downmixer to delete");
     }
 }
 
-status_t AudioMixer::track_t::prepareForDownmix()
+status_t AudioMixer::Track::prepareForDownmix()
 {
     ALOGV("AudioMixer::prepareForDownmix(%p) with mask 0x%x",
             this, channelMask);
@@ -345,40 +273,35 @@
     if (audio_channel_mask_get_representation(channelMask)
                 == AUDIO_CHANNEL_REPRESENTATION_POSITION
             && DownmixerBufferProvider::isMultichannelCapable()) {
-        DownmixerBufferProvider* pDbp = new DownmixerBufferProvider(channelMask,
+        mDownmixerBufferProvider.reset(new DownmixerBufferProvider(channelMask,
                 mMixerChannelMask,
                 AUDIO_FORMAT_PCM_16_BIT /* TODO: use mMixerInFormat, now only PCM 16 */,
-                sampleRate, sessionId, kCopyBufferFrameCount);
-
-        if (pDbp->isValid()) { // if constructor completed properly
+                sampleRate, sessionId, kCopyBufferFrameCount));
+        if (static_cast<DownmixerBufferProvider *>(mDownmixerBufferProvider.get())->isValid()) {
             mDownmixRequiresFormat = AUDIO_FORMAT_PCM_16_BIT; // PCM 16 bit required for downmix
-            downmixerBufferProvider = pDbp;
             reconfigureBufferProviders();
             return NO_ERROR;
         }
-        delete pDbp;
+        // mDownmixerBufferProvider reset below.
     }
 
     // Effect downmixer does not accept the channel conversion.  Let's use our remixer.
-    RemixBufferProvider* pRbp = new RemixBufferProvider(channelMask,
-            mMixerChannelMask, mMixerInFormat, kCopyBufferFrameCount);
+    mDownmixerBufferProvider.reset(new RemixBufferProvider(channelMask,
+            mMixerChannelMask, mMixerInFormat, kCopyBufferFrameCount));
     // Remix always finds a conversion whereas Downmixer effect above may fail.
-    downmixerBufferProvider = pRbp;
     reconfigureBufferProviders();
     return NO_ERROR;
 }
 
-void AudioMixer::track_t::unprepareForReformat() {
+void AudioMixer::Track::unprepareForReformat() {
     ALOGV("AudioMixer::unprepareForReformat(%p)", this);
     bool requiresReconfigure = false;
-    if (mReformatBufferProvider != NULL) {
-        delete mReformatBufferProvider;
-        mReformatBufferProvider = NULL;
+    if (mReformatBufferProvider.get() != nullptr) {
+        mReformatBufferProvider.reset(nullptr);
         requiresReconfigure = true;
     }
-    if (mPostDownmixReformatBufferProvider != NULL) {
-        delete mPostDownmixReformatBufferProvider;
-        mPostDownmixReformatBufferProvider = NULL;
+    if (mPostDownmixReformatBufferProvider.get() != nullptr) {
+        mPostDownmixReformatBufferProvider.reset(nullptr);
         requiresReconfigure = true;
     }
     if (requiresReconfigure) {
@@ -386,7 +309,7 @@
     }
 }
 
-status_t AudioMixer::track_t::prepareForReformat()
+status_t AudioMixer::Track::prepareForReformat()
 {
     ALOGV("AudioMixer::prepareForReformat(%p) with format %#x", this, mFormat);
     // discard previous reformatters
@@ -396,19 +319,19 @@
             ? mDownmixRequiresFormat : mMixerInFormat;
     bool requiresReconfigure = false;
     if (mFormat != targetFormat) {
-        mReformatBufferProvider = new ReformatBufferProvider(
+        mReformatBufferProvider.reset(new ReformatBufferProvider(
                 audio_channel_count_from_out_mask(channelMask),
                 mFormat,
                 targetFormat,
-                kCopyBufferFrameCount);
+                kCopyBufferFrameCount));
         requiresReconfigure = true;
     }
     if (targetFormat != mMixerInFormat) {
-        mPostDownmixReformatBufferProvider = new ReformatBufferProvider(
+        mPostDownmixReformatBufferProvider.reset(new ReformatBufferProvider(
                 audio_channel_count_from_out_mask(mMixerChannelMask),
                 targetFormat,
                 mMixerInFormat,
-                kCopyBufferFrameCount);
+                kCopyBufferFrameCount));
         requiresReconfigure = true;
     }
     if (requiresReconfigure) {
@@ -417,74 +340,63 @@
     return NO_ERROR;
 }
 
-void AudioMixer::track_t::reconfigureBufferProviders()
+void AudioMixer::Track::reconfigureBufferProviders()
 {
     bufferProvider = mInputBufferProvider;
-    if (mReformatBufferProvider) {
+    if (mReformatBufferProvider.get() != nullptr) {
         mReformatBufferProvider->setBufferProvider(bufferProvider);
-        bufferProvider = mReformatBufferProvider;
+        bufferProvider = mReformatBufferProvider.get();
     }
-    if (downmixerBufferProvider) {
-        downmixerBufferProvider->setBufferProvider(bufferProvider);
-        bufferProvider = downmixerBufferProvider;
+    if (mDownmixerBufferProvider.get() != nullptr) {
+        mDownmixerBufferProvider->setBufferProvider(bufferProvider);
+        bufferProvider = mDownmixerBufferProvider.get();
     }
-    if (mPostDownmixReformatBufferProvider) {
+    if (mPostDownmixReformatBufferProvider.get() != nullptr) {
         mPostDownmixReformatBufferProvider->setBufferProvider(bufferProvider);
-        bufferProvider = mPostDownmixReformatBufferProvider;
+        bufferProvider = mPostDownmixReformatBufferProvider.get();
     }
-    if (mTimestretchBufferProvider) {
+    if (mTimestretchBufferProvider.get() != nullptr) {
         mTimestretchBufferProvider->setBufferProvider(bufferProvider);
-        bufferProvider = mTimestretchBufferProvider;
+        bufferProvider = mTimestretchBufferProvider.get();
     }
 }
 
 void AudioMixer::deleteTrackName(int name)
 {
-    ALOGV("AudioMixer::deleteTrackName(%d)", name);
     name -= TRACK0;
-    LOG_ALWAYS_FATAL_IF(name < 0 || name >= (int)MAX_NUM_TRACKS, "bad track name %d", name);
+    LOG_ALWAYS_FATAL_IF(mTracks.find(name) == mTracks.end(), "invalid name: %d", name);
     ALOGV("deleteTrackName(%d)", name);
-    track_t& track(mState.tracks[ name ]);
-    if (track.enabled) {
-        track.enabled = false;
-        invalidateState(1<<name);
+
+    if (mTracks[name]->enabled) {
+        invalidate();
     }
-    // delete the resampler
-    delete track.resampler;
-    track.resampler = NULL;
-    // delete the downmixer
-    mState.tracks[name].unprepareForDownmix();
-    // delete the reformatter
-    mState.tracks[name].unprepareForReformat();
-    // delete the timestretch provider
-    delete track.mTimestretchBufferProvider;
-    track.mTimestretchBufferProvider = NULL;
-    mTrackNames &= ~(1<<name);
+    mTracks.erase(name); // deallocate track
+    mUnusedNames.emplace(name); // recycle name
 }
 
 void AudioMixer::enable(int name)
 {
     name -= TRACK0;
-    ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name);
-    track_t& track = mState.tracks[name];
+    LOG_ALWAYS_FATAL_IF(mTracks.find(name) == mTracks.end(), "invalid name: %d", name);
+    const std::shared_ptr<Track> &track = mTracks[name];
 
-    if (!track.enabled) {
-        track.enabled = true;
+    if (!track->enabled) {
+        track->enabled = true;
         ALOGV("enable(%d)", name);
-        invalidateState(1 << name);
+        invalidate();
     }
 }
 
 void AudioMixer::disable(int name)
 {
     name -= TRACK0;
-    ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name);
-    track_t& track = mState.tracks[name];
+    LOG_ALWAYS_FATAL_IF(mTracks.find(name) == mTracks.end(), "invalid name: %d", name);
+    const std::shared_ptr<Track> &track = mTracks[name];
 
-    if (track.enabled) {
-        track.enabled = false;
+    if (track->enabled) {
+        track->enabled = false;
         ALOGV("disable(%d)", name);
-        invalidateState(1 << name);
+        invalidate();
     }
 }
 
@@ -562,7 +474,8 @@
         ALOGD_IF(*pPrevVolume != *pSetVolume, "previous float ramp hasn't finished,"
                 " prev:%f  set_to:%f", *pPrevVolume, *pSetVolume);
         const float inc = (newVolume - *pPrevVolume) / ramp; // could be inf, nan, subnormal
-        const float maxv = max(newVolume, *pPrevVolume); // could be inf, cannot be nan, subnormal
+        // could be inf, cannot be nan, subnormal
+        const float maxv = std::max(newVolume, *pPrevVolume);
 
         if (isnormal(inc) // inc must be a normal number (no subnormals, infinite, nan)
                 && maxv + inc != maxv) { // inc must make forward progress
@@ -616,8 +529,8 @@
 void AudioMixer::setParameter(int name, int target, int param, void *value)
 {
     name -= TRACK0;
-    ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name);
-    track_t& track = mState.tracks[name];
+    LOG_ALWAYS_FATAL_IF(mTracks.find(name) == mTracks.end(), "invalid name: %d", name);
+    const std::shared_ptr<Track> &track = mTracks[name];
 
     int valueInt = static_cast<int>(reinterpret_cast<uintptr_t>(value));
     int32_t *valueBuf = reinterpret_cast<int32_t*>(value);
@@ -629,33 +542,33 @@
         case CHANNEL_MASK: {
             const audio_channel_mask_t trackChannelMask =
                 static_cast<audio_channel_mask_t>(valueInt);
-            if (setChannelMasks(name, trackChannelMask, track.mMixerChannelMask)) {
+            if (setChannelMasks(name, trackChannelMask, track->mMixerChannelMask)) {
                 ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", trackChannelMask);
-                invalidateState(1 << name);
+                invalidate();
             }
             } break;
         case MAIN_BUFFER:
-            if (track.mainBuffer != valueBuf) {
-                track.mainBuffer = valueBuf;
+            if (track->mainBuffer != valueBuf) {
+                track->mainBuffer = valueBuf;
                 ALOGV("setParameter(TRACK, MAIN_BUFFER, %p)", valueBuf);
-                invalidateState(1 << name);
+                invalidate();
             }
             break;
         case AUX_BUFFER:
-            if (track.auxBuffer != valueBuf) {
-                track.auxBuffer = valueBuf;
+            if (track->auxBuffer != valueBuf) {
+                track->auxBuffer = valueBuf;
                 ALOGV("setParameter(TRACK, AUX_BUFFER, %p)", valueBuf);
-                invalidateState(1 << name);
+                invalidate();
             }
             break;
         case FORMAT: {
             audio_format_t format = static_cast<audio_format_t>(valueInt);
-            if (track.mFormat != format) {
+            if (track->mFormat != format) {
                 ALOG_ASSERT(audio_is_linear_pcm(format), "Invalid format %#x", format);
-                track.mFormat = format;
+                track->mFormat = format;
                 ALOGV("setParameter(TRACK, FORMAT, %#x)", format);
-                track.prepareForReformat();
-                invalidateState(1 << name);
+                track->prepareForReformat();
+                invalidate();
             }
             } break;
         // FIXME do we want to support setting the downmix type from AudioFlinger?
@@ -664,17 +577,17 @@
             break          */
         case MIXER_FORMAT: {
             audio_format_t format = static_cast<audio_format_t>(valueInt);
-            if (track.mMixerFormat != format) {
-                track.mMixerFormat = format;
+            if (track->mMixerFormat != format) {
+                track->mMixerFormat = format;
                 ALOGV("setParameter(TRACK, MIXER_FORMAT, %#x)", format);
             }
             } break;
         case MIXER_CHANNEL_MASK: {
             const audio_channel_mask_t mixerChannelMask =
                     static_cast<audio_channel_mask_t>(valueInt);
-            if (setChannelMasks(name, track.channelMask, mixerChannelMask)) {
+            if (setChannelMasks(name, track->channelMask, mixerChannelMask)) {
                 ALOGV("setParameter(TRACK, MIXER_CHANNEL_MASK, %#x)", mixerChannelMask);
-                invalidateState(1 << name);
+                invalidate();
             }
             } break;
         default:
@@ -686,21 +599,20 @@
         switch (param) {
         case SAMPLE_RATE:
             ALOG_ASSERT(valueInt > 0, "bad sample rate %d", valueInt);
-            if (track.setResampler(uint32_t(valueInt), mSampleRate)) {
+            if (track->setResampler(uint32_t(valueInt), mSampleRate)) {
                 ALOGV("setParameter(RESAMPLE, SAMPLE_RATE, %u)",
                         uint32_t(valueInt));
-                invalidateState(1 << name);
+                invalidate();
             }
             break;
         case RESET:
-            track.resetResampler();
-            invalidateState(1 << name);
+            track->resetResampler();
+            invalidate();
             break;
         case REMOVE:
-            delete track.resampler;
-            track.resampler = NULL;
-            track.sampleRate = mSampleRate;
-            invalidateState(1 << name);
+            track->mResampler.reset(nullptr);
+            track->sampleRate = mSampleRate;
+            invalidate();
             break;
         default:
             LOG_ALWAYS_FATAL("setParameter resample: bad param %d", param);
@@ -712,26 +624,28 @@
         switch (param) {
         case AUXLEVEL:
             if (setVolumeRampVariables(*reinterpret_cast<float*>(value),
-                    target == RAMP_VOLUME ? mState.frameCount : 0,
-                    &track.auxLevel, &track.prevAuxLevel, &track.auxInc,
-                    &track.mAuxLevel, &track.mPrevAuxLevel, &track.mAuxInc)) {
+                    target == RAMP_VOLUME ? mFrameCount : 0,
+                    &track->auxLevel, &track->prevAuxLevel, &track->auxInc,
+                    &track->mAuxLevel, &track->mPrevAuxLevel, &track->mAuxInc)) {
                 ALOGV("setParameter(%s, AUXLEVEL: %04x)",
-                        target == VOLUME ? "VOLUME" : "RAMP_VOLUME", track.auxLevel);
-                invalidateState(1 << name);
+                        target == VOLUME ? "VOLUME" : "RAMP_VOLUME", track->auxLevel);
+                invalidate();
             }
             break;
         default:
             if ((unsigned)param >= VOLUME0 && (unsigned)param < VOLUME0 + MAX_NUM_VOLUMES) {
                 if (setVolumeRampVariables(*reinterpret_cast<float*>(value),
-                        target == RAMP_VOLUME ? mState.frameCount : 0,
-                        &track.volume[param - VOLUME0], &track.prevVolume[param - VOLUME0],
-                        &track.volumeInc[param - VOLUME0],
-                        &track.mVolume[param - VOLUME0], &track.mPrevVolume[param - VOLUME0],
-                        &track.mVolumeInc[param - VOLUME0])) {
+                        target == RAMP_VOLUME ? mFrameCount : 0,
+                        &track->volume[param - VOLUME0],
+                        &track->prevVolume[param - VOLUME0],
+                        &track->volumeInc[param - VOLUME0],
+                        &track->mVolume[param - VOLUME0],
+                        &track->mPrevVolume[param - VOLUME0],
+                        &track->mVolumeInc[param - VOLUME0])) {
                     ALOGV("setParameter(%s, VOLUME%d: %04x)",
                             target == VOLUME ? "VOLUME" : "RAMP_VOLUME", param - VOLUME0,
-                                    track.volume[param - VOLUME0]);
-                    invalidateState(1 << name);
+                                    track->volume[param - VOLUME0]);
+                    invalidate();
                 }
             } else {
                 LOG_ALWAYS_FATAL("setParameter volume: bad param %d", param);
@@ -744,16 +658,16 @@
                 const AudioPlaybackRate *playbackRate =
                         reinterpret_cast<AudioPlaybackRate*>(value);
                 ALOGW_IF(!isAudioPlaybackRateValid(*playbackRate),
-                        "bad parameters speed %f, pitch %f",playbackRate->mSpeed,
-                        playbackRate->mPitch);
-                if (track.setPlaybackRate(*playbackRate)) {
+                        "bad parameters speed %f, pitch %f",
+                        playbackRate->mSpeed, playbackRate->mPitch);
+                if (track->setPlaybackRate(*playbackRate)) {
                     ALOGV("setParameter(TIMESTRETCH, PLAYBACK_RATE, STRETCH_MODE, FALLBACK_MODE "
                             "%f %f %d %d",
                             playbackRate->mSpeed,
                             playbackRate->mPitch,
                             playbackRate->mStretchMode,
                             playbackRate->mFallbackMode);
-                    // invalidateState(1 << name);
+                    // invalidate();  (should not require reconfigure)
                 }
             } break;
             default:
@@ -766,12 +680,12 @@
     }
 }
 
-bool AudioMixer::track_t::setResampler(uint32_t trackSampleRate, uint32_t devSampleRate)
+bool AudioMixer::Track::setResampler(uint32_t trackSampleRate, uint32_t devSampleRate)
 {
-    if (trackSampleRate != devSampleRate || resampler != NULL) {
+    if (trackSampleRate != devSampleRate || mResampler.get() != nullptr) {
         if (sampleRate != trackSampleRate) {
             sampleRate = trackSampleRate;
-            if (resampler == NULL) {
+            if (mResampler.get() == nullptr) {
                 ALOGV("Creating resampler from track %d Hz to device %d Hz",
                         trackSampleRate, devSampleRate);
                 AudioResampler::src_quality quality;
@@ -787,15 +701,15 @@
 
                 // TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer
                 // but if none exists, it is the channel count (1 for mono).
-                const int resamplerChannelCount = downmixerBufferProvider != NULL
+                const int resamplerChannelCount = mDownmixerBufferProvider.get() != nullptr
                         ? mMixerChannelCount : channelCount;
                 ALOGVV("Creating resampler:"
                         " format(%#x) channels(%d) devSampleRate(%u) quality(%d)\n",
                         mMixerInFormat, resamplerChannelCount, devSampleRate, quality);
-                resampler = AudioResampler::create(
+                mResampler.reset(AudioResampler::create(
                         mMixerInFormat,
                         resamplerChannelCount,
-                        devSampleRate, quality);
+                        devSampleRate, quality));
             }
             return true;
         }
@@ -803,25 +717,25 @@
     return false;
 }
 
-bool AudioMixer::track_t::setPlaybackRate(const AudioPlaybackRate &playbackRate)
+bool AudioMixer::Track::setPlaybackRate(const AudioPlaybackRate &playbackRate)
 {
-    if ((mTimestretchBufferProvider == NULL &&
+    if ((mTimestretchBufferProvider.get() == nullptr &&
             fabs(playbackRate.mSpeed - mPlaybackRate.mSpeed) < AUDIO_TIMESTRETCH_SPEED_MIN_DELTA &&
             fabs(playbackRate.mPitch - mPlaybackRate.mPitch) < AUDIO_TIMESTRETCH_PITCH_MIN_DELTA) ||
             isAudioPlaybackRateEqual(playbackRate, mPlaybackRate)) {
         return false;
     }
     mPlaybackRate = playbackRate;
-    if (mTimestretchBufferProvider == NULL) {
+    if (mTimestretchBufferProvider.get() == nullptr) {
         // TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer
         // but if none exists, it is the channel count (1 for mono).
-        const int timestretchChannelCount = downmixerBufferProvider != NULL
+        const int timestretchChannelCount = mDownmixerBufferProvider.get() != nullptr
                 ? mMixerChannelCount : channelCount;
-        mTimestretchBufferProvider = new TimestretchBufferProvider(timestretchChannelCount,
-                mMixerInFormat, sampleRate, playbackRate);
+        mTimestretchBufferProvider.reset(new TimestretchBufferProvider(timestretchChannelCount,
+                mMixerInFormat, sampleRate, playbackRate));
         reconfigureBufferProviders();
     } else {
-        static_cast<TimestretchBufferProvider*>(mTimestretchBufferProvider)
+        static_cast<TimestretchBufferProvider*>(mTimestretchBufferProvider.get())
                 ->setPlaybackRate(playbackRate);
     }
     return true;
@@ -840,7 +754,7 @@
  *
  * There is a bit of duplicated code here, but it keeps backward compatibility.
  */
-inline void AudioMixer::track_t::adjustVolumeRamp(bool aux, bool useFloat)
+inline void AudioMixer::Track::adjustVolumeRamp(bool aux, bool useFloat)
 {
     if (useFloat) {
         for (uint32_t i = 0; i < MAX_NUM_VOLUMES; i++) {
@@ -895,8 +809,9 @@
 size_t AudioMixer::getUnreleasedFrames(int name) const
 {
     name -= TRACK0;
-    if (uint32_t(name) < MAX_NUM_TRACKS) {
-        return mState.tracks[name].getUnreleasedFrames();
+    const auto it = mTracks.find(name);
+    if (it != mTracks.end()) {
+        return it->second->getUnreleasedFrames();
     }
     return 0;
 }
@@ -904,87 +819,63 @@
 void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider)
 {
     name -= TRACK0;
-    ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name);
+    const std::shared_ptr<Track> &track = mTracks[name];
 
-    if (mState.tracks[name].mInputBufferProvider == bufferProvider) {
+    if (track->mInputBufferProvider == bufferProvider) {
         return; // don't reset any buffer providers if identical.
     }
-    if (mState.tracks[name].mReformatBufferProvider != NULL) {
-        mState.tracks[name].mReformatBufferProvider->reset();
-    } else if (mState.tracks[name].downmixerBufferProvider != NULL) {
-        mState.tracks[name].downmixerBufferProvider->reset();
-    } else if (mState.tracks[name].mPostDownmixReformatBufferProvider != NULL) {
-        mState.tracks[name].mPostDownmixReformatBufferProvider->reset();
-    } else if (mState.tracks[name].mTimestretchBufferProvider != NULL) {
-        mState.tracks[name].mTimestretchBufferProvider->reset();
+    if (track->mReformatBufferProvider.get() != nullptr) {
+        track->mReformatBufferProvider->reset();
+    } else if (track->mDownmixerBufferProvider != nullptr) {
+        track->mDownmixerBufferProvider->reset();
+    } else if (track->mPostDownmixReformatBufferProvider.get() != nullptr) {
+        track->mPostDownmixReformatBufferProvider->reset();
+    } else if (track->mTimestretchBufferProvider.get() != nullptr) {
+        track->mTimestretchBufferProvider->reset();
     }
 
-    mState.tracks[name].mInputBufferProvider = bufferProvider;
-    mState.tracks[name].reconfigureBufferProviders();
+    track->mInputBufferProvider = bufferProvider;
+    track->reconfigureBufferProviders();
 }
 
-
-void AudioMixer::process()
+void AudioMixer::process__validate()
 {
-    mState.hook(&mState);
-}
-
-
-void AudioMixer::process__validate(state_t* state)
-{
-    ALOGW_IF(!state->needsChanged,
-        "in process__validate() but nothing's invalid");
-
-    uint32_t changed = state->needsChanged;
-    state->needsChanged = 0; // clear the validation flag
-
-    // recompute which tracks are enabled / disabled
-    uint32_t enabled = 0;
-    uint32_t disabled = 0;
-    while (changed) {
-        const int i = 31 - __builtin_clz(changed);
-        const uint32_t mask = 1<<i;
-        changed &= ~mask;
-        track_t& t = state->tracks[i];
-        (t.enabled ? enabled : disabled) |= mask;
-    }
-    state->enabledTracks &= ~disabled;
-    state->enabledTracks |=  enabled;
-
-    // compute everything we need...
-    int countActiveTracks = 0;
     // TODO: fix all16BitsStereNoResample logic to
     // either properly handle muted tracks (it should ignore them)
     // or remove altogether as an obsolete optimization.
     bool all16BitsStereoNoResample = true;
     bool resampling = false;
     bool volumeRamp = false;
-    uint32_t en = state->enabledTracks;
-    while (en) {
-        const int i = 31 - __builtin_clz(en);
-        en &= ~(1<<i);
 
-        countActiveTracks++;
-        track_t& t = state->tracks[i];
+    mEnabled.clear();
+    mGroups.clear();
+    for (const auto &pair : mTracks) {
+        const int name = pair.first;
+        const std::shared_ptr<Track> &t = pair.second;
+        if (!t->enabled) continue;
+
+        mEnabled.emplace_back(name);  // we add to mEnabled in order of name.
+        mGroups[t->mainBuffer].emplace_back(name); // mGroups also in order of name.
+
         uint32_t n = 0;
         // FIXME can overflow (mask is only 3 bits)
-        n |= NEEDS_CHANNEL_1 + t.channelCount - 1;
-        if (t.doesResample()) {
+        n |= NEEDS_CHANNEL_1 + t->channelCount - 1;
+        if (t->doesResample()) {
             n |= NEEDS_RESAMPLE;
         }
-        if (t.auxLevel != 0 && t.auxBuffer != NULL) {
+        if (t->auxLevel != 0 && t->auxBuffer != NULL) {
             n |= NEEDS_AUX;
         }
 
-        if (t.volumeInc[0]|t.volumeInc[1]) {
+        if (t->volumeInc[0]|t->volumeInc[1]) {
             volumeRamp = true;
-        } else if (!t.doesResample() && t.volumeRL == 0) {
+        } else if (!t->doesResample() && t->volumeRL == 0) {
             n |= NEEDS_MUTE;
         }
-        t.needs = n;
+        t->needs = n;
 
         if (n & NEEDS_MUTE) {
-            t.hook = track__nop;
+            t->hook = &Track::track__nop;
         } else {
             if (n & NEEDS_AUX) {
                 all16BitsStereoNoResample = false;
@@ -992,23 +883,23 @@
             if (n & NEEDS_RESAMPLE) {
                 all16BitsStereoNoResample = false;
                 resampling = true;
-                t.hook = getTrackHook(TRACKTYPE_RESAMPLE, t.mMixerChannelCount,
-                        t.mMixerInFormat, t.mMixerFormat);
+                t->hook = Track::getTrackHook(TRACKTYPE_RESAMPLE, t->mMixerChannelCount,
+                        t->mMixerInFormat, t->mMixerFormat);
                 ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
                         "Track %d needs downmix + resample", i);
             } else {
                 if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){
-                    t.hook = getTrackHook(
-                            (t.mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO  // TODO: MONO_HACK
-                                    && t.channelMask == AUDIO_CHANNEL_OUT_MONO)
+                    t->hook = Track::getTrackHook(
+                            (t->mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO  // TODO: MONO_HACK
+                                    && t->channelMask == AUDIO_CHANNEL_OUT_MONO)
                                 ? TRACKTYPE_NORESAMPLEMONO : TRACKTYPE_NORESAMPLE,
-                            t.mMixerChannelCount,
-                            t.mMixerInFormat, t.mMixerFormat);
+                            t->mMixerChannelCount,
+                            t->mMixerInFormat, t->mMixerFormat);
                     all16BitsStereoNoResample = false;
                 }
                 if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2){
-                    t.hook = getTrackHook(TRACKTYPE_NORESAMPLE, t.mMixerChannelCount,
-                            t.mMixerInFormat, t.mMixerFormat);
+                    t->hook = Track::getTrackHook(TRACKTYPE_NORESAMPLE, t->mMixerChannelCount,
+                            t->mMixerInFormat, t->mMixerFormat);
                     ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
                             "Track %d needs downmix", i);
                 }
@@ -1017,137 +908,125 @@
     }
 
     // select the processing hooks
-    state->hook = process__nop;
-    if (countActiveTracks > 0) {
+    mHook = &AudioMixer::process__nop;
+    if (mEnabled.size() > 0) {
         if (resampling) {
-            if (!state->outputTemp) {
-                state->outputTemp = new int32_t[MAX_NUM_CHANNELS * state->frameCount];
+            if (mOutputTemp.get() == nullptr) {
+                mOutputTemp.reset(new int32_t[MAX_NUM_CHANNELS * mFrameCount]);
             }
-            if (!state->resampleTemp) {
-                state->resampleTemp = new int32_t[MAX_NUM_CHANNELS * state->frameCount];
+            if (mResampleTemp.get() == nullptr) {
+                mResampleTemp.reset(new int32_t[MAX_NUM_CHANNELS * mFrameCount]);
             }
-            state->hook = process__genericResampling;
+            mHook = &AudioMixer::process__genericResampling;
         } else {
-            if (state->outputTemp) {
-                delete [] state->outputTemp;
-                state->outputTemp = NULL;
-            }
-            if (state->resampleTemp) {
-                delete [] state->resampleTemp;
-                state->resampleTemp = NULL;
-            }
-            state->hook = process__genericNoResampling;
+            // we keep temp arrays around.
+            mHook = &AudioMixer::process__genericNoResampling;
             if (all16BitsStereoNoResample && !volumeRamp) {
-                if (countActiveTracks == 1) {
-                    const int i = 31 - __builtin_clz(state->enabledTracks);
-                    track_t& t = state->tracks[i];
-                    if ((t.needs & NEEDS_MUTE) == 0) {
+                if (mEnabled.size() == 1) {
+                    const std::shared_ptr<Track> &t = mTracks[mEnabled[0]];
+                    if ((t->needs & NEEDS_MUTE) == 0) {
                         // The check prevents a muted track from acquiring a process hook.
                         //
                         // This is dangerous if the track is MONO as that requires
                         // special case handling due to implicit channel duplication.
                         // Stereo or Multichannel should actually be fine here.
-                        state->hook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
-                                t.mMixerChannelCount, t.mMixerInFormat, t.mMixerFormat);
+                        mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
+                                t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat);
                     }
                 }
             }
         }
     }
 
-    ALOGV("mixer configuration change: %d activeTracks (%08x) "
+    ALOGV("mixer configuration change: %zu "
         "all16BitsStereoNoResample=%d, resampling=%d, volumeRamp=%d",
-        countActiveTracks, state->enabledTracks,
-        all16BitsStereoNoResample, resampling, volumeRamp);
+        mEnabled.size(), all16BitsStereoNoResample, resampling, volumeRamp);
 
-   state->hook(state);
+   process();
 
     // Now that the volume ramp has been done, set optimal state and
     // track hooks for subsequent mixer process
-    if (countActiveTracks > 0) {
+    if (mEnabled.size() > 0) {
         bool allMuted = true;
-        uint32_t en = state->enabledTracks;
-        while (en) {
-            const int i = 31 - __builtin_clz(en);
-            en &= ~(1<<i);
-            track_t& t = state->tracks[i];
-            if (!t.doesResample() && t.volumeRL == 0) {
-                t.needs |= NEEDS_MUTE;
-                t.hook = track__nop;
+
+        for (const int name : mEnabled) {
+            const std::shared_ptr<Track> &t = mTracks[name];
+            if (!t->doesResample() && t->volumeRL == 0) {
+                t->needs |= NEEDS_MUTE;
+                t->hook = &Track::track__nop;
             } else {
                 allMuted = false;
             }
         }
         if (allMuted) {
-            state->hook = process__nop;
+            mHook = &AudioMixer::process__nop;
         } else if (all16BitsStereoNoResample) {
-            if (countActiveTracks == 1) {
-                const int i = 31 - __builtin_clz(state->enabledTracks);
-                track_t& t = state->tracks[i];
+            if (mEnabled.size() == 1) {
+                //const int i = 31 - __builtin_clz(enabledTracks);
+                const std::shared_ptr<Track> &t = mTracks[mEnabled[0]];
                 // Muted single tracks handled by allMuted above.
-                state->hook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
-                        t.mMixerChannelCount, t.mMixerInFormat, t.mMixerFormat);
+                mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
+                        t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat);
             }
         }
     }
 }
 
-
-void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount,
-        int32_t* temp, int32_t* aux)
+void AudioMixer::Track::track__genericResample(
+        int32_t* out, size_t outFrameCount, int32_t* temp, int32_t* aux)
 {
     ALOGVV("track__genericResample\n");
-    t->resampler->setSampleRate(t->sampleRate);
+    mResampler->setSampleRate(sampleRate);
 
     // ramp gain - resample to temp buffer and scale/mix in 2nd step
     if (aux != NULL) {
         // always resample with unity gain when sending to auxiliary buffer to be able
         // to apply send level after resampling
-        t->resampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT);
-        memset(temp, 0, outFrameCount * t->mMixerChannelCount * sizeof(int32_t));
-        t->resampler->resample(temp, outFrameCount, t->bufferProvider);
-        if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc)) {
-            volumeRampStereo(t, out, outFrameCount, temp, aux);
+        mResampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT);
+        memset(temp, 0, outFrameCount * mMixerChannelCount * sizeof(int32_t));
+        mResampler->resample(temp, outFrameCount, bufferProvider);
+        if (CC_UNLIKELY(volumeInc[0]|volumeInc[1]|auxInc)) {
+            volumeRampStereo(out, outFrameCount, temp, aux);
         } else {
-            volumeStereo(t, out, outFrameCount, temp, aux);
+            volumeStereo(out, outFrameCount, temp, aux);
         }
     } else {
-        if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1])) {
-            t->resampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT);
+        if (CC_UNLIKELY(volumeInc[0]|volumeInc[1])) {
+            mResampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT);
             memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
-            t->resampler->resample(temp, outFrameCount, t->bufferProvider);
-            volumeRampStereo(t, out, outFrameCount, temp, aux);
+            mResampler->resample(temp, outFrameCount, bufferProvider);
+            volumeRampStereo(out, outFrameCount, temp, aux);
         }
 
         // constant gain
         else {
-            t->resampler->setVolume(t->mVolume[0], t->mVolume[1]);
-            t->resampler->resample(out, outFrameCount, t->bufferProvider);
+            mResampler->setVolume(mVolume[0], mVolume[1]);
+            mResampler->resample(out, outFrameCount, bufferProvider);
         }
     }
 }
 
-void AudioMixer::track__nop(track_t* t __unused, int32_t* out __unused,
+void AudioMixer::Track::track__nop(int32_t* out __unused,
         size_t outFrameCount __unused, int32_t* temp __unused, int32_t* aux __unused)
 {
 }
 
-void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp,
-        int32_t* aux)
+void AudioMixer::Track::volumeRampStereo(
+        int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
 {
-    int32_t vl = t->prevVolume[0];
-    int32_t vr = t->prevVolume[1];
-    const int32_t vlInc = t->volumeInc[0];
-    const int32_t vrInc = t->volumeInc[1];
+    int32_t vl = prevVolume[0];
+    int32_t vr = prevVolume[1];
+    const int32_t vlInc = volumeInc[0];
+    const int32_t vrInc = volumeInc[1];
 
     //ALOGD("[0] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
-    //        t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
+    //        t, vlInc/65536.0f, vl/65536.0f, volume[0],
     //       (vl + vlInc*frameCount)/65536.0f, frameCount);
 
     // ramp volume
     if (CC_UNLIKELY(aux != NULL)) {
-        int32_t va = t->prevAuxLevel;
-        const int32_t vaInc = t->auxInc;
+        int32_t va = prevAuxLevel;
+        const int32_t vaInc = auxInc;
         int32_t l;
         int32_t r;
 
@@ -1161,7 +1040,7 @@
             vr += vrInc;
             va += vaInc;
         } while (--frameCount);
-        t->prevAuxLevel = va;
+        prevAuxLevel = va;
     } else {
         do {
             *out++ += (vl >> 16) * (*temp++ >> 12);
@@ -1170,19 +1049,19 @@
             vr += vrInc;
         } while (--frameCount);
     }
-    t->prevVolume[0] = vl;
-    t->prevVolume[1] = vr;
-    t->adjustVolumeRamp(aux != NULL);
+    prevVolume[0] = vl;
+    prevVolume[1] = vr;
+    adjustVolumeRamp(aux != NULL);
 }
 
-void AudioMixer::volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp,
-        int32_t* aux)
+void AudioMixer::Track::volumeStereo(
+        int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
 {
-    const int16_t vl = t->volume[0];
-    const int16_t vr = t->volume[1];
+    const int16_t vl = volume[0];
+    const int16_t vr = volume[1];
 
     if (CC_UNLIKELY(aux != NULL)) {
-        const int16_t va = t->auxLevel;
+        const int16_t va = auxLevel;
         do {
             int16_t l = (int16_t)(*temp++ >> 12);
             int16_t r = (int16_t)(*temp++ >> 12);
@@ -1204,25 +1083,25 @@
     }
 }
 
-void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount,
-        int32_t* temp __unused, int32_t* aux)
+void AudioMixer::Track::track__16BitsStereo(
+        int32_t* out, size_t frameCount, int32_t* temp __unused, int32_t* aux)
 {
     ALOGVV("track__16BitsStereo\n");
-    const int16_t *in = static_cast<const int16_t *>(t->in);
+    const int16_t *in = static_cast<const int16_t *>(mIn);
 
     if (CC_UNLIKELY(aux != NULL)) {
         int32_t l;
         int32_t r;
         // ramp gain
-        if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc)) {
-            int32_t vl = t->prevVolume[0];
-            int32_t vr = t->prevVolume[1];
-            int32_t va = t->prevAuxLevel;
-            const int32_t vlInc = t->volumeInc[0];
-            const int32_t vrInc = t->volumeInc[1];
-            const int32_t vaInc = t->auxInc;
+        if (CC_UNLIKELY(volumeInc[0]|volumeInc[1]|auxInc)) {
+            int32_t vl = prevVolume[0];
+            int32_t vr = prevVolume[1];
+            int32_t va = prevAuxLevel;
+            const int32_t vlInc = volumeInc[0];
+            const int32_t vrInc = volumeInc[1];
+            const int32_t vaInc = auxInc;
             // ALOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
-            //        t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
+            //        t, vlInc/65536.0f, vl/65536.0f, volume[0],
             //        (vl + vlInc*frameCount)/65536.0f, frameCount);
 
             do {
@@ -1236,16 +1115,16 @@
                 va += vaInc;
             } while (--frameCount);
 
-            t->prevVolume[0] = vl;
-            t->prevVolume[1] = vr;
-            t->prevAuxLevel = va;
-            t->adjustVolumeRamp(true);
+            prevVolume[0] = vl;
+            prevVolume[1] = vr;
+            prevAuxLevel = va;
+            adjustVolumeRamp(true);
         }
 
         // constant gain
         else {
-            const uint32_t vrl = t->volumeRL;
-            const int16_t va = (int16_t)t->auxLevel;
+            const uint32_t vrl = volumeRL;
+            const int16_t va = (int16_t)auxLevel;
             do {
                 uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
                 int16_t a = (int16_t)(((int32_t)in[0] + in[1]) >> 1);
@@ -1259,14 +1138,14 @@
         }
     } else {
         // ramp gain
-        if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1])) {
-            int32_t vl = t->prevVolume[0];
-            int32_t vr = t->prevVolume[1];
-            const int32_t vlInc = t->volumeInc[0];
-            const int32_t vrInc = t->volumeInc[1];
+        if (CC_UNLIKELY(volumeInc[0]|volumeInc[1])) {
+            int32_t vl = prevVolume[0];
+            int32_t vr = prevVolume[1];
+            const int32_t vlInc = volumeInc[0];
+            const int32_t vrInc = volumeInc[1];
 
             // ALOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
-            //        t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
+            //        t, vlInc/65536.0f, vl/65536.0f, volume[0],
             //        (vl + vlInc*frameCount)/65536.0f, frameCount);
 
             do {
@@ -1276,14 +1155,14 @@
                 vr += vrInc;
             } while (--frameCount);
 
-            t->prevVolume[0] = vl;
-            t->prevVolume[1] = vr;
-            t->adjustVolumeRamp(false);
+            prevVolume[0] = vl;
+            prevVolume[1] = vr;
+            adjustVolumeRamp(false);
         }
 
         // constant gain
         else {
-            const uint32_t vrl = t->volumeRL;
+            const uint32_t vrl = volumeRL;
             do {
                 uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
                 in += 2;
@@ -1293,27 +1172,27 @@
             } while (--frameCount);
         }
     }
-    t->in = in;
+    mIn = in;
 }
 
-void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount,
-        int32_t* temp __unused, int32_t* aux)
+void AudioMixer::Track::track__16BitsMono(
+        int32_t* out, size_t frameCount, int32_t* temp __unused, int32_t* aux)
 {
     ALOGVV("track__16BitsMono\n");
-    const int16_t *in = static_cast<int16_t const *>(t->in);
+    const int16_t *in = static_cast<int16_t const *>(mIn);
 
     if (CC_UNLIKELY(aux != NULL)) {
         // ramp gain
-        if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc)) {
-            int32_t vl = t->prevVolume[0];
-            int32_t vr = t->prevVolume[1];
-            int32_t va = t->prevAuxLevel;
-            const int32_t vlInc = t->volumeInc[0];
-            const int32_t vrInc = t->volumeInc[1];
-            const int32_t vaInc = t->auxInc;
+        if (CC_UNLIKELY(volumeInc[0]|volumeInc[1]|auxInc)) {
+            int32_t vl = prevVolume[0];
+            int32_t vr = prevVolume[1];
+            int32_t va = prevAuxLevel;
+            const int32_t vlInc = volumeInc[0];
+            const int32_t vrInc = volumeInc[1];
+            const int32_t vaInc = auxInc;
 
             // ALOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
-            //         t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
+            //         t, vlInc/65536.0f, vl/65536.0f, volume[0],
             //         (vl + vlInc*frameCount)/65536.0f, frameCount);
 
             do {
@@ -1326,16 +1205,16 @@
                 va += vaInc;
             } while (--frameCount);
 
-            t->prevVolume[0] = vl;
-            t->prevVolume[1] = vr;
-            t->prevAuxLevel = va;
-            t->adjustVolumeRamp(true);
+            prevVolume[0] = vl;
+            prevVolume[1] = vr;
+            prevAuxLevel = va;
+            adjustVolumeRamp(true);
         }
         // constant gain
         else {
-            const int16_t vl = t->volume[0];
-            const int16_t vr = t->volume[1];
-            const int16_t va = (int16_t)t->auxLevel;
+            const int16_t vl = volume[0];
+            const int16_t vr = volume[1];
+            const int16_t va = (int16_t)auxLevel;
             do {
                 int16_t l = *in++;
                 out[0] = mulAdd(l, vl, out[0]);
@@ -1347,14 +1226,14 @@
         }
     } else {
         // ramp gain
-        if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1])) {
-            int32_t vl = t->prevVolume[0];
-            int32_t vr = t->prevVolume[1];
-            const int32_t vlInc = t->volumeInc[0];
-            const int32_t vrInc = t->volumeInc[1];
+        if (CC_UNLIKELY(volumeInc[0]|volumeInc[1])) {
+            int32_t vl = prevVolume[0];
+            int32_t vr = prevVolume[1];
+            const int32_t vlInc = volumeInc[0];
+            const int32_t vrInc = volumeInc[1];
 
             // ALOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
-            //         t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
+            //         t, vlInc/65536.0f, vl/65536.0f, volume[0],
             //         (vl + vlInc*frameCount)/65536.0f, frameCount);
 
             do {
@@ -1365,14 +1244,14 @@
                 vr += vrInc;
             } while (--frameCount);
 
-            t->prevVolume[0] = vl;
-            t->prevVolume[1] = vr;
-            t->adjustVolumeRamp(false);
+            prevVolume[0] = vl;
+            prevVolume[1] = vr;
+            adjustVolumeRamp(false);
         }
         // constant gain
         else {
-            const int16_t vl = t->volume[0];
-            const int16_t vr = t->volume[1];
+            const int16_t vl = volume[0];
+            const int16_t vr = volume[1];
             do {
                 int16_t l = *in++;
                 out[0] = mulAdd(l, vl, out[0]);
@@ -1381,274 +1260,214 @@
             } while (--frameCount);
         }
     }
-    t->in = in;
+    mIn = in;
 }
 
 // no-op case
-void AudioMixer::process__nop(state_t* state)
+void AudioMixer::process__nop()
 {
     ALOGVV("process__nop\n");
-    uint32_t e0 = state->enabledTracks;
-    while (e0) {
+
+    for (const auto &pair : mGroups) {
         // process by group of tracks with same output buffer to
         // avoid multiple memset() on same buffer
-        uint32_t e1 = e0, e2 = e0;
-        int i = 31 - __builtin_clz(e1);
-        {
-            track_t& t1 = state->tracks[i];
-            e2 &= ~(1<<i);
-            while (e2) {
-                i = 31 - __builtin_clz(e2);
-                e2 &= ~(1<<i);
-                track_t& t2 = state->tracks[i];
-                if (CC_UNLIKELY(t2.mainBuffer != t1.mainBuffer)) {
-                    e1 &= ~(1<<i);
-                }
-            }
-            e0 &= ~(e1);
+        const auto &group = pair.second;
 
-            memset(t1.mainBuffer, 0, state->frameCount * t1.mMixerChannelCount
-                    * audio_bytes_per_sample(t1.mMixerFormat));
-        }
+        const std::shared_ptr<Track> &t = mTracks[group[0]];
+        memset(t->mainBuffer, 0,
+                mFrameCount * t->mMixerChannelCount
+                * audio_bytes_per_sample(t->mMixerFormat));
 
-        while (e1) {
-            i = 31 - __builtin_clz(e1);
-            e1 &= ~(1<<i);
-            {
-                track_t& t3 = state->tracks[i];
-                size_t outFrames = state->frameCount;
-                while (outFrames) {
-                    t3.buffer.frameCount = outFrames;
-                    t3.bufferProvider->getNextBuffer(&t3.buffer);
-                    if (t3.buffer.raw == NULL) break;
-                    outFrames -= t3.buffer.frameCount;
-                    t3.bufferProvider->releaseBuffer(&t3.buffer);
-                }
+        // now consume data
+        for (const int name : group) {
+            const std::shared_ptr<Track> &t = mTracks[name];
+            size_t outFrames = mFrameCount;
+            while (outFrames) {
+                t->buffer.frameCount = outFrames;
+                t->bufferProvider->getNextBuffer(&t->buffer);
+                if (t->buffer.raw == NULL) break;
+                outFrames -= t->buffer.frameCount;
+                t->bufferProvider->releaseBuffer(&t->buffer);
             }
         }
     }
 }
 
 // generic code without resampling
-void AudioMixer::process__genericNoResampling(state_t* state)
+void AudioMixer::process__genericNoResampling()
 {
     ALOGVV("process__genericNoResampling\n");
     int32_t outTemp[BLOCKSIZE * MAX_NUM_CHANNELS] __attribute__((aligned(32)));
 
-    // acquire each track's buffer
-    uint32_t enabledTracks = state->enabledTracks;
-    uint32_t e0 = enabledTracks;
-    while (e0) {
-        const int i = 31 - __builtin_clz(e0);
-        e0 &= ~(1<<i);
-        track_t& t = state->tracks[i];
-        t.buffer.frameCount = state->frameCount;
-        t.bufferProvider->getNextBuffer(&t.buffer);
-        t.frameCount = t.buffer.frameCount;
-        t.in = t.buffer.raw;
-    }
+    for (const auto &pair : mGroups) {
+        // process by group of tracks with same output main buffer to
+        // avoid multiple memset() on same buffer
+        const auto &group = pair.second;
 
-    e0 = enabledTracks;
-    while (e0) {
-        // process by group of tracks with same output buffer to
-        // optimize cache use
-        uint32_t e1 = e0, e2 = e0;
-        int j = 31 - __builtin_clz(e1);
-        track_t& t1 = state->tracks[j];
-        e2 &= ~(1<<j);
-        while (e2) {
-            j = 31 - __builtin_clz(e2);
-            e2 &= ~(1<<j);
-            track_t& t2 = state->tracks[j];
-            if (CC_UNLIKELY(t2.mainBuffer != t1.mainBuffer)) {
-                e1 &= ~(1<<j);
-            }
+        // acquire buffer
+        for (const int name : group) {
+            const std::shared_ptr<Track> &t = mTracks[name];
+            t->buffer.frameCount = mFrameCount;
+            t->bufferProvider->getNextBuffer(&t->buffer);
+            t->frameCount = t->buffer.frameCount;
+            t->mIn = t->buffer.raw;
         }
-        e0 &= ~(e1);
-        // this assumes output 16 bits stereo, no resampling
-        int32_t *out = t1.mainBuffer;
+
+        int32_t *out = (int *)pair.first;
         size_t numFrames = 0;
         do {
-            const size_t frameCount = min((size_t)BLOCKSIZE, state->frameCount - numFrames);
+            const size_t frameCount = std::min((size_t)BLOCKSIZE, mFrameCount - numFrames);
             memset(outTemp, 0, sizeof(outTemp));
-            e2 = e1;
-            while (e2) {
-                const int i = 31 - __builtin_clz(e2);
-                e2 &= ~(1<<i);
-                track_t& t = state->tracks[i];
-                size_t outFrames = frameCount;
+            for (const int name : group) {
+                const std::shared_ptr<Track> &t = mTracks[name];
                 int32_t *aux = NULL;
-                if (CC_UNLIKELY(t.needs & NEEDS_AUX)) {
-                    aux = t.auxBuffer + numFrames;
+                if (CC_UNLIKELY(t->needs & NEEDS_AUX)) {
+                    aux = t->auxBuffer + numFrames;
                 }
-                while (outFrames) {
-                    // t.in == NULL can happen if the track was flushed just after having
+                for (int outFrames = frameCount; outFrames > 0; ) {
+                    // t->in == nullptr can happen if the track was flushed just after having
                     // been enabled for mixing.
-                   if (t.in == NULL) {
-                        enabledTracks &= ~(1<<i);
-                        e1 &= ~(1<<i);
+                    if (t->mIn == nullptr) {
                         break;
                     }
-                    size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount;
+                    size_t inFrames = (t->frameCount > outFrames)?outFrames:t->frameCount;
                     if (inFrames > 0) {
-                        t.hook(&t, outTemp + (frameCount - outFrames) * t.mMixerChannelCount,
-                                inFrames, state->resampleTemp, aux);
-                        t.frameCount -= inFrames;
+                        (t.get()->*t->hook)(
+                                outTemp + (frameCount - outFrames) * t->mMixerChannelCount,
+                                inFrames, mResampleTemp.get() /* naked ptr */, aux);
+                        t->frameCount -= inFrames;
                         outFrames -= inFrames;
                         if (CC_UNLIKELY(aux != NULL)) {
                             aux += inFrames;
                         }
                     }
-                    if (t.frameCount == 0 && outFrames) {
-                        t.bufferProvider->releaseBuffer(&t.buffer);
-                        t.buffer.frameCount = (state->frameCount - numFrames) -
+                    if (t->frameCount == 0 && outFrames) {
+                        t->bufferProvider->releaseBuffer(&t->buffer);
+                        t->buffer.frameCount = (mFrameCount - numFrames) -
                                 (frameCount - outFrames);
-                        t.bufferProvider->getNextBuffer(&t.buffer);
-                        t.in = t.buffer.raw;
-                        if (t.in == NULL) {
-                            enabledTracks &= ~(1<<i);
-                            e1 &= ~(1<<i);
+                        t->bufferProvider->getNextBuffer(&t->buffer);
+                        t->mIn = t->buffer.raw;
+                        if (t->mIn == nullptr) {
                             break;
                         }
-                        t.frameCount = t.buffer.frameCount;
+                        t->frameCount = t->buffer.frameCount;
                     }
                 }
             }
 
-            convertMixerFormat(out, t1.mMixerFormat, outTemp, t1.mMixerInFormat,
-                    frameCount * t1.mMixerChannelCount);
+            const std::shared_ptr<Track> &t1 = mTracks[group[0]];
+            convertMixerFormat(out, t1->mMixerFormat, outTemp, t1->mMixerInFormat,
+                    frameCount * t1->mMixerChannelCount);
             // TODO: fix ugly casting due to choice of out pointer type
             out = reinterpret_cast<int32_t*>((uint8_t*)out
-                    + frameCount * t1.mMixerChannelCount
-                        * audio_bytes_per_sample(t1.mMixerFormat));
+                    + frameCount * t1->mMixerChannelCount
+                    * audio_bytes_per_sample(t1->mMixerFormat));
             numFrames += frameCount;
-        } while (numFrames < state->frameCount);
-    }
+        } while (numFrames < mFrameCount);
 
-    // release each track's buffer
-    e0 = enabledTracks;
-    while (e0) {
-        const int i = 31 - __builtin_clz(e0);
-        e0 &= ~(1<<i);
-        track_t& t = state->tracks[i];
-        t.bufferProvider->releaseBuffer(&t.buffer);
+        // release each track's buffer
+        for (const int name : group) {
+            const std::shared_ptr<Track> &t = mTracks[name];
+            t->bufferProvider->releaseBuffer(&t->buffer);
+        }
     }
 }
 
-
 // generic code with resampling
-void AudioMixer::process__genericResampling(state_t* state)
+void AudioMixer::process__genericResampling()
 {
     ALOGVV("process__genericResampling\n");
-    // this const just means that local variable outTemp doesn't change
-    int32_t* const outTemp = state->outputTemp;
-    size_t numFrames = state->frameCount;
+    int32_t * const outTemp = mOutputTemp.get(); // naked ptr
+    size_t numFrames = mFrameCount;
 
-    uint32_t e0 = state->enabledTracks;
-    while (e0) {
-        // process by group of tracks with same output buffer
-        // to optimize cache use
-        uint32_t e1 = e0, e2 = e0;
-        int j = 31 - __builtin_clz(e1);
-        track_t& t1 = state->tracks[j];
-        e2 &= ~(1<<j);
-        while (e2) {
-            j = 31 - __builtin_clz(e2);
-            e2 &= ~(1<<j);
-            track_t& t2 = state->tracks[j];
-            if (CC_UNLIKELY(t2.mainBuffer != t1.mainBuffer)) {
-                e1 &= ~(1<<j);
-            }
-        }
-        e0 &= ~(e1);
-        int32_t *out = t1.mainBuffer;
-        memset(outTemp, 0, sizeof(*outTemp) * t1.mMixerChannelCount * state->frameCount);
-        while (e1) {
-            const int i = 31 - __builtin_clz(e1);
-            e1 &= ~(1<<i);
-            track_t& t = state->tracks[i];
+    for (const auto &pair : mGroups) {
+        const auto &group = pair.second;
+        const std::shared_ptr<Track> &t1 = mTracks[group[0]];
+
+        // clear temp buffer
+        memset(outTemp, 0, sizeof(*outTemp) * t1->mMixerChannelCount * mFrameCount);
+        for (const int name : group) {
+            const std::shared_ptr<Track> &t = mTracks[name];
             int32_t *aux = NULL;
-            if (CC_UNLIKELY(t.needs & NEEDS_AUX)) {
-                aux = t.auxBuffer;
+            if (CC_UNLIKELY(t->needs & NEEDS_AUX)) {
+                aux = t->auxBuffer;
             }
 
             // this is a little goofy, on the resampling case we don't
             // acquire/release the buffers because it's done by
             // the resampler.
-            if (t.needs & NEEDS_RESAMPLE) {
-                t.hook(&t, outTemp, numFrames, state->resampleTemp, aux);
+            if (t->needs & NEEDS_RESAMPLE) {
+                (t.get()->*t->hook)(outTemp, numFrames, mResampleTemp.get() /* naked ptr */, aux);
             } else {
 
                 size_t outFrames = 0;
 
                 while (outFrames < numFrames) {
-                    t.buffer.frameCount = numFrames - outFrames;
-                    t.bufferProvider->getNextBuffer(&t.buffer);
-                    t.in = t.buffer.raw;
-                    // t.in == NULL can happen if the track was flushed just after having
+                    t->buffer.frameCount = numFrames - outFrames;
+                    t->bufferProvider->getNextBuffer(&t->buffer);
+                    t->mIn = t->buffer.raw;
+                    // t->mIn == nullptr can happen if the track was flushed just after having
                     // been enabled for mixing.
-                    if (t.in == NULL) break;
+                    if (t->mIn == nullptr) break;
 
                     if (CC_UNLIKELY(aux != NULL)) {
                         aux += outFrames;
                     }
-                    t.hook(&t, outTemp + outFrames * t.mMixerChannelCount, t.buffer.frameCount,
-                            state->resampleTemp, aux);
-                    outFrames += t.buffer.frameCount;
-                    t.bufferProvider->releaseBuffer(&t.buffer);
+                    (t.get()->*t->hook)(
+                            outTemp + outFrames * t->mMixerChannelCount, t->buffer.frameCount,
+                            mResampleTemp.get() /* naked ptr */, aux);
+                    outFrames += t->buffer.frameCount;
+                    t->bufferProvider->releaseBuffer(&t->buffer);
                 }
             }
         }
-        convertMixerFormat(out, t1.mMixerFormat,
-                outTemp, t1.mMixerInFormat, numFrames * t1.mMixerChannelCount);
+        convertMixerFormat(t1->mainBuffer, t1->mMixerFormat,
+                outTemp, t1->mMixerInFormat, numFrames * t1->mMixerChannelCount);
     }
 }
 
 // one track, 16 bits stereo without resampling is the most common case
-void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state)
+void AudioMixer::process__oneTrack16BitsStereoNoResampling()
 {
-    ALOGVV("process__OneTrack16BitsStereoNoResampling\n");
-    // This method is only called when state->enabledTracks has exactly
-    // one bit set.  The asserts below would verify this, but are commented out
-    // since the whole point of this method is to optimize performance.
-    //ALOG_ASSERT(0 != state->enabledTracks, "no tracks enabled");
-    const int i = 31 - __builtin_clz(state->enabledTracks);
-    //ALOG_ASSERT((1 << i) == state->enabledTracks, "more than 1 track enabled");
-    const track_t& t = state->tracks[i];
+    ALOGVV("process__oneTrack16BitsStereoNoResampling\n");
+    LOG_ALWAYS_FATAL_IF(mEnabled.size() != 0,
+            "%zu != 1 tracks enabled", mEnabled.size());
+    const int name = mEnabled[0];
+    const std::shared_ptr<Track> &t = mTracks[name];
 
-    AudioBufferProvider::Buffer& b(t.buffer);
+    AudioBufferProvider::Buffer& b(t->buffer);
 
-    int32_t* out = t.mainBuffer;
+    int32_t* out = t->mainBuffer;
     float *fout = reinterpret_cast<float*>(out);
-    size_t numFrames = state->frameCount;
+    size_t numFrames = mFrameCount;
 
-    const int16_t vl = t.volume[0];
-    const int16_t vr = t.volume[1];
-    const uint32_t vrl = t.volumeRL;
+    const int16_t vl = t->volume[0];
+    const int16_t vr = t->volume[1];
+    const uint32_t vrl = t->volumeRL;
     while (numFrames) {
         b.frameCount = numFrames;
-        t.bufferProvider->getNextBuffer(&b);
+        t->bufferProvider->getNextBuffer(&b);
         const int16_t *in = b.i16;
 
         // in == NULL can happen if the track was flushed just after having
         // been enabled for mixing.
         if (in == NULL || (((uintptr_t)in) & 3)) {
-            if ( AUDIO_FORMAT_PCM_FLOAT == t.mMixerFormat ) {
+            if ( AUDIO_FORMAT_PCM_FLOAT == t->mMixerFormat ) {
                  memset((char*)fout, 0, numFrames
-                         * t.mMixerChannelCount * audio_bytes_per_sample(t.mMixerFormat));
+                         * t->mMixerChannelCount * audio_bytes_per_sample(t->mMixerFormat));
             } else {
                  memset((char*)out, 0, numFrames
-                         * t.mMixerChannelCount * audio_bytes_per_sample(t.mMixerFormat));
+                         * t->mMixerChannelCount * audio_bytes_per_sample(t->mMixerFormat));
             }
             ALOGE_IF((((uintptr_t)in) & 3),
-                    "process__OneTrack16BitsStereoNoResampling: misaligned buffer"
+                    "process__oneTrack16BitsStereoNoResampling: misaligned buffer"
                     " %p track %d, channels %d, needs %08x, volume %08x vfl %f vfr %f",
-                    in, i, t.channelCount, t.needs, vrl, t.mVolume[0], t.mVolume[1]);
+                    in, name, t->channelCount, t->needs, vrl, t->mVolume[0], t->mVolume[1]);
             return;
         }
         size_t outFrames = b.frameCount;
 
-        switch (t.mMixerFormat) {
+        switch (t->mMixerFormat) {
         case AUDIO_FORMAT_PCM_FLOAT:
             do {
                 uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
@@ -1686,10 +1505,10 @@
             }
             break;
         default:
-            LOG_ALWAYS_FATAL("bad mixer format: %d", t.mMixerFormat);
+            LOG_ALWAYS_FATAL("bad mixer format: %d", t->mMixerFormat);
         }
         numFrames -= b.frameCount;
-        t.bufferProvider->releaseBuffer(&b);
+        t->bufferProvider->releaseBuffer(&b);
     }
 }
 
@@ -1800,42 +1619,42 @@
  */
 template <int MIXTYPE, bool USEFLOATVOL, bool ADJUSTVOL,
     typename TO, typename TI, typename TA>
-void AudioMixer::volumeMix(TO *out, size_t outFrames,
-        const TI *in, TA *aux, bool ramp, AudioMixer::track_t *t)
+void AudioMixer::Track::volumeMix(TO *out, size_t outFrames,
+        const TI *in, TA *aux, bool ramp)
 {
     if (USEFLOATVOL) {
         if (ramp) {
-            volumeRampMulti<MIXTYPE>(t->mMixerChannelCount, out, outFrames, in, aux,
-                    t->mPrevVolume, t->mVolumeInc,
+            volumeRampMulti<MIXTYPE>(mMixerChannelCount, out, outFrames, in, aux,
+                    mPrevVolume, mVolumeInc,
 #ifdef FLOAT_AUX
-                    &t->mPrevAuxLevel, t->mAuxInc
+                    &mPrevAuxLevel, mAuxInc
 #else
-                    &t->prevAuxLevel, t->auxInc
+                    &prevAuxLevel, auxInc
 #endif
                 );
             if (ADJUSTVOL) {
-                t->adjustVolumeRamp(aux != NULL, true);
+                adjustVolumeRamp(aux != NULL, true);
             }
         } else {
-            volumeMulti<MIXTYPE>(t->mMixerChannelCount, out, outFrames, in, aux,
-                    t->mVolume,
+            volumeMulti<MIXTYPE>(mMixerChannelCount, out, outFrames, in, aux,
+                    mVolume,
 #ifdef FLOAT_AUX
-                    t->mAuxLevel
+                    mAuxLevel
 #else
-                    t->auxLevel
+                    auxLevel
 #endif
             );
         }
     } else {
         if (ramp) {
-            volumeRampMulti<MIXTYPE>(t->mMixerChannelCount, out, outFrames, in, aux,
-                    t->prevVolume, t->volumeInc, &t->prevAuxLevel, t->auxInc);
+            volumeRampMulti<MIXTYPE>(mMixerChannelCount, out, outFrames, in, aux,
+                    prevVolume, volumeInc, &prevAuxLevel, auxInc);
             if (ADJUSTVOL) {
-                t->adjustVolumeRamp(aux != NULL);
+                adjustVolumeRamp(aux != NULL);
             }
         } else {
-            volumeMulti<MIXTYPE>(t->mMixerChannelCount, out, outFrames, in, aux,
-                    t->volume, t->auxLevel);
+            volumeMulti<MIXTYPE>(mMixerChannelCount, out, outFrames, in, aux,
+                    volume, auxLevel);
         }
     }
 }
@@ -1850,19 +1669,18 @@
  * TA: int32_t (Q4.27)
  */
 template <int MIXTYPE, typename TO, typename TI, typename TA>
-void AudioMixer::process_NoResampleOneTrack(state_t* state)
+void AudioMixer::process__noResampleOneTrack()
 {
-    ALOGVV("process_NoResampleOneTrack\n");
-    // CLZ is faster than CTZ on ARM, though really not sure if true after 31 - clz.
-    const int i = 31 - __builtin_clz(state->enabledTracks);
-    ALOG_ASSERT((1 << i) == state->enabledTracks, "more than 1 track enabled");
-    track_t *t = &state->tracks[i];
+    ALOGVV("process__noResampleOneTrack\n");
+    LOG_ALWAYS_FATAL_IF(mEnabled.size() != 1,
+            "%zu != 1 tracks enabled", mEnabled.size());
+    const std::shared_ptr<Track> &t = mTracks[mEnabled[0]];
     const uint32_t channels = t->mMixerChannelCount;
     TO* out = reinterpret_cast<TO*>(t->mainBuffer);
     TA* aux = reinterpret_cast<TA*>(t->auxBuffer);
     const bool ramp = t->needsRamp();
 
-    for (size_t numFrames = state->frameCount; numFrames; ) {
+    for (size_t numFrames = mFrameCount; numFrames > 0; ) {
         AudioBufferProvider::Buffer& b(t->buffer);
         // get input buffer
         b.frameCount = numFrames;
@@ -1874,15 +1692,15 @@
         if (in == NULL || (((uintptr_t)in) & 3)) {
             memset(out, 0, numFrames
                     * channels * audio_bytes_per_sample(t->mMixerFormat));
-            ALOGE_IF((((uintptr_t)in) & 3), "process_NoResampleOneTrack: bus error: "
+            ALOGE_IF((((uintptr_t)in) & 3), "process__noResampleOneTrack: bus error: "
                     "buffer %p track %p, channels %d, needs %#x",
-                    in, t, t->channelCount, t->needs);
+                    in, &t, t->channelCount, t->needs);
             return;
         }
 
         const size_t outFrames = b.frameCount;
-        volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, false /* ADJUSTVOL */> (
-                out, outFrames, in, aux, ramp, t);
+        t->volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, false /* ADJUSTVOL */> (
+                out, outFrames, in, aux, ramp);
 
         out += outFrames * channels;
         if (aux != NULL) {
@@ -1907,30 +1725,30 @@
  * TA: int32_t (Q4.27) or float
  */
 template <int MIXTYPE, typename TO, typename TI, typename TA>
-void AudioMixer::track__Resample(track_t* t, TO* out, size_t outFrameCount, TO* temp, TA* aux)
+void AudioMixer::Track::track__Resample(TO* out, size_t outFrameCount, TO* temp, TA* aux)
 {
     ALOGVV("track__Resample\n");
-    t->resampler->setSampleRate(t->sampleRate);
-    const bool ramp = t->needsRamp();
+    mResampler->setSampleRate(sampleRate);
+    const bool ramp = needsRamp();
     if (ramp || aux != NULL) {
         // if ramp:        resample with unity gain to temp buffer and scale/mix in 2nd step.
         // if aux != NULL: resample with unity gain to temp buffer then apply send level.
 
-        t->resampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT);
-        memset(temp, 0, outFrameCount * t->mMixerChannelCount * sizeof(TO));
-        t->resampler->resample((int32_t*)temp, outFrameCount, t->bufferProvider);
+        mResampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT);
+        memset(temp, 0, outFrameCount * mMixerChannelCount * sizeof(TO));
+        mResampler->resample((int32_t*)temp, outFrameCount, bufferProvider);
 
         volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, true /* ADJUSTVOL */>(
-                out, outFrameCount, temp, aux, ramp, t);
+                out, outFrameCount, temp, aux, ramp);
 
     } else { // constant volume gain
-        t->resampler->setVolume(t->mVolume[0], t->mVolume[1]);
-        t->resampler->resample((int32_t*)out, outFrameCount, t->bufferProvider);
+        mResampler->setVolume(mVolume[0], mVolume[1]);
+        mResampler->resample((int32_t*)out, outFrameCount, bufferProvider);
     }
 }
 
 /* This track hook is called to mix a track, when no resampling is required.
- * The input buffer should be present in t->in.
+ * The input buffer should be present in in.
  *
  * MIXTYPE     (see AudioMixerOps.h MIXTYPE_* enumeration)
  * TO: int32_t (Q4.27) or float
@@ -1938,25 +1756,25 @@
  * TA: int32_t (Q4.27) or float
  */
 template <int MIXTYPE, typename TO, typename TI, typename TA>
-void AudioMixer::track__NoResample(track_t* t, TO* out, size_t frameCount,
-        TO* temp __unused, TA* aux)
+void AudioMixer::Track::track__NoResample(TO* out, size_t frameCount, TO* temp __unused, TA* aux)
 {
     ALOGVV("track__NoResample\n");
-    const TI *in = static_cast<const TI *>(t->in);
+    const TI *in = static_cast<const TI *>(mIn);
 
     volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, true /* ADJUSTVOL */>(
-            out, frameCount, in, aux, t->needsRamp(), t);
+            out, frameCount, in, aux, needsRamp());
 
     // MIXTYPE_MONOEXPAND reads a single input channel and expands to NCHAN output channels.
     // MIXTYPE_MULTI reads NCHAN input channels and places to NCHAN output channels.
-    in += (MIXTYPE == MIXTYPE_MONOEXPAND) ? frameCount : frameCount * t->mMixerChannelCount;
-    t->in = in;
+    in += (MIXTYPE == MIXTYPE_MONOEXPAND) ? frameCount : frameCount * mMixerChannelCount;
+    mIn = in;
 }
 
 /* The Mixer engine generates either int32_t (Q4_27) or float data.
  * We use this function to convert the engine buffers
  * to the desired mixer output format, either int16_t (Q.15) or float.
  */
+/* static */
 void AudioMixer::convertMixerFormat(void *out, audio_format_t mixerOutFormat,
         void *in, audio_format_t mixerInFormat, size_t sampleCount)
 {
@@ -1995,19 +1813,20 @@
 
 /* Returns the proper track hook to use for mixing the track into the output buffer.
  */
-AudioMixer::hook_t AudioMixer::getTrackHook(int trackType, uint32_t channelCount,
+/* static */
+AudioMixer::hook_t AudioMixer::Track::getTrackHook(int trackType, uint32_t channelCount,
         audio_format_t mixerInFormat, audio_format_t mixerOutFormat __unused)
 {
     if (!kUseNewMixer && channelCount == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) {
         switch (trackType) {
         case TRACKTYPE_NOP:
-            return track__nop;
+            return &Track::track__nop;
         case TRACKTYPE_RESAMPLE:
-            return track__genericResample;
+            return &Track::track__genericResample;
         case TRACKTYPE_NORESAMPLEMONO:
-            return track__16BitsMono;
+            return &Track::track__16BitsMono;
         case TRACKTYPE_NORESAMPLE:
-            return track__16BitsStereo;
+            return &Track::track__16BitsStereo;
         default:
             LOG_ALWAYS_FATAL("bad trackType: %d", trackType);
             break;
@@ -2016,14 +1835,14 @@
     LOG_ALWAYS_FATAL_IF(channelCount > MAX_NUM_CHANNELS);
     switch (trackType) {
     case TRACKTYPE_NOP:
-        return track__nop;
+        return &Track::track__nop;
     case TRACKTYPE_RESAMPLE:
         switch (mixerInFormat) {
         case AUDIO_FORMAT_PCM_FLOAT:
-            return (AudioMixer::hook_t)track__Resample<
+            return (AudioMixer::hook_t) &Track::track__Resample<
                     MIXTYPE_MULTI, float /*TO*/, float /*TI*/, TYPE_AUX>;
         case AUDIO_FORMAT_PCM_16_BIT:
-            return (AudioMixer::hook_t)track__Resample<
+            return (AudioMixer::hook_t) &Track::track__Resample<
                     MIXTYPE_MULTI, int32_t /*TO*/, int16_t /*TI*/, TYPE_AUX>;
         default:
             LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
@@ -2033,10 +1852,10 @@
     case TRACKTYPE_NORESAMPLEMONO:
         switch (mixerInFormat) {
         case AUDIO_FORMAT_PCM_FLOAT:
-            return (AudioMixer::hook_t)track__NoResample<
+            return (AudioMixer::hook_t) &Track::track__NoResample<
                             MIXTYPE_MONOEXPAND, float /*TO*/, float /*TI*/, TYPE_AUX>;
         case AUDIO_FORMAT_PCM_16_BIT:
-            return (AudioMixer::hook_t)track__NoResample<
+            return (AudioMixer::hook_t) &Track::track__NoResample<
                             MIXTYPE_MONOEXPAND, int32_t /*TO*/, int16_t /*TI*/, TYPE_AUX>;
         default:
             LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
@@ -2046,10 +1865,10 @@
     case TRACKTYPE_NORESAMPLE:
         switch (mixerInFormat) {
         case AUDIO_FORMAT_PCM_FLOAT:
-            return (AudioMixer::hook_t)track__NoResample<
+            return (AudioMixer::hook_t) &Track::track__NoResample<
                     MIXTYPE_MULTI, float /*TO*/, float /*TI*/, TYPE_AUX>;
         case AUDIO_FORMAT_PCM_16_BIT:
-            return (AudioMixer::hook_t)track__NoResample<
+            return (AudioMixer::hook_t) &Track::track__NoResample<
                     MIXTYPE_MULTI, int32_t /*TO*/, int16_t /*TI*/, TYPE_AUX>;
         default:
             LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
@@ -2070,7 +1889,9 @@
  * a stereo output track, the input track cannot be MONO.  This should be
  * prevented by the caller.
  */
-AudioMixer::process_hook_t AudioMixer::getProcessHook(int processType, uint32_t channelCount,
+/* static */
+AudioMixer::process_hook_t AudioMixer::getProcessHook(
+        int processType, uint32_t channelCount,
         audio_format_t mixerInFormat, audio_format_t mixerOutFormat)
 {
     if (processType != PROCESSTYPE_NORESAMPLEONETRACK) { // Only NORESAMPLEONETRACK
@@ -2078,17 +1899,17 @@
         return NULL;
     }
     if (!kUseNewMixer && channelCount == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) {
-        return process__OneTrack16BitsStereoNoResampling;
+        return &AudioMixer::process__oneTrack16BitsStereoNoResampling;
     }
     LOG_ALWAYS_FATAL_IF(channelCount > MAX_NUM_CHANNELS);
     switch (mixerInFormat) {
     case AUDIO_FORMAT_PCM_FLOAT:
         switch (mixerOutFormat) {
         case AUDIO_FORMAT_PCM_FLOAT:
-            return process_NoResampleOneTrack<
+            return &AudioMixer::process__noResampleOneTrack<
                     MIXTYPE_MULTI_SAVEONLY, float /*TO*/, float /*TI*/, TYPE_AUX>;
         case AUDIO_FORMAT_PCM_16_BIT:
-            return process_NoResampleOneTrack<
+            return &AudioMixer::process__noResampleOneTrack<
                     MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/, float /*TI*/, TYPE_AUX>;
         default:
             LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
@@ -2098,10 +1919,10 @@
     case AUDIO_FORMAT_PCM_16_BIT:
         switch (mixerOutFormat) {
         case AUDIO_FORMAT_PCM_FLOAT:
-            return process_NoResampleOneTrack<
+            return &AudioMixer::process__noResampleOneTrack<
                     MIXTYPE_MULTI_SAVEONLY, float /*TO*/, int16_t /*TI*/, TYPE_AUX>;
         case AUDIO_FORMAT_PCM_16_BIT:
-            return process_NoResampleOneTrack<
+            return &AudioMixer::process__noResampleOneTrack<
                     MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/, int16_t /*TI*/, TYPE_AUX>;
         default:
             LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
diff --git a/media/libheif/HeifDecoderImpl.cpp b/media/libheif/HeifDecoderImpl.cpp
index a63a2df..2dfbdca 100644
--- a/media/libheif/HeifDecoderImpl.cpp
+++ b/media/libheif/HeifDecoderImpl.cpp
@@ -22,6 +22,7 @@
 #include <stdio.h>
 
 #include <binder/IMemory.h>
+#include <binder/MemoryDealer.h>
 #include <drm/drm_framework_common.h>
 #include <media/IDataSource.h>
 #include <media/mediametadataretriever.h>
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 28684da..1377005 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -318,9 +318,7 @@
 
     srcs: [
         "JAudioTrack.cpp",
-        "MediaPlayer2Factory.cpp",
         "MediaPlayer2Manager.cpp",
-        "TestPlayerStub.cpp",
         "mediaplayer2.cpp",
     ],
 
diff --git a/media/libmedia/AudioParameter.cpp b/media/libmedia/AudioParameter.cpp
index 65fc70b..cb0e927 100644
--- a/media/libmedia/AudioParameter.cpp
+++ b/media/libmedia/AudioParameter.cpp
@@ -34,6 +34,8 @@
 const char * const AudioParameter::keyScreenState = AUDIO_PARAMETER_KEY_SCREEN_STATE;
 const char * const AudioParameter::keyBtNrec = AUDIO_PARAMETER_KEY_BT_NREC;
 const char * const AudioParameter::keyHwAvSync = AUDIO_PARAMETER_HW_AV_SYNC;
+const char * const AudioParameter::keyPresentationId = AUDIO_PARAMETER_STREAM_PRESENTATION_ID;
+const char * const AudioParameter::keyProgramId = AUDIO_PARAMETER_STREAM_PROGRAM_ID;
 const char * const AudioParameter::keyMonoOutput = AUDIO_PARAMETER_MONO_OUTPUT;
 const char * const AudioParameter::keyStreamHwAvSync = AUDIO_PARAMETER_STREAM_HW_AV_SYNC;
 const char * const AudioParameter::keyStreamConnect = AUDIO_PARAMETER_DEVICE_CONNECT;
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index 72f5f58..b2c91c4 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -64,6 +64,7 @@
     SET_INPUT_DEVICE,
     GET_ROUTED_DEVICE_ID,
     ENABLE_AUDIO_DEVICE_CALLBACK,
+    GET_ACTIVE_MICROPHONES,
 
 };
 
@@ -391,6 +392,21 @@
         }
         return reply.readInt32();
     }
+
+    status_t getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones)
+    {
+        ALOGV("getActiveMicrophones");
+        Parcel data, reply;
+        data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor());
+        status_t status = remote()->transact(GET_ACTIVE_MICROPHONES, data, &reply);
+        if (status != OK
+                || (status = (status_t)reply.readInt32()) != NO_ERROR) {
+            return status;
+        }
+        status = reply.readParcelableVector(activeMicrophones);
+        return status;
+    }
+
 };
 
 IMPLEMENT_META_INTERFACE(MediaRecorder, "android.media.IMediaRecorder");
@@ -631,6 +647,19 @@
                 reply->writeInt32(BAD_VALUE);
             }
             return NO_ERROR;
+        } break;
+        case GET_ACTIVE_MICROPHONES: {
+            ALOGV("GET_ACTIVE_MICROPHONES");
+            CHECK_INTERFACE(IMediaRecorder, data, reply);
+            std::vector<media::MicrophoneInfo> activeMicrophones;
+            status_t status = getActiveMicrophones(&activeMicrophones);
+            reply->writeInt32(status);
+            if (status != NO_ERROR) {
+                return NO_ERROR;
+            }
+            reply->writeParcelableVector(activeMicrophones);
+            return NO_ERROR;
+
         }
         default:
             return BBinder::onTransact(code, data, reply, flags);
diff --git a/media/libmedia/MediaPlayer2Factory.cpp b/media/libmedia/MediaPlayer2Factory.cpp
deleted file mode 100644
index df567ce..0000000
--- a/media/libmedia/MediaPlayer2Factory.cpp
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
-**
-** Copyright 2017, 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 "MediaPlayer2Factory"
-#include <utils/Log.h>
-
-#include <cutils/properties.h>
-#include <media/DataSource.h>
-#include <media/MediaPlayer2Engine.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <utils/Errors.h>
-#include <utils/misc.h>
-
-#include "MediaPlayer2Factory.h"
-
-#include "TestPlayerStub.h"
-#include "nuplayer2/NuPlayer2Driver.h"
-
-namespace android {
-
-Mutex MediaPlayer2Factory::sLock;
-MediaPlayer2Factory::tFactoryMap MediaPlayer2Factory::sFactoryMap;
-bool MediaPlayer2Factory::sInitComplete = false;
-
-status_t MediaPlayer2Factory::registerFactory_l(IFactory* factory,
-                                                player2_type type) {
-    if (NULL == factory) {
-        ALOGE("Failed to register MediaPlayer2Factory of type %d, factory is"
-              " NULL.", type);
-        return BAD_VALUE;
-    }
-
-    if (sFactoryMap.indexOfKey(type) >= 0) {
-        ALOGE("Failed to register MediaPlayer2Factory of type %d, type is"
-              " already registered.", type);
-        return ALREADY_EXISTS;
-    }
-
-    if (sFactoryMap.add(type, factory) < 0) {
-        ALOGE("Failed to register MediaPlayer2Factory of type %d, failed to add"
-              " to map.", type);
-        return UNKNOWN_ERROR;
-    }
-
-    return OK;
-}
-
-static player2_type getDefaultPlayerType() {
-    return PLAYER2_NU_PLAYER2;
-}
-
-status_t MediaPlayer2Factory::registerFactory(IFactory* factory,
-                                              player2_type type) {
-    Mutex::Autolock lock_(&sLock);
-    return registerFactory_l(factory, type);
-}
-
-void MediaPlayer2Factory::unregisterFactory(player2_type type) {
-    Mutex::Autolock lock_(&sLock);
-    sFactoryMap.removeItem(type);
-}
-
-#define GET_PLAYER_TYPE_IMPL(a...)                      \
-    Mutex::Autolock lock_(&sLock);                      \
-                                                        \
-    player2_type ret = PLAYER2_STAGEFRIGHT_PLAYER;      \
-    float bestScore = 0.0;                              \
-                                                        \
-    for (size_t i = 0; i < sFactoryMap.size(); ++i) {   \
-                                                        \
-        IFactory* v = sFactoryMap.valueAt(i);           \
-        float thisScore;                                \
-        CHECK(v != NULL);                               \
-        thisScore = v->scoreFactory(a, bestScore);      \
-        if (thisScore > bestScore) {                    \
-            ret = sFactoryMap.keyAt(i);                 \
-            bestScore = thisScore;                      \
-        }                                               \
-    }                                                   \
-                                                        \
-    if (0.0 == bestScore) {                             \
-        ret = getDefaultPlayerType();                   \
-    }                                                   \
-                                                        \
-    return ret;
-
-player2_type MediaPlayer2Factory::getPlayerType(const sp<MediaPlayer2Engine>& client,
-                                               const char* url) {
-    GET_PLAYER_TYPE_IMPL(client, url);
-}
-
-player2_type MediaPlayer2Factory::getPlayerType(const sp<MediaPlayer2Engine>& client,
-                                                int fd,
-                                                int64_t offset,
-                                                int64_t length) {
-    GET_PLAYER_TYPE_IMPL(client, fd, offset, length);
-}
-
-player2_type MediaPlayer2Factory::getPlayerType(const sp<MediaPlayer2Engine>& client,
-                                                const sp<IStreamSource> &source) {
-    GET_PLAYER_TYPE_IMPL(client, source);
-}
-
-player2_type MediaPlayer2Factory::getPlayerType(const sp<MediaPlayer2Engine>& client,
-                                                const sp<DataSource> &source) {
-    GET_PLAYER_TYPE_IMPL(client, source);
-}
-
-#undef GET_PLAYER_TYPE_IMPL
-
-sp<MediaPlayer2Base> MediaPlayer2Factory::createPlayer(
-        player2_type playerType,
-        const wp<MediaPlayer2Engine> &client,
-        MediaPlayer2Base::NotifyCallback notifyFunc,
-        pid_t pid) {
-    sp<MediaPlayer2Base> p;
-    IFactory* factory;
-    status_t init_result;
-    Mutex::Autolock lock_(&sLock);
-
-    if (sFactoryMap.indexOfKey(playerType) < 0) {
-        ALOGE("Failed to create player object of type %d, no registered"
-              " factory", playerType);
-        return p;
-    }
-
-    factory = sFactoryMap.valueFor(playerType);
-    CHECK(NULL != factory);
-    p = factory->createPlayer(pid);
-
-    if (p == NULL) {
-        ALOGE("Failed to create player object of type %d, create failed",
-              playerType);
-        return p;
-    }
-
-    init_result = p->initCheck();
-    if (init_result == NO_ERROR) {
-        p->setNotifyCallback(client, notifyFunc);
-    } else {
-        ALOGE("Failed to create player object of type %d, initCheck failed"
-              " (res = %d)", playerType, init_result);
-        p.clear();
-    }
-
-    return p;
-}
-
-/*****************************************************************************
- *                                                                           *
- *                     Built-In Factory Implementations                      *
- *                                                                           *
- *****************************************************************************/
-
-class NuPlayer2Factory : public MediaPlayer2Factory::IFactory {
-  public:
-    virtual float scoreFactory(const sp<MediaPlayer2Engine>& /*client*/,
-                               const char* url,
-                               float curScore) {
-        static const float kOurScore = 0.8;
-
-        if (kOurScore <= curScore) {
-            return 0.0;
-        }
-
-        if (!strncasecmp("http://", url, 7)
-                || !strncasecmp("https://", url, 8)
-                || !strncasecmp("file://", url, 7)) {
-            size_t len = strlen(url);
-            if (len >= 5 && !strcasecmp(".m3u8", &url[len - 5])) {
-                return kOurScore;
-            }
-
-            if (strstr(url,"m3u8")) {
-                return kOurScore;
-            }
-
-            if ((len >= 4 && !strcasecmp(".sdp", &url[len - 4])) || strstr(url, ".sdp?")) {
-                return kOurScore;
-            }
-        }
-
-        if (!strncasecmp("rtsp://", url, 7)) {
-            return kOurScore;
-        }
-
-        return 0.0;
-    }
-
-    virtual float scoreFactory(const sp<MediaPlayer2Engine>& /*client*/,
-                               const sp<IStreamSource>& /*source*/,
-                               float /*curScore*/) {
-        return 1.0;
-    }
-
-    virtual float scoreFactory(const sp<MediaPlayer2Engine>& /*client*/,
-                               const sp<DataSource>& /*source*/,
-                               float /*curScore*/) {
-        // Only NuPlayer2 supports setting a DataSource source directly.
-        return 1.0;
-    }
-
-    virtual sp<MediaPlayer2Base> createPlayer(pid_t pid) {
-        ALOGV(" create NuPlayer2");
-        return new NuPlayer2Driver(pid);
-    }
-};
-
-class TestPlayerFactory : public MediaPlayer2Factory::IFactory {
-  public:
-    virtual float scoreFactory(const sp<MediaPlayer2Engine>& /*client*/,
-                               const char* url,
-                               float /*curScore*/) {
-        if (TestPlayerStub::canBeUsed(url)) {
-            return 1.0;
-        }
-
-        return 0.0;
-    }
-
-    virtual sp<MediaPlayer2Base> createPlayer(pid_t /* pid */) {
-        ALOGV("Create Test Player stub");
-        return new TestPlayerStub();
-    }
-};
-
-void MediaPlayer2Factory::registerBuiltinFactories() {
-    Mutex::Autolock lock_(&sLock);
-
-    if (sInitComplete) {
-        return;
-    }
-
-    IFactory* factory = new NuPlayer2Factory();
-    if (registerFactory_l(factory, PLAYER2_NU_PLAYER2) != OK) {
-        delete factory;
-    }
-    factory = new TestPlayerFactory();
-    if (registerFactory_l(factory, PLAYER2_TEST_PLAYER) != OK) {
-        delete factory;
-    }
-
-    sInitComplete = true;
-}
-
-}  // namespace android
diff --git a/media/libmedia/MediaPlayer2Factory.h b/media/libmedia/MediaPlayer2Factory.h
deleted file mode 100644
index 799b5f3..0000000
--- a/media/libmedia/MediaPlayer2Factory.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
-**
-** Copyright 2017, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#ifndef ANDROID_MEDIAPLAYER2FACTORY_H
-#define ANDROID_MEDIAPLAYER2FACTORY_H
-
-#include <media/MediaPlayer2Interface.h>
-#include <media/stagefright/foundation/ABase.h>
-
-namespace android {
-
-class MediaPlayer2Factory {
-  public:
-    class IFactory {
-      public:
-        virtual ~IFactory() { }
-
-        virtual float scoreFactory(const sp<MediaPlayer2Engine>& /*client*/,
-                                   const char* /*url*/,
-                                   float /*curScore*/) { return 0.0; }
-
-        virtual float scoreFactory(const sp<MediaPlayer2Engine>& /*client*/,
-                                   int /*fd*/,
-                                   int64_t /*offset*/,
-                                   int64_t /*length*/,
-                                   float /*curScore*/) { return 0.0; }
-
-        virtual float scoreFactory(const sp<MediaPlayer2Engine>& /*client*/,
-                                   const sp<IStreamSource> &/*source*/,
-                                   float /*curScore*/) { return 0.0; }
-
-        virtual float scoreFactory(const sp<MediaPlayer2Engine>& /*client*/,
-                                   const sp<DataSource> &/*source*/,
-                                   float /*curScore*/) { return 0.0; }
-
-        virtual sp<MediaPlayer2Base> createPlayer(pid_t pid) = 0;
-    };
-
-    static status_t registerFactory(IFactory* factory,
-                                    player2_type type);
-    static void unregisterFactory(player2_type type);
-    static player2_type getPlayerType(const sp<MediaPlayer2Engine>& client,
-                                      const char* url);
-    static player2_type getPlayerType(const sp<MediaPlayer2Engine>& client,
-                                      int fd,
-                                      int64_t offset,
-                                      int64_t length);
-    static player2_type getPlayerType(const sp<MediaPlayer2Engine>& client,
-                                      const sp<IStreamSource> &source);
-    static player2_type getPlayerType(const sp<MediaPlayer2Engine>& client,
-                                      const sp<DataSource> &source);
-
-    static sp<MediaPlayer2Base> createPlayer(player2_type playerType,
-                                             const wp<MediaPlayer2Engine> &client,
-                                             MediaPlayer2Base::NotifyCallback notifyFunc,
-                                             pid_t pid);
-
-    static void registerBuiltinFactories();
-
-  private:
-    typedef KeyedVector<player2_type, IFactory*> tFactoryMap;
-
-    MediaPlayer2Factory() { }
-
-    static status_t registerFactory_l(IFactory* factory,
-                                      player2_type type);
-
-    static Mutex       sLock;
-    static tFactoryMap sFactoryMap;
-    static bool        sInitComplete;
-
-    DISALLOW_EVIL_CONSTRUCTORS(MediaPlayer2Factory);
-};
-
-}  // namespace android
-#endif  // ANDROID_MEDIAPLAYER2FACTORY_H
diff --git a/media/libmedia/MediaPlayer2Manager.cpp b/media/libmedia/MediaPlayer2Manager.cpp
index c119750..aefb91c 100644
--- a/media/libmedia/MediaPlayer2Manager.cpp
+++ b/media/libmedia/MediaPlayer2Manager.cpp
@@ -68,8 +68,8 @@
 
 #include <private/android_filesystem_config.h>
 
+#include <nuplayer2/NuPlayer2Driver.h>
 #include "MediaPlayer2Manager.h"
-#include "MediaPlayer2Factory.h"
 
 static const int kDumpLockRetries = 50;
 static const int kDumpLockSleepUs = 20000;
@@ -269,8 +269,6 @@
     mPid = IPCThreadState::self()->getCallingPid();
     mUid = IPCThreadState::self()->getCallingUid();
     mNextConnId = 1;
-
-    MediaPlayer2Factory::registerBuiltinFactories();
 }
 
 MediaPlayer2Manager::~MediaPlayer2Manager() {
@@ -330,7 +328,7 @@
             mPid, mConnId, mStatus, mLoop?"true": "false");
     result.append(buffer);
 
-    sp<MediaPlayer2Base> p;
+    sp<MediaPlayer2Interface> p;
     sp<AudioOutput> audioOutput;
     bool locked = false;
     for (int i = 0; i < kDumpLockRetries; ++i) {
@@ -534,7 +532,7 @@
     ALOGV("disconnect(%d) from pid %d", mConnId, mPid);
     // grab local reference and clear main reference to prevent future
     // access to object
-    sp<MediaPlayer2Base> p;
+    sp<MediaPlayer2Interface> p;
     {
         Mutex::Autolock l(mLock);
         p = mPlayer;
@@ -562,16 +560,17 @@
     IPCThreadState::self()->flushCommands();
 }
 
-sp<MediaPlayer2Base> MediaPlayer2Manager::Client::createPlayer(player2_type playerType)
-{
-    // determine if we have the right player type
-    sp<MediaPlayer2Base> p = getPlayer();
-    if ((p != NULL) && (p->playerType() != playerType)) {
-        ALOGV("delete player");
-        p.clear();
-    }
+sp<MediaPlayer2Interface> MediaPlayer2Manager::Client::createPlayer() {
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p == NULL) {
-        p = MediaPlayer2Factory::createPlayer(playerType, this, notify, mPid);
+        p = new NuPlayer2Driver(mPid);
+        status_t init_result = p->initCheck();
+        if (init_result == NO_ERROR) {
+            p->setNotifyCallback(this, notify);
+        } else {
+            ALOGE("Failed to create player, initCheck failed(res = %d)", init_result);
+            p.clear();
+        }
     }
 
     if (p != NULL) {
@@ -584,7 +583,7 @@
 void MediaPlayer2Manager::Client::AudioDeviceUpdatedNotifier::onAudioDeviceUpdate(
         audio_io_handle_t audioIo,
         audio_port_handle_t deviceId) {
-    sp<MediaPlayer2Base> listener = mListener.promote();
+    sp<MediaPlayer2Interface> listener = mListener.promote();
     if (listener != NULL) {
         listener->sendEvent(MEDIA2_AUDIO_ROUTING_CHANGED, audioIo, deviceId);
     } else {
@@ -592,13 +591,8 @@
     }
 }
 
-sp<MediaPlayer2Base> MediaPlayer2Manager::Client::setDataSource_pre(
-        player2_type playerType)
-{
-    ALOGV("player type = %d", playerType);
-
-    // create the right type of player
-    sp<MediaPlayer2Base> p = createPlayer(playerType);
+sp<MediaPlayer2Interface> MediaPlayer2Manager::Client::setDataSource_pre() {
+    sp<MediaPlayer2Interface> p = createPlayer();
     if (p == NULL) {
         return p;
     }
@@ -607,17 +601,15 @@
 
     mAudioDeviceUpdatedListener = new AudioDeviceUpdatedNotifier(p);
 
-    if (!p->hardwareOutput()) {
-        mAudioOutput = new AudioOutput(mAudioSessionId, mUid,
-                mPid, mAudioAttributes, mAudioDeviceUpdatedListener);
-        static_cast<MediaPlayer2Interface*>(p.get())->setAudioSink(mAudioOutput);
-    }
+    mAudioOutput = new AudioOutput(mAudioSessionId, mUid,
+            mPid, mAudioAttributes, mAudioDeviceUpdatedListener);
+    p->setAudioSink(mAudioOutput);
 
     return p;
 }
 
 status_t MediaPlayer2Manager::Client::setDataSource_post(
-        const sp<MediaPlayer2Base>& p,
+        const sp<MediaPlayer2Interface>& p,
         status_t status)
 {
     ALOGV(" setDataSource");
@@ -663,8 +655,7 @@
         mStatus = UNKNOWN_ERROR;
         return mStatus;
     } else {
-        player2_type playerType = MediaPlayer2Factory::getPlayerType(this, url);
-        sp<MediaPlayer2Base> p = setDataSource_pre(playerType);
+        sp<MediaPlayer2Interface> p = setDataSource_pre();
         if (p == NULL) {
             return NO_INIT;
         }
@@ -701,11 +692,7 @@
         ALOGV("calculated length = %lld", (long long)length);
     }
 
-    player2_type playerType = MediaPlayer2Factory::getPlayerType(this,
-                                                               fd,
-                                                               offset,
-                                                               length);
-    sp<MediaPlayer2Base> p = setDataSource_pre(playerType);
+    sp<MediaPlayer2Interface> p = setDataSource_pre();
     if (p == NULL) {
         return NO_INIT;
     }
@@ -716,9 +703,7 @@
 
 status_t MediaPlayer2Manager::Client::setDataSource(
         const sp<IStreamSource> &source) {
-    // create the right type of player
-    player2_type playerType = MediaPlayer2Factory::getPlayerType(this, source);
-    sp<MediaPlayer2Base> p = setDataSource_pre(playerType);
+    sp<MediaPlayer2Interface> p = setDataSource_pre();
     if (p == NULL) {
         return NO_INIT;
     }
@@ -729,8 +714,7 @@
 
 status_t MediaPlayer2Manager::Client::setDataSource(
         const sp<DataSource> &source) {
-    player2_type playerType = MediaPlayer2Factory::getPlayerType(this, source);
-    sp<MediaPlayer2Base> p = setDataSource_pre(playerType);
+    sp<MediaPlayer2Interface> p = setDataSource_pre();
     if (p == NULL) {
         return NO_INIT;
     }
@@ -757,7 +741,7 @@
     ALOGV("[%d] setVideoSurfaceTexture(%p)",
           mConnId,
           (nww == NULL ? NULL : nww->getANativeWindow()));
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p == 0) return UNKNOWN_ERROR;
 
     if (nww != NULL && nww->getANativeWindow() != NULL) {
@@ -810,7 +794,7 @@
 status_t MediaPlayer2Manager::Client::invoke(const Parcel& request,
                                             Parcel *reply)
 {
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p == NULL) return UNKNOWN_ERROR;
     return p->invoke(request, reply);
 }
@@ -834,7 +818,7 @@
 status_t MediaPlayer2Manager::Client::getMetadata(
         bool update_only, bool /*apply_filter*/, Parcel *reply)
 {
-    sp<MediaPlayer2Base> player = getPlayer();
+    sp<MediaPlayer2Interface> player = getPlayer();
     if (player == 0) return UNKNOWN_ERROR;
 
     status_t status;
@@ -879,7 +863,7 @@
 {
     ALOGV("[%d] setBufferingSettings{%s}",
             mConnId, buffering.toString().string());
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p == 0) return UNKNOWN_ERROR;
     return p->setBufferingSettings(buffering);
 }
@@ -887,7 +871,7 @@
 status_t MediaPlayer2Manager::Client::getBufferingSettings(
         BufferingSettings* buffering /* nonnull */)
 {
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     // TODO: create mPlayer on demand.
     if (p == 0) return UNKNOWN_ERROR;
     status_t ret = p->getBufferingSettings(buffering);
@@ -903,7 +887,7 @@
 status_t MediaPlayer2Manager::Client::prepareAsync()
 {
     ALOGV("[%d] prepareAsync", mConnId);
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p == 0) return UNKNOWN_ERROR;
     status_t ret = p->prepareAsync();
 #if CALLBACK_ANTAGONIZER
@@ -916,7 +900,7 @@
 status_t MediaPlayer2Manager::Client::start()
 {
     ALOGV("[%d] start", mConnId);
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p == 0) return UNKNOWN_ERROR;
     p->setLooping(mLoop);
     return p->start();
@@ -925,7 +909,7 @@
 status_t MediaPlayer2Manager::Client::stop()
 {
     ALOGV("[%d] stop", mConnId);
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p == 0) return UNKNOWN_ERROR;
     return p->stop();
 }
@@ -933,7 +917,7 @@
 status_t MediaPlayer2Manager::Client::pause()
 {
     ALOGV("[%d] pause", mConnId);
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p == 0) return UNKNOWN_ERROR;
     return p->pause();
 }
@@ -941,7 +925,7 @@
 status_t MediaPlayer2Manager::Client::isPlaying(bool* state)
 {
     *state = false;
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p == 0) return UNKNOWN_ERROR;
     *state = p->isPlaying();
     ALOGV("[%d] isPlaying: %d", mConnId, *state);
@@ -952,14 +936,14 @@
 {
     ALOGV("[%d] setPlaybackSettings(%f, %f, %d, %d)",
             mConnId, rate.mSpeed, rate.mPitch, rate.mFallbackMode, rate.mStretchMode);
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p == 0) return UNKNOWN_ERROR;
     return p->setPlaybackSettings(rate);
 }
 
 status_t MediaPlayer2Manager::Client::getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */)
 {
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p == 0) return UNKNOWN_ERROR;
     status_t ret = p->getPlaybackSettings(rate);
     if (ret == NO_ERROR) {
@@ -976,7 +960,7 @@
 {
     ALOGV("[%d] setSyncSettings(%u, %u, %f, %f)",
             mConnId, sync.mSource, sync.mAudioAdjustMode, sync.mTolerance, videoFpsHint);
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p == 0) return UNKNOWN_ERROR;
     return p->setSyncSettings(sync, videoFpsHint);
 }
@@ -984,7 +968,7 @@
 status_t MediaPlayer2Manager::Client::getSyncSettings(
         AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */)
 {
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p == 0) return UNKNOWN_ERROR;
     status_t ret = p->getSyncSettings(sync, videoFps);
     if (ret == NO_ERROR) {
@@ -999,7 +983,7 @@
 status_t MediaPlayer2Manager::Client::getCurrentPosition(int *msec)
 {
     ALOGV("getCurrentPosition");
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p == 0) return UNKNOWN_ERROR;
     status_t ret = p->getCurrentPosition(msec);
     if (ret == NO_ERROR) {
@@ -1013,7 +997,7 @@
 status_t MediaPlayer2Manager::Client::getDuration(int *msec)
 {
     ALOGV("getDuration");
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p == 0) return UNKNOWN_ERROR;
     status_t ret = p->getDuration(msec);
     if (ret == NO_ERROR) {
@@ -1037,7 +1021,7 @@
     if (c != NULL) {
         if (mAudioOutput != NULL) {
             mAudioOutput->setNextOutput(c->mAudioOutput);
-        } else if ((mPlayer != NULL) && !mPlayer->hardwareOutput()) {
+        } else {
             ALOGE("no current audio output");
         }
 
@@ -1054,13 +1038,9 @@
         const sp<VolumeShaper::Operation>& operation) {
     // for hardware output, call player instead
     ALOGV("Client::applyVolumeShaper(%p)", this);
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     {
         Mutex::Autolock l(mLock);
-        if (p != 0 && p->hardwareOutput()) {
-            // TODO: investigate internal implementation
-            return VolumeShaper::Status(INVALID_OPERATION);
-        }
         if (mAudioOutput.get() != nullptr) {
             return mAudioOutput->applyVolumeShaper(configuration, operation);
         }
@@ -1071,13 +1051,9 @@
 sp<VolumeShaper::State> MediaPlayer2Manager::Client::getVolumeShaperState(int id) {
     // for hardware output, call player instead
     ALOGV("Client::getVolumeShaperState(%p)", this);
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     {
         Mutex::Autolock l(mLock);
-        if (p != 0 && p->hardwareOutput()) {
-            // TODO: investigate internal implementation.
-            return nullptr;
-        }
         if (mAudioOutput.get() != nullptr) {
             return mAudioOutput->getVolumeShaperState(id);
         }
@@ -1088,7 +1064,7 @@
 status_t MediaPlayer2Manager::Client::seekTo(int msec, MediaPlayer2SeekMode mode)
 {
     ALOGV("[%d] seekTo(%d, %d)", mConnId, msec, mode);
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p == 0) return UNKNOWN_ERROR;
     return p->seekTo(msec, mode);
 }
@@ -1097,7 +1073,7 @@
 {
     ALOGV("[%d] reset", mConnId);
     mRetransmitEndpointValid = false;
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p == 0) return UNKNOWN_ERROR;
     return p->reset();
 }
@@ -1105,7 +1081,7 @@
 status_t MediaPlayer2Manager::Client::notifyAt(int64_t mediaTimeUs)
 {
     ALOGV("[%d] notifyAt(%lld)", mConnId, (long long)mediaTimeUs);
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p == 0) return UNKNOWN_ERROR;
     return p->notifyAt(mediaTimeUs);
 }
@@ -1142,7 +1118,7 @@
 {
     ALOGV("[%d] setLooping(%d)", mConnId, loop);
     mLoop = loop;
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p != 0) return p->setLooping(loop);
     return NO_ERROR;
 }
@@ -1152,17 +1128,10 @@
     ALOGV("[%d] setVolume(%f, %f)", mConnId, leftVolume, rightVolume);
 
     // for hardware output, call player instead
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     {
       Mutex::Autolock l(mLock);
-      if (p != 0 && p->hardwareOutput()) {
-          MediaPlayerHWInterface* hwp =
-                  reinterpret_cast<MediaPlayerHWInterface*>(p.get());
-          return hwp->setVolume(leftVolume, rightVolume);
-      } else {
-          if (mAudioOutput != 0) mAudioOutput->setVolume(leftVolume, rightVolume);
-          return NO_ERROR;
-      }
+      if (mAudioOutput != 0) mAudioOutput->setVolume(leftVolume, rightVolume);
     }
 
     return NO_ERROR;
@@ -1193,7 +1162,7 @@
         return setAudioAttributes_l(request);
     }
     default:
-        sp<MediaPlayer2Base> p = getPlayer();
+        sp<MediaPlayer2Interface> p = getPlayer();
         if (p == 0) { return UNKNOWN_ERROR; }
         return p->setParameter(key, request);
     }
@@ -1201,7 +1170,7 @@
 
 status_t MediaPlayer2Manager::Client::getParameter(int key, Parcel *reply) {
     ALOGV("[%d] getParameter(%d)", mConnId, key);
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p == 0) return UNKNOWN_ERROR;
     return p->getParameter(key, reply);
 }
@@ -1218,7 +1187,7 @@
         ALOGV("[%d] setRetransmitEndpoint = <none>", mConnId);
     }
 
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
 
     // Right now, the only valid time to set a retransmit endpoint is before
     // player selection has been made (since the presence or absence of a
@@ -1244,7 +1213,7 @@
     if (NULL == endpoint)
         return BAD_VALUE;
 
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
 
     if (p != NULL)
         return p->getRetransmitEndpoint(endpoint);
@@ -1347,7 +1316,7 @@
         const Vector<uint8_t>& drmSessionId)
 {
     ALOGV("[%d] prepareDrm", mConnId);
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p == 0) return UNKNOWN_ERROR;
 
     status_t ret = p->prepareDrm(uuid, drmSessionId);
@@ -1359,7 +1328,7 @@
 status_t MediaPlayer2Manager::Client::releaseDrm()
 {
     ALOGV("[%d] releaseDrm", mConnId);
-    sp<MediaPlayer2Base> p = getPlayer();
+    sp<MediaPlayer2Interface> p = getPlayer();
     if (p == 0) return UNKNOWN_ERROR;
 
     status_t ret = p->releaseDrm();
diff --git a/media/libmedia/MediaPlayer2Manager.h b/media/libmedia/MediaPlayer2Manager.h
index b42cbbb..20cc735 100644
--- a/media/libmedia/MediaPlayer2Manager.h
+++ b/media/libmedia/MediaPlayer2Manager.h
@@ -48,7 +48,7 @@
 class Antagonizer {
 public:
     Antagonizer(
-            MediaPlayer2Base::NotifyCallback cb,
+            MediaPlayer2Interface::NotifyCallback cb,
             const wp<MediaPlayer2Engine> &client);
     void start() { mActive = true; }
     void stop() { mActive = false; }
@@ -62,14 +62,14 @@
     bool                             mExit;
     bool                             mActive;
     wp<MediaPlayer2Engine>           mClient;
-    MediaPlayer2Base::NotifyCallback mCb;
+    MediaPlayer2Interface::NotifyCallback mCb;
 };
 #endif
 
 class MediaPlayer2Manager {
     class Client;
 
-    class AudioOutput : public MediaPlayer2Base::AudioSink
+    class AudioOutput : public MediaPlayer2Interface::AudioSink
     {
         class CallbackData;
 
@@ -287,7 +287,7 @@
                                         const sp<media::VolumeShaper::Operation>& operation) override;
         virtual sp<media::VolumeShaper::State> getVolumeShaperState(int id) override;
 
-        sp<MediaPlayer2Base>    createPlayer(player2_type playerType);
+        sp<MediaPlayer2Interface>    createPlayer();
 
         virtual status_t        setDataSource(
                         const sp<MediaHTTPService> &httpService,
@@ -300,8 +300,8 @@
         virtual status_t        setDataSource(const sp<DataSource> &source);
 
 
-        sp<MediaPlayer2Base>    setDataSource_pre(player2_type playerType);
-        status_t                setDataSource_post(const sp<MediaPlayer2Base>& p,
+        sp<MediaPlayer2Interface>    setDataSource_pre();
+        status_t                setDataSource_post(const sp<MediaPlayer2Interface>& p,
                                                    status_t status);
 
         static  void            notify(const wp<MediaPlayer2Engine> &listener, int msg,
@@ -323,7 +323,7 @@
         class AudioDeviceUpdatedNotifier: public AudioSystem::AudioDeviceCallback
         {
         public:
-            AudioDeviceUpdatedNotifier(const sp<MediaPlayer2Base>& listener) {
+            AudioDeviceUpdatedNotifier(const sp<MediaPlayer2Interface>& listener) {
                 mListener = listener;
             }
             ~AudioDeviceUpdatedNotifier() {}
@@ -332,7 +332,7 @@
                                              audio_port_handle_t deviceId);
 
         private:
-            wp<MediaPlayer2Base> mListener;
+            wp<MediaPlayer2Interface> mListener;
         };
 
         friend class MediaPlayer2Manager;
@@ -346,7 +346,7 @@
 
                 void            deletePlayer();
 
-        sp<MediaPlayer2Base>     getPlayer() const { Mutex::Autolock lock(mLock); return mPlayer; }
+        sp<MediaPlayer2Interface> getPlayer() const { Mutex::Autolock lock(mLock); return mPlayer; }
 
 
 
@@ -366,7 +366,7 @@
         status_t setAudioAttributes_l(const Parcel &request);
 
         mutable     Mutex                        mLock;
-                    sp<MediaPlayer2Base>         mPlayer;
+                    sp<MediaPlayer2Interface>    mPlayer;
                     sp<MediaPlayer2EngineClient> mClient;
                     sp<AudioOutput>              mAudioOutput;
                     pid_t                        mPid;
diff --git a/media/libmedia/TestPlayerStub.cpp b/media/libmedia/TestPlayerStub.cpp
deleted file mode 100644
index 3548793..0000000
--- a/media/libmedia/TestPlayerStub.cpp
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright 2017 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 "TestPlayerStub"
-#include "utils/Log.h"
-
-#include "TestPlayerStub.h"
-
-#include <dlfcn.h>  // for dlopen/dlclose
-#include <stdlib.h>
-#include <string.h>
-#include <cutils/properties.h>
-#include <utils/Errors.h>  // for status_t
-
-#include "media/MediaPlayer2Interface.h"
-
-
-namespace {
-using android::status_t;
-using android::MediaPlayer2Base;
-
-const char *kTestUrlScheme = "test:";
-const char *kUrlParam = "url=";
-
-const char *kBuildTypePropName = "ro.build.type";
-const char *kEngBuild = "eng";
-const char *kTestBuild = "test";
-
-// @return true if the current build is 'eng' or 'test'.
-bool isTestBuild()
-{
-    char prop[PROPERTY_VALUE_MAX] = { '\0', };
-
-    property_get(kBuildTypePropName, prop, "\0");
-    return strcmp(prop, kEngBuild) == 0 || strcmp(prop, kTestBuild) == 0;
-}
-
-// @return true if the url scheme is 'test:'
-bool isTestUrl(const char *url)
-{
-    return url && strncmp(url, kTestUrlScheme, strlen(kTestUrlScheme)) == 0;
-}
-
-}  // anonymous namespace
-
-namespace android {
-
-TestPlayerStub::TestPlayerStub()
-    :mUrl(NULL), mFilename(NULL), mContentUrl(NULL),
-     mHandle(NULL), mNewPlayer(NULL), mDeletePlayer(NULL),
-     mPlayer(NULL) { }
-
-TestPlayerStub::~TestPlayerStub()
-{
-    resetInternal();
-}
-
-status_t TestPlayerStub::initCheck()
-{
-    return isTestBuild() ? OK : INVALID_OPERATION;
-}
-
-// Parse mUrl to get:
-// * The library to be dlopened.
-// * The url to be passed to the real setDataSource impl.
-//
-// mUrl is expected to be in following format:
-//
-// test:<name of the .so>?url=<url for setDataSource>
-//
-// The value of the url parameter is treated as a string (no
-// unescaping of illegal charaters).
-status_t TestPlayerStub::parseUrl()
-{
-    if (strlen(mUrl) < strlen(kTestUrlScheme)) {
-        resetInternal();
-        return BAD_VALUE;
-    }
-
-    char *i = mUrl + strlen(kTestUrlScheme);
-
-    mFilename = i;
-
-    while (*i != '\0' && *i != '?') {
-        ++i;
-    }
-
-    if (*i == '\0' || strncmp(i + 1, kUrlParam, strlen(kUrlParam)) != 0) {
-        resetInternal();
-        return BAD_VALUE;
-    }
-    *i = '\0';  // replace '?' to nul-terminate mFilename
-
-    mContentUrl = i + 1 + strlen(kUrlParam);
-    return OK;
-}
-
-// Load the dynamic library.
-// Create the test player.
-// Call setDataSource on the test player with the url in param.
-status_t TestPlayerStub::setDataSource(
-        const sp<MediaHTTPService> &httpService,
-        const char *url,
-        const KeyedVector<String8, String8> *headers) {
-    if (!isTestUrl(url) || NULL != mHandle) {
-        return INVALID_OPERATION;
-    }
-
-    mUrl = strdup(url);
-
-    status_t status = parseUrl();
-
-    if (OK != status) {
-        resetInternal();
-        return status;
-    }
-
-    ::dlerror();  // Clears any pending error.
-
-    // Load the test player from the url. dlopen will fail if the lib
-    // is not there. dls are under /system/lib
-    // None of the entry points should be NULL.
-    mHandle = ::dlopen(mFilename, RTLD_NOW | RTLD_GLOBAL);
-    if (!mHandle) {
-        ALOGE("dlopen failed: %s", ::dlerror());
-        resetInternal();
-        return UNKNOWN_ERROR;
-    }
-
-    // Load the 2 entry points to create and delete instances.
-    const char *err;
-    mNewPlayer = reinterpret_cast<NEW_PLAYER>(dlsym(mHandle,
-                                                    "newPlayer"));
-    err = ::dlerror();
-    if (err || mNewPlayer == NULL) {
-        // if err is NULL the string <null> is inserted in the logs =>
-        // mNewPlayer was NULL.
-        ALOGE("dlsym for newPlayer failed %s", err);
-        resetInternal();
-        return UNKNOWN_ERROR;
-    }
-
-    mDeletePlayer = reinterpret_cast<DELETE_PLAYER>(dlsym(mHandle,
-                                                          "deletePlayer"));
-    err = ::dlerror();
-    if (err || mDeletePlayer == NULL) {
-        ALOGE("dlsym for deletePlayer failed %s", err);
-        resetInternal();
-        return UNKNOWN_ERROR;
-    }
-
-    mPlayer = (*mNewPlayer)();
-    return mPlayer->setDataSource(httpService, mContentUrl, headers);
-}
-
-// Internal cleanup.
-status_t TestPlayerStub::resetInternal()
-{
-    if(mUrl) {
-        free(mUrl);
-        mUrl = NULL;
-    }
-    mFilename = NULL;
-    mContentUrl = NULL;
-
-    if (mPlayer) {
-        ALOG_ASSERT(mDeletePlayer != NULL, "mDeletePlayer is null");
-        (*mDeletePlayer)(mPlayer);
-        mPlayer = NULL;
-    }
-
-    if (mHandle) {
-        ::dlclose(mHandle);
-        mHandle = NULL;
-    }
-    return OK;
-}
-
-/* static */ bool TestPlayerStub::canBeUsed(const char *url)
-{
-    return isTestBuild() && isTestUrl(url);
-}
-
-}  // namespace android
diff --git a/media/libmedia/TestPlayerStub.h b/media/libmedia/TestPlayerStub.h
deleted file mode 100644
index 27c8bf4..0000000
--- a/media/libmedia/TestPlayerStub.h
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_FRAMEWORKS_BASE_MEDIA_LIBMEDIA_TESTPLAYERSTUB_H__
-#define ANDROID_FRAMEWORKS_BASE_MEDIA_LIBMEDIA_TESTPLAYERSTUB_H__
-
-#include <media/MediaPlayer2Interface.h>
-#include <utils/Errors.h>
-
-namespace android {
-class MediaPlayer2Base;  // in media/MediaPlayer2Interface.h
-
-// Wrapper around a test media player that gets dynamically loaded.
-//
-// The URL passed to setDataSource has this format:
-//
-//   test:<name of the .so>?url=<url for the real setDataSource impl.>
-//
-// e.g:
-//   test:invoke_test_media_player.so?url=http://youtube.com/
-//   test:invoke_test_media_player.so?url=speedtest
-//
-// TestPlayerStub::setDataSource loads the library in the test url. 2
-// entry points with C linkage are expected. One to create the test
-// player and one to destroy it.
-//
-// extern "C" android::MediaPlayer2Base* newPlayer();
-// extern "C" android::status_t deletePlayer(android::MediaPlayer2Base *p);
-//
-// Once the test player has been loaded, its setDataSource
-// implementation is called with the value of the 'url' parameter.
-//
-// typical usage in a java test:
-// ============================
-//
-//  MediaPlayer2 p = new MediaPlayer2();
-//  p.setDataSource("test:invoke_mock_media_player.so?url=http://youtube.com");
-//  p.prepare();
-//  ...
-//  p.release();
-
-class TestPlayerStub : public MediaPlayer2Interface {
-  public:
-    typedef MediaPlayer2Base* (*NEW_PLAYER)();
-    typedef status_t (*DELETE_PLAYER)(MediaPlayer2Base *);
-
-    TestPlayerStub();
-    virtual ~TestPlayerStub();
-
-    // Called right after the constructor. Check if the current build
-    // allows test players.
-    virtual status_t initCheck();
-
-    // @param url Should be a test url. See class comment.
-    virtual status_t setDataSource(
-            const sp<MediaHTTPService> &httpService,
-            const char* url,
-            const KeyedVector<String8, String8> *headers);
-
-    // Test player for a file descriptor source is not supported.
-    virtual status_t setDataSource(int, int64_t, int64_t)  {
-        return INVALID_OPERATION;
-    }
-
-
-    // All the methods below wrap the mPlayer instance.
-    virtual status_t setVideoSurfaceTexture(
-            const android::sp<android::ANativeWindowWrapper>& st)  {
-        return mPlayer->setVideoSurfaceTexture(st);
-    }
-    virtual status_t prepare() {return mPlayer->prepare();}
-    virtual status_t prepareAsync()  {return mPlayer->prepareAsync();}
-    virtual status_t start()  {return mPlayer->start();}
-    virtual status_t stop()  {return mPlayer->stop();}
-    virtual status_t pause()  {return mPlayer->pause();}
-    virtual bool isPlaying() {return mPlayer->isPlaying();}
-    virtual status_t seekTo(
-            int msec,
-            MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC) {
-        return mPlayer->seekTo(msec, mode);
-    }
-    virtual status_t getCurrentPosition(int *p)  {
-        return mPlayer->getCurrentPosition(p);
-    }
-    virtual status_t getDuration(int *d)  {return mPlayer->getDuration(d);}
-    virtual status_t reset() {return mPlayer->reset();}
-    virtual status_t setLooping(int b)  {return mPlayer->setLooping(b);}
-    virtual player2_type playerType() {return mPlayer->playerType();}
-    virtual status_t invoke(const android::Parcel& in, android::Parcel *out) {
-        return mPlayer->invoke(in, out);
-    }
-    virtual status_t setParameter(int key, const Parcel &request) {
-        return mPlayer->setParameter(key, request);
-    }
-    virtual status_t getParameter(int key, Parcel *reply) {
-        return mPlayer->getParameter(key, reply);
-    }
-
-
-    // @return true if the current build is 'eng' or 'test' and the
-    //              url's scheme is 'test:'
-    static bool canBeUsed(const char *url);
-
-  private:
-    // Release the player, dlclose the library.
-    status_t resetInternal();
-    status_t parseUrl();
-
-    char *mUrl;                // test:foo.so?url=http://bar
-    char *mFilename;           // foo.so
-    char *mContentUrl;         // http://bar
-    void *mHandle;             // returned by dlopen
-    NEW_PLAYER    mNewPlayer;
-    DELETE_PLAYER mDeletePlayer;
-    MediaPlayer2Base *mPlayer; // wrapped player
-};
-
-}  // namespace android
-
-#endif  // ANDROID_FRAMEWORKS_BASE_MEDIA_LIBMEDIA_TESTPLAYERSTUB_H__
diff --git a/media/libmedia/include/media/CounterMetric.h b/media/libmedia/include/media/CounterMetric.h
new file mode 100644
index 0000000..b53470d
--- /dev/null
+++ b/media/libmedia/include/media/CounterMetric.h
@@ -0,0 +1,94 @@
+/*
+ * 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 ANDROID_COUNTER_METRIC_H_
+#define ANDROID_COUNTER_METRIC_H_
+
+#include <functional>
+#include <map>
+#include <string>
+
+#include <media/MediaAnalyticsItem.h>
+#include <utils/Log.h>
+
+namespace android {
+
+
+// The CounterMetric class is used to hold counts of operations or events.
+// A CounterMetric can break down counts by a dimension specified by the
+// application. E.g. an application may want to track counts broken out by
+// error code or the size of some parameter.
+//
+// Example:
+//
+//   CounterMetric<status_t> workCounter;
+//   workCounter("workCounterName", "result_status");
+//
+//   status_t err = DoWork();
+//
+//   // Increments the number of times called with the given error code.
+//   workCounter.Increment(err);
+//
+//   std::map<int, int64_t> values;
+//    metric.ExportValues(
+//        [&] (int attribute_value, int64_t value) {
+//             values[attribute_value] = value;
+//        });
+//
+//   // Do something with the exported stat.
+//
+template<typename AttributeType>
+class CounterMetric {
+ public:
+  // Instantiate the counter with the given metric name and
+  // attribute names. |attribute_names| must not be null.
+  CounterMetric(
+      const std::string& metric_name,
+      const std::string& attribute_name)
+          : metric_name_(metric_name),
+            attribute_name_(attribute_name) {}
+
+  // Increment the count of times the operation occurred with this
+  // combination of attributes.
+  void Increment(AttributeType attribute) {
+    if (values_.find(attribute) == values_.end()) {
+      values_[attribute] = 1;
+    } else {
+      values_[attribute] = values_[attribute] + 1;
+    }
+  };
+
+  // Export the metrics to the provided |function|. Each value for Attribute
+  // has a separate count. As such, |function| will be called once per value
+  // of Attribute.
+  void ExportValues(
+      std::function<void (const AttributeType&,
+                          const int64_t count)> function) const {
+    for (auto it = values_.begin(); it != values_.end(); it++) {
+      function(it->first, it->second);
+    }
+  }
+
+  const std::string& metric_name() const { return metric_name_; };
+
+ private:
+  const std::string metric_name_;
+  const std::string attribute_name_;
+  std::map<AttributeType, int64_t> values_;
+};
+
+}  // namespace android
+
+#endif  // ANDROID_COUNTER_METRIC_H_
diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h
index 1a0553e..ec3a9b3 100644
--- a/media/libmedia/include/media/DrmHal.h
+++ b/media/libmedia/include/media/DrmHal.h
@@ -23,6 +23,7 @@
 #include <android/hardware/drm/1.0/IDrmPluginListener.h>
 #include <android/hardware/drm/1.0/IDrmFactory.h>
 
+#include <media/DrmMetrics.h>
 #include <media/IDrm.h>
 #include <media/IDrmClient.h>
 #include <media/MediaAnalyticsItem.h>
@@ -96,10 +97,12 @@
                                               Vector<uint8_t> &wrappedKey);
 
     virtual status_t getSecureStops(List<Vector<uint8_t>> &secureStops);
+    virtual status_t getSecureStopIds(List<Vector<uint8_t>> &secureStopIds);
     virtual status_t getSecureStop(Vector<uint8_t> const &ssid, Vector<uint8_t> &secureStop);
 
     virtual status_t releaseSecureStops(Vector<uint8_t> const &ssRelease);
-    virtual status_t releaseAllSecureStops();
+    virtual status_t removeSecureStop(Vector<uint8_t> const &ssid);
+    virtual status_t removeAllSecureStops();
 
     virtual status_t getHdcpLevels(DrmPlugin::HdcpLevel *connectedLevel,
             DrmPlugin::HdcpLevel *maxLevel) const;
@@ -180,6 +183,9 @@
     sp<IDrmPlugin> mPlugin;
     sp<drm::V1_1::IDrmPlugin> mPluginV1_1;
 
+    // Mutable to allow modification within GetPropertyByteArray.
+    mutable MediaDrmMetrics mMetrics;
+
     Vector<Vector<uint8_t>> mOpenSessions;
     void closeOpenSessions();
 
diff --git a/media/libmedia/include/media/DrmMetrics.h b/media/libmedia/include/media/DrmMetrics.h
new file mode 100644
index 0000000..bb7509b
--- /dev/null
+++ b/media/libmedia/include/media/DrmMetrics.h
@@ -0,0 +1,66 @@
+/*
+ * 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 DRM_METRICS_H_
+#define DRM_METRICS_H_
+
+#include <map>
+
+#include <android/hardware/drm/1.0/types.h>
+#include <media/CounterMetric.h>
+#include <media/EventMetric.h>
+
+namespace android {
+
+/**
+ * This class contains the definition of metrics captured within MediaDrm.
+ * It also contains a method for exporting all of the metrics to a
+ * MediaAnalyticsItem instance.
+ */
+class MediaDrmMetrics {
+ public:
+  explicit MediaDrmMetrics();
+  // Count of openSession calls.
+  CounterMetric<status_t> mOpenSessionCounter;
+  // Count of closeSession calls.
+  CounterMetric<status_t> mCloseSessionCounter;
+  // Count and timing of getKeyRequest calls.
+  EventMetric<status_t> mGetKeyRequestTiming;
+  // Count and timing of provideKeyResponse calls.
+  EventMetric<status_t> mProvideKeyResponseTiming;
+  // Count of getProvisionRequest calls.
+  CounterMetric<status_t> mGetProvisionRequestCounter;
+  // Count of provideProvisionResponse calls.
+  CounterMetric<status_t> mProvideProvisionResponseCounter;
+
+  // Count of key status events broken out by status type.
+  CounterMetric<::android::hardware::drm::V1_0::KeyStatusType>
+      mKeyStatusChangeCounter;
+  // Count of events broken out by event type
+  CounterMetric<::android::hardware::drm::V1_0::EventType> mEventCounter;
+
+  // Count getPropertyByteArray calls to retrieve the device unique id.
+  CounterMetric<status_t> mGetDeviceUniqueIdCounter;
+
+  // TODO: Add session start and end time support. These are a special case.
+
+  // Export the metrics to a MediaAnalyticsItem.
+  void Export(MediaAnalyticsItem* item);
+};
+
+}  // namespace android
+
+#endif  // DRM_METRICS_H_
diff --git a/media/libmedia/include/media/EventMetric.h b/media/libmedia/include/media/EventMetric.h
new file mode 100644
index 0000000..dbb736a
--- /dev/null
+++ b/media/libmedia/include/media/EventMetric.h
@@ -0,0 +1,176 @@
+/*
+ * 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 ANDROID_EVENT_METRIC_H_
+#define ANDROID_EVENT_METRIC_H_
+
+#include <media/MediaAnalyticsItem.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+// This is a simple holder for the statistics recorded in EventMetric.
+struct EventStatistics {
+  // The count of times the event occurred.
+  int64_t count;
+
+  // The minimum and maximum values recorded in the Record method.
+  double min;
+  double max;
+
+  // The average (mean) of all values recorded.
+  double mean;
+  // The sum of squared devation. Variance can be calculated from
+  // this value.
+  //    var = sum_squared_deviation / count;
+  double sum_squared_deviation;
+};
+
+// The EventMetric class is used to accumulate stats about an event over time.
+// A common use case is to track clock timings for a method call or operation.
+// An EventMetric can break down stats by a dimension specified by the
+// application. E.g. an application may want to track counts broken out by
+// error code or the size of some parameter.
+//
+// Example:
+//
+//   struct C {
+//     status_t DoWork() {
+//       unsigned long start_time = now();
+//       status_t result;
+//
+//       // DO WORK and determine result;
+//
+//       work_event_.Record(now() - start_time, result);
+//
+//       return result;
+//     }
+//     EventMetric<status_t> work_event_;
+//   };
+//
+//   C c;
+//   c.DoWork();
+//
+//   std::map<int, int64_t> values;
+//   metric.ExportValues(
+//       [&] (int attribute_value, int64_t value) {
+//            values[attribute_value] = value;
+//       });
+//   // Do something with the exported stat.
+//
+template<typename AttributeType>
+class EventMetric {
+ public:
+  // Instantiate the counter with the given metric name and
+  // attribute names. |attribute_names| must not be null.
+  EventMetric(
+      const std::string& metric_name,
+      const std::string& attribute_name)
+          : metric_name_(metric_name),
+            attribute_name_(attribute_name) {}
+
+  // Increment the count of times the operation occurred with this
+  // combination of attributes.
+  void Record(double value, AttributeType attribute) {
+    if (values_.find(attribute) != values_.end()) {
+      EventStatistics* stats = values_[attribute].get();
+      // Using method of provisional means.
+      double deviation = value - stats->mean;
+      stats->mean = stats->mean + (deviation / stats->count);
+      stats->sum_squared_deviation =
+          stats->sum_squared_deviation + (deviation * (value - stats->mean));
+      stats->count++;
+
+      stats->min = stats->min < value ? stats->min : value;
+      stats->max = stats->max > value ? stats->max : value;
+    } else {
+      std::unique_ptr<EventStatistics> stats =
+          std::make_unique<EventStatistics>();
+      stats->count = 1;
+      stats->min = value;
+      stats->max = value;
+      stats->mean = value;
+      stats->sum_squared_deviation = 0;
+      values_[attribute] = std::move(stats);
+    }
+  };
+
+  // Export the metrics to the provided |function|. Each value for Attribute
+  // has a separate set of stats. As such, |function| will be called once per
+  // value of Attribute.
+  void ExportValues(
+      std::function<void (const AttributeType&,
+                          const EventStatistics&)> function) const {
+    for (auto it = values_.begin(); it != values_.end(); it++) {
+      function(it->first, *(it->second));
+    }
+  }
+
+  const std::string& metric_name() const { return metric_name_; };
+
+ private:
+  const std::string metric_name_;
+  const std::string attribute_name_;
+  std::map<AttributeType, std::unique_ptr<struct EventStatistics>> values_;
+};
+
+// The EventTimer is a supporting class for EventMetric instances that are used
+// to time methods. The EventTimer starts a timer when first in scope, and
+// records the timing when exiting scope.
+//
+// Example:
+//
+// EventMetric<int> my_metric;
+//
+// {
+//   EventTimer<int> my_timer(&my_metric);
+//   // Set the attribute to associate with this timing.
+//   my_timer.SetAttribtue(42);
+//
+//   // Do some work that you want to time.
+//
+// }  // The EventTimer destructor will record the the timing in my_metric;
+//
+template<typename AttributeType>
+class EventTimer {
+ public:
+  explicit EventTimer(EventMetric<AttributeType>* metric)
+      :start_time_(systemTime()), metric_(metric) {
+  }
+
+  virtual ~EventTimer() {
+    if (metric_) {
+      metric_->Record(ns2us(systemTime() - start_time_), attribute_);
+    }
+  }
+
+  // Set the attribute to associate with this timing. E.g. this can be used to
+  // record the return code from the work that was timed.
+  void SetAttribute(const AttributeType& attribute) {
+    attribute_ = attribute;
+  }
+
+ protected:
+  // Visible for testing only.
+  nsecs_t start_time_;
+
+ private:
+  EventMetric<AttributeType>* metric_;
+  AttributeType attribute_;
+};
+
+}  // namespace android
+
+#endif  // ANDROID_EVENT_METRIC_H_
diff --git a/media/libmedia/include/media/IDrm.h b/media/libmedia/include/media/IDrm.h
index 9266f99..994cade 100644
--- a/media/libmedia/include/media/IDrm.h
+++ b/media/libmedia/include/media/IDrm.h
@@ -74,10 +74,12 @@
                                               Vector<uint8_t> &wrappedKey) = 0;
 
     virtual status_t getSecureStops(List<Vector<uint8_t> > &secureStops) = 0;
+    virtual status_t getSecureStopIds(List<Vector<uint8_t> > &secureStopIds) = 0;
     virtual status_t getSecureStop(Vector<uint8_t> const &ssid, Vector<uint8_t> &secureStop) = 0;
 
     virtual status_t releaseSecureStops(Vector<uint8_t> const &ssRelease) = 0;
-    virtual status_t releaseAllSecureStops() = 0;
+    virtual status_t removeSecureStop(Vector<uint8_t> const &ssid) = 0;
+    virtual status_t removeAllSecureStops() = 0;
 
     virtual status_t getHdcpLevels(DrmPlugin::HdcpLevel *connectedLevel,
             DrmPlugin::HdcpLevel *maxLevel)
diff --git a/media/libmedia/include/media/IMediaRecorder.h b/media/libmedia/include/media/IMediaRecorder.h
index 3cef329..379000e 100644
--- a/media/libmedia/include/media/IMediaRecorder.h
+++ b/media/libmedia/include/media/IMediaRecorder.h
@@ -19,7 +19,9 @@
 #define ANDROID_IMEDIARECORDER_H
 
 #include <binder/IInterface.h>
+#include <media/MicrophoneInfo.h>
 #include <system/audio.h>
+#include <vector>
 
 namespace android {
 
@@ -69,6 +71,9 @@
     virtual status_t setInputDevice(audio_port_handle_t deviceId) = 0;
     virtual status_t getRoutedDeviceId(audio_port_handle_t *deviceId) = 0;
     virtual status_t enableAudioDeviceCallback(bool enabled) = 0;
+    virtual status_t getActiveMicrophones(
+                        std::vector<media::MicrophoneInfo>* activeMicrophones) = 0;
+
 };
 
 // ----------------------------------------------------------------------------
diff --git a/media/libmedia/include/media/MediaPlayer2Interface.h b/media/libmedia/include/media/MediaPlayer2Interface.h
index 931a110..699618e 100644
--- a/media/libmedia/include/media/MediaPlayer2Interface.h
+++ b/media/libmedia/include/media/MediaPlayer2Interface.h
@@ -47,16 +47,6 @@
 
 template<typename T> class SortedVector;
 
-enum player2_type {
-    PLAYER2_STAGEFRIGHT_PLAYER = 3,
-    PLAYER2_NU_PLAYER2 = 4,
-    // Test players are available only in the 'test' and 'eng' builds.
-    // The shared library with the test player is passed passed as an
-    // argument to the 'test:' url in the setDataSource call.
-    PLAYER2_TEST_PLAYER = 5,
-};
-
-
 #define DEFAULT_AUDIOSINK_BUFFERCOUNT 4
 #define DEFAULT_AUDIOSINK_BUFFERSIZE 1200
 #define DEFAULT_AUDIOSINK_SAMPLERATE 44100
@@ -68,7 +58,7 @@
 #define AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US 5000000
 
 // abstract base class - use MediaPlayer2Interface
-class MediaPlayer2Base : public AHandler
+class MediaPlayer2Interface : public AHandler
 {
 public:
     // callback mechanism for passing messages to MediaPlayer2 object
@@ -158,15 +148,16 @@
         virtual status_t    enableAudioDeviceCallback(bool enabled);
     };
 
-                        MediaPlayer2Base() : mClient(0), mNotify(0) {}
-    virtual             ~MediaPlayer2Base() {}
+                        MediaPlayer2Interface() : mClient(0), mNotify(0) { }
+    virtual             ~MediaPlayer2Interface() { }
     virtual status_t    initCheck() = 0;
-    virtual bool        hardwareOutput() = 0;
 
     virtual status_t    setUID(uid_t /* uid */) {
         return INVALID_OPERATION;
     }
 
+    virtual void        setAudioSink(const sp<AudioSink>& audioSink) { mAudioSink = audioSink; }
+
     virtual status_t    setDataSource(
             const sp<MediaHTTPService> &httpService,
             const char *url,
@@ -234,7 +225,6 @@
         return INVALID_OPERATION;
     }
     virtual status_t    setLooping(int loop) = 0;
-    virtual player2_type playerType() = 0;
     virtual status_t    setParameter(int key, const Parcel &request) = 0;
     virtual status_t    getParameter(int key, Parcel *reply) = 0;
 
@@ -245,7 +235,7 @@
     virtual status_t getRetransmitEndpoint(struct sockaddr_in* /* endpoint */) {
         return INVALID_OPERATION;
     }
-    virtual status_t setNextPlayer(const sp<MediaPlayer2Base>& /* next */) {
+    virtual status_t setNextPlayer(const sp<MediaPlayer2Interface>& /* next */) {
         return OK;
     }
 
@@ -303,6 +293,9 @@
         return INVALID_OPERATION;
     }
 
+protected:
+    sp<AudioSink>       mAudioSink;
+
 private:
     friend class MediaPlayer2Manager;
 
@@ -311,27 +304,6 @@
     NotifyCallback         mNotify;
 };
 
-// Implement this class for media players that use the AudioFlinger software mixer
-class MediaPlayer2Interface : public MediaPlayer2Base
-{
-public:
-    virtual             ~MediaPlayer2Interface() { }
-    virtual bool        hardwareOutput() { return false; }
-    virtual void        setAudioSink(const sp<AudioSink>& audioSink) { mAudioSink = audioSink; }
-protected:
-    sp<AudioSink>       mAudioSink;
-};
-
-// Implement this class for media players that output audio directly to hardware
-class MediaPlayer2HWInterface : public MediaPlayer2Base
-{
-public:
-    virtual             ~MediaPlayer2HWInterface() {}
-    virtual bool        hardwareOutput() { return true; }
-    virtual status_t    setVolume(float leftVolume, float rightVolume) = 0;
-    virtual status_t    setAudioStreamType(audio_stream_type_t streamType) = 0;
-};
-
 }; // namespace android
 
 #endif // __cplusplus
diff --git a/media/libmedia/include/media/MediaRecorderBase.h b/media/libmedia/include/media/MediaRecorderBase.h
index 748153c..5340dde 100644
--- a/media/libmedia/include/media/MediaRecorderBase.h
+++ b/media/libmedia/include/media/MediaRecorderBase.h
@@ -19,10 +19,13 @@
 #define MEDIA_RECORDER_BASE_H_
 
 #include <media/AudioSystem.h>
+#include <media/MicrophoneInfo.h>
 #include <media/mediarecorder.h>
 
 #include <system/audio.h>
 
+#include <vector>
+
 namespace android {
 
 class ICameraRecordingProxy;
@@ -67,6 +70,9 @@
     virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId) = 0;
     virtual void setAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback) = 0;
     virtual status_t enableAudioDeviceCallback(bool enabled) = 0;
+    virtual status_t getActiveMicrophones(
+                        std::vector<media::MicrophoneInfo>* activeMicrophones) = 0;
+
 
 
 protected:
diff --git a/media/libmedia/include/media/mediarecorder.h b/media/libmedia/include/media/mediarecorder.h
index 5f2a6fe..d8b0fe7 100644
--- a/media/libmedia/include/media/mediarecorder.h
+++ b/media/libmedia/include/media/mediarecorder.h
@@ -24,6 +24,7 @@
 #include <utils/Errors.h>
 #include <media/IMediaRecorderClient.h>
 #include <media/IMediaDeathNotifier.h>
+#include <media/MicrophoneInfo.h>
 
 namespace android {
 
@@ -258,6 +259,7 @@
     status_t    setInputDevice(audio_port_handle_t deviceId);
     status_t    getRoutedDeviceId(audio_port_handle_t *deviceId);
     status_t    enableAudioDeviceCallback(bool enabled);
+    status_t    getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones);
 
 private:
     void                    doCleanUp();
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index aab845b..721a043 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -829,4 +829,15 @@
     return mMediaRecorder->enableAudioDeviceCallback(enabled);
 }
 
+status_t MediaRecorder::getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones)
+{
+    ALOGV("getActiveMicrophones");
+
+    if (mMediaRecorder == NULL) {
+        ALOGE("media recorder is not initialized yet");
+        return INVALID_OPERATION;
+    }
+    return mMediaRecorder->getActiveMicrophones(activeMicrophones);
+}
+
 } // namespace android
diff --git a/media/libmedia/nuplayer2/GenericSource.cpp b/media/libmedia/nuplayer2/GenericSource.cpp
index 6d5b14d..6907216 100644
--- a/media/libmedia/nuplayer2/GenericSource.cpp
+++ b/media/libmedia/nuplayer2/GenericSource.cpp
@@ -26,6 +26,7 @@
 #include <media/DataSource.h>
 #include <media/MediaBufferHolder.h>
 #include <media/IMediaExtractorService.h>
+#include <media/IMediaSource.h>
 #include <media/MediaHTTPService.h>
 #include <media/MediaExtractor.h>
 #include <media/MediaSource.h>
@@ -1646,19 +1647,15 @@
         return OK; // source without DRM info
     }
 
-    Parcel parcel;
-    NuPlayer2Drm::retrieveDrmInfo(pssh, psshsize, &parcel);
-    ALOGV("checkDrmInfo: MEDIA2_DRM_INFO PSSH size: %d  Parcel size: %d  objects#: %d",
-          (int)psshsize, (int)parcel.dataSize(), (int)parcel.objectsCount());
+    sp<ABuffer> drmInfoBuffer = NuPlayer2Drm::retrieveDrmInfo(pssh, psshsize);
+    ALOGV("checkDrmInfo: MEDIA2_DRM_INFO PSSH size: %d drmInfoBuffer size: %d",
+          (int)psshsize, (int)drmInfoBuffer->size());
 
-    if (parcel.dataSize() == 0) {
-        ALOGE("checkDrmInfo: Unexpected parcel size: 0");
+    if (drmInfoBuffer->size() == 0) {
+        ALOGE("checkDrmInfo: Unexpected drmInfoBuffer size: 0");
         return UNKNOWN_ERROR;
     }
 
-    // Can't pass parcel as a message to the player. Converting Parcel->ABuffer to pass it
-    // to the Player's onSourceNotify then back to Parcel for calling driver's notifyListener.
-    sp<ABuffer> drmInfoBuffer = ABuffer::CreateAsCopy(parcel.data(), parcel.dataSize());
     notifyDrmInfo(drmInfoBuffer);
 
     return OK;
diff --git a/media/libmedia/nuplayer2/GenericSource.h b/media/libmedia/nuplayer2/GenericSource.h
index 0666d27..5a71edb 100644
--- a/media/libmedia/nuplayer2/GenericSource.h
+++ b/media/libmedia/nuplayer2/GenericSource.h
@@ -33,6 +33,7 @@
 struct ARTSPController;
 class DataSource;
 class IDataSource;
+class IMediaSource;
 struct MediaHTTPService;
 struct MediaSource;
 class MediaBuffer;
diff --git a/media/libmedia/nuplayer2/NuPlayer2.cpp b/media/libmedia/nuplayer2/NuPlayer2.cpp
index 2745219..2c7f416 100644
--- a/media/libmedia/nuplayer2/NuPlayer2.cpp
+++ b/media/libmedia/nuplayer2/NuPlayer2.cpp
@@ -64,7 +64,7 @@
 
 namespace android {
 
-static status_t sendMetaDataToHal(sp<MediaPlayer2Base::AudioSink>& sink,
+static status_t sendMetaDataToHal(sp<MediaPlayer2Interface::AudioSink>& sink,
                                   const sp<MetaData>& meta) {
     int32_t sampleRate = 0;
     int32_t bitRate = 0;
@@ -417,7 +417,7 @@
     msg->post();
 }
 
-void NuPlayer2::setAudioSink(const sp<MediaPlayer2Base::AudioSink> &sink) {
+void NuPlayer2::setAudioSink(const sp<MediaPlayer2Interface::AudioSink> &sink) {
     sp<AMessage> msg = new AMessage(kWhatSetAudioSink, this);
     msg->setObject("sink", sink);
     msg->post();
@@ -850,7 +850,7 @@
             sp<RefBase> obj;
             CHECK(msg->findObject("sink", &obj));
 
-            mAudioSink = static_cast<MediaPlayer2Base::AudioSink *>(obj.get());
+            mAudioSink = static_cast<MediaPlayer2Interface::AudioSink *>(obj.get());
             break;
         }
 
diff --git a/media/libmedia/nuplayer2/NuPlayer2.h b/media/libmedia/nuplayer2/NuPlayer2.h
index 638b259..23c4fdf 100644
--- a/media/libmedia/nuplayer2/NuPlayer2.h
+++ b/media/libmedia/nuplayer2/NuPlayer2.h
@@ -61,7 +61,7 @@
 
     void setVideoSurfaceTextureAsync(const sp<ANativeWindowWrapper> &nww);
 
-    void setAudioSink(const sp<MediaPlayer2Base::AudioSink> &sink);
+    void setAudioSink(const sp<MediaPlayer2Interface::AudioSink> &sink);
     status_t setPlaybackSettings(const AudioPlaybackRate &rate);
     status_t getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */);
     status_t setSyncSettings(const AVSyncSettings &sync, float videoFpsHint);
@@ -168,7 +168,7 @@
     sp<Source> mSource;
     uint32_t mSourceFlags;
     sp<ANativeWindowWrapper> mNativeWindow;
-    sp<MediaPlayer2Base::AudioSink> mAudioSink;
+    sp<MediaPlayer2Interface::AudioSink> mAudioSink;
     sp<DecoderBase> mVideoDecoder;
     bool mOffloadAudio;
     sp<DecoderBase> mAudioDecoder;
diff --git a/media/libmedia/nuplayer2/NuPlayer2Driver.cpp b/media/libmedia/nuplayer2/NuPlayer2Driver.cpp
index 629a7eb..e48acea 100644
--- a/media/libmedia/nuplayer2/NuPlayer2Driver.cpp
+++ b/media/libmedia/nuplayer2/NuPlayer2Driver.cpp
@@ -77,7 +77,7 @@
 };
 
 // key for media statistics
-static const char *kKeyPlayer = "nuplayer2";
+static const char *kKeyPlayer = "nuplayer";
 // attrs for media statistics
 static const char *kPlayerVMime = "android.media.mediaplayer.video.mime";
 static const char *kPlayerVCodec = "android.media.mediaplayer.video.codec";
@@ -129,7 +129,6 @@
 
     // set up an analytics record
     mAnalyticsItem = new MediaAnalyticsItem(kKeyPlayer);
-    mAnalyticsItem->generateSessionID();
 
     mNuPlayer2Looper->start(
             false, /* runOnCallingThread */
@@ -661,14 +660,12 @@
     // So the canonical "empty" record has 3 elements in it.
     if (mAnalyticsItem->count() > 3) {
 
-        mAnalyticsItem->setFinalized(true);
         mAnalyticsItem->selfrecord();
 
         // re-init in case we prepare() and start() again.
         delete mAnalyticsItem ;
-        mAnalyticsItem = new MediaAnalyticsItem("nuplayer");
+        mAnalyticsItem = new MediaAnalyticsItem(kKeyPlayer);
         if (mAnalyticsItem) {
-            mAnalyticsItem->generateSessionID();
             mAnalyticsItem->setUid(mClientUid);
         }
     } else {
@@ -736,10 +733,6 @@
     return OK;
 }
 
-player2_type NuPlayer2Driver::playerType() {
-    return PLAYER2_NU_PLAYER2;
-}
-
 status_t NuPlayer2Driver::invoke(const Parcel &request, Parcel *reply) {
     if (reply == NULL) {
         ALOGE("reply is a NULL pointer");
diff --git a/media/libmedia/nuplayer2/NuPlayer2Driver.h b/media/libmedia/nuplayer2/NuPlayer2Driver.h
index d393f9d..7bbb367 100644
--- a/media/libmedia/nuplayer2/NuPlayer2Driver.h
+++ b/media/libmedia/nuplayer2/NuPlayer2Driver.h
@@ -66,7 +66,6 @@
     virtual status_t reset();
     virtual status_t notifyAt(int64_t mediaTimeUs) override;
     virtual status_t setLooping(int loop);
-    virtual player2_type playerType();
     virtual status_t invoke(const Parcel &request, Parcel *reply);
     virtual void setAudioSink(const sp<AudioSink> &audioSink);
     virtual status_t setParameter(int key, const Parcel &request);
diff --git a/media/libmedia/nuplayer2/NuPlayer2Drm.cpp b/media/libmedia/nuplayer2/NuPlayer2Drm.cpp
index 4751849..4853ae1 100644
--- a/media/libmedia/nuplayer2/NuPlayer2Drm.cpp
+++ b/media/libmedia/nuplayer2/NuPlayer2Drm.cpp
@@ -21,7 +21,7 @@
 
 #include <media/NdkWrapper.h>
 #include <utils/Log.h>
-
+#include <sstream>
 
 namespace android {
 
@@ -105,26 +105,70 @@
     return supportedDRMs;
 }
 
-// Parcel has only private copy constructor so passing it in rather than returning
-void NuPlayer2Drm::retrieveDrmInfo(const void *pssh, size_t psshsize, Parcel *parcel)
+sp<ABuffer> NuPlayer2Drm::retrieveDrmInfo(const void *pssh, uint32_t psshsize)
 {
-    // 1) PSSH bytes
-    parcel->writeUint32(psshsize);
-    parcel->writeByteArray(psshsize, (const uint8_t*)pssh);
+    std::ostringstream buf;
 
-    ALOGV("retrieveDrmInfo: MEDIA2_DRM_INFO  PSSH: size: %zu %s", psshsize,
+    // 1) PSSH bytes
+    buf.write(reinterpret_cast<const char *>(&psshsize), sizeof(psshsize));
+    buf.write(reinterpret_cast<const char *>(pssh), psshsize);
+
+    ALOGV("retrieveDrmInfo: MEDIA2_DRM_INFO  PSSH: size: %u %s", psshsize,
             DrmUUID::arrayToHex((uint8_t*)pssh, psshsize).string());
 
     // 2) supportedDRMs
     Vector<DrmUUID> supportedDRMs = getSupportedDrmSchemes(pssh, psshsize);
-    parcel->writeUint32(supportedDRMs.size());
-    for (size_t i = 0; i < supportedDRMs.size(); i++) {
+    uint32_t n = supportedDRMs.size();
+    buf.write(reinterpret_cast<char *>(&n), sizeof(n));
+    for (size_t i = 0; i < n; i++) {
         DrmUUID uuid = supportedDRMs[i];
-        parcel->writeByteArray(DrmUUID::UUID_SIZE, uuid.ptr());
+        buf.write(reinterpret_cast<const char *>(&n), sizeof(n));
+        buf.write(reinterpret_cast<const char *>(uuid.ptr()), DrmUUID::UUID_SIZE);
 
         ALOGV("retrieveDrmInfo: MEDIA2_DRM_INFO  supportedScheme[%zu] %s", i,
                 uuid.toHexString().string());
     }
+
+    sp<ABuffer> drmInfoBuffer = ABuffer::CreateAsCopy(buf.str().c_str(), buf.tellp());
+    return drmInfoBuffer;
+}
+
+sp<ABuffer> NuPlayer2Drm::retrieveDrmInfo(PsshInfo *psshInfo)
+{
+
+    std::ostringstream pssh, drmInfo;
+
+    // 0) Generate PSSH bytes
+    for (size_t i = 0; i < psshInfo->numentries; i++) {
+        PsshEntry *entry = &psshInfo->entries[i];
+        uint32_t datalen = entry->datalen;
+        pssh.write(reinterpret_cast<const char *>(&entry->uuid), sizeof(entry->uuid));
+        pssh.write(reinterpret_cast<const char *>(&datalen), sizeof(datalen));
+        pssh.write(reinterpret_cast<const char *>(entry->data), datalen);
+    }
+
+    uint32_t psshSize = pssh.tellp();
+    const uint8_t* psshPtr = reinterpret_cast<const uint8_t*>(pssh.str().c_str());
+    const char *psshHex = DrmUUID::arrayToHex(psshPtr, psshSize).string();
+    ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO  PSSH: size: %u %s", psshSize, psshHex);
+
+    // 1) Write PSSH bytes
+    drmInfo.write(reinterpret_cast<const char *>(&psshSize), sizeof(psshSize));
+    drmInfo.write(reinterpret_cast<const char *>(pssh.str().c_str()), psshSize);
+
+    // 2) Write supportedDRMs
+    uint32_t numentries = psshInfo->numentries;
+    drmInfo.write(reinterpret_cast<const char *>(&numentries), sizeof(numentries));
+    for (size_t i = 0; i < numentries; i++) {
+        PsshEntry *entry = &psshInfo->entries[i];
+        drmInfo.write(reinterpret_cast<const char *>(&entry->uuid), sizeof(entry->uuid));
+        ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO  supportedScheme[%zu] %s", i,
+                DrmUUID::arrayToHex((const uint8_t*)&entry->uuid, sizeof(AMediaUUID)).string());
+    }
+
+    sp<ABuffer> drmInfoBuf = ABuffer::CreateAsCopy(drmInfo.str().c_str(), drmInfo.tellp());
+    drmInfoBuf->setRange(0, drmInfo.tellp());
+    return drmInfoBuf;
 }
 
 }   // namespace android
diff --git a/media/libmedia/nuplayer2/NuPlayer2Drm.h b/media/libmedia/nuplayer2/NuPlayer2Drm.h
index f9c8711..99d2415 100644
--- a/media/libmedia/nuplayer2/NuPlayer2Drm.h
+++ b/media/libmedia/nuplayer2/NuPlayer2Drm.h
@@ -17,8 +17,11 @@
 #ifndef NUPLAYER2_DRM_H_
 #define NUPLAYER2_DRM_H_
 
-#include <binder/Parcel.h>
+#include <media/NdkMediaExtractor.h>
+#include <media/stagefright/foundation/ABuffer.h>
 
+#include <utils/String8.h>
+#include <utils/Vector.h>
 
 namespace android {
 
@@ -76,8 +79,8 @@
         // static helpers - public
 
     public:
-        // Parcel has only private copy constructor so passing it in rather than returning
-        static void retrieveDrmInfo(const void *pssh, size_t psshsize, Parcel *parcel);
+        static sp<ABuffer> retrieveDrmInfo(const void *pssh, uint32_t psshsize);
+        static sp<ABuffer> retrieveDrmInfo(PsshInfo *);
 
     };  // NuPlayer2Drm
 
diff --git a/media/libmedia/nuplayer2/NuPlayer2Renderer.cpp b/media/libmedia/nuplayer2/NuPlayer2Renderer.cpp
index 1a9f246..a0bd900 100644
--- a/media/libmedia/nuplayer2/NuPlayer2Renderer.cpp
+++ b/media/libmedia/nuplayer2/NuPlayer2Renderer.cpp
@@ -87,7 +87,7 @@
 const int64_t NuPlayer2::Renderer::kMinPositionUpdateDelayUs = 100000ll;
 
 NuPlayer2::Renderer::Renderer(
-        const sp<MediaPlayer2Base::AudioSink> &sink,
+        const sp<MediaPlayer2Interface::AudioSink> &sink,
         const sp<MediaClock> &mediaClock,
         const sp<AMessage> &notify,
         uint32_t flags)
@@ -805,28 +805,28 @@
 
 // static
 size_t NuPlayer2::Renderer::AudioSinkCallback(
-        MediaPlayer2Base::AudioSink * /* audioSink */,
+        MediaPlayer2Interface::AudioSink * /* audioSink */,
         void *buffer,
         size_t size,
         void *cookie,
-        MediaPlayer2Base::AudioSink::cb_event_t event) {
+        MediaPlayer2Interface::AudioSink::cb_event_t event) {
     NuPlayer2::Renderer *me = (NuPlayer2::Renderer *)cookie;
 
     switch (event) {
-        case MediaPlayer2Base::AudioSink::CB_EVENT_FILL_BUFFER:
+        case MediaPlayer2Interface::AudioSink::CB_EVENT_FILL_BUFFER:
         {
             return me->fillAudioBuffer(buffer, size);
             break;
         }
 
-        case MediaPlayer2Base::AudioSink::CB_EVENT_STREAM_END:
+        case MediaPlayer2Interface::AudioSink::CB_EVENT_STREAM_END:
         {
             ALOGV("AudioSink::CB_EVENT_STREAM_END");
             me->notifyEOSCallback();
             break;
         }
 
-        case MediaPlayer2Base::AudioSink::CB_EVENT_TEAR_DOWN:
+        case MediaPlayer2Interface::AudioSink::CB_EVENT_TEAR_DOWN:
         {
             ALOGV("AudioSink::CB_EVENT_TEAR_DOWN");
             me->notifyAudioTearDown(kDueToError);
diff --git a/media/libmedia/nuplayer2/NuPlayer2Renderer.h b/media/libmedia/nuplayer2/NuPlayer2Renderer.h
index 3007654..62cf0d8 100644
--- a/media/libmedia/nuplayer2/NuPlayer2Renderer.h
+++ b/media/libmedia/nuplayer2/NuPlayer2Renderer.h
@@ -35,15 +35,15 @@
         FLAG_REAL_TIME = 1,
         FLAG_OFFLOAD_AUDIO = 2,
     };
-    Renderer(const sp<MediaPlayer2Base::AudioSink> &sink,
+    Renderer(const sp<MediaPlayer2Interface::AudioSink> &sink,
              const sp<MediaClock> &mediaClock,
              const sp<AMessage> &notify,
              uint32_t flags = 0);
 
     static size_t AudioSinkCallback(
-            MediaPlayer2Base::AudioSink *audioSink,
+            MediaPlayer2Interface::AudioSink *audioSink,
             void *data, size_t size, void *me,
-            MediaPlayer2Base::AudioSink::cb_event_t event);
+            MediaPlayer2Interface::AudioSink::cb_event_t event);
 
     void queueBuffer(
             bool audio,
@@ -148,7 +148,7 @@
 
     static const int64_t kMinPositionUpdateDelayUs;
 
-    sp<MediaPlayer2Base::AudioSink> mAudioSink;
+    sp<MediaPlayer2Interface::AudioSink> mAudioSink;
     bool mUseVirtualAudioSink;
     sp<AMessage> mNotify;
     Mutex mLock;
diff --git a/media/libmediaextractor/Android.bp b/media/libmediaextractor/Android.bp
index 83fea39..4071fba 100644
--- a/media/libmediaextractor/Android.bp
+++ b/media/libmediaextractor/Android.bp
@@ -26,8 +26,10 @@
         "DataSource.cpp",
         "MediaBuffer.cpp",
         "MediaBufferGroup.cpp",
+        "MediaSourceBase.cpp",
         "MediaSource.cpp",
         "MediaExtractor.cpp",
+        "MetaData.cpp",
     ],
 
     clang: true,
diff --git a/media/libmediaextractor/MediaSource.cpp b/media/libmediaextractor/MediaSource.cpp
index a5d41f7..5bbd3d8 100644
--- a/media/libmediaextractor/MediaSource.cpp
+++ b/media/libmediaextractor/MediaSource.cpp
@@ -25,45 +25,4 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-MediaSource::ReadOptions::ReadOptions() {
-    reset();
-}
-
-void MediaSource::ReadOptions::reset() {
-    mOptions = 0;
-    mSeekTimeUs = 0;
-    mNonBlocking = false;
-}
-
-void MediaSource::ReadOptions::setNonBlocking() {
-    mNonBlocking = true;
-}
-
-void MediaSource::ReadOptions::clearNonBlocking() {
-    mNonBlocking = false;
-}
-
-bool MediaSource::ReadOptions::getNonBlocking() const {
-    return mNonBlocking;
-}
-
-void MediaSource::ReadOptions::setSeekTo(int64_t time_us, SeekMode mode) {
-    mOptions |= kSeekTo_Option;
-    mSeekTimeUs = time_us;
-    mSeekMode = mode;
-}
-
-void MediaSource::ReadOptions::clearSeekTo() {
-    mOptions &= ~kSeekTo_Option;
-    mSeekTimeUs = 0;
-    mSeekMode = SEEK_CLOSEST_SYNC;
-}
-
-bool MediaSource::ReadOptions::getSeekTo(
-        int64_t *time_us, SeekMode *mode) const {
-    *time_us = mSeekTimeUs;
-    *mode = mSeekMode;
-    return (mOptions & kSeekTo_Option) != 0;
-}
-
 }  // namespace android
diff --git a/media/libmediaextractor/MediaSourceBase.cpp b/media/libmediaextractor/MediaSourceBase.cpp
new file mode 100644
index 0000000..6d45c90
--- /dev/null
+++ b/media/libmediaextractor/MediaSourceBase.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/MediaSourceBase.h>
+
+namespace android {
+
+MediaSourceBase::MediaSourceBase() {}
+
+MediaSourceBase::~MediaSourceBase() {}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MediaSourceBase::ReadOptions::ReadOptions() {
+    reset();
+}
+
+void MediaSourceBase::ReadOptions::reset() {
+    mOptions = 0;
+    mSeekTimeUs = 0;
+    mNonBlocking = false;
+}
+
+void MediaSourceBase::ReadOptions::setNonBlocking() {
+    mNonBlocking = true;
+}
+
+void MediaSourceBase::ReadOptions::clearNonBlocking() {
+    mNonBlocking = false;
+}
+
+bool MediaSourceBase::ReadOptions::getNonBlocking() const {
+    return mNonBlocking;
+}
+
+void MediaSourceBase::ReadOptions::setSeekTo(int64_t time_us, SeekMode mode) {
+    mOptions |= kSeekTo_Option;
+    mSeekTimeUs = time_us;
+    mSeekMode = mode;
+}
+
+void MediaSourceBase::ReadOptions::clearSeekTo() {
+    mOptions &= ~kSeekTo_Option;
+    mSeekTimeUs = 0;
+    mSeekMode = SEEK_CLOSEST_SYNC;
+}
+
+bool MediaSourceBase::ReadOptions::getSeekTo(
+        int64_t *time_us, SeekMode *mode) const {
+    *time_us = mSeekTimeUs;
+    *mode = mSeekMode;
+    return (mOptions & kSeekTo_Option) != 0;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/foundation/MetaData.cpp b/media/libmediaextractor/MetaData.cpp
similarity index 99%
rename from media/libstagefright/foundation/MetaData.cpp
rename to media/libmediaextractor/MetaData.cpp
index 2415c61..98cddbe 100644
--- a/media/libstagefright/foundation/MetaData.cpp
+++ b/media/libmediaextractor/MetaData.cpp
@@ -17,6 +17,7 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "MetaData"
 #include <inttypes.h>
+#include <binder/Parcel.h>
 #include <utils/KeyedVector.h>
 #include <utils/Log.h>
 
diff --git a/media/libmediaextractor/include/media/DataSource.h b/media/libmediaextractor/include/media/DataSource.h
index 44f94a0..9f00e0a 100644
--- a/media/libmediaextractor/include/media/DataSource.h
+++ b/media/libmediaextractor/include/media/DataSource.h
@@ -19,23 +19,15 @@
 #define DATA_SOURCE_H_
 
 #include <sys/types.h>
-#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaErrors.h>
 #include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/List.h>
 #include <utils/RefBase.h>
 #include <utils/threads.h>
 #include <drm/DrmManagerClient.h>
 
 namespace android {
 
-struct AMessage;
-struct AString;
-class  IDataSource;
-struct IMediaHTTPService;
 class String8;
-struct HTTPBase;
 
 class DataSource : public RefBase {
 public:
@@ -67,20 +59,6 @@
     bool getUInt32Var(off64_t offset, uint32_t *x, size_t size);
     bool getUInt64Var(off64_t offset, uint64_t *x, size_t size);
 
-    // Reads in "count" entries of type T into vector *x.
-    // Returns true if "count" entries can be read.
-    // If fewer than "count" entries can be read, return false. In this case,
-    // the output vector *x will still have those entries that were read. Call
-    // x->size() to obtain the number of entries read.
-    // The optional parameter chunkSize specifies how many entries should be
-    // read from the data source at one time into a temporary buffer. Increasing
-    // chunkSize can improve the performance at the cost of extra memory usage.
-    // The default value for chunkSize is set to read at least 4k bytes at a
-    // time, depending on sizeof(T).
-    template <typename T>
-    bool getVector(off64_t offset, Vector<T>* x, size_t count,
-                   size_t chunkSize = (4095 / sizeof(T)) + 1);
-
     // May return ERROR_UNSUPPORTED.
     virtual status_t getSize(off64_t *size);
 
@@ -119,51 +97,6 @@
     DataSource &operator=(const DataSource &);
 };
 
-template <typename T>
-bool DataSource::getVector(off64_t offset, Vector<T>* x, size_t count,
-                           size_t chunkSize)
-{
-    x->clear();
-    if (chunkSize == 0) {
-        return false;
-    }
-    if (count == 0) {
-        return true;
-    }
-
-    T tmp[chunkSize];
-    ssize_t numBytesRead;
-    size_t numBytesPerChunk = chunkSize * sizeof(T);
-    size_t i;
-
-    for (i = 0; i + chunkSize < count; i += chunkSize) {
-        // This loops is executed when more than chunkSize records need to be
-        // read.
-        numBytesRead = this->readAt(offset, (void*)&tmp, numBytesPerChunk);
-        if (numBytesRead == -1) { // If readAt() returns -1, there is an error.
-            return false;
-        }
-        if (static_cast<size_t>(numBytesRead) < numBytesPerChunk) {
-            // This case is triggered when the stream ends before the whole
-            // chunk is read.
-            x->appendArray(tmp, (size_t)numBytesRead / sizeof(T));
-            return false;
-        }
-        x->appendArray(tmp, chunkSize);
-        offset += numBytesPerChunk;
-    }
-
-    // There are (count - i) more records to read.
-    // Right now, (count - i) <= chunkSize.
-    // We do the same thing as above, but with chunkSize replaced by count - i.
-    numBytesRead = this->readAt(offset, (void*)&tmp, (count - i) * sizeof(T));
-    if (numBytesRead == -1) {
-        return false;
-    }
-    x->appendArray(tmp, (size_t)numBytesRead / sizeof(T));
-    return x->size() == count;
-}
-
 }  // namespace android
 
 #endif  // DATA_SOURCE_H_
diff --git a/media/libmediaextractor/include/media/MediaExtractor.h b/media/libmediaextractor/include/media/MediaExtractor.h
index f197b5e..27581f3 100644
--- a/media/libmediaextractor/include/media/MediaExtractor.h
+++ b/media/libmediaextractor/include/media/MediaExtractor.h
@@ -22,24 +22,37 @@
 #include <vector>
 
 #include <utils/Errors.h>
+#include <utils/Log.h>
 #include <utils/RefBase.h>
 
 namespace android {
 
 class DataSource;
-class IMediaSource;
-class MediaExtractorFactory;
 class MetaData;
-class Parcel;
 class String8;
 struct AMessage;
-struct MediaSource;
+struct MediaSourceBase;
 typedef std::vector<uint8_t> HInterfaceToken;
 
-class MediaExtractor : public RefBase {
+
+class ExtractorAllocTracker {
 public:
+    ExtractorAllocTracker() {
+        ALOGD("extractor allocated: %p", this);
+    }
+    virtual ~ExtractorAllocTracker() {
+        ALOGD("extractor freed: %p", this);
+    }
+};
+
+
+class MediaExtractor
+// : public ExtractorAllocTracker
+{
+public:
+    virtual ~MediaExtractor();
     virtual size_t countTracks() = 0;
-    virtual sp<MediaSource> getTrack(size_t index) = 0;
+    virtual MediaSourceBase *getTrack(size_t index) = 0;
 
     enum GetTrackMetaDataFlags {
         kIncludeExtensiveMetaData = 1
@@ -115,7 +128,6 @@
 
 protected:
     MediaExtractor();
-    virtual ~MediaExtractor();
 
 private:
     MediaExtractor(const MediaExtractor &);
diff --git a/media/libmediaextractor/include/media/MediaSource.h b/media/libmediaextractor/include/media/MediaSource.h
index 25d691d..45070d6 100644
--- a/media/libmediaextractor/include/media/MediaSource.h
+++ b/media/libmediaextractor/include/media/MediaSource.h
@@ -20,113 +20,19 @@
 
 #include <sys/types.h>
 
-#include <binder/IMemory.h>
-#include <binder/MemoryDealer.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
 #include <utils/RefBase.h>
-#include <utils/Vector.h>
+
+#include "media/MediaSourceBase.h"
 
 namespace android {
 
 class MediaBuffer;
-class IMediaSource;
 
-struct MediaSource : public virtual RefBase {
+struct MediaSource : public MediaSourceBase, public virtual RefBase {
     MediaSource();
 
-    // To be called before any other methods on this object, except
-    // getFormat().
-    virtual status_t start(MetaData *params = NULL) = 0;
-
-    // Any blocking read call returns immediately with a result of NO_INIT.
-    // It is an error to call any methods other than start after this call
-    // returns. Any buffers the object may be holding onto at the time of
-    // the stop() call are released.
-    // Also, it is imperative that any buffers output by this object and
-    // held onto by callers be released before a call to stop() !!!
-    virtual status_t stop() = 0;
-
-    // Returns the format of the data output by this media source.
-    virtual sp<MetaData> getFormat() = 0;
-
-    // Options that modify read() behaviour. The default is to
-    // a) not request a seek
-    // b) not be late, i.e. lateness_us = 0
-    struct ReadOptions {
-        enum SeekMode : int32_t {
-            SEEK_PREVIOUS_SYNC,
-            SEEK_NEXT_SYNC,
-            SEEK_CLOSEST_SYNC,
-            SEEK_CLOSEST,
-            SEEK_FRAME_INDEX,
-        };
-
-        ReadOptions();
-
-        // Reset everything back to defaults.
-        void reset();
-
-        void setSeekTo(int64_t time_us, SeekMode mode = SEEK_CLOSEST_SYNC);
-        void clearSeekTo();
-        bool getSeekTo(int64_t *time_us, SeekMode *mode) const;
-
-        void setNonBlocking();
-        void clearNonBlocking();
-        bool getNonBlocking() const;
-
-        // Used to clear all non-persistent options for multiple buffer reads.
-        void clearNonPersistent() {
-            clearSeekTo();
-        }
-
-    private:
-        enum Options {
-            kSeekTo_Option      = 1,
-        };
-
-        uint32_t mOptions;
-        int64_t mSeekTimeUs;
-        SeekMode mSeekMode;
-        bool mNonBlocking;
-    } __attribute__((packed)); // sent through Binder
-
-    // Returns a new buffer of data. Call blocks until a
-    // buffer is available, an error is encountered of the end of the stream
-    // is reached.
-    // End of stream is signalled by a result of ERROR_END_OF_STREAM.
-    // A result of INFO_FORMAT_CHANGED indicates that the format of this
-    // MediaSource has changed mid-stream, the client can continue reading
-    // but should be prepared for buffers of the new configuration.
-    virtual status_t read(
-            MediaBuffer **buffer, const ReadOptions *options = NULL) = 0;
-
-    // Causes this source to suspend pulling data from its upstream source
-    // until a subsequent read-with-seek. This is currently not supported
-    // as such by any source. E.g. MediaCodecSource does not suspend its
-    // upstream source, and instead discard upstream data while paused.
-    virtual status_t pause() {
-        return ERROR_UNSUPPORTED;
-    }
-
-    // The consumer of this media source requests the source stops sending
-    // buffers with timestamp larger than or equal to stopTimeUs. stopTimeUs
-    // must be in the same time base as the startTime passed in start(). If
-    // source does not support this request, ERROR_UNSUPPORTED will be returned.
-    // If stopTimeUs is invalid, BAD_VALUE will be returned. This could be
-    // called at any time even before source starts and it could be called
-    // multiple times. Setting stopTimeUs to be -1 will effectively cancel the stopTimeUs
-    // set previously. If stopTimeUs is larger than or equal to last buffer's timestamp,
-    // source will start to drop buffer when it gets a buffer with timestamp larger
-    // than or equal to stopTimeUs. If stopTimeUs is smaller than or equal to last
-    // buffer's timestamp, source will drop all the incoming buffers immediately.
-    // After setting stopTimeUs, source may still stop sending buffers with timestamp
-    // less than stopTimeUs if it is stopped by the consumer.
-    virtual status_t setStopTimeUs(int64_t /* stopTimeUs */) {
-        return ERROR_UNSUPPORTED;
-    }
-
-protected:
     virtual ~MediaSource();
 
 private:
diff --git a/media/libmediaextractor/include/media/MediaSourceBase.h b/media/libmediaextractor/include/media/MediaSourceBase.h
new file mode 100644
index 0000000..9db6099
--- /dev/null
+++ b/media/libmediaextractor/include/media/MediaSourceBase.h
@@ -0,0 +1,150 @@
+/*
+ * 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 MEDIA_SOURCE_BASE_H_
+
+#define MEDIA_SOURCE_BASE_H_
+
+#include <sys/types.h>
+
+#include <binder/IMemory.h>
+#include <binder/MemoryDealer.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/Log.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class MediaBuffer;
+
+class SourceBaseAllocTracker {
+public:
+    SourceBaseAllocTracker() {
+        ALOGD("sourcebase allocated: %p", this);
+    }
+    virtual ~SourceBaseAllocTracker() {
+        ALOGD("sourcebase freed: %p", this);
+    }
+};
+
+struct MediaSourceBase
+//    : public SourceBaseAllocTracker
+{
+    MediaSourceBase();
+
+    // To be called before any other methods on this object, except
+    // getFormat().
+    virtual status_t start(MetaData *params = NULL) = 0;
+
+    // Any blocking read call returns immediately with a result of NO_INIT.
+    // It is an error to call any methods other than start after this call
+    // returns. Any buffers the object may be holding onto at the time of
+    // the stop() call are released.
+    // Also, it is imperative that any buffers output by this object and
+    // held onto by callers be released before a call to stop() !!!
+    virtual status_t stop() = 0;
+
+    // Returns the format of the data output by this media source.
+    virtual sp<MetaData> getFormat() = 0;
+
+    // Options that modify read() behaviour. The default is to
+    // a) not request a seek
+    // b) not be late, i.e. lateness_us = 0
+    struct ReadOptions {
+        enum SeekMode : int32_t {
+            SEEK_PREVIOUS_SYNC,
+            SEEK_NEXT_SYNC,
+            SEEK_CLOSEST_SYNC,
+            SEEK_CLOSEST,
+            SEEK_FRAME_INDEX,
+        };
+
+        ReadOptions();
+
+        // Reset everything back to defaults.
+        void reset();
+
+        void setSeekTo(int64_t time_us, SeekMode mode = SEEK_CLOSEST_SYNC);
+        void clearSeekTo();
+        bool getSeekTo(int64_t *time_us, SeekMode *mode) const;
+
+        void setNonBlocking();
+        void clearNonBlocking();
+        bool getNonBlocking() const;
+
+        // Used to clear all non-persistent options for multiple buffer reads.
+        void clearNonPersistent() {
+            clearSeekTo();
+        }
+
+    private:
+        enum Options {
+            kSeekTo_Option      = 1,
+        };
+
+        uint32_t mOptions;
+        int64_t mSeekTimeUs;
+        SeekMode mSeekMode;
+        bool mNonBlocking;
+    } __attribute__((packed)); // sent through Binder
+
+    // Returns a new buffer of data. Call blocks until a
+    // buffer is available, an error is encountered of the end of the stream
+    // is reached.
+    // End of stream is signalled by a result of ERROR_END_OF_STREAM.
+    // A result of INFO_FORMAT_CHANGED indicates that the format of this
+    // MediaSource has changed mid-stream, the client can continue reading
+    // but should be prepared for buffers of the new configuration.
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options = NULL) = 0;
+
+    // Causes this source to suspend pulling data from its upstream source
+    // until a subsequent read-with-seek. This is currently not supported
+    // as such by any source. E.g. MediaCodecSource does not suspend its
+    // upstream source, and instead discard upstream data while paused.
+    virtual status_t pause() {
+        return ERROR_UNSUPPORTED;
+    }
+
+    // The consumer of this media source requests the source stops sending
+    // buffers with timestamp larger than or equal to stopTimeUs. stopTimeUs
+    // must be in the same time base as the startTime passed in start(). If
+    // source does not support this request, ERROR_UNSUPPORTED will be returned.
+    // If stopTimeUs is invalid, BAD_VALUE will be returned. This could be
+    // called at any time even before source starts and it could be called
+    // multiple times. Setting stopTimeUs to be -1 will effectively cancel the stopTimeUs
+    // set previously. If stopTimeUs is larger than or equal to last buffer's timestamp,
+    // source will start to drop buffer when it gets a buffer with timestamp larger
+    // than or equal to stopTimeUs. If stopTimeUs is smaller than or equal to last
+    // buffer's timestamp, source will drop all the incoming buffers immediately.
+    // After setting stopTimeUs, source may still stop sending buffers with timestamp
+    // less than stopTimeUs if it is stopped by the consumer.
+    virtual status_t setStopTimeUs(int64_t /* stopTimeUs */) {
+        return ERROR_UNSUPPORTED;
+    }
+
+    virtual ~MediaSourceBase();
+
+private:
+    MediaSourceBase(const MediaSourceBase &);
+    MediaSourceBase &operator=(const MediaSourceBase &);
+};
+
+}  // namespace android
+
+#endif  // MEDIA_SOURCE_BASE_H_
diff --git a/media/libmediaextractor/include/media/stagefright/MediaBuffer.h b/media/libmediaextractor/include/media/stagefright/MediaBuffer.h
index 2b51081..a8f8375 100644
--- a/media/libmediaextractor/include/media/stagefright/MediaBuffer.h
+++ b/media/libmediaextractor/include/media/stagefright/MediaBuffer.h
@@ -30,7 +30,6 @@
 namespace android {
 
 struct ABuffer;
-class GraphicBuffer;
 class MediaBuffer;
 class MediaBufferObserver;
 class MetaData;
@@ -143,7 +142,7 @@
         return mObserver != nullptr;
     }
 
-    ~MediaBuffer();
+    virtual ~MediaBuffer();
 
     sp<IMemory> mMemory;
 
diff --git a/media/libstagefright/include/media/stagefright/MetaData.h b/media/libmediaextractor/include/media/stagefright/MetaData.h
similarity index 99%
rename from media/libstagefright/include/media/stagefright/MetaData.h
rename to media/libmediaextractor/include/media/stagefright/MetaData.h
index 5959e86..e4a84b7 100644
--- a/media/libstagefright/include/media/stagefright/MetaData.h
+++ b/media/libmediaextractor/include/media/stagefright/MetaData.h
@@ -22,12 +22,13 @@
 
 #include <stdint.h>
 
-#include <binder/Parcel.h>
 #include <utils/RefBase.h>
 #include <utils/String8.h>
 
 namespace android {
 
+class Parcel;
+
 // The following keys map to int32_t data unless indicated otherwise.
 enum {
     kKeyMIMEType          = 'mime',  // cstring
diff --git a/media/libmediametrics/MediaAnalyticsItem.cpp b/media/libmediametrics/MediaAnalyticsItem.cpp
index 423dfb8..dc2bec8 100644
--- a/media/libmediametrics/MediaAnalyticsItem.cpp
+++ b/media/libmediametrics/MediaAnalyticsItem.cpp
@@ -60,7 +60,7 @@
       mPkgVersionCode(0),
       mSessionID(MediaAnalyticsItem::SessionIDNone),
       mTimestamp(0),
-      mFinalized(0),
+      mFinalized(1),
       mPropCount(0), mPropSize(0), mProps(NULL)
 {
     mKey = MediaAnalyticsItem::kKeyNone;
@@ -72,7 +72,7 @@
       mPkgVersionCode(0),
       mSessionID(MediaAnalyticsItem::SessionIDNone),
       mTimestamp(0),
-      mFinalized(0),
+      mFinalized(1),
       mPropCount(0), mPropSize(0), mProps(NULL)
 {
     if (DEBUG_ALLOCATIONS) {
@@ -137,16 +137,6 @@
     return dst;
 }
 
-// so clients can send intermediate values to be overlaid later
-MediaAnalyticsItem &MediaAnalyticsItem::setFinalized(bool value) {
-    mFinalized = value;
-    return *this;
-}
-
-bool MediaAnalyticsItem::getFinalized() const {
-    return mFinalized;
-}
-
 MediaAnalyticsItem &MediaAnalyticsItem::setSessionID(MediaAnalyticsItem::SessionID_t id) {
     mSessionID = id;
     return *this;
@@ -636,7 +626,10 @@
     mPkgName = data.readCString();
     mPkgVersionCode = data.readInt64();
     mSessionID = data.readInt64();
+    // We no longer pay attention to user setting of finalized, BUT it's
+    // still part of the wire packet -- so read & discard.
     mFinalized = data.readInt32();
+    mFinalized = 1;
     mTimestamp = data.readInt64();
 
     int count = data.readInt32();
@@ -722,7 +715,7 @@
 
 
 std::string MediaAnalyticsItem::toString() {
-   return toString(-1);
+   return toString(PROTO_LAST);
 }
 
 std::string MediaAnalyticsItem::toString(int version) {
@@ -978,9 +971,6 @@
         mSessionID = incoming->mSessionID;
     }
 
-    // we always take the more recent 'finalized' value
-    setFinalized(incoming->getFinalized());
-
     // for each attribute from 'incoming', resolve appropriately
     int nattr = incoming->mPropCount;
     for (int i = 0 ; i < nattr; i++ ) {
diff --git a/media/libmediametrics/include/MediaAnalyticsItem.h b/media/libmediametrics/include/MediaAnalyticsItem.h
index 79ff093..263cde7 100644
--- a/media/libmediametrics/include/MediaAnalyticsItem.h
+++ b/media/libmediametrics/include/MediaAnalyticsItem.h
@@ -89,10 +89,6 @@
         MediaAnalyticsItem(Key);
         ~MediaAnalyticsItem();
 
-        // so clients can send intermediate values to be overlaid later
-        MediaAnalyticsItem &setFinalized(bool);
-        bool getFinalized() const;
-
         // SessionID ties multiple submissions for same key together
         // so that if video "height" and "width" are known at one point
         // and "framerate" is only known later, they can be be brought
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index dcd393b..4206647 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -527,4 +527,14 @@
     }
     return NO_INIT;
 }
+
+status_t MediaRecorderClient::getActiveMicrophones(
+        std::vector<media::MicrophoneInfo>* activeMicrophones) {
+    ALOGV("getActiveMicrophones");
+    Mutex::Autolock lock(mLock);
+    if (mRecorder != NULL) {
+        return mRecorder->getActiveMicrophones(activeMicrophones);
+    }
+    return NO_INIT;
+}
 }; // namespace android
diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h
index 538b461..d2e681f 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -107,6 +107,8 @@
     virtual     status_t   setInputDevice(audio_port_handle_t deviceId);
     virtual     status_t   getRoutedDeviceId(audio_port_handle_t* deviceId);
     virtual     status_t   enableAudioDeviceCallback(bool enabled);
+    virtual     status_t   getActiveMicrophones(
+                              std::vector<media::MicrophoneInfo>* activeMicrophones);
 
 private:
     friend class           MediaPlayerService;  // for accessing private constructor
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 77eaefe..bc8d8c8 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -125,7 +125,6 @@
     if (mAnalyticsDirty && mAnalyticsItem != NULL) {
         updateMetrics();
         if (mAnalyticsItem->count() > 0) {
-            mAnalyticsItem->setFinalized(true);
             mAnalyticsItem->selfrecord();
         }
         delete mAnalyticsItem;
@@ -185,14 +184,12 @@
     if (mAnalyticsDirty && mAnalyticsItem != NULL) {
         updateMetrics();
         if (mAnalyticsItem->count() > 0) {
-            mAnalyticsItem->setFinalized(true);
             mAnalyticsItem->selfrecord();
         }
         delete mAnalyticsItem;
         mAnalyticsItem = NULL;
     }
     mAnalyticsItem = new MediaAnalyticsItem(kKeyRecorder);
-    (void) mAnalyticsItem->generateSessionID();
     mAnalyticsDirty = false;
 }
 
@@ -2163,6 +2160,15 @@
     return NO_ERROR;
 }
 
+status_t StagefrightRecorder::getActiveMicrophones(
+        std::vector<media::MicrophoneInfo>* activeMicrophones) {
+    if (mAudioSourceNode != 0) {
+        return mAudioSourceNode->getActiveMicrophones(activeMicrophones);
+    }
+    return NO_INIT;
+}
+
+
 status_t StagefrightRecorder::dump(
         int fd, const Vector<String16>& args) const {
     ALOGV("dump");
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index ec7e8ed..18c9256 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -76,6 +76,8 @@
     virtual status_t getRoutedDeviceId(audio_port_handle_t* deviceId);
     virtual void setAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
     virtual status_t enableAudioDeviceCallback(bool enabled);
+    virtual status_t getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones);
+
 
 private:
     mutable Mutex mLock;
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 856f03b..2406665 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -35,6 +35,7 @@
 class IDataSource;
 struct IMediaHTTPService;
 struct MediaSource;
+class IMediaSource;
 class MediaBuffer;
 struct MediaClock;
 struct NuCachedSource2;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 8aa06fc..b3fd00a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -91,7 +91,6 @@
 
     // set up an analytics record
     mAnalyticsItem = new MediaAnalyticsItem(kKeyPlayer);
-    mAnalyticsItem->generateSessionID();
 
     mLooper->start(
             false, /* runOnCallingThread */
@@ -617,14 +616,12 @@
     // So the canonical "empty" record has 3 elements in it.
     if (mAnalyticsItem->count() > 3) {
 
-        mAnalyticsItem->setFinalized(true);
         mAnalyticsItem->selfrecord();
 
         // re-init in case we prepare() and start() again.
         delete mAnalyticsItem ;
         mAnalyticsItem = new MediaAnalyticsItem("nuplayer");
         if (mAnalyticsItem) {
-            mAnalyticsItem->generateSessionID();
             mAnalyticsItem->setUid(mClientUid);
         }
     } else {
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 0ded5be..541093a 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -126,6 +126,32 @@
     }
 }
 
+static OMX_VIDEO_CONTROLRATETYPE getVideoBitrateMode(const sp<AMessage> &msg) {
+    int32_t tmp;
+    if (msg->findInt32("bitrate-mode", &tmp)) {
+        // explicitly translate from MediaCodecInfo.EncoderCapabilities.
+        // BITRATE_MODE_* into OMX bitrate mode.
+        switch (tmp) {
+            //BITRATE_MODE_CQ
+            case 0: return OMX_Video_ControlRateConstantQuality;
+            //BITRATE_MODE_VBR
+            case 1: return OMX_Video_ControlRateVariable;
+            //BITRATE_MODE_CBR
+            case 2: return OMX_Video_ControlRateConstant;
+            default: break;
+        }
+    }
+    return OMX_Video_ControlRateVariable;
+}
+
+static bool findVideoBitrateControlInfo(const sp<AMessage> &msg,
+        OMX_VIDEO_CONTROLRATETYPE *mode, int32_t *bitrate, int32_t *quality) {
+    *mode = getVideoBitrateMode(msg);
+    bool isCQ = (*mode == OMX_Video_ControlRateConstantQuality);
+    return (!isCQ && msg->findInt32("bitrate", bitrate))
+         || (isCQ && msg->findInt32("quality", quality));
+}
+
 struct MessageList : public RefBase {
     MessageList() {
     }
@@ -1686,6 +1712,7 @@
     mConfigFormat = msg;
 
     mIsEncoder = encoder;
+    mIsVideo = !strncasecmp(mime, "video/", 6);
 
     mPortMode[kPortIndexInput] = IOMX::kPortModePresetByteBuffer;
     mPortMode[kPortIndexOutput] = IOMX::kPortModePresetByteBuffer;
@@ -1696,19 +1723,26 @@
         return err;
     }
 
-    int32_t bitRate = 0;
-    // FLAC encoder doesn't need a bitrate, other encoders do
-    if (encoder && strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)
-            && !msg->findInt32("bitrate", &bitRate)) {
-        return INVALID_OPERATION;
+    OMX_VIDEO_CONTROLRATETYPE bitrateMode;
+    int32_t bitrate = 0, quality;
+    // FLAC encoder or video encoder in constant quality mode doesn't need a
+    // bitrate, other encoders do.
+    if (encoder) {
+        if (mIsVideo && !findVideoBitrateControlInfo(
+                msg, &bitrateMode, &bitrate, &quality)) {
+            return INVALID_OPERATION;
+        } else if (!mIsVideo && strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)
+            && !msg->findInt32("bitrate", &bitrate)) {
+            return INVALID_OPERATION;
+        }
     }
 
     // propagate bitrate to the output so that the muxer has it
-    if (encoder && msg->findInt32("bitrate", &bitRate)) {
+    if (encoder && msg->findInt32("bitrate", &bitrate)) {
         // Technically ISO spec says that 'bitrate' should be 0 for VBR even though it is the
         // average bitrate. We've been setting both bitrate and max-bitrate to this same value.
-        outputFormat->setInt32("bitrate", bitRate);
-        outputFormat->setInt32("max-bitrate", bitRate);
+        outputFormat->setInt32("bitrate", bitrate);
+        outputFormat->setInt32("max-bitrate", bitrate);
     }
 
     int32_t storeMeta;
@@ -1765,9 +1799,7 @@
     // Only enable metadata mode on encoder output if encoder can prepend
     // sps/pps to idr frames, since in metadata mode the bitstream is in an
     // opaque handle, to which we don't have access.
-    int32_t video = !strncasecmp(mime, "video/", 6);
-    mIsVideo = video;
-    if (encoder && video) {
+    if (encoder && mIsVideo) {
         OMX_BOOL enable = (OMX_BOOL) (prependSPSPPS
             && msg->findInt32("android._store-metadata-in-buffers-output", &storeMeta)
             && storeMeta != 0);
@@ -1813,9 +1845,9 @@
     // NOTE: we only use native window for video decoders
     sp<RefBase> obj;
     bool haveNativeWindow = msg->findObject("native-window", &obj)
-            && obj != NULL && video && !encoder;
+            && obj != NULL && mIsVideo && !encoder;
     mUsingNativeWindow = haveNativeWindow;
-    if (video && !encoder) {
+    if (mIsVideo && !encoder) {
         inputFormat->setInt32("adaptive-playback", false);
 
         int32_t usageProtected;
@@ -1978,7 +2010,7 @@
     (void)msg->findInt32("pcm-encoding", (int32_t*)&pcmEncoding);
     // invalid encodings will default to PCM-16bit in setupRawAudioFormat.
 
-    if (video) {
+    if (mIsVideo) {
         // determine need for software renderer
         bool usingSwRenderer = false;
         if (haveNativeWindow && mComponentName.startsWith("OMX.google.")) {
@@ -2112,14 +2144,14 @@
             }
 
             err = setupAACCodec(
-                    encoder, numChannels, sampleRate, bitRate, aacProfile,
+                    encoder, numChannels, sampleRate, bitrate, aacProfile,
                     isADTS != 0, sbrMode, maxOutputChannelCount, drc,
                     pcmLimiterEnable);
         }
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) {
-        err = setupAMRCodec(encoder, false /* isWAMR */, bitRate);
+        err = setupAMRCodec(encoder, false /* isWAMR */, bitrate);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {
-        err = setupAMRCodec(encoder, true /* isWAMR */, bitRate);
+        err = setupAMRCodec(encoder, true /* isWAMR */, bitrate);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_ALAW)
             || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_MLAW)) {
         // These are PCM-like formats with a fixed sample rate but
@@ -2233,7 +2265,7 @@
         rateFloat = (float)rateInt;  // 16MHz (FLINTMAX) is OK for upper bound.
     }
     if (rateFloat > 0) {
-        err = setOperatingRate(rateFloat, video);
+        err = setOperatingRate(rateFloat, mIsVideo);
         err = OK; // ignore errors
     }
 
@@ -2258,7 +2290,7 @@
     }
 
     // create data converters if needed
-    if (!video && err == OK) {
+    if (!mIsVideo && err == OK) {
         AudioEncoding codecPcmEncoding = kAudioEncodingPcm16bit;
         if (encoder) {
             (void)mInputFormat->findInt32("pcm-encoding", (int32_t*)&codecPcmEncoding);
@@ -3709,10 +3741,12 @@
         return err;
     }
 
-    int32_t width, height, bitrate;
+    OMX_VIDEO_CONTROLRATETYPE bitrateMode;
+    int32_t width, height, bitrate = 0, quality;
     if (!msg->findInt32("width", &width)
             || !msg->findInt32("height", &height)
-            || !msg->findInt32("bitrate", &bitrate)) {
+            || !findVideoBitrateControlInfo(
+                    msg, &bitrateMode, &bitrate, &quality)) {
         return INVALID_OPERATION;
     }
 
@@ -3965,15 +3999,6 @@
     return ret > 0 ? ret - 1 : 0;
 }
 
-static OMX_VIDEO_CONTROLRATETYPE getBitrateMode(const sp<AMessage> &msg) {
-    int32_t tmp;
-    if (!msg->findInt32("bitrate-mode", &tmp)) {
-        return OMX_Video_ControlRateVariable;
-    }
-
-    return static_cast<OMX_VIDEO_CONTROLRATETYPE>(tmp);
-}
-
 status_t ACodec::setupMPEG4EncoderParameters(const sp<AMessage> &msg) {
     int32_t bitrate;
     float iFrameInterval;
@@ -3982,7 +4007,7 @@
         return INVALID_OPERATION;
     }
 
-    OMX_VIDEO_CONTROLRATETYPE bitrateMode = getBitrateMode(msg);
+    OMX_VIDEO_CONTROLRATETYPE bitrateMode = getVideoBitrateMode(msg);
 
     float frameRate;
     if (!msg->findFloat("frame-rate", &frameRate)) {
@@ -4047,7 +4072,7 @@
         return err;
     }
 
-    err = configureBitrate(bitrate, bitrateMode);
+    err = configureBitrate(bitrateMode, bitrate);
 
     if (err != OK) {
         return err;
@@ -4064,7 +4089,7 @@
         return INVALID_OPERATION;
     }
 
-    OMX_VIDEO_CONTROLRATETYPE bitrateMode = getBitrateMode(msg);
+    OMX_VIDEO_CONTROLRATETYPE bitrateMode = getVideoBitrateMode(msg);
 
     float frameRate;
     if (!msg->findFloat("frame-rate", &frameRate)) {
@@ -4124,7 +4149,7 @@
         return err;
     }
 
-    err = configureBitrate(bitrate, bitrateMode);
+    err = configureBitrate(bitrateMode, bitrate);
 
     if (err != OK) {
         return err;
@@ -4194,7 +4219,7 @@
         return INVALID_OPERATION;
     }
 
-    OMX_VIDEO_CONTROLRATETYPE bitrateMode = getBitrateMode(msg);
+    OMX_VIDEO_CONTROLRATETYPE bitrateMode = getVideoBitrateMode(msg);
 
     float frameRate;
     if (!msg->findFloat("frame-rate", &frameRate)) {
@@ -4350,18 +4375,20 @@
         }
     }
 
-    return configureBitrate(bitrate, bitrateMode);
+    return configureBitrate(bitrateMode, bitrate);
 }
 
 status_t ACodec::setupHEVCEncoderParameters(const sp<AMessage> &msg) {
-    int32_t bitrate;
     float iFrameInterval;
-    if (!msg->findInt32("bitrate", &bitrate)
-            || !msg->findAsFloat("i-frame-interval", &iFrameInterval)) {
+    if (!msg->findAsFloat("i-frame-interval", &iFrameInterval)) {
         return INVALID_OPERATION;
     }
 
-    OMX_VIDEO_CONTROLRATETYPE bitrateMode = getBitrateMode(msg);
+    OMX_VIDEO_CONTROLRATETYPE bitrateMode;
+    int32_t bitrate, quality;
+    if (!findVideoBitrateControlInfo(msg, &bitrateMode, &bitrate, &quality)) {
+        return INVALID_OPERATION;
+    }
 
     float frameRate;
     if (!msg->findFloat("frame-rate", &frameRate)) {
@@ -4407,7 +4434,7 @@
         return err;
     }
 
-    return configureBitrate(bitrate, bitrateMode);
+    return configureBitrate(bitrateMode, bitrate, quality);
 }
 
 status_t ACodec::setupVPXEncoderParameters(const sp<AMessage> &msg, sp<AMessage> &outputFormat) {
@@ -4428,7 +4455,7 @@
     }
     msg->findAsFloat("i-frame-interval", &iFrameInterval);
 
-    OMX_VIDEO_CONTROLRATETYPE bitrateMode = getBitrateMode(msg);
+    OMX_VIDEO_CONTROLRATETYPE bitrateMode = getVideoBitrateMode(msg);
 
     float frameRate;
     if (!msg->findFloat("frame-rate", &frameRate)) {
@@ -4505,7 +4532,7 @@
         }
     }
 
-    return configureBitrate(bitrate, bitrateMode);
+    return configureBitrate(bitrateMode, bitrate);
 }
 
 status_t ACodec::verifySupportForProfileAndLevel(
@@ -4541,7 +4568,7 @@
 }
 
 status_t ACodec::configureBitrate(
-        int32_t bitrate, OMX_VIDEO_CONTROLRATETYPE bitrateMode) {
+        OMX_VIDEO_CONTROLRATETYPE bitrateMode, int32_t bitrate, int32_t quality) {
     OMX_VIDEO_PARAM_BITRATETYPE bitrateType;
     InitOMXParams(&bitrateType);
     bitrateType.nPortIndex = kPortIndexOutput;
@@ -4554,7 +4581,13 @@
     }
 
     bitrateType.eControlRate = bitrateMode;
-    bitrateType.nTargetBitrate = bitrate;
+
+    // write it out explicitly even if it's a union
+    if (bitrateMode == OMX_Video_ControlRateConstantQuality) {
+        bitrateType.nQualityFactor = quality;
+    } else {
+        bitrateType.nTargetBitrate = bitrate;
+    }
 
     return mOMXNode->setParameter(
             OMX_IndexParamVideoBitrate, &bitrateType, sizeof(bitrateType));
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 01f73a1..329b901 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -26,6 +26,28 @@
     shared_libs: ["libmedia"],
 }
 
+cc_library_static {
+    name: "libstagefright_metadatautils",
+
+    srcs: ["MetaDataUtils.cpp"],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+
+    shared_libs: ["libmedia"],
+}
+
 cc_library_shared {
     name: "libstagefright",
 
@@ -35,6 +57,7 @@
         "AACWriter.cpp",
         "AMRWriter.cpp",
         "AudioPlayer.cpp",
+        "AudioPresentationInfo.cpp",
         "AudioSource.cpp",
         "BufferImpl.cpp",
         "CCodec.cpp",
@@ -78,8 +101,8 @@
         "StagefrightMetadataRetriever.cpp",
         "SurfaceMediaSource.cpp",
         "SurfaceUtils.cpp",
-        "ThrottledSource.cpp",
         "Utils.cpp",
+        "ThrottledSource.cpp",
         "VideoFrameScheduler.cpp",
     ],
 
@@ -104,7 +127,9 @@
         "libutils",
         "libmedia_helper",
         "libstagefright_codec2",
+        "libstagefright_codec2_vndk",
         "libstagefright_foundation",
+        "libstagefright_gbs",
         "libstagefright_omx",
         "libstagefright_omx_utils",
         "libstagefright_xmlparser",
@@ -131,8 +156,6 @@
         "libstagefright_esds",
         "libstagefright_id3",
         "libFLAC",
-
-        "libstagefright_codec2_vndk",
     ],
 
     export_shared_lib_headers: [
diff --git a/media/libstagefright/AudioPresentationInfo.cpp b/media/libstagefright/AudioPresentationInfo.cpp
new file mode 100644
index 0000000..86e1859
--- /dev/null
+++ b/media/libstagefright/AudioPresentationInfo.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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 "AudioPresentationInfo"
+
+#include <media/AudioPresentationInfo.h>
+
+namespace android {
+
+AudioPresentationInfo::AudioPresentationInfo() {
+}
+
+AudioPresentationInfo::~AudioPresentationInfo() {
+    mPresentations.clear();
+}
+
+void AudioPresentationInfo::addPresentation(sp<AudioPresentation> presentation) {
+    mPresentations.push(presentation);
+}
+
+size_t AudioPresentationInfo::countPresentations() const {
+    return mPresentations.size();
+}
+
+// Returns an AudioPresentation for the given valid index
+// index must be >=0 and < countPresentations()
+const sp<AudioPresentation> AudioPresentationInfo::getPresentation(size_t index) const {
+    return mPresentations[index];
+}
+
+}  // namespace android
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index b8da980..70ce38c 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -498,4 +498,12 @@
     return NO_INIT;
 }
 
+status_t AudioSource::getActiveMicrophones(
+        std::vector<media::MicrophoneInfo>* activeMicrophones) {
+    if (mRecord != 0) {
+        return mRecord->getActiveMicrophones(activeMicrophones);
+    }
+    return NO_INIT;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/CCodec.cpp b/media/libstagefright/CCodec.cpp
index 0103abd..1e0a53f 100644
--- a/media/libstagefright/CCodec.cpp
+++ b/media/libstagefright/CCodec.cpp
@@ -23,7 +23,9 @@
 #include <C2PlatformSupport.h>
 
 #include <gui/Surface.h>
+#include <media/stagefright/BufferProducerWrapper.h>
 #include <media/stagefright/CCodec.h>
+#include <media/stagefright/PersistentSurface.h>
 
 #include "include/CCodecBufferChannel.h"
 
@@ -111,33 +113,35 @@
 
 class CCodecListener : public C2Component::Listener {
 public:
-    CCodecListener(const std::shared_ptr<CCodecBufferChannel> &channel)
-        : mChannel(channel) {
-    }
+    explicit CCodecListener(const wp<CCodec> &codec) : mCodec(codec) {}
 
     virtual void onWorkDone_nb(
             std::weak_ptr<C2Component> component,
-            std::vector<std::unique_ptr<C2Work>> workItems) override {
-        (void) component;
-        mChannel->onWorkDone(std::move(workItems));
+            std::list<std::unique_ptr<C2Work>> workItems) override {
+        (void)component;
+        sp<CCodec> codec(mCodec.promote());
+        if (!codec) {
+            return;
+        }
+        codec->onWorkDone(workItems);
     }
 
     virtual void onTripped_nb(
             std::weak_ptr<C2Component> component,
             std::vector<std::shared_ptr<C2SettingResult>> settingResult) override {
         // TODO
-        (void) component;
-        (void) settingResult;
+        (void)component;
+        (void)settingResult;
     }
 
     virtual void onError_nb(std::weak_ptr<C2Component> component, uint32_t errorCode) override {
         // TODO
-        (void) component;
-        (void) errorCode;
+        (void)component;
+        (void)errorCode;
     }
 
 private:
-    std::shared_ptr<CCodecBufferChannel> mChannel;
+    wp<CCodec> mCodec;
 };
 
 }  // namespace
@@ -159,11 +163,11 @@
 void CCodec::initiateAllocateComponent(const sp<AMessage> &msg) {
     {
         Mutexed<State>::Locked state(mState);
-        if (state->mState != RELEASED) {
+        if (state->get() != RELEASED) {
             mCallback->onError(INVALID_OPERATION, ACTION_CODE_FATAL);
             return;
         }
-        state->mState = ALLOCATING;
+        state->set(ALLOCATING);
     }
 
     AString componentName;
@@ -178,14 +182,14 @@
 
 void CCodec::allocate(const AString &componentName) {
     // TODO: use C2ComponentStore to create component
-    mListener.reset(new CCodecListener(mChannel));
+    mListener.reset(new CCodecListener(this));
 
     std::shared_ptr<C2Component> comp;
     c2_status_t err = GetCodec2PlatformComponentStore()->createComponent(
             componentName.c_str(), &comp);
     if (err != C2_OK) {
         Mutexed<State>::Locked state(mState);
-        state->mState = RELEASED;
+        state->set(RELEASED);
         state.unlock();
         mCallback->onError(err, ACTION_CODE_FATAL);
         state.lock();
@@ -194,15 +198,15 @@
     comp->setListener_vb(mListener, C2_MAY_BLOCK);
     {
         Mutexed<State>::Locked state(mState);
-        if (state->mState != ALLOCATING) {
-            state->mState = RELEASED;
+        if (state->get() != ALLOCATING) {
+            state->set(RELEASED);
             state.unlock();
             mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
             state.lock();
             return;
         }
-        state->mState = ALLOCATED;
-        state->mComp = comp;
+        state->set(ALLOCATED);
+        state->comp = comp;
     }
     mChannel->setComponent(comp);
     mCallback->onComponentAllocated(comp->intf()->getName().c_str());
@@ -211,7 +215,7 @@
 void CCodec::initiateConfigureComponent(const sp<AMessage> &format) {
     {
         Mutexed<State>::Locked state(mState);
-        if (state->mState != ALLOCATED) {
+        if (state->get() != ALLOCATED) {
             mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
             return;
         }
@@ -252,6 +256,9 @@
                 inputFormat->setInt32("sample-rate", 44100);
                 outputFormat->setInt32("channel-count", 1);
                 outputFormat->setInt32("sample-rate", 44100);
+            } else {
+                outputFormat->setInt32("width", 1080);
+                outputFormat->setInt32("height", 1920);
             }
         } else {
             inputFormat->setString("mime", mime);
@@ -272,32 +279,81 @@
 
     {
         Mutexed<Formats>::Locked formats(mFormats);
-        formats->mInputFormat = inputFormat;
-        formats->mOutputFormat = outputFormat;
+        formats->inputFormat = inputFormat;
+        formats->outputFormat = outputFormat;
     }
     mCallback->onComponentConfigured(inputFormat, outputFormat);
 }
 
-
 void CCodec::initiateCreateInputSurface() {
-    // TODO
+    (new AMessage(kWhatCreateInputSurface, this))->post();
+}
+
+void CCodec::createInputSurface() {
+    sp<IGraphicBufferProducer> producer;
+    sp<GraphicBufferSource> source(new GraphicBufferSource);
+
+    status_t err = source->initCheck();
+    if (err != OK) {
+        ALOGE("Failed to initialize graphic buffer source: %d", err);
+        mCallback->onInputSurfaceCreationFailed(err);
+        return;
+    }
+    producer = source->getIGraphicBufferProducer();
+
+    err = setupInputSurface(source);
+    if (err != OK) {
+        ALOGE("Failed to set up input surface: %d", err);
+        mCallback->onInputSurfaceCreationFailed(err);
+        return;
+    }
+
+    sp<AMessage> inputFormat;
+    sp<AMessage> outputFormat;
+    {
+        Mutexed<Formats>::Locked formats(mFormats);
+        inputFormat = formats->inputFormat;
+        outputFormat = formats->outputFormat;
+    }
+    mCallback->onInputSurfaceCreated(
+            inputFormat,
+            outputFormat,
+            new BufferProducerWrapper(producer));
+}
+
+status_t CCodec::setupInputSurface(const sp<GraphicBufferSource> &source) {
+    status_t err = mChannel->setGraphicBufferSource(source);
+    if (err != OK) {
+        return err;
+    }
+
+    // TODO: configure |source| with other settings.
+    return OK;
 }
 
 void CCodec::initiateSetInputSurface(const sp<PersistentSurface> &surface) {
+    sp<AMessage> msg = new AMessage(kWhatSetInputSurface, this);
+    msg->setObject("surface", surface);
+    msg->post();
+}
+
+void CCodec::setInputSurface(const sp<PersistentSurface> &surface) {
     // TODO
-    (void) surface;
+    (void)surface;
+
+    mCallback->onInputSurfaceDeclined(ERROR_UNSUPPORTED);
 }
 
 void CCodec::initiateStart() {
     {
         Mutexed<State>::Locked state(mState);
-        if (state->mState != ALLOCATED) {
+        if (state->get() != ALLOCATED) {
             state.unlock();
             mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
             state.lock();
             return;
         }
-        state->mState = STARTING;
+        state->set(STARTING);
     }
 
     (new AMessage(kWhatStart, this))->post();
@@ -307,13 +363,13 @@
     std::shared_ptr<C2Component> comp;
     {
         Mutexed<State>::Locked state(mState);
-        if (state->mState != STARTING) {
+        if (state->get() != STARTING) {
             state.unlock();
             mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
             state.lock();
             return;
         }
-        comp = state->mComp;
+        comp = state->comp;
     }
     c2_status_t err = comp->start();
     if (err != C2_OK) {
@@ -325,20 +381,20 @@
     sp<AMessage> outputFormat;
     {
         Mutexed<Formats>::Locked formats(mFormats);
-        inputFormat = formats->mInputFormat;
-        outputFormat = formats->mOutputFormat;
+        inputFormat = formats->inputFormat;
+        outputFormat = formats->outputFormat;
     }
     mChannel->start(inputFormat, outputFormat);
 
     {
         Mutexed<State>::Locked state(mState);
-        if (state->mState != STARTING) {
+        if (state->get() != STARTING) {
             state.unlock();
             mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
             state.lock();
             return;
         }
-        state->mState = RUNNING;
+        state->set(RUNNING);
     }
     mCallback->onStartCompleted();
 }
@@ -354,17 +410,17 @@
 void CCodec::initiateStop() {
     {
         Mutexed<State>::Locked state(mState);
-        if (state->mState == ALLOCATED
-                || state->mState  == RELEASED
-                || state->mState == STOPPING
-                || state->mState == RELEASING) {
+        if (state->get() == ALLOCATED
+                || state->get()  == RELEASED
+                || state->get() == STOPPING
+                || state->get() == RELEASING) {
             // We're already stopped, released, or doing it right now.
             state.unlock();
             mCallback->onStopCompleted();
             state.lock();
             return;
         }
-        state->mState = STOPPING;
+        state->set(STOPPING);
     }
 
     (new AMessage(kWhatStop, this))->post();
@@ -374,19 +430,19 @@
     std::shared_ptr<C2Component> comp;
     {
         Mutexed<State>::Locked state(mState);
-        if (state->mState == RELEASING) {
+        if (state->get() == RELEASING) {
             state.unlock();
             // We're already stopped or release is in progress.
             mCallback->onStopCompleted();
             state.lock();
             return;
-        } else if (state->mState != STOPPING) {
+        } else if (state->get() != STOPPING) {
             state.unlock();
             mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
             state.lock();
             return;
         }
-        comp = state->mComp;
+        comp = state->comp;
     }
     mChannel->stop();
     status_t err = comp->stop();
@@ -397,8 +453,8 @@
 
     {
         Mutexed<State>::Locked state(mState);
-        if (state->mState == STOPPING) {
-            state->mState = ALLOCATED;
+        if (state->get() == STOPPING) {
+            state->set(ALLOCATED);
         }
     }
     mCallback->onStopCompleted();
@@ -407,7 +463,7 @@
 void CCodec::initiateRelease(bool sendCallback /* = true */) {
     {
         Mutexed<State>::Locked state(mState);
-        if (state->mState == RELEASED || state->mState == RELEASING) {
+        if (state->get() == RELEASED || state->get() == RELEASING) {
             // We're already released or doing it right now.
             if (sendCallback) {
                 state.unlock();
@@ -416,8 +472,8 @@
             }
             return;
         }
-        if (state->mState == ALLOCATING) {
-            state->mState = RELEASING;
+        if (state->get() == ALLOCATING) {
+            state->set(RELEASING);
             // With the altered state allocate() would fail and clean up.
             if (sendCallback) {
                 state.unlock();
@@ -426,7 +482,7 @@
             }
             return;
         }
-        state->mState = RELEASING;
+        state->set(RELEASING);
     }
 
     std::thread([this, sendCallback] { release(sendCallback); }).detach();
@@ -436,7 +492,7 @@
     std::shared_ptr<C2Component> comp;
     {
         Mutexed<State>::Locked state(mState);
-        if (state->mState == RELEASED) {
+        if (state->get() == RELEASED) {
             if (sendCallback) {
                 state.unlock();
                 mCallback->onReleaseCompleted();
@@ -444,15 +500,15 @@
             }
             return;
         }
-        comp = state->mComp;
+        comp = state->comp;
     }
     mChannel->stop();
     comp->release();
 
     {
         Mutexed<State>::Locked state(mState);
-        state->mState = RELEASED;
-        state->mComp.reset();
+        state->set(RELEASED);
+        state->comp.reset();
     }
     if (sendCallback) {
         mCallback->onReleaseCompleted();
@@ -466,11 +522,11 @@
 void CCodec::signalFlush() {
     {
         Mutexed<State>::Locked state(mState);
-        if (state->mState != RUNNING) {
+        if (state->get() != RUNNING) {
             mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
             return;
         }
-        state->mState = FLUSHING;
+        state->set(FLUSHING);
     }
 
     (new AMessage(kWhatFlush, this))->post();
@@ -480,13 +536,13 @@
     std::shared_ptr<C2Component> comp;
     {
         Mutexed<State>::Locked state(mState);
-        if (state->mState != FLUSHING) {
+        if (state->get() != FLUSHING) {
             state.unlock();
             mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
             state.lock();
             return;
         }
-        comp = state->mComp;
+        comp = state->comp;
     }
 
     mChannel->stop();
@@ -502,7 +558,7 @@
 
     {
         Mutexed<State>::Locked state(mState);
-        state->mState = FLUSHED;
+        state->set(FLUSHED);
     }
     mCallback->onFlushCompleted();
 }
@@ -510,26 +566,26 @@
 void CCodec::signalResume() {
     {
         Mutexed<State>::Locked state(mState);
-        if (state->mState != FLUSHED) {
+        if (state->get() != FLUSHED) {
             state.unlock();
             mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
             state.lock();
             return;
         }
-        state->mState = RESUMING;
+        state->set(RESUMING);
     }
 
     mChannel->start(nullptr, nullptr);
 
     {
         Mutexed<State>::Locked state(mState);
-        if (state->mState != RESUMING) {
+        if (state->get() != RESUMING) {
             state.unlock();
             mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
             state.lock();
             return;
         }
-        state->mState = RUNNING;
+        state->set(RUNNING);
     }
 }
 
@@ -545,6 +601,14 @@
     // TODO
 }
 
+void CCodec::onWorkDone(std::list<std::unique_ptr<C2Work>> &workItems) {
+    Mutexed<std::list<std::unique_ptr<C2Work>>>::Locked queue(mWorkDoneQueue);
+    for (std::unique_ptr<C2Work> &item : workItems) {
+        queue->push_back(std::move(item));
+    }
+    (new AMessage(kWhatWorkDone, this))->post();
+}
+
 void CCodec::onMessageReceived(const sp<AMessage> &msg) {
     TimePoint now = std::chrono::steady_clock::now();
     switch (msg->what()) {
@@ -582,6 +646,37 @@
             flush();
             break;
         }
+        case kWhatCreateInputSurface: {
+            // Surface operations may be briefly blocking.
+            setDeadline(now + 100ms);
+            createInputSurface();
+            break;
+        }
+        case kWhatSetInputSurface: {
+            // Surface operations may be briefly blocking.
+            setDeadline(now + 100ms);
+            sp<RefBase> obj;
+            CHECK(msg->findObject("surface", &obj));
+            sp<PersistentSurface> surface(static_cast<PersistentSurface *>(obj.get()));
+            setInputSurface(surface);
+            break;
+        }
+        case kWhatWorkDone: {
+            std::unique_ptr<C2Work> work;
+            {
+                Mutexed<std::list<std::unique_ptr<C2Work>>>::Locked queue(mWorkDoneQueue);
+                if (queue->empty()) {
+                    break;
+                }
+                work.swap(queue->front());
+                queue->pop_front();
+                if (!queue->empty()) {
+                    (new AMessage(kWhatWorkDone, this))->post();
+                }
+            }
+            mChannel->onWorkDone(work);
+            break;
+        }
         default: {
             ALOGE("unrecognized message");
             break;
diff --git a/media/libstagefright/CCodecBufferChannel.cpp b/media/libstagefright/CCodecBufferChannel.cpp
index eea9c78..04f7593 100644
--- a/media/libstagefright/CCodecBufferChannel.cpp
+++ b/media/libstagefright/CCodecBufferChannel.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 #define LOG_TAG "CCodecBufferChannel"
 #include <utils/Log.h>
 
@@ -48,10 +48,189 @@
 using namespace hardware::cas::V1_0;
 using namespace hardware::cas::native::V1_0;
 
+/**
+ * Base class for representation of buffers at one port.
+ */
+class CCodecBufferChannel::Buffers {
+public:
+    Buffers() = default;
+    virtual ~Buffers() = default;
+
+    /**
+     * Set format for MediaCodec-facing buffers.
+     */
+    void setFormat(const sp<AMessage> &format) { mFormat = format; }
+
+    /**
+     * Returns true if the buffers are operating under array mode.
+     */
+    virtual bool isArrayMode() const { return false; }
+
+    /**
+     * Fills the vector with MediaCodecBuffer's if in array mode; otherwise,
+     * no-op.
+     */
+    virtual void getArray(Vector<sp<MediaCodecBuffer>> *) const {}
+
+protected:
+    // Format to be used for creating MediaCodec-facing buffers.
+    sp<AMessage> mFormat;
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(Buffers);
+};
+
+class CCodecBufferChannel::InputBuffers : public CCodecBufferChannel::Buffers {
+public:
+    InputBuffers() = default;
+    virtual ~InputBuffers() = default;
+
+    /**
+     * Set a block pool to obtain input memory blocks.
+     */
+    void setPool(const std::shared_ptr<C2BlockPool> &pool) { mPool = pool; }
+
+    /**
+     * Get a new MediaCodecBuffer for input and its corresponding index.
+     * Returns false if no new buffer can be obtained at the moment.
+     */
+    virtual bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) = 0;
+
+    /**
+     * Release the buffer obtained from requestNewBuffer() and get the
+     * associated C2Buffer object back. Returns empty shared_ptr if the
+     * buffer is not on file.
+     *
+     * XXX: this is a quick hack to be removed
+     */
+    virtual std::shared_ptr<C2Buffer> releaseBufferIndex(size_t /* index */) { return nullptr; }
+
+    /**
+     * Release the buffer obtained from requestNewBuffer() and get the
+     * associated C2Buffer object back. Returns empty shared_ptr if the
+     * buffer is not on file.
+     */
+    virtual std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) = 0;
+
+    /**
+     * Flush internal state. After this call, no index or buffer previously
+     * returned from requestNewBuffer() is valid.
+     */
+    virtual void flush() = 0;
+
+    /**
+     * Return array-backed version of input buffers. The returned object
+     * shall retain the internal state so that it will honor index and
+     * buffer from previous calls of requestNewBuffer().
+     */
+    virtual std::unique_ptr<InputBuffers> toArrayMode() = 0;
+
+protected:
+    // Pool to obtain blocks for input buffers.
+    std::shared_ptr<C2BlockPool> mPool;
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(InputBuffers);
+};
+
+class CCodecBufferChannel::InputBufferClient {
+public:
+    explicit InputBufferClient(
+            const std::shared_ptr<CCodecBufferChannel> &channel) : mChannel(channel) {}
+    virtual ~InputBufferClient() = default;
+
+    virtual void onInputBufferAdded(size_t index, const sp<MediaCodecBuffer> &buffer) {
+        std::shared_ptr<CCodecBufferChannel> channel = mChannel.lock();
+        if (!channel) {
+            return;
+        }
+        channel->mCallback->onInputBufferAvailable(index, buffer);
+    }
+
+    virtual void onStart() {
+        // no-op
+    }
+
+    virtual void onStop() {
+        // no-op
+    }
+
+    virtual void onRelease() {
+        // no-op
+    }
+
+    virtual void onInputBufferAvailable(size_t index, const sp<MediaCodecBuffer> &buffer) {
+        std::shared_ptr<CCodecBufferChannel> channel = mChannel.lock();
+        if (!channel) {
+            return;
+        }
+        channel->mCallback->onInputBufferAvailable(index, buffer);
+    }
+
+protected:
+    InputBufferClient() = default;
+    std::weak_ptr<CCodecBufferChannel> mChannel;
+
+    DISALLOW_EVIL_CONSTRUCTORS(InputBufferClient);
+};
+
+class CCodecBufferChannel::OutputBuffers : public CCodecBufferChannel::Buffers {
+public:
+    OutputBuffers() = default;
+    virtual ~OutputBuffers() = default;
+
+    /**
+     * Register output C2Buffer from the component and obtain corresponding
+     * index and MediaCodecBuffer object. Returns false if registration
+     * fails.
+     */
+    virtual bool registerBuffer(
+            const std::shared_ptr<C2Buffer> &buffer,
+            size_t *index,
+            sp<MediaCodecBuffer> *codecBuffer) = 0;
+
+    /**
+     * Register codec specific data as a buffer to be consistent with
+     * MediaCodec behavior.
+     */
+    virtual bool registerCsd(
+            const C2StreamCsdInfo::output * /* csd */,
+            size_t * /* index */,
+            sp<MediaCodecBuffer> * /* codecBuffer */) {
+        return false;
+    }
+
+    /**
+     * Release the buffer obtained from registerBuffer() and get the
+     * associated C2Buffer object back. Returns empty shared_ptr if the
+     * buffer is not on file.
+     */
+    virtual std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) = 0;
+
+    /**
+     * Flush internal state. After this call, no index or buffer previously
+     * returned from registerBuffer() is valid.
+     */
+    virtual void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) = 0;
+
+    /**
+     * Return array-backed version of output buffers. The returned object
+     * shall retain the internal state so that it will honor index and
+     * buffer from previous calls of registerBuffer().
+     */
+    virtual std::unique_ptr<OutputBuffers> toArrayMode() = 0;
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(OutputBuffers);
+};
+
 namespace {
 
+constexpr int32_t kMaskI32 = ~0;
+
 // TODO: get this info from component
 const static size_t kMinBufferArraySize = 16;
+const static size_t kLinearBufferSize = 524288;
 
 template <class T>
 ssize_t findBufferSlot(
@@ -88,9 +267,14 @@
     return Codec2Buffer::allocate(format, block);
 }
 
-class LinearBuffer : public C2Buffer {
+class Buffer1D : public C2Buffer {
 public:
-    explicit LinearBuffer(C2ConstLinearBlock block) : C2Buffer({ block }) {}
+    explicit Buffer1D(C2ConstLinearBlock block) : C2Buffer({ block }) {}
+};
+
+class Buffer2D : public C2Buffer {
+public:
+    explicit Buffer2D(C2ConstGraphicBlock block) : C2Buffer({ block }) {}
 };
 
 class InputBuffersArray : public CCodecBufferChannel::InputBuffers {
@@ -99,36 +283,37 @@
 
     void add(
             size_t index,
-            const sp<MediaCodecBuffer> &clientBuffer,
-            const std::shared_ptr<C2Buffer> &compBuffer,
+            const sp<Codec2Buffer> &clientBuffer,
             bool available) {
-        if (mBufferArray.size() < index) {
+        if (mBufferArray.size() <= index) {
+            // TODO: make this more efficient
             mBufferArray.resize(index + 1);
         }
         mBufferArray[index].clientBuffer = clientBuffer;
-        mBufferArray[index].compBuffer = compBuffer;
         mBufferArray[index].available = available;
     }
 
-    bool isArrayMode() final { return true; }
+    bool isArrayMode() const final { return true; }
 
     std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode() final {
         return nullptr;
     }
 
-    void getArray(Vector<sp<MediaCodecBuffer>> *array) final {
+    void getArray(Vector<sp<MediaCodecBuffer>> *array) const final {
         array->clear();
-        for (const auto &entry : mBufferArray) {
+        for (const Entry &entry : mBufferArray) {
             array->push(entry.clientBuffer);
         }
     }
 
     bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
         for (size_t i = 0; i < mBufferArray.size(); ++i) {
-            if (mBufferArray[i].available) {
+            if (mBufferArray[i].available && mBufferArray[i].compBuffer.expired()) {
                 mBufferArray[i].available = false;
                 *index = i;
                 *buffer = mBufferArray[i].clientBuffer;
+                (*buffer)->meta()->clear();
+                (*buffer)->setRange(0, (*buffer)->capacity());
                 return true;
             }
         }
@@ -138,8 +323,10 @@
     std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
         for (size_t i = 0; i < mBufferArray.size(); ++i) {
             if (!mBufferArray[i].available && mBufferArray[i].clientBuffer == buffer) {
+                std::shared_ptr<C2Buffer> buffer = std::make_shared<Buffer1D>(mBufferArray[i].clientBuffer->share());
                 mBufferArray[i].available = true;
-                return std::move(mBufferArray[i].compBuffer);
+                mBufferArray[i].compBuffer = buffer;
+                return buffer;
             }
         }
         return nullptr;
@@ -148,14 +335,13 @@
     void flush() override {
         for (size_t i = 0; i < mBufferArray.size(); ++i) {
             mBufferArray[i].available = true;
-            mBufferArray[i].compBuffer.reset();
         }
     }
 
 private:
     struct Entry {
-        sp<MediaCodecBuffer> clientBuffer;
-        std::shared_ptr<C2Buffer> compBuffer;
+        sp<Codec2Buffer> clientBuffer;
+        std::weak_ptr<C2Buffer> compBuffer;
         bool available;
     };
 
@@ -177,7 +363,7 @@
         // TODO: proper max input size and usage
         // TODO: read usage from intf
         C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
-        sp<Codec2Buffer> newBuffer = allocateLinearBuffer(mPool, mFormat, 65536, usage);
+        sp<Codec2Buffer> newBuffer = allocateLinearBuffer(mPool, mFormat, kLinearBufferSize, usage);
         if (newBuffer == nullptr) {
             return false;
         }
@@ -195,7 +381,7 @@
         sp<Codec2Buffer> codecBuffer = it->promote();
         // We got sp<> reference from the caller so this should never happen..
         CHECK(codecBuffer != nullptr);
-        return std::make_shared<LinearBuffer>(codecBuffer->share());
+        return std::make_shared<Buffer1D>(codecBuffer->share());
     }
 
     void flush() override {
@@ -203,8 +389,10 @@
 
     std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode() final {
         std::unique_ptr<InputBuffersArray> array(new InputBuffersArray);
+        array->setFormat(mFormat);
         // TODO
         const size_t size = std::max(kMinBufferArraySize, mBuffers.size());
+        mBuffers.resize(size);
         for (size_t i = 0; i < size; ++i) {
             sp<Codec2Buffer> clientBuffer = mBuffers[i].promote();
             bool available = false;
@@ -212,13 +400,12 @@
                 // TODO: proper max input size
                 // TODO: read usage from intf
                 C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
-                clientBuffer = allocateLinearBuffer(mPool, mFormat, 65536, usage);
+                clientBuffer = allocateLinearBuffer(mPool, mFormat, kLinearBufferSize, usage);
                 available = true;
             }
             array->add(
                     i,
                     clientBuffer,
-                    std::make_shared<LinearBuffer>(clientBuffer->share()),
                     available);
         }
         return std::move(array);
@@ -230,19 +417,30 @@
     std::vector<wp<Codec2Buffer>> mBuffers;
 };
 
-// TODO: stub
 class GraphicInputBuffers : public CCodecBufferChannel::InputBuffers {
 public:
-    using CCodecBufferChannel::InputBuffers::InputBuffers;
+    GraphicInputBuffers() = default;
 
     bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
-        (void)index;
-        (void)buffer;
-        return false;
+        *buffer = nullptr;
+        for (size_t i = 0; i < mAvailable.size(); ++i) {
+            if (mAvailable[i]) {
+                *index = i;
+                mAvailable[i] = false;
+                return true;
+            }
+        }
+        *index = mAvailable.size();
+        mAvailable.push_back(false);
+        return true;
     }
 
-    std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
-        (void)buffer;
+    std::shared_ptr<C2Buffer> releaseBufferIndex(size_t index) override {
+        mAvailable[index] = true;
+        return nullptr;
+    }
+
+    std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &) override {
         return nullptr;
     }
 
@@ -252,6 +450,9 @@
     std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode() final {
         return nullptr;
     }
+
+private:
+    std::vector<bool> mAvailable;
 };
 
 class OutputBuffersArray : public CCodecBufferChannel::OutputBuffers {
@@ -263,7 +464,8 @@
             const sp<MediaCodecBuffer> &clientBuffer,
             const std::shared_ptr<C2Buffer> &compBuffer,
             bool available) {
-        if (mBufferArray.size() < index) {
+        if (mBufferArray.size() <= index) {
+            // TODO: make this more efficient
             mBufferArray.resize(index + 1);
         }
         mBufferArray[index].clientBuffer = clientBuffer;
@@ -271,7 +473,7 @@
         mBufferArray[index].available = available;
     }
 
-    bool isArrayMode() final { return true; }
+    bool isArrayMode() const final { return true; }
 
     std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode() final {
         return nullptr;
@@ -285,6 +487,8 @@
             if (mBufferArray[i].available && copy(buffer, mBufferArray[i].clientBuffer)) {
                 *index = i;
                 *codecBuffer = mBufferArray[i].clientBuffer;
+                (*codecBuffer)->setFormat(mFormat);
+                (*codecBuffer)->meta()->clear();
                 mBufferArray[i].compBuffer = buffer;
                 mBufferArray[i].available = false;
                 return true;
@@ -299,10 +503,18 @@
             sp<MediaCodecBuffer> *codecBuffer) final {
         for (size_t i = 0; i < mBufferArray.size(); ++i) {
             if (mBufferArray[i].available
-                    && mBufferArray[i].clientBuffer->capacity() <= csd->flexCount()) {
+                    && mBufferArray[i].clientBuffer->capacity() >= csd->flexCount()) {
+                // TODO: proper format update
+                sp<ABuffer> csdBuffer = ABuffer::CreateAsCopy(csd->m.value, csd->flexCount());
+                mFormat = mFormat->dup();
+                mFormat->setBuffer("csd-0", csdBuffer);
+
                 memcpy(mBufferArray[i].clientBuffer->base(), csd->m.value, csd->flexCount());
+                mBufferArray[i].clientBuffer->setRange(0, csd->flexCount());
                 *index = i;
                 *codecBuffer = mBufferArray[i].clientBuffer;
+                (*codecBuffer)->setFormat(mFormat);
+                (*codecBuffer)->meta()->clear();
                 mBufferArray[i].available = false;
                 return true;
             }
@@ -333,9 +545,9 @@
             const std::shared_ptr<C2Buffer> &buffer,
             const sp<MediaCodecBuffer> &clientBuffer) = 0;
 
-    void getArray(Vector<sp<MediaCodecBuffer>> *array) final {
+    void getArray(Vector<sp<MediaCodecBuffer>> *array) const final {
         array->clear();
-        for (const auto &entry : mBufferArray) {
+        for (const Entry &entry : mBufferArray) {
             array->push(entry.clientBuffer);
         }
     }
@@ -363,6 +575,7 @@
         }
         C2ReadView view = buffer->data().linearBlocks().front().map().get();
         if (clientBuffer->capacity() < view.capacity()) {
+            ALOGV("view.capacity() = %u", view.capacity());
             return false;
         }
         clientBuffer->setRange(0u, view.capacity());
@@ -425,9 +638,11 @@
         if (ret < 0) {
             return false;
         }
-        sp<MediaCodecBuffer> newBuffer = new MediaCodecBuffer(
-                mFormat,
-                ABuffer::CreateAsCopy(csd->m.value, csd->flexCount()));
+        // TODO: proper format update
+        sp<ABuffer> csdBuffer = ABuffer::CreateAsCopy(csd->m.value, csd->flexCount());
+        mFormat = mFormat->dup();
+        mFormat->setBuffer("csd-0", csdBuffer);
+        sp<MediaCodecBuffer> newBuffer = new MediaCodecBuffer(mFormat, csdBuffer);
         mBuffers[ret] = { newBuffer, nullptr };
         *index = ret;
         *codecBuffer = newBuffer;
@@ -498,15 +713,17 @@
 
     std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode() override {
         std::unique_ptr<OutputBuffersArray> array(new LinearOutputBuffersArray);
+        array->setFormat(mFormat);
 
         const size_t size = std::max(kMinBufferArraySize, mBuffers.size());
+        mBuffers.resize(size);
         for (size_t i = 0; i < size; ++i) {
             sp<MediaCodecBuffer> clientBuffer = mBuffers[i].clientBuffer.promote();
             std::shared_ptr<C2Buffer> compBuffer = mBuffers[i].bufferRef;
             bool available = false;
             if (clientBuffer == nullptr) {
                 // TODO: proper max input size
-                clientBuffer = new MediaCodecBuffer(mFormat, new ABuffer(65536));
+                clientBuffer = new MediaCodecBuffer(mFormat, new ABuffer(kLinearBufferSize));
                 available = true;
                 compBuffer.reset();
             }
@@ -526,8 +743,10 @@
 
     std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode() override {
         std::unique_ptr<OutputBuffersArray> array(new GraphicOutputBuffersArray);
+        array->setFormat(mFormat);
 
         const size_t size = std::max(kMinBufferArraySize, mBuffers.size());
+        mBuffers.resize(size);
         for (size_t i = 0; i < size; ++i) {
             sp<MediaCodecBuffer> clientBuffer = mBuffers[i].clientBuffer.promote();
             std::shared_ptr<C2Buffer> compBuffer = mBuffers[i].bufferRef;
@@ -543,8 +762,163 @@
     }
 };
 
+class BufferQueueClient : public CCodecBufferChannel::InputBufferClient {
+public:
+    explicit BufferQueueClient(const sp<GraphicBufferSource> &source) : mSource(source) {}
+    virtual ~BufferQueueClient() = default;
+
+    void onInputBufferAdded(size_t index, const sp<MediaCodecBuffer> &buffer) override {
+        (void)buffer;
+        mSource->onInputBufferAdded(index & kMaskI32);
+    }
+
+    void onStart() override {
+        mSource->start();
+    }
+
+    void onStop() override {
+        mSource->stop();
+    }
+
+    void onRelease() override {
+        mSource->release();
+    }
+
+    void onInputBufferAvailable(size_t index, const sp<MediaCodecBuffer> &buffer) override {
+        ALOGV("onInputBufferEmptied index = %zu", index);
+        (void)buffer;
+        // TODO: can we really ignore fence here?
+        mSource->onInputBufferEmptied(index & kMaskI32, -1 /* fenceFd */);
+    }
+
+private:
+    sp<GraphicBufferSource> mSource;
+};
+
+class GraphicBlock : public C2GraphicBlock {
+    using C2GraphicBlock::C2GraphicBlock;
+    friend class ::android::CCodecBufferChannel;
+};
+
 }  // namespace
 
+class CCodecBufferChannel::C2ComponentWrapper : public ComponentWrapper {
+public:
+    explicit C2ComponentWrapper(
+            const std::shared_ptr<CCodecBufferChannel> &channel)
+        : mChannel(channel), mLastTimestamp(0) {}
+
+    virtual ~C2ComponentWrapper() {
+        for (const std::pair<int32_t, C2Handle *> &entry : mHandles) {
+            native_handle_delete(entry.second);
+        }
+    }
+
+    status_t submitBuffer(
+            int32_t bufferId, const sp<GraphicBuffer> &buffer,
+            int64_t timestamp, int fenceFd) override {
+        ALOGV("submitBuffer bufferId = %d", bufferId);
+        // TODO: Use fd to construct fence
+        (void)fenceFd;
+
+        std::shared_ptr<CCodecBufferChannel> channel = mChannel.lock();
+        if (!channel) {
+            return NO_INIT;
+        }
+
+        std::shared_ptr<C2Allocator> allocator = mAllocator.lock();
+        if (!allocator) {
+            c2_status_t err = GetCodec2PlatformAllocatorStore()->fetchAllocator(
+                    C2AllocatorStore::PLATFORM_START + 1,  // GRALLOC
+                    &allocator);
+            if (err != OK) {
+                return UNKNOWN_ERROR;
+            }
+            mAllocator = allocator;
+        }
+
+        std::shared_ptr<C2GraphicAllocation> alloc;
+        C2Handle *handle = WrapNativeCodec2GrallocHandle(
+                buffer->handle, buffer->width, buffer->height,
+                buffer->format, buffer->usage, buffer->stride);
+        c2_status_t err = allocator->priorGraphicAllocation(handle, &alloc);
+        if (err != OK) {
+            return UNKNOWN_ERROR;
+        }
+        std::shared_ptr<C2GraphicBlock> block(new GraphicBlock(alloc));
+
+        std::unique_ptr<C2Work> work(new C2Work);
+        work->input.flags = (C2FrameData::flags_t)0;
+        work->input.ordinal.timestamp = timestamp;
+        work->input.ordinal.frameIndex = channel->mFrameIndex++;
+        work->input.buffers.clear();
+        work->input.buffers.emplace_back(new Buffer2D(
+                // TODO: fence
+                block->share(C2Rect(block->width(), block->height()), ::android::C2Fence())));
+        work->worklets.clear();
+        work->worklets.emplace_back(new C2Worklet);
+        std::list<std::unique_ptr<C2Work>> items;
+        items.push_back(std::move(work));
+
+        err = channel->mComponent->queue_nb(&items);
+        if (err != OK) {
+            native_handle_delete(handle);
+            return UNKNOWN_ERROR;
+        }
+
+        mLastTimestamp = timestamp;
+        if (mHandles.count(bufferId) > 0) {
+            native_handle_delete(mHandles[bufferId]);
+        }
+        mHandles[bufferId] = handle;
+
+        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(channel->mInputBuffers);
+        ALOGV("releaseBufferIndex index = %d", bufferId);
+        (*buffers)->releaseBufferIndex(bufferId);
+
+        return OK;
+    }
+
+    status_t submitEos(int32_t bufferId) override {
+        std::shared_ptr<CCodecBufferChannel> channel = mChannel.lock();
+        if (!channel) {
+            return NO_INIT;
+        }
+
+        std::unique_ptr<C2Work> work(new C2Work);
+        work->input.flags = C2FrameData::FLAG_END_OF_STREAM;
+        work->input.ordinal.timestamp = mLastTimestamp;
+        work->input.ordinal.frameIndex = channel->mFrameIndex++;
+        work->input.buffers.clear();
+        work->input.buffers.push_back(nullptr);
+        work->worklets.clear();
+        work->worklets.emplace_back(new C2Worklet);
+        std::list<std::unique_ptr<C2Work>> items;
+        items.push_back(std::move(work));
+
+        c2_status_t err = channel->mComponent->queue_nb(&items);
+
+        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(channel->mInputBuffers);
+        (*buffers)->releaseBufferIndex(bufferId);
+
+        return (err == C2_OK) ? OK : UNKNOWN_ERROR;
+    }
+
+    void dispatchDataSpaceChanged(
+            int32_t dataSpace, int32_t aspects, int32_t pixelFormat) override {
+        // TODO
+        (void)dataSpace;
+        (void)aspects;
+        (void)pixelFormat;
+    }
+
+private:
+    std::weak_ptr<CCodecBufferChannel> mChannel;
+    std::map<int32_t, C2Handle *> mHandles;
+    int64_t mLastTimestamp;
+    std::weak_ptr<C2Allocator> mAllocator;
+};
+
 CCodecBufferChannel::QueueGuard::QueueGuard(
         CCodecBufferChannel::QueueSync &sync) : mSync(sync) {
     std::unique_lock<std::mutex> l(mSync.mMutex);
@@ -601,10 +975,14 @@
     if (mCrypto != nullptr && mDealer != nullptr && mHeapSeqNum >= 0) {
         mCrypto->unsetHeap(mHeapSeqNum);
     }
+    // TODO: is this the right place?
+    mInputClient->onRelease();
 }
 
 void CCodecBufferChannel::setComponent(const std::shared_ptr<C2Component> &component) {
     mComponent = component;
+    mInputClient.reset(new InputBufferClient(shared_from_this()));
+
     C2StreamFormatConfig::input inputFormat(0u);
     C2StreamFormatConfig::output outputFormat(0u);
     c2_status_t err = mComponent->intf()->query_vb(
@@ -638,6 +1016,10 @@
         } else {
             // TODO: error
         }
+        // TODO: remove once we switch to proper buffer pool.
+        if (!graphic) {
+            *buffers = (*buffers)->toArrayMode();
+        }
     }
 
     {
@@ -652,6 +1034,18 @@
     }
 }
 
+status_t CCodecBufferChannel::setGraphicBufferSource(
+        const sp<GraphicBufferSource> &source) {
+    ALOGV("setGraphicBufferSource");
+    mInputClient.reset(new BufferQueueClient(source));
+
+    // TODO: proper color aspect & dataspace
+    android_dataspace dataSpace = HAL_DATASPACE_BT709;
+    // TODO: read settings properly from the interface
+    return source->configure(new C2ComponentWrapper(
+            shared_from_this()), dataSpace, 16, 1080, 1920, GRALLOC_USAGE_SW_READ_OFTEN);
+}
+
 status_t CCodecBufferChannel::queueInputBuffer(const sp<MediaCodecBuffer> &buffer) {
     QueueGuard guard(mSync);
     if (!guard.isRunning()) {
@@ -665,16 +1059,16 @@
     int32_t flags = 0;
     int32_t tmp = 0;
     if (buffer->meta()->findInt32("eos", &tmp) && tmp) {
-        flags |= C2BufferPack::FLAG_END_OF_STREAM;
+        flags |= C2FrameData::FLAG_END_OF_STREAM;
         ALOGV("input EOS");
     }
     if (buffer->meta()->findInt32("csd", &tmp) && tmp) {
-        flags |= C2BufferPack::FLAG_CODEC_CONFIG;
+        flags |= C2FrameData::FLAG_CODEC_CONFIG;
     }
     std::unique_ptr<C2Work> work(new C2Work);
-    work->input.flags = (C2BufferPack::flags_t)flags;
+    work->input.flags = (C2FrameData::flags_t)flags;
     work->input.ordinal.timestamp = timeUs;
-    work->input.ordinal.frame_index = mFrameIndex++;
+    work->input.ordinal.frameIndex = mFrameIndex++;
     work->input.buffers.clear();
     {
         Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
@@ -708,9 +1102,24 @@
     return -ENOSYS;
 }
 
+void CCodecBufferChannel::feedInputBufferIfAvailable() {
+    sp<MediaCodecBuffer> inBuffer;
+    size_t index;
+    {
+        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
+        if (!(*buffers)->requestNewBuffer(&index, &inBuffer)) {
+            ALOGW("no new buffer available");
+            inBuffer = nullptr;
+        }
+    }
+    ALOGV("new input index = %zu", index);
+    mInputClient->onInputBufferAvailable(index, inBuffer);
+}
+
 status_t CCodecBufferChannel::renderOutputBuffer(
         const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) {
     ALOGV("renderOutputBuffer");
+    feedInputBufferIfAvailable();
 
     std::shared_ptr<C2Buffer> c2Buffer;
     {
@@ -724,7 +1133,7 @@
         return OK;
     }
 
-    std::list<C2ConstGraphicBlock> blocks = c2Buffer->data().graphicBlocks();
+    std::vector<C2ConstGraphicBlock> blocks = c2Buffer->data().graphicBlocks();
     if (blocks.size() != 1u) {
         ALOGE("# of graphic blocks expected to be 1, but %zu", blocks.size());
         return UNKNOWN_ERROR;
@@ -780,7 +1189,9 @@
     }
     {
         Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-        (void)(*buffers)->releaseBuffer(buffer);
+        if((*buffers)->releaseBuffer(buffer)) {
+            feedInputBufferIfAvailable();
+        }
     }
     return OK;
 }
@@ -832,13 +1243,15 @@
                 return;
             }
         }
-        mCallback->onInputBufferAvailable(index, buffer);
+        mInputClient->onInputBufferAdded(index, buffer);
     }
+    mInputClient->onStart();
 }
 
 void CCodecBufferChannel::stop() {
     mSync.stop();
     mFirstValidFrameIndex = mFrameIndex.load();
+    mInputClient->onStop();
 }
 
 void CCodecBufferChannel::flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) {
@@ -852,106 +1265,110 @@
     }
 }
 
-void CCodecBufferChannel::onWorkDone(std::vector<std::unique_ptr<C2Work>> workItems) {
-    for (const auto &work : workItems) {
-        sp<MediaCodecBuffer> inBuffer;
-        size_t index;
-        {
-            Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-            if (!(*buffers)->requestNewBuffer(&index, &inBuffer)) {
-                ALOGW("no new buffer available");
-                inBuffer = nullptr;
-            }
-        }
-        if (inBuffer != nullptr) {
-            mCallback->onInputBufferAvailable(index, inBuffer);
-        }
+void CCodecBufferChannel::onWorkDone(const std::unique_ptr<C2Work> &work) {
+    if (work->result != OK) {
+        ALOGE("work failed to complete: %d", work->result);
+        mOnError(work->result, ACTION_CODE_FATAL);
+        return;
+    }
 
-        if (work->result != OK) {
-            ALOGE("work failed to complete: %d", work->result);
-            mOnError(work->result, ACTION_CODE_FATAL);
+    // NOTE: MediaCodec usage supposedly have only one worklet
+    if (work->worklets.size() != 1u) {
+        ALOGE("onWorkDone: incorrect number of worklets: %zu",
+                work->worklets.size());
+        mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+        return;
+    }
+
+    const std::unique_ptr<C2Worklet> &worklet = work->worklets.front();
+    if ((worklet->output.ordinal.frameIndex - mFirstValidFrameIndex.load()).peek() < 0) {
+        // Discard frames from previous generation.
+        return;
+    }
+    std::shared_ptr<C2Buffer> buffer;
+    // NOTE: MediaCodec usage supposedly have only one output stream.
+    if (worklet->output.buffers.size() > 1u) {
+        ALOGE("onWorkDone: incorrect number of output buffers: %zu",
+                worklet->output.buffers.size());
+        mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+        return;
+    } else if (worklet->output.buffers.size() == 1u) {
+        buffer = worklet->output.buffers[0];
+        if (!buffer) {
+            ALOGW("onWorkDone: nullptr found in buffers; ignored.");
+        }
+    }
+
+    const C2StreamCsdInfo::output *csdInfo = nullptr;
+    for (const std::unique_ptr<C2Param> &info : worklet->output.configUpdate) {
+        if (info->coreIndex() == C2StreamCsdInfo::output::CORE_INDEX) {
+            ALOGV("onWorkDone: csd found");
+            csdInfo = static_cast<const C2StreamCsdInfo::output *>(info.get());
+        }
+    }
+
+    int32_t flags = 0;
+    if (worklet->output.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        flags |= MediaCodec::BUFFER_FLAG_EOS;
+        ALOGV("onWorkDone: output EOS");
+    }
+
+    sp<MediaCodecBuffer> outBuffer;
+    size_t index;
+    if (csdInfo != nullptr) {
+        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
+        if ((*buffers)->registerCsd(csdInfo, &index, &outBuffer)) {
+            outBuffer->meta()->setInt64("timeUs", worklet->output.ordinal.timestamp.peek());
+            outBuffer->meta()->setInt32("flags", flags | MediaCodec::BUFFER_FLAG_CODECCONFIG);
+            ALOGV("onWorkDone: csd index = %zu", index);
+
+            buffers.unlock();
+            mCallback->onOutputBufferAvailable(index, outBuffer);
+            buffers.lock();
+        } else {
+            ALOGE("onWorkDone: unable to register csd");
+            buffers.unlock();
+            mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+            buffers.lock();
             return;
         }
-
-        // NOTE: MediaCodec usage supposedly have only one worklet
-        if (work->worklets.size() != 1u) {
-            ALOGE("incorrect number of worklets: %zu", work->worklets.size());
-            mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
-            continue;
-        }
-
-        const std::unique_ptr<C2Worklet> &worklet = work->worklets.front();
-        if (worklet->output.ordinal.frame_index < mFirstValidFrameIndex) {
-            // Discard frames from previous generation.
-            continue;
-        }
-        // NOTE: MediaCodec usage supposedly have only one output stream.
-        if (worklet->output.buffers.size() != 1u) {
-            ALOGE("incorrect number of output buffers: %zu", worklet->output.buffers.size());
-            mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
-            continue;
-        }
-
-        const std::shared_ptr<C2Buffer> &buffer = worklet->output.buffers[0];
-        const C2StreamCsdInfo::output *csdInfo = nullptr;
-        if (buffer) {
-            // TODO: transfer infos() into buffer metadata
-        }
-        for (const auto &info : worklet->output.infos) {
-            if (info->coreIndex() == C2StreamCsdInfo::output::CORE_INDEX) {
-                ALOGV("csd found");
-                csdInfo = static_cast<const C2StreamCsdInfo::output *>(info.get());
-            }
-        }
-
-        int32_t flags = 0;
-        if (worklet->output.flags & C2BufferPack::FLAG_END_OF_STREAM) {
-            flags |= MediaCodec::BUFFER_FLAG_EOS;
-            ALOGV("output EOS");
-        }
-
-        sp<MediaCodecBuffer> outBuffer;
-        if (csdInfo != nullptr) {
-            Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-            if ((*buffers)->registerCsd(csdInfo, &index, &outBuffer)) {
-                outBuffer->meta()->setInt64("timeUs", worklet->output.ordinal.timestamp);
-                outBuffer->meta()->setInt32("flags", flags | MediaCodec::BUFFER_FLAG_CODECCONFIG);
-                ALOGV("csd index = %zu", index);
-
-                buffers.unlock();
-                mCallback->onOutputBufferAvailable(index, outBuffer);
-                buffers.lock();
-            } else {
-                ALOGE("unable to register output buffer");
-                buffers.unlock();
-                mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
-                buffers.lock();
-                continue;
-            }
-        }
-
-        if (!buffer && !flags) {
-            ALOGV("Not reporting output buffer");
-            continue;
-        }
-
-        {
-            Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
-            if (!(*buffers)->registerBuffer(buffer, &index, &outBuffer)) {
-                ALOGE("unable to register output buffer");
-
-                buffers.unlock();
-                mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
-                buffers.lock();
-                continue;
-            }
-        }
-
-        outBuffer->meta()->setInt64("timeUs", worklet->output.ordinal.timestamp);
-        outBuffer->meta()->setInt32("flags", flags);
-        ALOGV("index = %zu", index);
-        mCallback->onOutputBufferAvailable(index, outBuffer);
     }
+
+    if (!buffer && !flags) {
+        ALOGV("onWorkDone: Not reporting output buffer");
+        return;
+    }
+
+    if (buffer) {
+        for (const std::shared_ptr<const C2Info> &info : buffer->info()) {
+            // TODO: properly translate these to metadata
+            switch (info->coreIndex().coreIndex()) {
+                case C2StreamPictureTypeMaskInfo::CORE_INDEX:
+                    if (((C2StreamPictureTypeMaskInfo *)info.get())->value & C2PictureTypeKeyFrame) {
+                        flags |= MediaCodec::BUFFER_FLAG_SYNCFRAME;
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    {
+        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
+        if (!(*buffers)->registerBuffer(buffer, &index, &outBuffer)) {
+            ALOGE("onWorkDone: unable to register output buffer");
+            buffers.unlock();
+            mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+            buffers.lock();
+            return;
+        }
+    }
+
+    outBuffer->meta()->setInt64("timeUs", worklet->output.ordinal.timestamp.peek());
+    outBuffer->meta()->setInt32("flags", flags);
+    ALOGV("onWorkDone: out buffer index = %zu", index);
+    mCallback->onOutputBufferAvailable(index, outBuffer);
 }
 
 status_t CCodecBufferChannel::setSurface(const sp<Surface> &newSurface) {
diff --git a/media/libstagefright/InterfaceUtils.cpp b/media/libstagefright/InterfaceUtils.cpp
index f174ba4..284e63b 100644
--- a/media/libstagefright/InterfaceUtils.cpp
+++ b/media/libstagefright/InterfaceUtils.cpp
@@ -19,7 +19,6 @@
 #include <media/stagefright/CallbackMediaSource.h>
 #include <media/stagefright/InterfaceUtils.h>
 #include <media/stagefright/RemoteDataSource.h>
-#include <media/stagefright/RemoteMediaExtractor.h>
 #include <media/stagefright/RemoteMediaSource.h>
 
 namespace android {
@@ -39,7 +38,7 @@
 }
 
 sp<IMediaExtractor> CreateIMediaExtractorFromMediaExtractor(
-        const sp<MediaExtractor> &extractor, const sp<RefBase> &plugin) {
+        MediaExtractor *extractor, const sp<RefBase> &plugin) {
     if (extractor == nullptr) {
         return nullptr;
     }
@@ -53,12 +52,13 @@
     return new CallbackMediaSource(source);
 }
 
-sp<IMediaSource> CreateIMediaSourceFromMediaSource(
-        const sp<MediaSource> &source, const sp<RefBase> &plugin) {
+sp<IMediaSource> CreateIMediaSourceFromMediaSourceBase(
+        const sp<RemoteMediaExtractor> &extractor,
+        MediaSourceBase *source, const sp<RefBase> &plugin) {
     if (source == nullptr) {
         return nullptr;
     }
-    return RemoteMediaSource::wrap(source, plugin);
+    return RemoteMediaSource::wrap(extractor, source, plugin);
 }
 
 }  // namespace android
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 559b108..56ac3ed 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -502,7 +502,6 @@
     // set up our new record, get a sessionID, put it into the in-progress list
     mAnalyticsItem = new MediaAnalyticsItem(kCodecKeyName);
     if (mAnalyticsItem != NULL) {
-        (void) mAnalyticsItem->generateSessionID();
         // don't record it yet; only at the end, when we have decided that we have
         // data worth writing (e.g. .count() > 0)
     }
@@ -512,7 +511,6 @@
     if (mAnalyticsItem != NULL) {
         // don't log empty records
         if (mAnalyticsItem->count() > 0) {
-            mAnalyticsItem->setFinalized(true);
             mAnalyticsItem->selfrecord();
         }
         delete mAnalyticsItem;
diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp
index 472c137..e79696c 100644
--- a/media/libstagefright/MediaExtractorFactory.cpp
+++ b/media/libstagefright/MediaExtractorFactory.cpp
@@ -358,12 +358,16 @@
     Mutex::Autolock autoLock(gPluginMutex);
     String8 out;
     out.append("Available extractors:\n");
-    for (auto it = gPlugins->begin(); it != gPlugins->end(); ++it) {
-        out.appendFormat("  %25s: uuid(%s), version(%u), path(%s)\n",
-                (*it)->def.extractor_name,
-                (*it)->uuidString.c_str(),
-                (*it)->def.extractor_version,
-                (*it)->libPath.c_str());
+    if (gPluginsRegistered) {
+        for (auto it = gPlugins->begin(); it != gPlugins->end(); ++it) {
+            out.appendFormat("  %25s: uuid(%s), version(%u), path(%s)\n",
+                    (*it)->def.extractor_name,
+                    (*it)->uuidString.c_str(),
+                    (*it)->def.extractor_version,
+                    (*it)->libPath.c_str());
+        }
+    } else {
+        out.append("  (no plugins registered)\n");
     }
     write(fd, out.string(), out.size());
     return OK;
diff --git a/media/libstagefright/MetaDataUtils.cpp b/media/libstagefright/MetaDataUtils.cpp
new file mode 100644
index 0000000..fd51a2c
--- /dev/null
+++ b/media/libstagefright/MetaDataUtils.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MetaDataUtils"
+
+#include <media/stagefright/foundation/avc_utils.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaDataUtils.h>
+
+namespace android {
+
+sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit) {
+    int32_t width;
+    int32_t height;
+    int32_t sarWidth;
+    int32_t sarHeight;
+    sp<ABuffer> csd = MakeAVCCodecSpecificData(accessUnit, &width, &height, &sarWidth, &sarHeight);
+    if (csd == nullptr) {
+        return nullptr;
+    }
+    sp<MetaData> meta = new MetaData;
+    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+
+    meta->setData(kKeyAVCC, kTypeAVCC, csd->data(), csd->size());
+    meta->setInt32(kKeyWidth, width);
+    meta->setInt32(kKeyHeight, height);
+    if (sarWidth > 0 && sarHeight > 0) {
+        meta->setInt32(kKeySARWidth, sarWidth);
+        meta->setInt32(kKeySARHeight, sarHeight);
+    }
+    return meta;
+}
+
+sp<MetaData> MakeAACCodecSpecificData(
+        unsigned profile, unsigned sampling_freq_index,
+        unsigned channel_configuration) {
+    int32_t sampleRate;
+    int32_t channelCount;
+    sp<ABuffer> csd = MakeAACCodecSpecificData(profile, sampling_freq_index,
+            channel_configuration, &sampleRate, &channelCount);
+    if (csd == nullptr) {
+        return nullptr;
+    }
+    sp<MetaData> meta = new MetaData;
+    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
+
+    meta->setInt32(kKeySampleRate, sampleRate);
+    meta->setInt32(kKeyChannelCount, channelCount);
+
+    meta->setData(kKeyESDS, 0, csd->data(), csd->size());
+    return meta;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index 17c9648..d96f7e0 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -660,6 +660,28 @@
     return err;
 }
 
+status_t NuMediaExtractor::getSampleSize(size_t *sampleSize) {
+    Mutex::Autolock autoLock(mLock);
+
+    ssize_t minIndex = fetchAllTrackSamples();
+
+    if (minIndex < 0) {
+        return ERROR_END_OF_STREAM;
+    }
+
+    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
+    auto it = info->mSamples.begin();
+    *sampleSize = it->mBuffer->range_length();
+
+    if (info->mTrackFlags & kIsVorbis) {
+        // Each sample's data is suffixed by the number of page samples
+        // or -1 if not available.
+        *sampleSize += sizeof(int32_t);
+    }
+
+    return OK;
+}
+
 status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
     Mutex::Autolock autoLock(mLock);
 
diff --git a/media/libstagefright/RemoteMediaExtractor.cpp b/media/libstagefright/RemoteMediaExtractor.cpp
index 12654d9..3ebee51 100644
--- a/media/libstagefright/RemoteMediaExtractor.cpp
+++ b/media/libstagefright/RemoteMediaExtractor.cpp
@@ -37,14 +37,13 @@
 static const char *kExtractorFormat = "android.media.mediaextractor.fmt";
 
 RemoteMediaExtractor::RemoteMediaExtractor(
-        const sp<MediaExtractor> &extractor, const sp<RefBase> &plugin)
+        MediaExtractor *extractor, const sp<RefBase> &plugin)
     :mExtractor(extractor),
     mExtractorPlugin(plugin) {
 
     mAnalyticsItem = nullptr;
     if (MEDIA_LOG) {
         mAnalyticsItem = new MediaAnalyticsItem(kKeyExtractor);
-        (void) mAnalyticsItem->generateSessionID();
 
         // track the container format (mpeg, aac, wvm, etc)
         size_t ntracks = extractor->countTracks();
@@ -67,13 +66,12 @@
 }
 
 RemoteMediaExtractor::~RemoteMediaExtractor() {
-    mExtractor = nullptr;
+    delete mExtractor;
     mExtractorPlugin = nullptr;
     // log the current record, provided it has some information worth recording
     if (MEDIA_LOG) {
         if (mAnalyticsItem != nullptr) {
             if (mAnalyticsItem->count() > 0) {
-                mAnalyticsItem->setFinalized(true);
                 mAnalyticsItem->selfrecord();
             }
         }
@@ -89,9 +87,9 @@
 }
 
 sp<IMediaSource> RemoteMediaExtractor::getTrack(size_t index) {
-    sp<MediaSource> source = mExtractor->getTrack(index);
-    return (source.get() == nullptr)
-            ? nullptr : CreateIMediaSourceFromMediaSource(source, mExtractorPlugin);
+    MediaSourceBase *source = mExtractor->getTrack(index);
+    return (source == nullptr)
+            ? nullptr : CreateIMediaSourceFromMediaSourceBase(this, source, mExtractorPlugin);
 }
 
 sp<MetaData> RemoteMediaExtractor::getTrackMetaData(size_t index, uint32_t flags) {
@@ -139,8 +137,8 @@
 
 // static
 sp<IMediaExtractor> RemoteMediaExtractor::wrap(
-        const sp<MediaExtractor> &extractor, const sp<RefBase> &plugin) {
-    if (extractor.get() == nullptr) {
+        MediaExtractor *extractor, const sp<RefBase> &plugin) {
+    if (extractor == nullptr) {
         return nullptr;
     }
     return new RemoteMediaExtractor(extractor, plugin);
diff --git a/media/libstagefright/RemoteMediaSource.cpp b/media/libstagefright/RemoteMediaSource.cpp
index d97329c..6b48ce8 100644
--- a/media/libstagefright/RemoteMediaSource.cpp
+++ b/media/libstagefright/RemoteMediaSource.cpp
@@ -14,17 +14,22 @@
  * limitations under the License.
  */
 
+#include <media/stagefright/RemoteMediaExtractor.h>
 #include <media/stagefright/RemoteMediaSource.h>
 #include <media/IMediaSource.h>
 
 namespace android {
 
-RemoteMediaSource::RemoteMediaSource(const sp<MediaSource> &source, const sp<RefBase> &plugin)
-    :mSource(source),
-    mExtractorPlugin(plugin) {}
+RemoteMediaSource::RemoteMediaSource(
+        const sp<RemoteMediaExtractor> &extractor,
+        MediaSourceBase *source,
+        const sp<RefBase> &plugin)
+    : mExtractor(extractor),
+      mSource(source),
+      mExtractorPlugin(plugin) {}
 
 RemoteMediaSource::~RemoteMediaSource() {
-    mSource = nullptr;
+    delete mSource;
     mExtractorPlugin = nullptr;
 }
 
@@ -55,11 +60,13 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 // static
-sp<IMediaSource> RemoteMediaSource::wrap(const sp<MediaSource> &source, const sp<RefBase> &plugin) {
-    if (source.get() == nullptr) {
+sp<IMediaSource> RemoteMediaSource::wrap(
+        const sp<RemoteMediaExtractor> &extractor,
+        MediaSourceBase *source, const sp<RefBase> &plugin) {
+    if (source == nullptr) {
         return nullptr;
     }
-    return new RemoteMediaSource(source, plugin);
+    return new RemoteMediaSource(extractor, source, plugin);
 }
 
 }  // namespace android
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 53699ef..4a93051 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -667,7 +667,7 @@
         if (!strncasecmp("image/", mime, 6)) {
             int32_t gridWidth, gridHeight, gridRows, gridCols;
             if (meta->findInt32(kKeyGridWidth, &gridWidth)
-                    && meta->findInt32(kKeyHeight, &gridHeight)
+                    && meta->findInt32(kKeyGridHeight, &gridHeight)
                     && meta->findInt32(kKeyGridRows, &gridRows)
                     && meta->findInt32(kKeyGridCols, &gridCols)) {
                 msg->setInt32("grid-width", gridWidth);
diff --git a/media/libstagefright/codec2/SimpleC2Component.cpp b/media/libstagefright/codec2/SimpleC2Component.cpp
index 4d75a31..01e9e43 100644
--- a/media/libstagefright/codec2/SimpleC2Component.cpp
+++ b/media/libstagefright/codec2/SimpleC2Component.cpp
@@ -237,8 +237,8 @@
 
 namespace {
 
-std::vector<std::unique_ptr<C2Work>> vec(std::unique_ptr<C2Work> &work) {
-    std::vector<std::unique_ptr<C2Work>> ret;
+std::list<std::unique_ptr<C2Work>> vec(std::unique_ptr<C2Work> &work) {
+    std::list<std::unique_ptr<C2Work>> ret;
     ret.push_back(std::move(work));
     return ret;
 }
@@ -342,7 +342,7 @@
             return;
         }
     }
-    if (work->worklets_processed != 0u) {
+    if (work->workletsProcessed != 0u) {
         Mutexed<ExecState>::Locked state(mExecState);
         ALOGV("returning this work");
         state->mListener->onWorkDone_nb(shared_from_this(), vec(work));
@@ -351,7 +351,7 @@
         std::unique_ptr<C2Work> unexpected;
         {
             Mutexed<PendingWork>::Locked pending(mPendingWork);
-            uint64_t frameIndex = work->input.ordinal.frame_index;
+            uint64_t frameIndex = work->input.ordinal.frameIndex.peeku();
             if (pending->count(frameIndex) != 0) {
                 unexpected = std::move(pending->at(frameIndex));
                 pending->erase(frameIndex);
diff --git a/media/libstagefright/codec2/include/C2Buffer.h b/media/libstagefright/codec2/include/C2Buffer.h
index cd90978..2ca8222 100644
--- a/media/libstagefright/codec2/include/C2Buffer.h
+++ b/media/libstagefright/codec2/include/C2Buffer.h
@@ -679,14 +679,14 @@
      *
      * \param size    number of bytes to share
      * \param fence   fence to be used for the section
-     * \param blocks  list where the blocks of the section are appended to
+     * \param blocks  vector where the blocks of the section are appended to
      *
      * \retval C2_OK            the portion was successfully shared
      * \retval C2_NO_MEMORY     not enough memory to share the portion
      * \retval C2_TIMED_OUT     the operation timed out (unexpected)
      * \retval C2_CORRUPTED     some unknown error prevented sharing the data (unexpected)
      */
-    c2_status_t share(size_t size, C2Fence fence, std::list<C2ConstLinearBlock> &blocks);
+    c2_status_t share(size_t size, C2Fence fence, std::vector<C2ConstLinearBlock> &blocks);
 
     /**
      * Returns the beginning offset of this segment from the start of this circular block.
@@ -1203,14 +1203,14 @@
      * \return a constant list of const linear blocks of this buffer.
      * \retval empty list if this buffer does not contain linear block(s).
      */
-    const std::list<C2ConstLinearBlock> linearBlocks() const;
+    const std::vector<C2ConstLinearBlock> linearBlocks() const;
 
     /**
      * Gets the graphic blocks of this buffer.
      * \return a constant list of const graphic blocks of this buffer.
      * \retval empty list if this buffer does not contain graphic block(s).
      */
-    const std::list<C2ConstGraphicBlock> graphicBlocks() const;
+    const std::vector<C2ConstGraphicBlock> graphicBlocks() const;
 
 private:
     class Impl;
@@ -1218,8 +1218,8 @@
 
 protected:
     // no public constructor
-    explicit C2BufferData(const std::list<C2ConstLinearBlock> &blocks);
-    explicit C2BufferData(const std::list<C2ConstGraphicBlock> &blocks);
+    explicit C2BufferData(const std::vector<C2ConstLinearBlock> &blocks);
+    explicit C2BufferData(const std::vector<C2ConstGraphicBlock> &blocks);
 };
 
 /**
@@ -1301,7 +1301,7 @@
      *
      * \return a constant list of info objects associated with this buffer.
      */
-    const std::list<std::shared_ptr<const C2Info>> infos() const;
+    const std::vector<std::shared_ptr<const C2Info>> info() const;
 
     /**
      * Attaches (or updates) an (existing) metadata for this buffer.
@@ -1328,8 +1328,8 @@
 
 protected:
     // no public constructor
-    explicit C2Buffer(const std::list<C2ConstLinearBlock> &blocks);
-    explicit C2Buffer(const std::list<C2ConstGraphicBlock> &blocks);
+    explicit C2Buffer(const std::vector<C2ConstLinearBlock> &blocks);
+    explicit C2Buffer(const std::vector<C2ConstGraphicBlock> &blocks);
 
 private:
     class Impl;
diff --git a/media/libstagefright/codec2/include/C2Component.h b/media/libstagefright/codec2/include/C2Component.h
index a2168a0..aa0974a 100644
--- a/media/libstagefright/codec2/include/C2Component.h
+++ b/media/libstagefright/codec2/include/C2Component.h
@@ -332,7 +332,7 @@
     class Listener {
     public:
         virtual void onWorkDone_nb(std::weak_ptr<C2Component> component,
-                                std::vector<std::unique_ptr<C2Work>> workItems) = 0;
+                                std::list<std::unique_ptr<C2Work>> workItems) = 0;
 
         virtual void onTripped_nb(std::weak_ptr<C2Component> component,
                                std::vector<std::shared_ptr<C2SettingResult>> settingResult) = 0;
@@ -682,7 +682,7 @@
      */
     virtual c2_status_t reset() { return C2_OK; }
 
-    virtual c2_status_t parseFrame(C2BufferPack &frame);
+    virtual c2_status_t parseFrame(C2FrameData &frame);
 
     virtual ~C2FrameInfoParser() = default;
 };
diff --git a/media/libstagefright/codec2/include/C2Config.h b/media/libstagefright/codec2/include/C2Config.h
index 83cb72c..2a2b9de 100644
--- a/media/libstagefright/codec2/include/C2Config.h
+++ b/media/libstagefright/codec2/include/C2Config.h
@@ -67,6 +67,7 @@
     kParamIndexVideoSizeTuning,
 
     kParamIndexCsd,
+    kParamIndexPictureTypeMask,
 
     // video info
 
@@ -133,6 +134,12 @@
 
 typedef C2StreamParam<C2Info, C2BlobValue, kParamIndexCsd> C2StreamCsdInfo;
 
+C2ENUM(C2PictureTypeMask, uint32_t,
+    C2PictureTypeKeyFrame = (1u << 0),
+)
+
+typedef C2StreamParam<C2Info, C2Uint32Value, kParamIndexPictureTypeMask> C2StreamPictureTypeMaskInfo;
+
 /*
    Component description fields:
 
diff --git a/media/libstagefright/codec2/include/C2Param.h b/media/libstagefright/codec2/include/C2Param.h
index 0540155..4d9f707 100644
--- a/media/libstagefright/codec2/include/C2Param.h
+++ b/media/libstagefright/codec2/include/C2Param.h
@@ -721,6 +721,10 @@
 
     DEFINE_OTHER_COMPARISON_OPERATORS(C2ParamField)
 
+protected:
+    inline C2ParamField(C2Param::Index index, uint32_t offset, uint32_t size)
+        : _mIndex(index), _mFieldId(offset, size) {}
+
 private:
     friend struct _C2ParamInspector;
 
@@ -729,6 +733,17 @@
 };
 
 /**
+ * Structure uniquely specifying a field, an array element of a field, or a
+ * parameter in a configuration
+ */
+struct C2ParamOrField : public C2ParamField {
+//public:
+    template<typename S>
+    inline C2ParamOrField(S* param)
+        : C2ParamField(param->index(), 0u, param->size()) {}
+};
+
+/**
  * A shared (union) representation of numeric values
  */
 class C2Value {
@@ -1005,7 +1020,7 @@
      * For vendor-defined components, it can be true even for vendor-defined params,
      * but it is not recommended, in case the component becomes platform-defined.
      */
-    inline bool isRequired() const { return _mIsRequired; }
+    inline bool isRequired() const { return _mAttrib & IS_REQUIRED; }
 
     /**
      * Returns whether this parameter is persistent. This is always true for C2Tuning and C2Setting,
@@ -1014,35 +1029,43 @@
      * current frame and is not assumed to have the same value (or even be present) on subsequent
      * frames, unless it is specified for those frames.
      */
-    inline bool isPersistent() const { return _mIsPersistent; }
+    inline bool isPersistent() const { return _mAttrib & IS_PERSISTENT; }
 
     /// Returns the name of this param.
     /// This defaults to the underlying C2Struct's name, but could be altered for a component.
     inline C2String name() const { return _mName; }
 
-    /// Returns the parameter type
-    /// \todo fix this
-    inline C2Param::Type type() const { return _mType; }
+    /// Returns the parameter index
+    inline C2Param::Index index() const { return _mIndex; }
+
+    /// Returns the indices of parameters that this parameter has a dependency on
+    inline const std::vector<C2Param::Index> &dependencies() const { return mDependencies; }
+
+    // TODO: add more constructors that allow setting dependencies and attributes
 
     template<typename T>
     inline C2ParamDescriptor(bool isRequired, C2StringLiteral name, const T*)
-        : _mIsRequired(isRequired),
-          _mIsPersistent(true),
-          _mName(name),
-          _mType(T::PARAM_TYPE) { }
+        : _mIndex(T::PARAM_TYPE),
+          _mAttrib(IS_PERSISTENT | (isRequired ? IS_REQUIRED : 0)),
+          _mName(name) { }
 
     inline C2ParamDescriptor(
-            bool isRequired, C2StringLiteral name, C2Param::Type type)
-        : _mIsRequired(isRequired),
-          _mIsPersistent(true),
-          _mName(name),
-          _mType(type) { }
+            bool isRequired, C2StringLiteral name, C2Param::Index index)
+        : _mIndex(index),
+          _mAttrib(IS_PERSISTENT | (isRequired ? IS_REQUIRED : 0)),
+          _mName(name) { }
 
 private:
-    const bool _mIsRequired;
-    const bool _mIsPersistent;
+    enum attrib_t : uint32_t {
+        IS_REQUIRED   = 1u << 0,
+        IS_PERSISTENT = 1u << 1,
+    };
+    const C2Param::Index _mIndex;
+    const uint32_t _mAttrib;
     const C2String _mName;
-    const C2Param::Type _mType;
+    std::vector<C2Param::Index> mDependencies;
+
+    friend struct _C2ParamInspector;
 };
 
 /// \ingroup internal
@@ -1285,7 +1308,7 @@
         Primitive min;
         Primitive max;
         Primitive step;
-        Primitive nom;
+        Primitive num;
         Primitive denom;
     } range;
     std::vector<Primitive> values;
@@ -1300,9 +1323,9 @@
           range{min, max, step, (T)1, (T)1} { }
 
     template<typename T>
-    C2FieldSupportedValues(T min, T max, T nom, T den) :
+    C2FieldSupportedValues(T min, T max, T num, T den) :
         type(RANGE),
-        range{min, max, (T)0, nom, den} { }
+        range{min, max, (T)0, num, den} { }
 
     template<typename T>
     C2FieldSupportedValues(bool flags, std::initializer_list<T> list)
diff --git a/media/libstagefright/codec2/include/C2ParamDef.h b/media/libstagefright/codec2/include/C2ParamDef.h
index b5834f2..3691e01 100644
--- a/media/libstagefright/codec2/include/C2ParamDef.h
+++ b/media/libstagefright/codec2/include/C2ParamDef.h
@@ -225,7 +225,7 @@
 #define DEFINE_CAST_OPERATORS(_Type) \
     inline static _Type* From(C2Param *other) { \
         return (_Type*)C2Param::ifSuitable( \
-                other, sizeof(_Type),_Type::PARAM_TYPE, _Type::FLEX_SIZE, \
+                other, sizeof(_Type), _Type::PARAM_TYPE, _Type::FLEX_SIZE, \
                 (_Type::PARAM_TYPE & T::Index::DIR_UNDEFINED) != T::Index::DIR_UNDEFINED); \
     } \
     inline static const _Type* From(const C2Param *other) { \
diff --git a/media/libstagefright/codec2/include/C2Work.h b/media/libstagefright/codec2/include/C2Work.h
index 105cf81..b6c5814 100644
--- a/media/libstagefright/codec2/include/C2Work.h
+++ b/media/libstagefright/codec2/include/C2Work.h
@@ -68,7 +68,7 @@
 
     /// Conflicting parameters or fields with optional suggestions with (optional) suggested values
     /// for any conflicting fields to avoid the conflict.
-    std::list<C2ParamFieldValues> conflicts;
+    std::vector<C2ParamFieldValues> conflicts;
 };
 
 // ================================================================================================
@@ -83,32 +83,64 @@
     kParamIndexWorkOrdinal,
 };
 
+/**
+ * Information for ordering work items on a component port.
+ */
 struct C2WorkOrdinalStruct {
-    uint64_t timestamp;
-    uint64_t frame_index;    // submission ordinal on the initial component
-    uint64_t custom_ordinal; // can be given by the component, e.g. decode order
+//public:
+    c2_cntr64_t timestamp;     /** frame timestamp in microseconds */
+    c2_cntr64_t frameIndex;    /** submission ordinal on the initial component */
+    c2_cntr64_t customOrdinal; /** can be given by the component, e.g. decode order */
 
     DEFINE_AND_DESCRIBE_C2STRUCT(WorkOrdinal)
     C2FIELD(timestamp, "timestamp")
-    C2FIELD(frame_index, "frame-index")
-    C2FIELD(custom_ordinal, "custom-ordinal")
+    C2FIELD(frameIndex, "frame-index")
+    C2FIELD(customOrdinal, "custom-ordinal")
 };
 
-struct C2BufferPack {
+/**
+ * This structure represents a Codec 2.0 frame with its metadata.
+ *
+ * A frame basically consists of an ordered sets of buffers, configuration changes and info buffers
+ * along with some non-configuration metadata.
+ */
+struct C2FrameData {
 //public:
     enum flags_t : uint32_t {
-        FLAG_CODEC_CONFIG  = (1 << 0),
-        FLAG_DROP_FRAME    = (1 << 1),
-        FLAG_END_OF_STREAM = (1 << 2),
+        /**
+         * For input frames: no output frame shall be generated when processing this frame, but
+         * metadata shall still be processed.
+         * For output frames: this frame shall be discarded and but metadata is still valid.
+         */
+        FLAG_DROP_FRAME    = (1 << 0),
+        /**
+         * This frame is the last frame of the current stream. Further frames are part of a new
+         * stream.
+         */
+        FLAG_END_OF_STREAM = (1 << 1),
+        /**
+         * This frame shall be discarded with its metadata.
+         * This flag is only set by components - e.g. as a response to the flush command.
+         */
+        FLAG_DISCARD_FRAME = (1 << 2),
+        /**
+         * This frame contains only codec-specific configuration data, and no actual access unit.
+         *
+         * \deprecated pass codec configuration with using the \todo codec-specific configuration
+         * info together with the access unit.
+         */
+        FLAG_CODEC_CONFIG  = (1u << 31),
     };
 
+    /**
+     * Frame flags */
     flags_t  flags;
     C2WorkOrdinalStruct ordinal;
     std::vector<std::shared_ptr<C2Buffer>> buffers;
     //< for initial work item, these may also come from the parser - if provided
     //< for output buffers, these are the responses to requestedInfos
-    std::list<std::unique_ptr<C2Info>>       infos;
-    std::list<std::shared_ptr<C2InfoBuffer>> infoBuffers;
+    std::vector<std::unique_ptr<C2Param>>      configUpdate;
+    std::vector<std::shared_ptr<C2InfoBuffer>> infoBuffers;
 };
 
 struct C2Worklet {
@@ -116,63 +148,65 @@
     // IN
     c2_node_id_t component;
 
-    std::list<std::unique_ptr<C2Param>> tunings; //< tunings to be applied before processing this
-                                                 // worklet
-    std::list<C2Param::Type> requestedInfos;
-    std::vector<std::shared_ptr<C2BlockPool>> allocators; //< This vector shall be the same size as
-                                                          //< output.buffers. \deprecated
+    /** Configuration changes to be applied before processing this worklet. */
+    std::vector<std::unique_ptr<C2Tuning>> tunings;
+    std::vector<std::unique_ptr<C2SettingResult>> failures;
 
     // OUT
-    C2BufferPack output;
-    std::list<std::unique_ptr<C2SettingResult>> failures;
+    C2FrameData output;
 };
 
 /**
+ * Information about partial work-chains not part of the current work items.
+ *
+ * To be defined later.
+ */
+struct C2WorkChainInfo;
+
+/**
  * This structure holds information about all a single work item.
  *
  * This structure shall be passed by the client to the component for the first worklet. As such,
  * worklets must not be empty. The ownership of this object is passed.
- *
- * input:
- *      The input data to be processed. This is provided by the client with ownership. When the work
- *      is returned, the input buffer-pack's buffer vector shall contain nullptrs.
- *
- * worklets:
- *      The chain of components and associated allocators, tunings and info requests that the data
- *      must pass through. If this has more than a single element, the tunnels between successive
- *      components of the worklet chain must have been (successfully) pre-registered at the time
- *      the work is submitted. Allocating the output buffers in the worklets is the responsibility
- *      of each component. Upon work submission, each output buffer-pack shall be an appropriately
- *      sized vector containing nullptrs. When the work is completed/returned to the client,
- *
- * worklets_processed:
- *      It shall be initialized to 0 by the client when the work is submitted.
- *      It shall contain the number of worklets that were successfully processed when the work is
- *      returned. If this is less then the number of worklets, result must not be success.
- *      It must be in the range of [0, worklets.size()].
- *
- * result:
- *      The final outcome of the work. If 0 when work is returned, it is assumed that all worklets
- *      have been processed.
  */
 struct C2Work {
 //public:
-    // pre-chain infos (for portions of a tunneling chain that happend before this work-chain for
-    // this work item - due to framework facilitated (non-tunneled) work-chaining)
-    std::list<std::pair<std::unique_ptr<C2PortMimeConfig>, std::unique_ptr<C2Info>>> preChainInfos;
-    std::list<std::pair<std::unique_ptr<C2PortMimeConfig>, std::unique_ptr<C2Buffer>>> preChainInfoBlobs;
+    /// additional work chain info not part of this work
+    std::shared_ptr<C2WorkChainInfo> chainInfo;
 
-    C2BufferPack input;
+    /// The input data to be processed as part of this work/work-chain. This is provided by the
+    /// client with ownership. When the work is returned (via onWorkDone), the input buffer-pack's
+    /// buffer vector shall contain nullptrs.
+    C2FrameData input;
+
+    /// The chain of components, tunings (including output buffer pool IDs) and info requests that the
+    /// data must pass through. If this has more than a single element, the tunnels between successive
+    /// components of the worklet chain must have been (successfully) pre-registered at the time that
+    /// the work is submitted. Allocating the output buffers in the worklets is the responsibility of
+    /// each component. Upon work submission, each output buffer-pack shall be an appropriately sized
+    /// vector containing nullptrs. When the work is completed/returned to the client, output buffers
+    /// pointers from all but the final worklet shall be nullptrs.
     std::list<std::unique_ptr<C2Worklet>> worklets;
 
-    uint32_t worklets_processed;
+    /// Number of worklets successfully processed in this chain. This shall be initialized to 0 by the
+    /// client when the work is submitted. It shall contain the number of worklets that were
+    /// successfully processed when the work is returned to the client. If this is less then the number
+    /// of worklets, result must not be success. It must be in the range of [0, worklets.size()].
+    uint32_t workletsProcessed;
+
+    /// The final outcome of the work (corresponding to the current workletsProcessed). If 0 when
+    /// work is returned, it is assumed that all worklets have been processed.
     c2_status_t result;
 };
 
+/**
+ * Information about a future work to be submitted to the component. The information is used to
+ * reserve the work for work ordering purposes.
+ */
 struct C2WorkOutline {
 //public:
     C2WorkOrdinalStruct ordinal;
-    std::list<c2_node_id_t> chain;
+    std::vector<c2_node_id_t> chain;
 };
 
 /// @}
diff --git a/media/libstagefright/codec2/tests/C2ComponentInterface_test.cpp b/media/libstagefright/codec2/tests/C2ComponentInterface_test.cpp
index f50af81..7da824b 100644
--- a/media/libstagefright/codec2/tests/C2ComponentInterface_test.cpp
+++ b/media/libstagefright/codec2/tests/C2ComponentInterface_test.cpp
@@ -410,17 +410,17 @@
             // 1. integer geometric case
             // 2. float geometric case
 
-            auto nom = prim2Value(range.nom);
+            auto num = prim2Value(range.num);
             auto denom = prim2Value(range.denom);
 
-            // If both range.nom and range.denom are 1 and step is 0, we should use
+            // If both range.num and range.denom are 1 and step is 0, we should use
             // VALUES, shouldn't we?
-            ASSERT_FALSE(nom == 1 && denom == 1);
+            ASSERT_FALSE(num == 1 && denom == 1);
 
-            // (nom / denom) is not less than 1.
+            // (num / denom) is not less than 1.
             ASSERT_FALSE(denom == 0);
-            ASSERT_LE(denom, nom);
-            for (auto v = rmin; v <= rmax; v = v * nom / denom) {
+            ASSERT_LE(denom, num);
+            for (auto v = rmin; v <= rmax; v = v * num / denom) {
                 validValues->emplace_back(v);
             }
 
@@ -529,7 +529,7 @@
         const C2Param &param,
         const std::vector<std::shared_ptr<C2ParamDescriptor>> &sParams) {
     for (const auto &pd : sParams) {
-        if (param.type() == pd->type().type()) {
+        if (param.type() == pd->index().type()) {
             return true;
         }
     }
diff --git a/media/libstagefright/codec2/tests/C2Param_test.cpp b/media/libstagefright/codec2/tests/C2Param_test.cpp
index d186292..1a29add 100644
--- a/media/libstagefright/codec2/tests/C2Param_test.cpp
+++ b/media/libstagefright/codec2/tests/C2Param_test.cpp
@@ -2622,8 +2622,8 @@
         if (get(sv.range.step, t) != std::is_integral<T>::value) {
             cout << ":" << get(sv.range.step, t);
         }
-        if (get(sv.range.nom, t) != 1 || get(sv.range.denom, t) != 1) {
-            cout << ":" << get(sv.range.nom, t) << "/" << get(sv.range.denom, t);
+        if (get(sv.range.num, t) != 1 || get(sv.range.denom, t) != 1) {
+            cout << ":" << get(sv.range.num, t) << "/" << get(sv.range.denom, t);
         }
         cout << get(sv.range.max, t) << ")";
     }
@@ -2736,7 +2736,7 @@
         cout << "persistent ";
     }
     cout << "struct ";
-    dumpType(pd.type());
+    dumpType(C2Param::Type(pd.index().type()));
     cout << " " << pd.name() << ";" << endl;
 }
 
@@ -2769,7 +2769,7 @@
     Uint32TestInfo t;
     std::vector<C2FieldSupportedValues> values;
     values.push_back(C2FieldSupportedValues(0, 10, 1));  // min, max, step
-    values.push_back(C2FieldSupportedValues(1, 64, 2, 1));  // min, max, nom, den
+    values.push_back(C2FieldSupportedValues(1, 64, 2, 1));  // min, max, num, den
     values.push_back(C2FieldSupportedValues(false, {1, 2, 3}));  // flags, std::initializer_list
     uint32_t val[] = {1, 3, 5, 7};
     std::vector<uint32_t> v(std::begin(val), std::end(val));
diff --git a/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp b/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp
index f6e6478..a310717 100644
--- a/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp
+++ b/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp
@@ -359,14 +359,14 @@
 
 class BufferData : public C2BufferData {
 public:
-    explicit BufferData(const std::list<C2ConstLinearBlock> &blocks) : C2BufferData(blocks) {}
-    explicit BufferData(const std::list<C2ConstGraphicBlock> &blocks) : C2BufferData(blocks) {}
+    explicit BufferData(const std::vector<C2ConstLinearBlock> &blocks) : C2BufferData(blocks) {}
+    explicit BufferData(const std::vector<C2ConstGraphicBlock> &blocks) : C2BufferData(blocks) {}
 };
 
 class Buffer : public C2Buffer {
 public:
-    explicit Buffer(const std::list<C2ConstLinearBlock> &blocks) : C2Buffer(blocks) {}
-    explicit Buffer(const std::list<C2ConstGraphicBlock> &blocks) : C2Buffer(blocks) {}
+    explicit Buffer(const std::vector<C2ConstLinearBlock> &blocks) : C2Buffer(blocks) {}
+    explicit Buffer(const std::vector<C2ConstGraphicBlock> &blocks) : C2Buffer(blocks) {}
 };
 
 TEST_F(C2BufferTest, BufferDataTest) {
@@ -487,45 +487,45 @@
     std::shared_ptr<C2Info> info1(new C2Number1Info(1));
     std::shared_ptr<C2Info> info2(new C2Number2Info(2));
     buffer.reset(new Buffer( { block->share(0, kCapacity, C2Fence()) }));
-    EXPECT_TRUE(buffer->infos().empty());
+    EXPECT_TRUE(buffer->info().empty());
     EXPECT_FALSE(buffer->hasInfo(info1->type()));
     EXPECT_FALSE(buffer->hasInfo(info2->type()));
 
     ASSERT_EQ(C2_OK, buffer->setInfo(info1));
-    EXPECT_EQ(1u, buffer->infos().size());
-    EXPECT_EQ(*info1, *buffer->infos().front());
+    EXPECT_EQ(1u, buffer->info().size());
+    EXPECT_EQ(*info1, *buffer->info().front());
     EXPECT_TRUE(buffer->hasInfo(info1->type()));
     EXPECT_FALSE(buffer->hasInfo(info2->type()));
 
     ASSERT_EQ(C2_OK, buffer->setInfo(info2));
-    EXPECT_EQ(2u, buffer->infos().size());
+    EXPECT_EQ(2u, buffer->info().size());
     EXPECT_TRUE(buffer->hasInfo(info1->type()));
     EXPECT_TRUE(buffer->hasInfo(info2->type()));
 
     std::shared_ptr<C2Info> removed = buffer->removeInfo(info1->type());
     ASSERT_TRUE(removed);
     EXPECT_EQ(*removed, *info1);
-    EXPECT_EQ(1u, buffer->infos().size());
-    EXPECT_EQ(*info2, *buffer->infos().front());
+    EXPECT_EQ(1u, buffer->info().size());
+    EXPECT_EQ(*info2, *buffer->info().front());
     EXPECT_FALSE(buffer->hasInfo(info1->type()));
     EXPECT_TRUE(buffer->hasInfo(info2->type()));
 
     removed = buffer->removeInfo(info1->type());
     ASSERT_FALSE(removed);
-    EXPECT_EQ(1u, buffer->infos().size());
+    EXPECT_EQ(1u, buffer->info().size());
     EXPECT_FALSE(buffer->hasInfo(info1->type()));
     EXPECT_TRUE(buffer->hasInfo(info2->type()));
 
     std::shared_ptr<C2Info> info3(new C2Number2Info(3));
     ASSERT_EQ(C2_OK, buffer->setInfo(info3));
-    EXPECT_EQ(1u, buffer->infos().size());
+    EXPECT_EQ(1u, buffer->info().size());
     EXPECT_FALSE(buffer->hasInfo(info1->type()));
     EXPECT_TRUE(buffer->hasInfo(info2->type()));
 
     removed = buffer->removeInfo(info2->type());
     ASSERT_TRUE(removed);
     EXPECT_EQ(*info3, *removed);
-    EXPECT_TRUE(buffer->infos().empty());
+    EXPECT_TRUE(buffer->info().empty());
     EXPECT_FALSE(buffer->hasInfo(info1->type()));
     EXPECT_FALSE(buffer->hasInfo(info2->type()));
 }
diff --git a/media/libstagefright/codec2/vndk/Android.bp b/media/libstagefright/codec2/vndk/Android.bp
index cc79dc0..cdd0488 100644
--- a/media/libstagefright/codec2/vndk/Android.bp
+++ b/media/libstagefright/codec2/vndk/Android.bp
@@ -1,4 +1,14 @@
-cc_library {
+cc_library_headers {
+    name: "libstagefright_codec2_internal",
+
+    export_include_dirs: [
+        "internal",
+    ],
+
+    vendor_available: false,
+}
+
+cc_library_shared {
     name: "libstagefright_codec2_vndk",
 
     srcs: [
@@ -13,6 +23,10 @@
         "include",
     ],
 
+    header_libs:[
+        "libstagefright_codec2_internal",
+    ],
+
     include_dirs: [
         "frameworks/av/media/libstagefright/codec2/include",
         "frameworks/native/include/media/hardware",
diff --git a/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp b/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp
index 18db3e9..a5ea511 100644
--- a/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp
@@ -38,6 +38,15 @@
 using ::android::hardware::hidl_handle;
 using ::android::hardware::hidl_vec;
 
+namespace {
+
+struct BufferDescriptorInfo {
+    IMapper::BufferDescriptorInfo mapperInfo;
+    uint32_t stride;
+};
+
+}
+
 /* ===================================== GRALLOC ALLOCATION ==================================== */
 static c2_status_t maperr2error(Error maperr) {
     switch (maperr) {
@@ -73,6 +82,7 @@
         uint32_t format;
         uint32_t usage_lo;
         uint32_t usage_hi;
+        uint32_t stride;
         uint32_t magic;
     };
 
@@ -109,13 +119,16 @@
 
     static C2HandleGralloc* WrapNativeHandle(
             const native_handle_t *const handle,
-            uint32_t width, uint32_t height, uint32_t format, uint64_t usage) {
+            uint32_t width, uint32_t height, uint32_t format, uint64_t usage, uint32_t stride) {
         //CHECK(handle != nullptr);
         if (native_handle_is_invalid(handle) ||
             handle->numInts > int((INT_MAX - handle->version) / sizeof(int)) - NUM_INTS - handle->numFds) {
             return nullptr;
         }
-        ExtraData xd = { width, height, format, uint32_t(usage & 0xFFFFFFFF), uint32_t(usage >> 32), MAGIC };
+        ExtraData xd = {
+            width, height, format, uint32_t(usage & 0xFFFFFFFF), uint32_t(usage >> 32),
+            stride, MAGIC
+        };
         native_handle_t *res = native_handle_create(handle->numFds, handle->numInts + NUM_INTS);
         if (res != nullptr) {
             memcpy(&res->data, &handle->data, sizeof(int) * (handle->numFds + handle->numInts));
@@ -138,7 +151,8 @@
 
     static const C2HandleGralloc* Import(
             const C2Handle *const handle,
-            uint32_t *width, uint32_t *height, uint32_t *format, uint64_t *usage) {
+            uint32_t *width, uint32_t *height, uint32_t *format,
+            uint64_t *usage, uint32_t *stride) {
         const ExtraData *xd = getExtraData(handle);
         if (xd == nullptr) {
             return nullptr;
@@ -147,15 +161,22 @@
         *height = xd->height;
         *format = xd->format;
         *usage = xd->usage_lo | (uint64_t(xd->usage_hi) << 32);
+        *stride = xd->stride;
 
         return reinterpret_cast<const C2HandleGralloc *>(handle);
     }
 };
 
-native_handle_t* UnwrapNativeCodec2GrallocHandle(const C2Handle *const handle) {
+native_handle_t *UnwrapNativeCodec2GrallocHandle(const C2Handle *const handle) {
     return C2HandleGralloc::UnwrapNativeHandle(handle);
 }
 
+C2Handle *WrapNativeCodec2GrallocHandle(
+        const native_handle_t *const handle,
+        uint32_t width, uint32_t height, uint32_t format, uint64_t usage, uint32_t stride) {
+    return C2HandleGralloc::WrapNativeHandle(handle, width, height, format, usage, stride);
+}
+
 class C2AllocationGralloc : public C2GraphicAllocation {
 public:
     virtual ~C2AllocationGralloc() override;
@@ -171,7 +192,7 @@
     // internal methods
     // |handle| will be moved.
     C2AllocationGralloc(
-              const IMapper::BufferDescriptorInfo &info,
+              const BufferDescriptorInfo &info,
               const sp<IMapper> &mapper,
               hidl_handle &hidlHandle,
               const C2HandleGralloc *const handle);
@@ -179,7 +200,7 @@
     c2_status_t status() const;
 
 private:
-    const IMapper::BufferDescriptorInfo mInfo;
+    const BufferDescriptorInfo mInfo;
     const sp<IMapper> mMapper;
     const hidl_handle mHidlHandle;
     const C2HandleGralloc *mHandle;
@@ -189,11 +210,11 @@
 };
 
 C2AllocationGralloc::C2AllocationGralloc(
-          const IMapper::BufferDescriptorInfo &info,
+          const BufferDescriptorInfo &info,
           const sp<IMapper> &mapper,
           hidl_handle &hidlHandle,
           const C2HandleGralloc *const handle)
-    : C2GraphicAllocation(info.width, info.height),
+    : C2GraphicAllocation(info.mapperInfo.width, info.mapperInfo.height),
       mInfo(info),
       mMapper(mapper),
       mHidlHandle(std::move(hidlHandle)),
@@ -241,83 +262,133 @@
             return C2_CORRUPTED;
         }
         mLockedHandle = C2HandleGralloc::WrapNativeHandle(
-                mBuffer, mInfo.width, mInfo.height, (uint32_t)mInfo.format, mInfo.usage);
+                mBuffer, mInfo.mapperInfo.width, mInfo.mapperInfo.height,
+                (uint32_t)mInfo.mapperInfo.format, mInfo.mapperInfo.usage, mInfo.stride);
     }
 
-    if (mInfo.format == PixelFormat::YCBCR_420_888 || mInfo.format == PixelFormat::YV12) {
-        YCbCrLayout ycbcrLayout;
-        mMapper->lockYCbCr(
-                const_cast<native_handle_t *>(mBuffer),
-                BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN,
-                { (int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width, (int32_t)rect.height },
-                // TODO: fence
-                hidl_handle(),
-                [&err, &ycbcrLayout](const auto &maperr, const auto &mapLayout) {
-                    err = maperr2error(maperr);
-                    if (err == C2_OK) {
-                        ycbcrLayout = mapLayout;
-                    }
-                });
-        if (err != C2_OK) {
-            return err;
+    switch (mInfo.mapperInfo.format) {
+        case PixelFormat::YCBCR_420_888:
+        case PixelFormat::YV12: {
+            YCbCrLayout ycbcrLayout;
+            mMapper->lockYCbCr(
+                    const_cast<native_handle_t *>(mBuffer),
+                    BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN,
+                    { (int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width, (int32_t)rect.height },
+                    // TODO: fence
+                    hidl_handle(),
+                    [&err, &ycbcrLayout](const auto &maperr, const auto &mapLayout) {
+                        err = maperr2error(maperr);
+                        if (err == C2_OK) {
+                            ycbcrLayout = mapLayout;
+                        }
+                    });
+            if (err != C2_OK) {
+                return err;
+            }
+            addr[C2PlanarLayout::PLANE_Y] = (uint8_t *)ycbcrLayout.y;
+            addr[C2PlanarLayout::PLANE_U] = (uint8_t *)ycbcrLayout.cb;
+            addr[C2PlanarLayout::PLANE_V] = (uint8_t *)ycbcrLayout.cr;
+            layout->type = C2PlanarLayout::TYPE_YUV;
+            layout->numPlanes = 3;
+            layout->planes[C2PlanarLayout::PLANE_Y] = {
+                C2PlaneInfo::CHANNEL_Y,         // channel
+                1,                              // colInc
+                (int32_t)ycbcrLayout.yStride,   // rowInc
+                1,                              // mColSampling
+                1,                              // mRowSampling
+                8,                              // allocatedDepth
+                8,                              // bitDepth
+                0,                              // rightShift
+                C2PlaneInfo::NATIVE,            // endianness
+            };
+            layout->planes[C2PlanarLayout::PLANE_U] = {
+                C2PlaneInfo::CHANNEL_CB,          // channel
+                (int32_t)ycbcrLayout.chromaStep,  // colInc
+                (int32_t)ycbcrLayout.cStride,     // rowInc
+                2,                                // mColSampling
+                2,                                // mRowSampling
+                8,                                // allocatedDepth
+                8,                                // bitDepth
+                0,                                // rightShift
+                C2PlaneInfo::NATIVE,              // endianness
+            };
+            layout->planes[C2PlanarLayout::PLANE_V] = {
+                C2PlaneInfo::CHANNEL_CR,          // channel
+                (int32_t)ycbcrLayout.chromaStep,  // colInc
+                (int32_t)ycbcrLayout.cStride,     // rowInc
+                2,                                // mColSampling
+                2,                                // mRowSampling
+                8,                                // allocatedDepth
+                8,                                // bitDepth
+                0,                                // rightShift
+                C2PlaneInfo::NATIVE,              // endianness
+            };
+            break;
         }
-        addr[C2PlanarLayout::PLANE_Y] = (uint8_t *)ycbcrLayout.y;
-        addr[C2PlanarLayout::PLANE_U] = (uint8_t *)ycbcrLayout.cb;
-        addr[C2PlanarLayout::PLANE_V] = (uint8_t *)ycbcrLayout.cr;
-        layout->type = C2PlanarLayout::TYPE_YUV;
-        layout->numPlanes = 3;
-        layout->planes[C2PlanarLayout::PLANE_Y] = {
-            C2PlaneInfo::CHANNEL_Y,         // channel
-            1,                              // colInc
-            (int32_t)ycbcrLayout.yStride,   // rowInc
-            1,                              // mColSampling
-            1,                              // mRowSampling
-            8,                              // allocatedDepth
-            8,                              // bitDepth
-            0,                              // rightShift
-            C2PlaneInfo::NATIVE,            // endianness
-        };
-        layout->planes[C2PlanarLayout::PLANE_U] = {
-            C2PlaneInfo::CHANNEL_CB,          // channel
-            (int32_t)ycbcrLayout.chromaStep,  // colInc
-            (int32_t)ycbcrLayout.cStride,     // rowInc
-            2,                                // mColSampling
-            2,                                // mRowSampling
-            8,                                // allocatedDepth
-            8,                                // bitDepth
-            0,                                // rightShift
-            C2PlaneInfo::NATIVE,              // endianness
-        };
-        layout->planes[C2PlanarLayout::PLANE_V] = {
-            C2PlaneInfo::CHANNEL_CR,          // channel
-            (int32_t)ycbcrLayout.chromaStep,  // colInc
-            (int32_t)ycbcrLayout.cStride,     // rowInc
-            2,                                // mColSampling
-            2,                                // mRowSampling
-            8,                                // allocatedDepth
-            8,                                // bitDepth
-            0,                                // rightShift
-            C2PlaneInfo::NATIVE,              // endianness
-        };
-    } else {
-        void *pointer = nullptr;
-        mMapper->lock(
-                const_cast<native_handle_t *>(mBuffer),
-                BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN,
-                { (int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width, (int32_t)rect.height },
-                // TODO: fence
-                hidl_handle(),
-                [&err, &pointer](const auto &maperr, const auto &mapPointer) {
-                    err = maperr2error(maperr);
-                    if (err == C2_OK) {
-                        pointer = mapPointer;
-                    }
-                });
-        if (err != C2_OK) {
-            return err;
+
+        case PixelFormat::RGBA_8888:
+            // TODO: alpha channel
+            // fall-through
+        case PixelFormat::RGBX_8888: {
+            void *pointer = nullptr;
+            mMapper->lock(
+                    const_cast<native_handle_t *>(mBuffer),
+                    BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN,
+                    { (int32_t)rect.left, (int32_t)rect.top, (int32_t)rect.width, (int32_t)rect.height },
+                    // TODO: fence
+                    hidl_handle(),
+                    [&err, &pointer](const auto &maperr, const auto &mapPointer) {
+                        err = maperr2error(maperr);
+                        if (err == C2_OK) {
+                            pointer = mapPointer;
+                        }
+                    });
+            if (err != C2_OK) {
+                return err;
+            }
+            addr[C2PlanarLayout::PLANE_R] = (uint8_t *)pointer;
+            addr[C2PlanarLayout::PLANE_G] = (uint8_t *)pointer + 1;
+            addr[C2PlanarLayout::PLANE_B] = (uint8_t *)pointer + 2;
+            layout->type = C2PlanarLayout::TYPE_RGB;
+            layout->numPlanes = 3;
+            layout->planes[C2PlanarLayout::PLANE_R] = {
+                C2PlaneInfo::CHANNEL_R,         // channel
+                4,                              // colInc
+                4 * (int32_t)mInfo.stride,      // rowInc
+                1,                              // mColSampling
+                1,                              // mRowSampling
+                8,                              // allocatedDepth
+                8,                              // bitDepth
+                0,                              // rightShift
+                C2PlaneInfo::NATIVE,            // endianness
+            };
+            layout->planes[C2PlanarLayout::PLANE_G] = {
+                C2PlaneInfo::CHANNEL_G,         // channel
+                4,                              // colInc
+                4 * (int32_t)mInfo.stride,      // rowInc
+                1,                              // mColSampling
+                1,                              // mRowSampling
+                8,                              // allocatedDepth
+                8,                              // bitDepth
+                0,                              // rightShift
+                C2PlaneInfo::NATIVE,            // endianness
+            };
+            layout->planes[C2PlanarLayout::PLANE_B] = {
+                C2PlaneInfo::CHANNEL_B,         // channel
+                4,                              // colInc
+                4 * (int32_t)mInfo.stride,      // rowInc
+                1,                              // mColSampling
+                1,                              // mRowSampling
+                8,                              // allocatedDepth
+                8,                              // bitDepth
+                0,                              // rightShift
+                C2PlaneInfo::NATIVE,            // endianness
+            };
+            break;
         }
-        // TODO
-        return C2_OMITTED;
+        default: {
+            return C2_OMITTED;
+        }
     }
     mLocked = true;
 
@@ -396,17 +467,20 @@
     // TODO: buffer usage should be determined according to |usage|
     (void) usage;
 
-    IMapper::BufferDescriptorInfo info = {
-        width,
-        height,
-        1u,  // layerCount
-        (PixelFormat)format,
-        BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN,
+    BufferDescriptorInfo info = {
+        {
+            width,
+            height,
+            1u,  // layerCount
+            (PixelFormat)format,
+            BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN,
+        },
+        0u,  // stride placeholder
     };
     c2_status_t err = C2_OK;
     BufferDescriptor desc;
     mMapper->createDescriptor(
-            info, [&err, &desc](const auto &maperr, const auto &descriptor) {
+            info.mapperInfo, [&err, &desc](const auto &maperr, const auto &descriptor) {
                 err = maperr2error(maperr);
                 if (err == C2_OK) {
                     desc = descriptor;
@@ -421,8 +495,7 @@
     mAllocator->allocate(
             desc,
             1u,
-            [&err, &buffer](const auto &maperr, const auto &stride, auto &buffers) {
-                (void) stride;
+            [&err, &buffer, &info](const auto &maperr, const auto &stride, auto &buffers) {
                 err = maperr2error(maperr);
                 if (err != C2_OK) {
                     return;
@@ -431,6 +504,7 @@
                     err = C2_CORRUPTED;
                     return;
                 }
+                info.stride = stride;
                 buffer = std::move(buffers[0]);
             });
     if (err != C2_OK) {
@@ -442,18 +516,20 @@
             info, mMapper, buffer,
             C2HandleGralloc::WrapNativeHandle(
                     buffer.getNativeHandle(),
-                    info.width, info.height, (uint32_t)info.format, info.usage)));
+                    info.mapperInfo.width, info.mapperInfo.height,
+                    (uint32_t)info.mapperInfo.format, info.mapperInfo.usage, info.stride)));
     return C2_OK;
 }
 
 c2_status_t C2AllocatorGralloc::Impl::priorGraphicAllocation(
         const C2Handle *handle,
         std::shared_ptr<C2GraphicAllocation> *allocation) {
-    IMapper::BufferDescriptorInfo info;
-    info.layerCount = 1u;
+    BufferDescriptorInfo info;
+    info.mapperInfo.layerCount = 1u;
     const C2HandleGralloc *grallocHandle = C2HandleGralloc::Import(
             handle,
-            &info.width, &info.height, (uint32_t *)&info.format, (uint64_t *)&info.usage);
+            &info.mapperInfo.width, &info.mapperInfo.height,
+            (uint32_t *)&info.mapperInfo.format, (uint64_t *)&info.mapperInfo.usage, &info.stride);
     if (grallocHandle == nullptr) {
         return C2_BAD_VALUE;
     }
@@ -461,7 +537,7 @@
     hidl_handle hidlHandle = C2HandleGralloc::UnwrapNativeHandle(grallocHandle);
 
     allocation->reset(new C2AllocationGralloc(info, mMapper, hidlHandle, grallocHandle));
-    return C2_OMITTED;
+    return C2_OK;
 }
 
 C2AllocatorGralloc::C2AllocatorGralloc() : mImpl(new Impl) {}
diff --git a/media/libstagefright/codec2/vndk/C2Buffer.cpp b/media/libstagefright/codec2/vndk/C2Buffer.cpp
index 65a271e..4ab3e05 100644
--- a/media/libstagefright/codec2/vndk/C2Buffer.cpp
+++ b/media/libstagefright/codec2/vndk/C2Buffer.cpp
@@ -602,44 +602,44 @@
 
 class C2BufferData::Impl {
 public:
-    explicit Impl(const std::list<C2ConstLinearBlock> &blocks)
+    explicit Impl(const std::vector<C2ConstLinearBlock> &blocks)
         : mType(blocks.size() == 1 ? LINEAR : LINEAR_CHUNKS),
           mLinearBlocks(blocks) {
     }
 
-    explicit Impl(const std::list<C2ConstGraphicBlock> &blocks)
+    explicit Impl(const std::vector<C2ConstGraphicBlock> &blocks)
         : mType(blocks.size() == 1 ? GRAPHIC : GRAPHIC_CHUNKS),
           mGraphicBlocks(blocks) {
     }
 
     Type type() const { return mType; }
-    const std::list<C2ConstLinearBlock> &linearBlocks() const { return mLinearBlocks; }
-    const std::list<C2ConstGraphicBlock> &graphicBlocks() const { return mGraphicBlocks; }
+    const std::vector<C2ConstLinearBlock> &linearBlocks() const { return mLinearBlocks; }
+    const std::vector<C2ConstGraphicBlock> &graphicBlocks() const { return mGraphicBlocks; }
 
 private:
     Type mType;
-    std::list<C2ConstLinearBlock> mLinearBlocks;
-    std::list<C2ConstGraphicBlock> mGraphicBlocks;
+    std::vector<C2ConstLinearBlock> mLinearBlocks;
+    std::vector<C2ConstGraphicBlock> mGraphicBlocks;
 };
 
-C2BufferData::C2BufferData(const std::list<C2ConstLinearBlock> &blocks) : mImpl(new Impl(blocks)) {}
-C2BufferData::C2BufferData(const std::list<C2ConstGraphicBlock> &blocks) : mImpl(new Impl(blocks)) {}
+C2BufferData::C2BufferData(const std::vector<C2ConstLinearBlock> &blocks) : mImpl(new Impl(blocks)) {}
+C2BufferData::C2BufferData(const std::vector<C2ConstGraphicBlock> &blocks) : mImpl(new Impl(blocks)) {}
 
 C2BufferData::Type C2BufferData::type() const { return mImpl->type(); }
 
-const std::list<C2ConstLinearBlock> C2BufferData::linearBlocks() const {
+const std::vector<C2ConstLinearBlock> C2BufferData::linearBlocks() const {
     return mImpl->linearBlocks();
 }
 
-const std::list<C2ConstGraphicBlock> C2BufferData::graphicBlocks() const {
+const std::vector<C2ConstGraphicBlock> C2BufferData::graphicBlocks() const {
     return mImpl->graphicBlocks();
 }
 
 class C2Buffer::Impl {
 public:
-    Impl(C2Buffer *thiz, const std::list<C2ConstLinearBlock> &blocks)
+    Impl(C2Buffer *thiz, const std::vector<C2ConstLinearBlock> &blocks)
         : mThis(thiz), mData(blocks) {}
-    Impl(C2Buffer *thiz, const std::list<C2ConstGraphicBlock> &blocks)
+    Impl(C2Buffer *thiz, const std::vector<C2ConstGraphicBlock> &blocks)
         : mThis(thiz), mData(blocks) {}
 
     ~Impl() {
@@ -676,8 +676,8 @@
         return C2_OK;
     }
 
-    std::list<std::shared_ptr<const C2Info>> infos() const {
-        std::list<std::shared_ptr<const C2Info>> result(mInfos.size());
+    std::vector<std::shared_ptr<const C2Info>> info() const {
+        std::vector<std::shared_ptr<const C2Info>> result(mInfos.size());
         std::transform(
                 mInfos.begin(), mInfos.end(), result.begin(),
                 [] (const auto &elem) { return elem.second; });
@@ -712,10 +712,10 @@
     std::list<std::pair<OnDestroyNotify, void *>> mNotify;
 };
 
-C2Buffer::C2Buffer(const std::list<C2ConstLinearBlock> &blocks)
+C2Buffer::C2Buffer(const std::vector<C2ConstLinearBlock> &blocks)
     : mImpl(new Impl(this, blocks)) {}
 
-C2Buffer::C2Buffer(const std::list<C2ConstGraphicBlock> &blocks)
+C2Buffer::C2Buffer(const std::vector<C2ConstGraphicBlock> &blocks)
     : mImpl(new Impl(this, blocks)) {}
 
 const C2BufferData C2Buffer::data() const { return mImpl->data(); }
@@ -728,8 +728,8 @@
     return mImpl->unregisterOnDestroyNotify(onDestroyNotify, arg);
 }
 
-const std::list<std::shared_ptr<const C2Info>> C2Buffer::infos() const {
-    return mImpl->infos();
+const std::vector<std::shared_ptr<const C2Info>> C2Buffer::info() const {
+    return mImpl->info();
 }
 
 c2_status_t C2Buffer::setInfo(const std::shared_ptr<C2Info> &info) {
diff --git a/media/libstagefright/codec2/vndk/C2Store.cpp b/media/libstagefright/codec2/vndk/C2Store.cpp
index 555e77b..a49fd24 100644
--- a/media/libstagefright/codec2/vndk/C2Store.cpp
+++ b/media/libstagefright/codec2/vndk/C2Store.cpp
@@ -404,8 +404,10 @@
 C2PlatformComponentStore::C2PlatformComponentStore() {
     // TODO: move this also into a .so so it can be updated
     mComponents.emplace("c2.google.avc.decoder", "libstagefright_soft_c2avcdec.so");
+    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");
 }
 
 c2_status_t C2PlatformComponentStore::copyBuffer(
diff --git a/media/libstagefright/codec2/vndk/include/C2AllocatorGralloc.h b/media/libstagefright/codec2/vndk/include/C2AllocatorGralloc.h
index 5311747..56fa317 100644
--- a/media/libstagefright/codec2/vndk/include/C2AllocatorGralloc.h
+++ b/media/libstagefright/codec2/vndk/include/C2AllocatorGralloc.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright (C) 2016 The Android Open Source Project
  *
@@ -32,7 +31,17 @@
  *
  * @return a new NON-OWNING native handle that must be deleted using native_handle_delete.
  */
-native_handle_t*UnwrapNativeCodec2GrallocHandle(const C2Handle *const handle);
+native_handle_t *UnwrapNativeCodec2GrallocHandle(const C2Handle *const handle);
+
+/**
+ * Wrap the gralloc handle and metadata into Codec2 handle recognized by
+ * C2AllocatorGralloc.
+ *
+ * @return a new NON-OWNING C2Handle that must be deleted using native_handle_delete.
+ */
+C2Handle *WrapNativeCodec2GrallocHandle(
+        const native_handle_t *const handle,
+        uint32_t width, uint32_t height, uint32_t format, uint64_t usage, uint32_t stride);
 
 class C2AllocatorGralloc : public C2Allocator {
 public:
diff --git a/media/libstagefright/codec2/vndk/C2ParamInternal.h b/media/libstagefright/codec2/vndk/internal/C2ParamInternal.h
similarity index 88%
rename from media/libstagefright/codec2/vndk/C2ParamInternal.h
rename to media/libstagefright/codec2/vndk/internal/C2ParamInternal.h
index 0f3812a..5bf3009 100644
--- a/media/libstagefright/codec2/vndk/C2ParamInternal.h
+++ b/media/libstagefright/codec2/vndk/internal/C2ParamInternal.h
@@ -22,7 +22,7 @@
 namespace android {
 
 struct C2_HIDE _C2ParamInspector {
-   inline static uint32_t getIndex(const C2ParamField &pf) {
+    inline static uint32_t getIndex(const C2ParamField &pf) {
         return pf._mIndex;
     }
 
@@ -34,6 +34,10 @@
         return pf._mFieldId._mSize;
     }
 
+    inline static uint32_t getAttrib(const C2ParamDescriptor &pd) {
+        return pd._mAttrib;
+    }
+
     inline static
     C2ParamField CreateParamField(C2Param::Index index, uint32_t offset, uint32_t size) {
         return C2ParamField(index, offset, size);
diff --git a/media/libstagefright/codecs/aacdec/Android.bp b/media/libstagefright/codecs/aacdec/Android.bp
index f1ff11b..9931e2d 100644
--- a/media/libstagefright/codecs/aacdec/Android.bp
+++ b/media/libstagefright/codecs/aacdec/Android.bp
@@ -25,7 +25,6 @@
 
     static_libs: [
         "libFraunhoferAAC",
-        "libstagefright_codec2_vndk"
     ],
 
     shared_libs: [
@@ -33,6 +32,7 @@
         "libion",
         "liblog",
         "libstagefright_codec2",
+        "libstagefright_codec2_vndk",
         "libstagefright_foundation",
         "libstagefright_simple_c2component",
         "libutils",
diff --git a/media/libstagefright/codecs/aacdec/C2SoftAac.cpp b/media/libstagefright/codecs/aacdec/C2SoftAac.cpp
index 390f36c..b57c2aa 100644
--- a/media/libstagefright/codecs/aacdec/C2SoftAac.cpp
+++ b/media/libstagefright/codecs/aacdec/C2SoftAac.cpp
@@ -316,9 +316,9 @@
             work->worklets.front()->output.buffers.clear();
             work->worklets.front()->output.buffers.push_back(buffer);
             work->worklets.front()->output.ordinal = work->input.ordinal;
-            work->worklets_processed = 1u;
+            work->workletsProcessed = 1u;
         };
-        if (work && work->input.ordinal.frame_index == outInfo.frameIndex) {
+        if (work && work->input.ordinal.frameIndex == c2_cntr64_t(outInfo.frameIndex)) {
             fillWork(work);
         } else {
             finish(outInfo.frameIndex, fillWork);
@@ -332,7 +332,7 @@
 void C2SoftAac::process(
         const std::unique_ptr<C2Work> &work,
         const std::shared_ptr<C2BlockPool> &pool) {
-    work->worklets_processed = 0u;
+    work->workletsProcessed = 0u;
     if (mSignalledError) {
         return;
     }
@@ -346,8 +346,8 @@
     size_t offset = 0u;
     size_t size = view.capacity();
 
-    bool eos = (work->input.flags & C2BufferPack::FLAG_END_OF_STREAM) != 0;
-    bool codecConfig = (work->input.flags & C2BufferPack::FLAG_CODEC_CONFIG) != 0;
+    bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
+    bool codecConfig = (work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) != 0;
 
     //TODO
 #if 0
@@ -375,14 +375,13 @@
 
         work->worklets.front()->output.ordinal = work->input.ordinal;
         work->worklets.front()->output.buffers.clear();
-        work->worklets.front()->output.buffers.push_back(nullptr);
 
         return;
     }
 
     Info inInfo;
-    inInfo.frameIndex = work->input.ordinal.frame_index;
-    inInfo.timestamp = work->input.ordinal.timestamp;
+    inInfo.frameIndex = work->input.ordinal.frameIndex.peeku();
+    inInfo.timestamp = work->input.ordinal.timestamp.peeku();
     inInfo.bufferSize = size;
     inInfo.decodedSizes.clear();
     while (size > 0u) {
@@ -602,15 +601,14 @@
         auto 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.buffers.emplace_back(nullptr);
             work->worklets.front()->output.ordinal = work->input.ordinal;
-            work->worklets_processed = 1u;
+            work->workletsProcessed = 1u;
         };
         while (mBuffersInfo.size() > 1u) {
             finish(mBuffersInfo.front().frameIndex, fillEmptyWork);
             mBuffersInfo.pop_front();
         }
-        if (work->worklets_processed == 0u) {
+        if (work->workletsProcessed == 0u) {
             fillEmptyWork(work);
         }
         mBuffersInfo.clear();
diff --git a/media/libstagefright/codecs/aacenc/Android.bp b/media/libstagefright/codecs/aacenc/Android.bp
index 4caef92..9386c6e 100644
--- a/media/libstagefright/codecs/aacenc/Android.bp
+++ b/media/libstagefright/codecs/aacenc/Android.bp
@@ -22,7 +22,6 @@
 
     static_libs: [
         "libFraunhoferAAC",
-        "libstagefright_codec2_vndk"
     ],
 
     shared_libs: [
@@ -30,6 +29,7 @@
         "libion",
         "liblog",
         "libstagefright_codec2",
+        "libstagefright_codec2_vndk",
         "libstagefright_foundation",
         "libstagefright_simple_c2component",
         "libutils",
diff --git a/media/libstagefright/codecs/aacenc/C2SoftAacEnc.cpp b/media/libstagefright/codecs/aacenc/C2SoftAacEnc.cpp
index 94308c4..6f1b325 100644
--- a/media/libstagefright/codecs/aacenc/C2SoftAacEnc.cpp
+++ b/media/libstagefright/codecs/aacenc/C2SoftAacEnc.cpp
@@ -176,12 +176,12 @@
 void C2SoftAacEnc::process(
         const std::unique_ptr<C2Work> &work,
         const std::shared_ptr<C2BlockPool> &pool) {
-    work->worklets_processed = 0u;
+    work->workletsProcessed = 0u;
 
     if (mSignalledError) {
         return;
     }
-    bool eos = (work->input.flags & C2BufferPack::FLAG_END_OF_STREAM) != 0;
+    bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
 
     if (!mSentCodecSpecificData) {
         // The very first thing we want to output is the codec specific
@@ -215,7 +215,7 @@
 #if defined(LOG_NDEBUG) && !LOG_NDEBUG
         hexdump(csd->m.value, csd->flexCount());
 #endif
-        work->worklets.front()->output.infos.push_back(std::move(csd));
+        work->worklets.front()->output.configUpdate.push_back(std::move(csd));
 
         mOutBufferSize = encInfo.maxOutBufBytes;
         mNumBytesPerInputFrame = encInfo.frameLength * mNumChannels * sizeof(int16_t);
@@ -225,7 +225,7 @@
     }
 
     C2ReadView view = work->input.buffers[0]->data().linearBlocks().front().map().get();
-    uint64_t timestamp = mInputTimeUs;
+    uint64_t timestamp = mInputTimeUs.peeku();
 
     size_t numFrames = (view.capacity() + mInputSize + (eos ? mNumBytesPerInputFrame - 1 : 0))
             / mNumBytesPerInputFrame;
@@ -336,21 +336,19 @@
     }
 
     work->worklets.front()->output.flags =
-        (C2BufferPack::flags_t)(eos ? C2BufferPack::FLAG_END_OF_STREAM : 0);
+        (C2FrameData::flags_t)(eos ? C2FrameData::FLAG_END_OF_STREAM : 0);
     work->worklets.front()->output.buffers.clear();
     work->worklets.front()->output.ordinal = work->input.ordinal;
     work->worklets.front()->output.ordinal.timestamp = timestamp;
-    work->worklets_processed = 1u;
+    work->workletsProcessed = 1u;
     if (nOutputBytes) {
         work->worklets.front()->output.buffers.push_back(
                 createLinearBuffer(block, 0, nOutputBytes));
-    } else {
-        work->worklets.front()->output.buffers.emplace_back(nullptr);
     }
 
 #if 0
     ALOGI("sending %d bytes of data (time = %lld us, flags = 0x%08lx)",
-          nOutputBytes, mInputTimeUs, outHeader->nFlags);
+          nOutputBytes, mInputTimeUs.peekll(), outHeader->nFlags);
 
     hexdump(outHeader->pBuffer + outHeader->nOffset, outHeader->nFilledLen);
 #endif
diff --git a/media/libstagefright/codecs/aacenc/C2SoftAacEnc.h b/media/libstagefright/codecs/aacenc/C2SoftAacEnc.h
index 947c960..c9f440f 100644
--- a/media/libstagefright/codecs/aacenc/C2SoftAacEnc.h
+++ b/media/libstagefright/codecs/aacenc/C2SoftAacEnc.h
@@ -57,7 +57,7 @@
 
     bool mSentCodecSpecificData;
     size_t mInputSize;
-    int64_t mInputTimeUs;
+    c2_cntr64_t mInputTimeUs;
 
     bool mSignalledError;
 
diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
index 0da9cc7..306d0a5 100644
--- a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
+++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
@@ -99,7 +99,7 @@
             }
         case C2FieldSupportedValues::RANGE:
             {
-                // TODO: handle step, nom, denom
+                // TODO: handle step, num, denom
                 return Getter<T>::get(supportedValues.range.min) <= value
                         && value <= Getter<T>::get(supportedValues.range.max);
             }
@@ -206,14 +206,13 @@
 
 void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
     uint32_t flags = 0;
-    if ((work->input.flags & C2BufferPack::FLAG_END_OF_STREAM)) {
-        flags |= C2BufferPack::FLAG_END_OF_STREAM;
+    if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM)) {
+        flags |= C2FrameData::FLAG_END_OF_STREAM;
     }
-    work->worklets.front()->output.flags = (C2BufferPack::flags_t)flags;
+    work->worklets.front()->output.flags = (C2FrameData::flags_t)flags;
     work->worklets.front()->output.buffers.clear();
-    work->worklets.front()->output.buffers.emplace_back(nullptr);
     work->worklets.front()->output.ordinal = work->input.ordinal;
-    work->worklets_processed = 1u;
+    work->workletsProcessed = 1u;
 }
 
 }  // namespace
@@ -1061,17 +1060,17 @@
     std::shared_ptr<C2Buffer> buffer = createGraphicBuffer(std::move(mAllocatedBlock));
     auto fillWork = [buffer](const std::unique_ptr<C2Work> &work) {
         uint32_t flags = 0;
-        if (work->input.flags & C2BufferPack::FLAG_END_OF_STREAM) {
-            flags |= C2BufferPack::FLAG_END_OF_STREAM;
+        if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+            flags |= C2FrameData::FLAG_END_OF_STREAM;
             ALOGV("EOS");
         }
-        work->worklets.front()->output.flags = (C2BufferPack::flags_t)flags;
+        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->worklets_processed = 1u;
+        work->workletsProcessed = 1u;
     };
-    if (work && index == work->input.ordinal.frame_index) {
+    if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
         fillWork(work);
     } else {
         finish(index, fillWork);
@@ -1084,25 +1083,25 @@
     bool eos = false;
 
     work->result = C2_OK;
-    work->worklets_processed = 0u;
+    work->workletsProcessed = 0u;
 
     const C2ConstLinearBlock &buffer =
         work->input.buffers[0]->data().linearBlocks().front();
     if (buffer.capacity() == 0) {
-        ALOGV("empty input: %llu", (long long)work->input.ordinal.frame_index);
+        ALOGV("empty input: %llu", work->input.ordinal.frameIndex.peekull());
         // TODO: result?
         fillEmptyWork(work);
-        if ((work->input.flags & C2BufferPack::FLAG_END_OF_STREAM)) {
+        if ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM)) {
             eos = true;
         }
         return;
-    } else if (work->input.flags & C2BufferPack::FLAG_END_OF_STREAM) {
-        ALOGV("input EOS: %llu", (long long)work->input.ordinal.frame_index);
+    } else if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        ALOGV("input EOS: %llu", work->input.ordinal.frameIndex.peekull());
         eos = true;
     }
 
     C2ReadView input = work->input.buffers[0]->data().linearBlocks().front().map().get();
-    uint32_t workIndex = work->input.ordinal.frame_index & 0xFFFFFFFF;
+    uint32_t workIndex = work->input.ordinal.frameIndex.peeku() & 0xFFFFFFFF;
     size_t inOffset = 0u;
 
     while (inOffset < input.capacity()) {
@@ -1266,7 +1265,7 @@
     }
 
     if (drainMode == DRAIN_COMPONENT_WITH_EOS
-            && work && work->worklets_processed == 0u) {
+            && work && work->workletsProcessed == 0u) {
         fillEmptyWork(work);
     }
 
diff --git a/media/libstagefright/codecs/avcenc/Android.bp b/media/libstagefright/codecs/avcenc/Android.bp
index cefe77c..67a2fdc 100644
--- a/media/libstagefright/codecs/avcenc/Android.bp
+++ b/media/libstagefright/codecs/avcenc/Android.bp
@@ -1,4 +1,48 @@
 cc_library_shared {
+    name: "libstagefright_soft_c2avcenc",
+//    vendor_available: true,
+//    vndk: {
+//        enabled: true,
+//    },
+
+    static_libs: [ "libavcenc" ],
+    srcs: ["C2SoftAvcEnc.cpp"],
+
+    include_dirs: [
+        "external/libavc/encoder",
+        "external/libavc/common",
+        "frameworks/av/media/libstagefright/include",
+        "frameworks/native/include/media/hardware",
+    ],
+
+    shared_libs: [
+        "libstagefright_codec2",
+        "libstagefright_codec2_vndk",
+        "libstagefright_foundation",
+        "libstagefright_simple_c2component",
+        "libutils",
+        "liblog",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-variable",
+    ],
+    ldflags: ["-Wl,-Bsymbolic"],
+}
+
+cc_library_shared {
     name: "libstagefright_soft_avcenc",
     vendor_available: true,
     vndk: {
diff --git a/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp b/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp
new file mode 100644
index 0000000..7c281e3
--- /dev/null
+++ b/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp
@@ -0,0 +1,1239 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "C2SoftAvcEncEnc"
+#include <utils/Log.h>
+#include <utils/misc.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include "ih264_typedefs.h"
+#include "ih264e.h"
+#include "ih264e_error.h"
+#include "iv2.h"
+#include "ive2.h"
+#include "C2SoftAvcEnc.h"
+
+namespace android {
+
+#define ive_api_function ih264e_api_function
+
+namespace {
+
+// From external/libavc/encoder/ih264e_bitstream.h
+constexpr uint32_t MIN_STREAM_SIZE = 0x800;
+
+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;
+}
+
+void ConvertRGBToPlanarYUV(
+        uint8_t *dstY, size_t dstStride, size_t dstVStride,
+        const C2GraphicView &src) {
+    CHECK((src.width() & 1) == 0);
+    CHECK((src.height() & 1) == 0);
+
+    uint8_t *dstU = dstY + dstStride * dstVStride;
+    uint8_t *dstV = dstU + (dstStride >> 1) * (dstVStride >> 1);
+
+    const C2PlanarLayout &layout = src.layout();
+    const uint8_t *pRed   = src.data()[C2PlanarLayout::PLANE_R];
+    const uint8_t *pGreen = src.data()[C2PlanarLayout::PLANE_G];
+    const uint8_t *pBlue  = src.data()[C2PlanarLayout::PLANE_B];
+
+    for (size_t y = 0; y < src.height(); ++y) {
+        for (size_t x = 0; x < src.width(); ++x) {
+            unsigned red   = *pRed;
+            unsigned green = *pGreen;
+            unsigned blue  = *pBlue;
+
+            // using ITU-R BT.601 conversion matrix
+            unsigned luma =
+                ((red * 66 + green * 129 + blue * 25) >> 8) + 16;
+
+            dstY[x] = luma;
+
+            if ((x & 1) == 0 && (y & 1) == 0) {
+                unsigned U =
+                    ((-red * 38 - green * 74 + blue * 112) >> 8) + 128;
+
+                unsigned V =
+                    ((red * 112 - green * 94 - blue * 18) >> 8) + 128;
+
+                dstU[x >> 1] = U;
+                dstV[x >> 1] = V;
+            }
+            pRed   += layout.planes[C2PlanarLayout::PLANE_R].colInc;
+            pGreen += layout.planes[C2PlanarLayout::PLANE_G].colInc;
+            pBlue  += layout.planes[C2PlanarLayout::PLANE_B].colInc;
+        }
+
+        if ((y & 1) == 0) {
+            dstU += dstStride >> 1;
+            dstV += dstStride >> 1;
+        }
+
+        pRed   -= layout.planes[C2PlanarLayout::PLANE_R].colInc * src.width();
+        pGreen -= layout.planes[C2PlanarLayout::PLANE_G].colInc * src.width();
+        pBlue  -= layout.planes[C2PlanarLayout::PLANE_B].colInc * src.width();
+        pRed   += layout.planes[C2PlanarLayout::PLANE_R].rowInc;
+        pGreen += layout.planes[C2PlanarLayout::PLANE_G].rowInc;
+        pBlue  += layout.planes[C2PlanarLayout::PLANE_B].rowInc;
+
+        dstY += dstStride;
+    }
+}
+
+}  // namespace
+
+C2SoftAvcEnc::C2SoftAvcEnc(const char *name, c2_node_id_t id)
+    : SimpleC2Component(
+          SimpleC2Interface::Builder(name, id)
+          .inputFormat(C2FormatVideo)
+          .outputFormat(C2FormatCompressed)
+          .build()),
+      mUpdateFlag(0),
+      mIvVideoColorFormat(IV_YUV_420P),
+      mAVCEncProfile(IV_PROFILE_BASE),
+      mAVCEncLevel(41),
+      mStarted(false),
+      mSawInputEOS(false),
+      mSawOutputEOS(false),
+      mSignalledError(false),
+      mCodecCtx(NULL),
+      mWidth(1080),
+      mHeight(1920),
+      mFramerate(60),
+      mBitrate(20000),
+      // TODO: output buffer size
+      mOutBufferSize(524288) {
+
+    // If dump is enabled, then open create an empty file
+    GENERATE_FILE_NAMES();
+    CREATE_DUMP_FILE(mInFile);
+    CREATE_DUMP_FILE(mOutFile);
+
+    initEncParams();
+}
+
+C2SoftAvcEnc::~C2SoftAvcEnc() {
+    releaseEncoder();
+}
+
+c2_status_t C2SoftAvcEnc::onInit() {
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::onStop() {
+    return C2_OK;
+}
+
+void C2SoftAvcEnc::onReset() {
+    // TODO: use IVE_CMD_CTL_RESET?
+    releaseEncoder();
+    initEncParams();
+}
+
+void C2SoftAvcEnc::onRelease() {
+    releaseEncoder();
+}
+
+c2_status_t C2SoftAvcEnc::onFlush_sm() {
+    // TODO: use IVE_CMD_CTL_FLUSH?
+    return C2_OK;
+}
+
+void  C2SoftAvcEnc::initEncParams() {
+    mCodecCtx = NULL;
+    mMemRecords = NULL;
+    mNumMemRecords = DEFAULT_MEM_REC_CNT;
+    mHeaderGenerated = 0;
+    mNumCores = GetCPUCoreCount();
+    mArch = DEFAULT_ARCH;
+    mSliceMode = DEFAULT_SLICE_MODE;
+    mSliceParam = DEFAULT_SLICE_PARAM;
+    mHalfPelEnable = DEFAULT_HPEL;
+    mIInterval = DEFAULT_I_INTERVAL;
+    mIDRInterval = DEFAULT_IDR_INTERVAL;
+    mDisableDeblkLevel = DEFAULT_DISABLE_DEBLK_LEVEL;
+    mEnableFastSad = DEFAULT_ENABLE_FAST_SAD;
+    mEnableAltRef = DEFAULT_ENABLE_ALT_REF;
+    mEncSpeed = DEFAULT_ENC_SPEED;
+    mIntra4x4 = DEFAULT_INTRA4x4;
+    mConstrainedIntraFlag = DEFAULT_CONSTRAINED_INTRA;
+    mAIRMode = DEFAULT_AIR;
+    mAIRRefreshPeriod = DEFAULT_AIR_REFRESH_PERIOD;
+    mPSNREnable = DEFAULT_PSNR_ENABLE;
+    mReconEnable = DEFAULT_RECON_ENABLE;
+    mEntropyMode = DEFAULT_ENTROPY_MODE;
+    mBframes = DEFAULT_B_FRAMES;
+
+    gettimeofday(&mTimeStart, NULL);
+    gettimeofday(&mTimeEnd, NULL);
+}
+
+c2_status_t C2SoftAvcEnc::setDimensions() {
+    ive_ctl_set_dimensions_ip_t s_dimensions_ip;
+    ive_ctl_set_dimensions_op_t s_dimensions_op;
+    IV_STATUS_T status;
+
+    s_dimensions_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_dimensions_ip.e_sub_cmd = IVE_CMD_CTL_SET_DIMENSIONS;
+    s_dimensions_ip.u4_ht = mHeight;
+    s_dimensions_ip.u4_wd = mWidth;
+
+    s_dimensions_ip.u4_timestamp_high = -1;
+    s_dimensions_ip.u4_timestamp_low = -1;
+
+    s_dimensions_ip.u4_size = sizeof(ive_ctl_set_dimensions_ip_t);
+    s_dimensions_op.u4_size = sizeof(ive_ctl_set_dimensions_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_dimensions_ip, &s_dimensions_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set frame dimensions = 0x%x\n",
+                s_dimensions_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setNumCores() {
+    IV_STATUS_T status;
+    ive_ctl_set_num_cores_ip_t s_num_cores_ip;
+    ive_ctl_set_num_cores_op_t s_num_cores_op;
+    s_num_cores_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_num_cores_ip.e_sub_cmd = IVE_CMD_CTL_SET_NUM_CORES;
+    s_num_cores_ip.u4_num_cores = MIN(mNumCores, CODEC_MAX_CORES);
+    s_num_cores_ip.u4_timestamp_high = -1;
+    s_num_cores_ip.u4_timestamp_low = -1;
+    s_num_cores_ip.u4_size = sizeof(ive_ctl_set_num_cores_ip_t);
+
+    s_num_cores_op.u4_size = sizeof(ive_ctl_set_num_cores_op_t);
+
+    status = ive_api_function(
+            mCodecCtx, (void *) &s_num_cores_ip, (void *) &s_num_cores_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set processor params = 0x%x\n",
+                s_num_cores_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setFrameRate() {
+    ive_ctl_set_frame_rate_ip_t s_frame_rate_ip;
+    ive_ctl_set_frame_rate_op_t s_frame_rate_op;
+    IV_STATUS_T status;
+
+    s_frame_rate_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_frame_rate_ip.e_sub_cmd = IVE_CMD_CTL_SET_FRAMERATE;
+
+    s_frame_rate_ip.u4_src_frame_rate = mFramerate;
+    s_frame_rate_ip.u4_tgt_frame_rate = mFramerate;
+
+    s_frame_rate_ip.u4_timestamp_high = -1;
+    s_frame_rate_ip.u4_timestamp_low = -1;
+
+    s_frame_rate_ip.u4_size = sizeof(ive_ctl_set_frame_rate_ip_t);
+    s_frame_rate_op.u4_size = sizeof(ive_ctl_set_frame_rate_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_frame_rate_ip, &s_frame_rate_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set frame rate = 0x%x\n",
+                s_frame_rate_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setIpeParams() {
+    ive_ctl_set_ipe_params_ip_t s_ipe_params_ip;
+    ive_ctl_set_ipe_params_op_t s_ipe_params_op;
+    IV_STATUS_T status;
+
+    s_ipe_params_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_ipe_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_IPE_PARAMS;
+
+    s_ipe_params_ip.u4_enable_intra_4x4 = mIntra4x4;
+    s_ipe_params_ip.u4_enc_speed_preset = mEncSpeed;
+    s_ipe_params_ip.u4_constrained_intra_pred = mConstrainedIntraFlag;
+
+    s_ipe_params_ip.u4_timestamp_high = -1;
+    s_ipe_params_ip.u4_timestamp_low = -1;
+
+    s_ipe_params_ip.u4_size = sizeof(ive_ctl_set_ipe_params_ip_t);
+    s_ipe_params_op.u4_size = sizeof(ive_ctl_set_ipe_params_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_ipe_params_ip, &s_ipe_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set ipe params = 0x%x\n",
+                s_ipe_params_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setBitRate() {
+    ive_ctl_set_bitrate_ip_t s_bitrate_ip;
+    ive_ctl_set_bitrate_op_t s_bitrate_op;
+    IV_STATUS_T status;
+
+    s_bitrate_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_bitrate_ip.e_sub_cmd = IVE_CMD_CTL_SET_BITRATE;
+
+    s_bitrate_ip.u4_target_bitrate = mBitrate;
+
+    s_bitrate_ip.u4_timestamp_high = -1;
+    s_bitrate_ip.u4_timestamp_low = -1;
+
+    s_bitrate_ip.u4_size = sizeof(ive_ctl_set_bitrate_ip_t);
+    s_bitrate_op.u4_size = sizeof(ive_ctl_set_bitrate_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_bitrate_ip, &s_bitrate_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set bit rate = 0x%x\n", s_bitrate_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setFrameType(IV_PICTURE_CODING_TYPE_T e_frame_type) {
+    ive_ctl_set_frame_type_ip_t s_frame_type_ip;
+    ive_ctl_set_frame_type_op_t s_frame_type_op;
+    IV_STATUS_T status;
+    s_frame_type_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_frame_type_ip.e_sub_cmd = IVE_CMD_CTL_SET_FRAMETYPE;
+
+    s_frame_type_ip.e_frame_type = e_frame_type;
+
+    s_frame_type_ip.u4_timestamp_high = -1;
+    s_frame_type_ip.u4_timestamp_low = -1;
+
+    s_frame_type_ip.u4_size = sizeof(ive_ctl_set_frame_type_ip_t);
+    s_frame_type_op.u4_size = sizeof(ive_ctl_set_frame_type_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_frame_type_ip, &s_frame_type_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set frame type = 0x%x\n",
+                s_frame_type_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setQp() {
+    ive_ctl_set_qp_ip_t s_qp_ip;
+    ive_ctl_set_qp_op_t s_qp_op;
+    IV_STATUS_T status;
+
+    s_qp_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_qp_ip.e_sub_cmd = IVE_CMD_CTL_SET_QP;
+
+    s_qp_ip.u4_i_qp = DEFAULT_I_QP;
+    s_qp_ip.u4_i_qp_max = DEFAULT_QP_MAX;
+    s_qp_ip.u4_i_qp_min = DEFAULT_QP_MIN;
+
+    s_qp_ip.u4_p_qp = DEFAULT_P_QP;
+    s_qp_ip.u4_p_qp_max = DEFAULT_QP_MAX;
+    s_qp_ip.u4_p_qp_min = DEFAULT_QP_MIN;
+
+    s_qp_ip.u4_b_qp = DEFAULT_P_QP;
+    s_qp_ip.u4_b_qp_max = DEFAULT_QP_MAX;
+    s_qp_ip.u4_b_qp_min = DEFAULT_QP_MIN;
+
+    s_qp_ip.u4_timestamp_high = -1;
+    s_qp_ip.u4_timestamp_low = -1;
+
+    s_qp_ip.u4_size = sizeof(ive_ctl_set_qp_ip_t);
+    s_qp_op.u4_size = sizeof(ive_ctl_set_qp_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_qp_ip, &s_qp_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set qp 0x%x\n", s_qp_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setEncMode(IVE_ENC_MODE_T e_enc_mode) {
+    IV_STATUS_T status;
+    ive_ctl_set_enc_mode_ip_t s_enc_mode_ip;
+    ive_ctl_set_enc_mode_op_t s_enc_mode_op;
+
+    s_enc_mode_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_enc_mode_ip.e_sub_cmd = IVE_CMD_CTL_SET_ENC_MODE;
+
+    s_enc_mode_ip.e_enc_mode = e_enc_mode;
+
+    s_enc_mode_ip.u4_timestamp_high = -1;
+    s_enc_mode_ip.u4_timestamp_low = -1;
+
+    s_enc_mode_ip.u4_size = sizeof(ive_ctl_set_enc_mode_ip_t);
+    s_enc_mode_op.u4_size = sizeof(ive_ctl_set_enc_mode_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_enc_mode_ip, &s_enc_mode_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set in header encode mode = 0x%x\n",
+                s_enc_mode_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setVbvParams() {
+    ive_ctl_set_vbv_params_ip_t s_vbv_ip;
+    ive_ctl_set_vbv_params_op_t s_vbv_op;
+    IV_STATUS_T status;
+
+    s_vbv_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_vbv_ip.e_sub_cmd = IVE_CMD_CTL_SET_VBV_PARAMS;
+
+    s_vbv_ip.u4_vbv_buf_size = 0;
+    s_vbv_ip.u4_vbv_buffer_delay = 1000;
+
+    s_vbv_ip.u4_timestamp_high = -1;
+    s_vbv_ip.u4_timestamp_low = -1;
+
+    s_vbv_ip.u4_size = sizeof(ive_ctl_set_vbv_params_ip_t);
+    s_vbv_op.u4_size = sizeof(ive_ctl_set_vbv_params_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_vbv_ip, &s_vbv_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set VBV params = 0x%x\n", s_vbv_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setAirParams() {
+    ive_ctl_set_air_params_ip_t s_air_ip;
+    ive_ctl_set_air_params_op_t s_air_op;
+    IV_STATUS_T status;
+
+    s_air_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_air_ip.e_sub_cmd = IVE_CMD_CTL_SET_AIR_PARAMS;
+
+    s_air_ip.e_air_mode = mAIRMode;
+    s_air_ip.u4_air_refresh_period = mAIRRefreshPeriod;
+
+    s_air_ip.u4_timestamp_high = -1;
+    s_air_ip.u4_timestamp_low = -1;
+
+    s_air_ip.u4_size = sizeof(ive_ctl_set_air_params_ip_t);
+    s_air_op.u4_size = sizeof(ive_ctl_set_air_params_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_air_ip, &s_air_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set air params = 0x%x\n", s_air_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setMeParams() {
+    IV_STATUS_T status;
+    ive_ctl_set_me_params_ip_t s_me_params_ip;
+    ive_ctl_set_me_params_op_t s_me_params_op;
+
+    s_me_params_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_me_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_ME_PARAMS;
+
+    s_me_params_ip.u4_enable_fast_sad = mEnableFastSad;
+    s_me_params_ip.u4_enable_alt_ref = mEnableAltRef;
+
+    s_me_params_ip.u4_enable_hpel = mHalfPelEnable;
+    s_me_params_ip.u4_enable_qpel = DEFAULT_QPEL;
+    s_me_params_ip.u4_me_speed_preset = DEFAULT_ME_SPEED;
+    s_me_params_ip.u4_srch_rng_x = DEFAULT_SRCH_RNG_X;
+    s_me_params_ip.u4_srch_rng_y = DEFAULT_SRCH_RNG_Y;
+
+    s_me_params_ip.u4_timestamp_high = -1;
+    s_me_params_ip.u4_timestamp_low = -1;
+
+    s_me_params_ip.u4_size = sizeof(ive_ctl_set_me_params_ip_t);
+    s_me_params_op.u4_size = sizeof(ive_ctl_set_me_params_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_me_params_ip, &s_me_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set me params = 0x%x\n", s_me_params_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setGopParams() {
+    IV_STATUS_T status;
+    ive_ctl_set_gop_params_ip_t s_gop_params_ip;
+    ive_ctl_set_gop_params_op_t s_gop_params_op;
+
+    s_gop_params_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_gop_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_GOP_PARAMS;
+
+    s_gop_params_ip.u4_i_frm_interval = mIInterval;
+    s_gop_params_ip.u4_idr_frm_interval = mIDRInterval;
+
+    s_gop_params_ip.u4_timestamp_high = -1;
+    s_gop_params_ip.u4_timestamp_low = -1;
+
+    s_gop_params_ip.u4_size = sizeof(ive_ctl_set_gop_params_ip_t);
+    s_gop_params_op.u4_size = sizeof(ive_ctl_set_gop_params_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_gop_params_ip, &s_gop_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set GOP params = 0x%x\n",
+                s_gop_params_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setProfileParams() {
+    IV_STATUS_T status;
+    ive_ctl_set_profile_params_ip_t s_profile_params_ip;
+    ive_ctl_set_profile_params_op_t s_profile_params_op;
+
+    s_profile_params_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_profile_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_PROFILE_PARAMS;
+
+    s_profile_params_ip.e_profile = DEFAULT_EPROFILE;
+    s_profile_params_ip.u4_entropy_coding_mode = mEntropyMode;
+    s_profile_params_ip.u4_timestamp_high = -1;
+    s_profile_params_ip.u4_timestamp_low = -1;
+
+    s_profile_params_ip.u4_size = sizeof(ive_ctl_set_profile_params_ip_t);
+    s_profile_params_op.u4_size = sizeof(ive_ctl_set_profile_params_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_profile_params_ip, &s_profile_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to set profile params = 0x%x\n",
+                s_profile_params_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setDeblockParams() {
+    IV_STATUS_T status;
+    ive_ctl_set_deblock_params_ip_t s_deblock_params_ip;
+    ive_ctl_set_deblock_params_op_t s_deblock_params_op;
+
+    s_deblock_params_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_deblock_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_DEBLOCK_PARAMS;
+
+    s_deblock_params_ip.u4_disable_deblock_level = mDisableDeblkLevel;
+
+    s_deblock_params_ip.u4_timestamp_high = -1;
+    s_deblock_params_ip.u4_timestamp_low = -1;
+
+    s_deblock_params_ip.u4_size = sizeof(ive_ctl_set_deblock_params_ip_t);
+    s_deblock_params_op.u4_size = sizeof(ive_ctl_set_deblock_params_op_t);
+
+    status = ive_api_function(mCodecCtx, &s_deblock_params_ip, &s_deblock_params_op);
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to enable/disable deblock params = 0x%x\n",
+                s_deblock_params_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+    return C2_OK;
+}
+
+void C2SoftAvcEnc::logVersion() {
+    ive_ctl_getversioninfo_ip_t s_ctl_ip;
+    ive_ctl_getversioninfo_op_t s_ctl_op;
+    UWORD8 au1_buf[512];
+    IV_STATUS_T status;
+
+    s_ctl_ip.e_cmd = IVE_CMD_VIDEO_CTL;
+    s_ctl_ip.e_sub_cmd = IVE_CMD_CTL_GETVERSION;
+    s_ctl_ip.u4_size = sizeof(ive_ctl_getversioninfo_ip_t);
+    s_ctl_op.u4_size = sizeof(ive_ctl_getversioninfo_op_t);
+    s_ctl_ip.pu1_version = au1_buf;
+    s_ctl_ip.u4_version_bufsize = sizeof(au1_buf);
+
+    status = ive_api_function(mCodecCtx, (void *) &s_ctl_ip, (void *) &s_ctl_op);
+
+    if (status != IV_SUCCESS) {
+        ALOGE("Error in getting version: 0x%x", s_ctl_op.u4_error_code);
+    } else {
+        ALOGV("Ittiam encoder version: %s", (char *)s_ctl_ip.pu1_version);
+    }
+    return;
+}
+
+c2_status_t C2SoftAvcEnc::initEncoder() {
+    IV_STATUS_T status;
+    WORD32 level;
+    uint32_t displaySizeY;
+
+    CHECK(!mStarted);
+
+    c2_status_t errType = C2_OK;
+
+    displaySizeY = mWidth * mHeight;
+    if (displaySizeY > (1920 * 1088)) {
+        level = 50;
+    } else if (displaySizeY > (1280 * 720)) {
+        level = 40;
+    } else if (displaySizeY > (720 * 576)) {
+        level = 31;
+    } else if (displaySizeY > (624 * 320)) {
+        level = 30;
+    } else if (displaySizeY > (352 * 288)) {
+        level = 21;
+    } else if (displaySizeY > (176 * 144)) {
+        level = 20;
+    } else {
+        level = 10;
+    }
+    mAVCEncLevel = MAX(level, mAVCEncLevel);
+
+    mStride = mWidth;
+
+    // TODO
+    mIvVideoColorFormat = IV_YUV_420P;
+
+    ALOGD("Params width %d height %d level %d colorFormat %d", mWidth,
+            mHeight, mAVCEncLevel, mIvVideoColorFormat);
+
+    /* Getting Number of MemRecords */
+    {
+        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(iv_num_mem_rec_ip_t);
+        s_num_mem_rec_op.u4_size = sizeof(iv_num_mem_rec_op_t);
+
+        s_num_mem_rec_ip.e_cmd = IV_CMD_GET_NUM_MEM_REC;
+
+        status = ive_api_function(0, &s_num_mem_rec_ip, &s_num_mem_rec_op);
+
+        if (status != IV_SUCCESS) {
+            ALOGE("Get number of memory records failed = 0x%x\n",
+                    s_num_mem_rec_op.u4_error_code);
+            return C2_CORRUPTED;
+        }
+
+        mNumMemRecords = s_num_mem_rec_op.u4_num_mem_rec;
+    }
+
+    /* Allocate array to hold memory records */
+    if (mNumMemRecords > SIZE_MAX / sizeof(iv_mem_rec_t)) {
+        ALOGE("requested memory size is too big.");
+        return C2_CORRUPTED;
+    }
+    mMemRecords = (iv_mem_rec_t *)malloc(mNumMemRecords * sizeof(iv_mem_rec_t));
+    if (NULL == mMemRecords) {
+        ALOGE("Unable to allocate memory for hold memory records: Size %zu",
+                mNumMemRecords * sizeof(iv_mem_rec_t));
+        mSignalledError = true;
+        // TODO: notify(error, C2_CORRUPTED, 0, 0);
+        return C2_CORRUPTED;
+    }
+
+    {
+        iv_mem_rec_t *ps_mem_rec;
+        ps_mem_rec = mMemRecords;
+        for (size_t i = 0; i < mNumMemRecords; i++) {
+            ps_mem_rec->u4_size = sizeof(iv_mem_rec_t);
+            ps_mem_rec->pv_base = NULL;
+            ps_mem_rec->u4_mem_size = 0;
+            ps_mem_rec->u4_mem_alignment = 0;
+            ps_mem_rec->e_mem_type = IV_NA_MEM_TYPE;
+
+            ps_mem_rec++;
+        }
+    }
+
+    /* Getting MemRecords Attributes */
+    {
+        iv_fill_mem_rec_ip_t s_fill_mem_rec_ip;
+        iv_fill_mem_rec_op_t s_fill_mem_rec_op;
+
+        s_fill_mem_rec_ip.u4_size = sizeof(iv_fill_mem_rec_ip_t);
+        s_fill_mem_rec_op.u4_size = sizeof(iv_fill_mem_rec_op_t);
+
+        s_fill_mem_rec_ip.e_cmd = IV_CMD_FILL_NUM_MEM_REC;
+        s_fill_mem_rec_ip.ps_mem_rec = mMemRecords;
+        s_fill_mem_rec_ip.u4_num_mem_rec = mNumMemRecords;
+        s_fill_mem_rec_ip.u4_max_wd = mWidth;
+        s_fill_mem_rec_ip.u4_max_ht = mHeight;
+        s_fill_mem_rec_ip.u4_max_level = mAVCEncLevel;
+        s_fill_mem_rec_ip.e_color_format = DEFAULT_INP_COLOR_FORMAT;
+        s_fill_mem_rec_ip.u4_max_ref_cnt = DEFAULT_MAX_REF_FRM;
+        s_fill_mem_rec_ip.u4_max_reorder_cnt = DEFAULT_MAX_REORDER_FRM;
+        s_fill_mem_rec_ip.u4_max_srch_rng_x = DEFAULT_MAX_SRCH_RANGE_X;
+        s_fill_mem_rec_ip.u4_max_srch_rng_y = DEFAULT_MAX_SRCH_RANGE_Y;
+
+        status = ive_api_function(0, &s_fill_mem_rec_ip, &s_fill_mem_rec_op);
+
+        if (status != IV_SUCCESS) {
+            ALOGE("Fill memory records failed = 0x%x\n",
+                    s_fill_mem_rec_op.u4_error_code);
+            mSignalledError = true;
+            // TODO: notify(error, C2_CORRUPTED, 0, 0);
+            return C2_CORRUPTED;
+        }
+    }
+
+    /* Allocating Memory for Mem Records */
+    {
+        WORD32 total_size;
+        iv_mem_rec_t *ps_mem_rec;
+        total_size = 0;
+        ps_mem_rec = mMemRecords;
+
+        for (size_t i = 0; i < mNumMemRecords; i++) {
+            ps_mem_rec->pv_base = ive_aligned_malloc(
+                    ps_mem_rec->u4_mem_alignment, ps_mem_rec->u4_mem_size);
+            if (ps_mem_rec->pv_base == NULL) {
+                ALOGE("Allocation failure for mem record id %zu size %u\n", i,
+                        ps_mem_rec->u4_mem_size);
+                mSignalledError = true;
+                // TODO: notify(error, C2_CORRUPTED, 0, 0);
+                return C2_CORRUPTED;
+
+            }
+            total_size += ps_mem_rec->u4_mem_size;
+
+            ps_mem_rec++;
+        }
+    }
+
+    /* Codec Instance Creation */
+    {
+        ive_init_ip_t s_init_ip;
+        ive_init_op_t s_init_op;
+
+        mCodecCtx = (iv_obj_t *)mMemRecords[0].pv_base;
+        mCodecCtx->u4_size = sizeof(iv_obj_t);
+        mCodecCtx->pv_fxns = (void *)ive_api_function;
+
+        s_init_ip.u4_size = sizeof(ive_init_ip_t);
+        s_init_op.u4_size = sizeof(ive_init_op_t);
+
+        s_init_ip.e_cmd = IV_CMD_INIT;
+        s_init_ip.u4_num_mem_rec = mNumMemRecords;
+        s_init_ip.ps_mem_rec = mMemRecords;
+        s_init_ip.u4_max_wd = mWidth;
+        s_init_ip.u4_max_ht = mHeight;
+        s_init_ip.u4_max_ref_cnt = DEFAULT_MAX_REF_FRM;
+        s_init_ip.u4_max_reorder_cnt = DEFAULT_MAX_REORDER_FRM;
+        s_init_ip.u4_max_level = mAVCEncLevel;
+        s_init_ip.e_inp_color_fmt = mIvVideoColorFormat;
+
+        if (mReconEnable || mPSNREnable) {
+            s_init_ip.u4_enable_recon = 1;
+        } else {
+            s_init_ip.u4_enable_recon = 0;
+        }
+        s_init_ip.e_recon_color_fmt = DEFAULT_RECON_COLOR_FORMAT;
+        s_init_ip.e_rc_mode = DEFAULT_RC_MODE;
+        s_init_ip.u4_max_framerate = DEFAULT_MAX_FRAMERATE;
+        s_init_ip.u4_max_bitrate = DEFAULT_MAX_BITRATE;
+        s_init_ip.u4_num_bframes = mBframes;
+        s_init_ip.e_content_type = IV_PROGRESSIVE;
+        s_init_ip.u4_max_srch_rng_x = DEFAULT_MAX_SRCH_RANGE_X;
+        s_init_ip.u4_max_srch_rng_y = DEFAULT_MAX_SRCH_RANGE_Y;
+        s_init_ip.e_slice_mode = mSliceMode;
+        s_init_ip.u4_slice_param = mSliceParam;
+        s_init_ip.e_arch = mArch;
+        s_init_ip.e_soc = DEFAULT_SOC;
+
+        status = ive_api_function(mCodecCtx, &s_init_ip, &s_init_op);
+
+        if (status != IV_SUCCESS) {
+            ALOGE("Init encoder failed = 0x%x\n", s_init_op.u4_error_code);
+            mSignalledError = true;
+            // TODO: notify(error, C2_CORRUPTED, 0 /* arg2 */, NULL /* data */);
+            return C2_CORRUPTED;
+        }
+    }
+
+    /* Get Codec Version */
+    logVersion();
+
+    /* set processor details */
+    setNumCores();
+
+    /* Video control Set Frame dimensions */
+    setDimensions();
+
+    /* Video control Set Frame rates */
+    setFrameRate();
+
+    /* Video control Set IPE Params */
+    setIpeParams();
+
+    /* Video control Set Bitrate */
+    setBitRate();
+
+    /* Video control Set QP */
+    setQp();
+
+    /* Video control Set AIR params */
+    setAirParams();
+
+    /* Video control Set VBV params */
+    setVbvParams();
+
+    /* Video control Set Motion estimation params */
+    setMeParams();
+
+    /* Video control Set GOP params */
+    setGopParams();
+
+    /* Video control Set Deblock params */
+    setDeblockParams();
+
+    /* Video control Set Profile params */
+    setProfileParams();
+
+    /* Video control Set in Encode header mode */
+    setEncMode(IVE_ENC_MODE_HEADER);
+
+    ALOGV("init_codec successfull");
+
+    mSpsPpsHeaderReceived = false;
+    mStarted = true;
+
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::releaseEncoder() {
+    IV_STATUS_T status = IV_SUCCESS;
+    iv_retrieve_mem_rec_ip_t s_retrieve_mem_ip;
+    iv_retrieve_mem_rec_op_t s_retrieve_mem_op;
+    iv_mem_rec_t *ps_mem_rec;
+
+    if (!mStarted) {
+        return C2_OK;
+    }
+
+    s_retrieve_mem_ip.u4_size = sizeof(iv_retrieve_mem_rec_ip_t);
+    s_retrieve_mem_op.u4_size = sizeof(iv_retrieve_mem_rec_op_t);
+    s_retrieve_mem_ip.e_cmd = IV_CMD_RETRIEVE_MEMREC;
+    s_retrieve_mem_ip.ps_mem_rec = mMemRecords;
+
+    status = ive_api_function(mCodecCtx, &s_retrieve_mem_ip, &s_retrieve_mem_op);
+
+    if (status != IV_SUCCESS) {
+        ALOGE("Unable to retrieve memory records = 0x%x\n",
+                s_retrieve_mem_op.u4_error_code);
+        return C2_CORRUPTED;
+    }
+
+    /* Free memory records */
+    ps_mem_rec = mMemRecords;
+    for (size_t i = 0; i < s_retrieve_mem_op.u4_num_mem_rec_filled; i++) {
+        ive_aligned_free(ps_mem_rec->pv_base);
+        ps_mem_rec++;
+    }
+
+    free(mMemRecords);
+
+    // clear other pointers into the space being free()d
+    mCodecCtx = NULL;
+
+    mStarted = false;
+
+    return C2_OK;
+}
+
+c2_status_t C2SoftAvcEnc::setEncodeArgs(
+        ive_video_encode_ip_t *ps_encode_ip,
+        ive_video_encode_op_t *ps_encode_op,
+        const C2GraphicView *const input,
+        uint8_t *base,
+        uint32_t capacity,
+        uint64_t timestamp) {
+    iv_raw_buf_t *ps_inp_raw_buf;
+
+    ps_inp_raw_buf = &ps_encode_ip->s_inp_buf;
+    ps_encode_ip->s_out_buf.pv_buf = base;
+    ps_encode_ip->s_out_buf.u4_bytes = 0;
+    ps_encode_ip->s_out_buf.u4_bufsize = capacity;
+    ps_encode_ip->u4_size = sizeof(ive_video_encode_ip_t);
+    ps_encode_op->u4_size = sizeof(ive_video_encode_op_t);
+
+    ps_encode_ip->e_cmd = IVE_CMD_VIDEO_ENCODE;
+    ps_encode_ip->pv_bufs = NULL;
+    ps_encode_ip->pv_mb_info = NULL;
+    ps_encode_ip->pv_pic_info = NULL;
+    ps_encode_ip->u4_mb_info_type = 0;
+    ps_encode_ip->u4_pic_info_type = 0;
+    ps_encode_ip->u4_is_last = 0;
+    ps_encode_ip->u4_timestamp_high = timestamp >> 32;
+    ps_encode_ip->u4_timestamp_low = timestamp & 0xFFFFFFFF;
+    ps_encode_op->s_out_buf.pv_buf = NULL;
+
+    /* Initialize color formats */
+    memset(ps_inp_raw_buf, 0, sizeof(iv_raw_buf_t));
+    ps_inp_raw_buf->u4_size = sizeof(iv_raw_buf_t);
+    ps_inp_raw_buf->e_color_fmt = mIvVideoColorFormat;
+    if (input == nullptr) {
+        if (mSawInputEOS){
+            ps_encode_ip->u4_is_last = 1;
+        }
+        return C2_OK;
+    }
+
+    ALOGV("width = %d, height = %d", input->width(), input->height());
+    const C2PlanarLayout &layout = input->layout();
+    uint8_t *yPlane = const_cast<uint8_t *>(input->data()[C2PlanarLayout::PLANE_Y]);
+    uint8_t *uPlane = const_cast<uint8_t *>(input->data()[C2PlanarLayout::PLANE_U]);
+    uint8_t *vPlane = const_cast<uint8_t *>(input->data()[C2PlanarLayout::PLANE_V]);
+    int32_t yStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+    int32_t uStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc;
+    int32_t vStride = layout.planes[C2PlanarLayout::PLANE_V].rowInc;
+
+    switch (layout.type) {
+        case C2PlanarLayout::TYPE_RGB:
+            // fall-through
+        case C2PlanarLayout::TYPE_RGBA: {
+            size_t yPlaneSize = input->width() * input->height();
+            std::unique_ptr<uint8_t[]> freeBuffer;
+            if (mFreeConversionBuffers.empty()) {
+                freeBuffer.reset(new uint8_t[yPlaneSize * 3 / 2]);
+            } else {
+                freeBuffer.swap(mFreeConversionBuffers.front());
+                mFreeConversionBuffers.pop_front();
+            }
+            yPlane = freeBuffer.get();
+            mConversionBuffersInUse.push_back(std::move(freeBuffer));
+            uPlane = yPlane + yPlaneSize;
+            vPlane = uPlane + yPlaneSize / 4;
+            yStride = input->width();
+            uStride = vStride = input->width() / 2;
+            ConvertRGBToPlanarYUV(yPlane, yStride, input->height(), *input);
+            break;
+        }
+        case C2PlanarLayout::TYPE_YUV:
+            // fall-through
+        case C2PlanarLayout::TYPE_YUVA:
+            // Do nothing
+            break;
+        default:
+            ALOGE("Unrecognized plane type: %d", layout.type);
+            return C2_BAD_VALUE;
+    }
+
+    switch (mIvVideoColorFormat) {
+        case IV_YUV_420P:
+        {
+            // input buffer is supposed to be const but Ittiam API wants bare pointer.
+            ps_inp_raw_buf->apv_bufs[0] = yPlane;
+            ps_inp_raw_buf->apv_bufs[1] = uPlane;
+            ps_inp_raw_buf->apv_bufs[2] = vPlane;
+
+            ps_inp_raw_buf->au4_wd[0] = input->width();
+            ps_inp_raw_buf->au4_wd[1] = input->width() / 2;
+            ps_inp_raw_buf->au4_wd[2] = input->width() / 2;
+
+            ps_inp_raw_buf->au4_ht[0] = input->height();
+            ps_inp_raw_buf->au4_ht[1] = input->height() / 2;
+            ps_inp_raw_buf->au4_ht[2] = input->height() / 2;
+
+            ps_inp_raw_buf->au4_strd[0] = yStride;
+            ps_inp_raw_buf->au4_strd[1] = uStride;
+            ps_inp_raw_buf->au4_strd[2] = vStride;
+            break;
+        }
+
+        case IV_YUV_422ILE:
+        {
+            // TODO
+            // ps_inp_raw_buf->apv_bufs[0] = pu1_buf;
+            // ps_inp_raw_buf->au4_wd[0] = mWidth * 2;
+            // ps_inp_raw_buf->au4_ht[0] = mHeight;
+            // ps_inp_raw_buf->au4_strd[0] = mStride * 2;
+            break;
+        }
+
+        case IV_YUV_420SP_UV:
+        case IV_YUV_420SP_VU:
+        default:
+        {
+            ps_inp_raw_buf->apv_bufs[0] = yPlane;
+            ps_inp_raw_buf->apv_bufs[1] = uPlane;
+
+            ps_inp_raw_buf->au4_wd[0] = input->width();
+            ps_inp_raw_buf->au4_wd[1] = input->width();
+
+            ps_inp_raw_buf->au4_ht[0] = input->height();
+            ps_inp_raw_buf->au4_ht[1] = input->height() / 2;
+
+            ps_inp_raw_buf->au4_strd[0] = yStride;
+            ps_inp_raw_buf->au4_strd[1] = uStride;
+            break;
+        }
+    }
+    return C2_OK;
+}
+
+void C2SoftAvcEnc::process(
+        const std::unique_ptr<C2Work> &work,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    work->workletsProcessed = 0u;
+
+    IV_STATUS_T status;
+    WORD32 timeDelay, timeTaken;
+    uint64_t timestamp = work->input.ordinal.timestamp.peekull();
+
+    // Initialize encoder if not already initialized
+    if (mCodecCtx == NULL) {
+        if (C2_OK != initEncoder()) {
+            ALOGE("Failed to initialize encoder");
+            // TODO: notify(error, C2_CORRUPTED, 0 /* arg2 */, NULL /* data */);
+            return;
+        }
+    }
+    if (mSignalledError) {
+        return;
+    }
+
+    // while (!mSawOutputEOS && !outQueue.empty()) {
+    c2_status_t error;
+    ive_video_encode_ip_t s_encode_ip;
+    ive_video_encode_op_t s_encode_op;
+
+    if (!mSpsPpsHeaderReceived) {
+        constexpr uint32_t kHeaderLength = MIN_STREAM_SIZE;
+        uint8_t header[kHeaderLength];
+        error = setEncodeArgs(
+                &s_encode_ip, &s_encode_op, NULL, header, kHeaderLength, timestamp);
+        if (error != C2_OK) {
+            mSignalledError = true;
+            // TODO: notify(error, C2_CORRUPTED, 0, 0);
+            return;
+        }
+        status = ive_api_function(mCodecCtx, &s_encode_ip, &s_encode_op);
+
+        if (IV_SUCCESS != status) {
+            ALOGE("Encode header failed = 0x%x\n",
+                    s_encode_op.u4_error_code);
+            return;
+        } else {
+            ALOGV("Bytes Generated in header %d\n",
+                    s_encode_op.s_out_buf.u4_bytes);
+        }
+
+        mSpsPpsHeaderReceived = true;
+
+        std::unique_ptr<C2StreamCsdInfo::output> csd =
+            C2StreamCsdInfo::output::alloc_unique(s_encode_op.s_out_buf.u4_bytes, 0u);
+        memcpy(csd->m.value, header, s_encode_op.s_out_buf.u4_bytes);
+        work->worklets.front()->output.configUpdate.push_back(std::move(csd));
+
+        DUMP_TO_FILE(
+                mOutFile, csd->m.value, csd->flexCount());
+    }
+
+    if (mUpdateFlag) {
+        if (mUpdateFlag & kUpdateBitrate) {
+            setBitRate();
+        }
+        if (mUpdateFlag & kRequestKeyFrame) {
+            setFrameType(IV_IDR_FRAME);
+        }
+        if (mUpdateFlag & kUpdateAIRMode) {
+            setAirParams();
+            // notify(OMX_EventPortSettingsChanged, kOutputPortIndex,
+            //         OMX_IndexConfigAndroidIntraRefresh, NULL);
+        }
+        mUpdateFlag = 0;
+    }
+
+    if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+        mSawInputEOS = true;
+    }
+
+    /* In normal mode, store inputBufferInfo and this will be returned
+       when encoder consumes this input */
+    // if (!mInputDataIsMeta && (inputBufferInfo != NULL)) {
+    //     for (size_t i = 0; i < MAX_INPUT_BUFFER_HEADERS; i++) {
+    //         if (NULL == mInputBufferInfo[i]) {
+    //             mInputBufferInfo[i] = inputBufferInfo;
+    //             break;
+    //         }
+    //     }
+    // }
+    const C2GraphicView view =
+        work->input.buffers[0]->data().graphicBlocks().front().map().get();
+    if (view.error() != C2_OK) {
+        ALOGE("graphic view map err = %d", view.error());
+        return;
+    }
+
+    std::shared_ptr<C2LinearBlock> block;
+
+    do {
+        C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+        // TODO: error handling, proper usage, etc.
+        c2_status_t err = pool->fetchLinearBlock(mOutBufferSize, usage, &block);
+        if (err != C2_OK) {
+            ALOGE("fetch linear block err = %d", err);
+            return;
+        }
+        C2WriteView wView = block->map().get();
+        if (wView.error() != C2_OK) {
+            ALOGE("write view map err = %d", wView.error());
+            return;
+        }
+
+        error = setEncodeArgs(
+                &s_encode_ip, &s_encode_op, &view, wView.base(), wView.capacity(), timestamp);
+        if (error != C2_OK) {
+            mSignalledError = true;
+            ALOGE("setEncodeArgs failed : %d", error);
+            // TODO: notify(error, C2_CORRUPTED, 0, 0);
+            return;
+        }
+
+        // DUMP_TO_FILE(
+        //         mInFile, s_encode_ip.s_inp_buf.apv_bufs[0],
+        //         (mHeight * mStride * 3 / 2));
+
+        GETTIME(&mTimeStart, NULL);
+        /* Compute time elapsed between end of previous decode()
+         * to start of current decode() */
+        TIME_DIFF(mTimeEnd, mTimeStart, timeDelay);
+        status = ive_api_function(mCodecCtx, &s_encode_ip, &s_encode_op);
+
+        if (IV_SUCCESS != status) {
+            if ((s_encode_op.u4_error_code & 0xFF) == IH264E_BITSTREAM_BUFFER_OVERFLOW) {
+                // TODO: use IVE_CMD_CTL_GETBUFINFO for proper max input size?
+                mOutBufferSize *= 2;
+                continue;
+            }
+            ALOGE("Encode Frame failed = 0x%x\n",
+                    s_encode_op.u4_error_code);
+            mSignalledError = true;
+            // TODO: notify(error, C2_CORRUPTED, 0, 0);
+            return;
+        }
+    } while (IV_SUCCESS != status);
+
+    // Hold input buffer reference
+    mBuffers[s_encode_ip.s_inp_buf.apv_bufs[0]] = work->input.buffers[0];
+
+    GETTIME(&mTimeEnd, NULL);
+    /* Compute time taken for decode() */
+    TIME_DIFF(mTimeStart, mTimeEnd, timeTaken);
+
+    ALOGV("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay,
+            s_encode_op.s_out_buf.u4_bytes);
+
+    void *freed = s_encode_op.s_inp_buf.apv_bufs[0];
+    /* If encoder frees up an input buffer, mark it as free */
+    if (freed != NULL) {
+        if (mBuffers.count(freed) == 0u) {
+            // TODO: error
+            return;
+        }
+        // Release input buffer reference
+        mBuffers.erase(freed);
+
+        auto it = std::find_if(
+                mConversionBuffersInUse.begin(), mConversionBuffersInUse.end(),
+                [freed](const auto &elem) { return elem.get() == freed; });
+        if (it != mConversionBuffersInUse.end()) {
+            mFreeConversionBuffers.push_back(std::move(*it));
+            mConversionBuffersInUse.erase(it);
+        }
+    }
+
+    work->worklets.front()->output.flags = work->input.flags;
+    work->worklets.front()->output.ordinal = work->input.ordinal;
+    work->worklets.front()->output.ordinal.timestamp =
+        ((uint64_t)s_encode_op.u4_timestamp_high << 32) | s_encode_op.u4_timestamp_low;
+    work->worklets.front()->output.buffers.clear();
+    std::shared_ptr<C2Buffer> buffer =
+        createLinearBuffer(block, 0, s_encode_op.s_out_buf.u4_bytes);
+    work->worklets.front()->output.buffers.push_back(buffer);
+    work->workletsProcessed = 1u;
+
+    if (IV_IDR_FRAME == s_encode_op.u4_encoded_frame_type) {
+        buffer->setInfo(std::make_shared<C2StreamPictureTypeMaskInfo::output>(
+                0u /* stream id */, C2PictureTypeKeyFrame));
+    }
+
+    if (s_encode_op.u4_is_last) {
+        // outputBufferHeader->nFlags |= OMX_BUFFERFLAG_EOS;
+        mSawOutputEOS = true;
+    } else {
+        // outputBufferHeader->nFlags &= ~OMX_BUFFERFLAG_EOS;
+    }
+}
+
+c2_status_t C2SoftAvcEnc::drain(
+        uint32_t drainMode,
+        const std::shared_ptr<C2BlockPool> &pool) {
+    // TODO: use IVE_CMD_CTL_FLUSH?
+    (void)drainMode;
+    (void)pool;
+    return C2_OK;
+}
+
+
+class C2SoftAvcEncFactory : public C2ComponentFactory {
+public:
+    virtual c2_status_t createComponent(
+            c2_node_id_t id, std::shared_ptr<C2Component>* const component,
+            std::function<void(::android::C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(new C2SoftAvcEnc("avcenc", id), deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(::android::C2ComponentInterface*)> deleter) override {
+        *interface =
+              SimpleC2Interface::Builder("avcenc", id, deleter)
+              .inputFormat(C2FormatVideo)
+              .outputFormat(C2FormatCompressed)
+              .build();
+        return C2_OK;
+    }
+
+    virtual ~C2SoftAvcEncFactory() override = default;
+};
+
+}  // namespace android
+
+extern "C" ::android::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftAvcEncFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::android::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
diff --git a/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.h b/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.h
new file mode 100644
index 0000000..a113436
--- /dev/null
+++ b/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.h
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef C2_SOFT_AVC_ENC_H__
+#define C2_SOFT_AVC_ENC_H__
+
+#include <map>
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Vector.h>
+
+#include <SimpleC2Component.h>
+
+namespace android {
+
+#define CODEC_MAX_CORES          4
+#define LEN_STATUS_BUFFER        (10  * 1024)
+#define MAX_VBV_BUFF_SIZE        (120 * 16384)
+#define MAX_NUM_IO_BUFS           3
+
+#define DEFAULT_MAX_REF_FRM         2
+#define DEFAULT_MAX_REORDER_FRM     0
+#define DEFAULT_QP_MIN              10
+#define DEFAULT_QP_MAX              40
+#define DEFAULT_MAX_BITRATE         20000000
+#define DEFAULT_MAX_SRCH_RANGE_X    256
+#define DEFAULT_MAX_SRCH_RANGE_Y    256
+#define DEFAULT_MAX_FRAMERATE       120000
+#define DEFAULT_NUM_CORES           1
+#define DEFAULT_NUM_CORES_PRE_ENC   0
+#define DEFAULT_FPS                 30
+#define DEFAULT_ENC_SPEED           IVE_NORMAL
+
+#define DEFAULT_MEM_REC_CNT         0
+#define DEFAULT_RECON_ENABLE        0
+#define DEFAULT_CHKSUM_ENABLE       0
+#define DEFAULT_START_FRM           0
+#define DEFAULT_NUM_FRMS            0xFFFFFFFF
+#define DEFAULT_INP_COLOR_FORMAT       IV_YUV_420SP_VU
+#define DEFAULT_RECON_COLOR_FORMAT     IV_YUV_420P
+#define DEFAULT_LOOPBACK            0
+#define DEFAULT_SRC_FRAME_RATE      30
+#define DEFAULT_TGT_FRAME_RATE      30
+#define DEFAULT_MAX_WD              1920
+#define DEFAULT_MAX_HT              1920
+#define DEFAULT_MAX_LEVEL           41
+#define DEFAULT_STRIDE              0
+#define DEFAULT_WD                  1280
+#define DEFAULT_HT                  720
+#define DEFAULT_PSNR_ENABLE         0
+#define DEFAULT_ME_SPEED            100
+#define DEFAULT_ENABLE_FAST_SAD     0
+#define DEFAULT_ENABLE_ALT_REF      0
+#define DEFAULT_RC_MODE             IVE_RC_STORAGE
+#define DEFAULT_BITRATE             6000000
+#define DEFAULT_I_QP                22
+#define DEFAULT_I_QP_MAX            DEFAULT_QP_MAX
+#define DEFAULT_I_QP_MIN            DEFAULT_QP_MIN
+#define DEFAULT_P_QP                28
+#define DEFAULT_P_QP_MAX            DEFAULT_QP_MAX
+#define DEFAULT_P_QP_MIN            DEFAULT_QP_MIN
+#define DEFAULT_B_QP                22
+#define DEFAULT_B_QP_MAX            DEFAULT_QP_MAX
+#define DEFAULT_B_QP_MIN            DEFAULT_QP_MIN
+#define DEFAULT_AIR                 IVE_AIR_MODE_NONE
+#define DEFAULT_AIR_REFRESH_PERIOD  30
+#define DEFAULT_SRCH_RNG_X          64
+#define DEFAULT_SRCH_RNG_Y          48
+#define DEFAULT_I_INTERVAL          30
+#define DEFAULT_IDR_INTERVAL        1000
+#define DEFAULT_B_FRAMES            0
+#define DEFAULT_DISABLE_DEBLK_LEVEL 0
+#define DEFAULT_HPEL                1
+#define DEFAULT_QPEL                1
+#define DEFAULT_I4                  1
+#define DEFAULT_EPROFILE            IV_PROFILE_BASE
+#define DEFAULT_ENTROPY_MODE        0
+#define DEFAULT_SLICE_MODE          IVE_SLICE_MODE_NONE
+#define DEFAULT_SLICE_PARAM         256
+#define DEFAULT_ARCH                ARCH_ARM_A9Q
+#define DEFAULT_SOC                 SOC_GENERIC
+#define DEFAULT_INTRA4x4            0
+#define STRLENGTH                   500
+#define DEFAULT_CONSTRAINED_INTRA   0
+
+#define MIN(a, b) ((a) < (b))? (a) : (b)
+#define MAX(a, b) ((a) > (b))? (a) : (b)
+#define ALIGN16(x) ((((x) + 15) >> 4) << 4)
+#define ALIGN128(x) ((((x) + 127) >> 7) << 7)
+#define ALIGN4096(x) ((((x) + 4095) >> 12) << 12)
+
+/** Used to remove warnings about unused parameters */
+#define UNUSED(x) ((void)(x))
+
+/** Get time */
+#define GETTIME(a, b) gettimeofday(a, b);
+
+/** Compute difference between start and end */
+#define TIME_DIFF(start, end, diff) \
+    diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \
+            ((end).tv_usec - (start).tv_usec);
+
+#define ive_aligned_malloc(alignment, size) memalign(alignment, size)
+#define ive_aligned_free(buf) free(buf)
+
+struct C2SoftAvcEnc : public SimpleC2Component {
+    C2SoftAvcEnc(const char *name, c2_node_id_t id);
+
+    // 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;
+
+protected:
+    virtual ~C2SoftAvcEnc();
+
+private:
+    enum {
+        kUpdateBitrate            = 1 << 0,
+        kRequestKeyFrame          = 1 << 1,
+        kUpdateAIRMode            = 1 << 2,
+    };
+
+    // OMX input buffer's timestamp and flags
+    typedef struct {
+        int64_t mTimeUs;
+        int32_t mFlags;
+    } InputBufferInfo;
+
+    int32_t mStride;
+
+    struct timeval mTimeStart;   // Time at the start of decode()
+    struct timeval mTimeEnd;     // Time at the end of decode()
+
+    int mUpdateFlag;
+
+#ifdef FILE_DUMP_ENABLE
+    char mInFile[200];
+    char mOutFile[200];
+#endif /* FILE_DUMP_ENABLE */
+
+    IV_COLOR_FORMAT_T mIvVideoColorFormat;
+
+    IV_PROFILE_T mAVCEncProfile;
+    WORD32   mAVCEncLevel;
+    bool     mStarted;
+    bool     mSpsPpsHeaderReceived;
+
+    bool     mSawInputEOS;
+    bool     mSawOutputEOS;
+    bool     mSignalledError;
+    bool     mIntra4x4;
+    bool     mEnableFastSad;
+    bool     mEnableAltRef;
+    bool     mReconEnable;
+    bool     mPSNREnable;
+    bool     mEntropyMode;
+    bool     mConstrainedIntraFlag;
+    IVE_SPEED_CONFIG     mEncSpeed;
+
+    iv_obj_t *mCodecCtx;         // Codec context
+    iv_mem_rec_t *mMemRecords;   // Memory records requested by the codec
+    size_t mNumMemRecords;       // Number of memory records requested by codec
+    size_t mNumCores;            // Number of cores used by the codec
+
+    uint32_t mWidth;
+    uint32_t mHeight;
+    uint32_t mFramerate;
+    uint32_t mBitrate;
+    uint32_t mOutBufferSize;
+    UWORD32 mHeaderGenerated;
+    UWORD32 mBframes;
+    IV_ARCH_T mArch;
+    IVE_SLICE_MODE_T mSliceMode;
+    UWORD32 mSliceParam;
+    bool mHalfPelEnable;
+    UWORD32 mIInterval;
+    UWORD32 mIDRInterval;
+    UWORD32 mDisableDeblkLevel;
+    IVE_AIR_MODE_T mAIRMode;
+    UWORD32 mAIRRefreshPeriod;
+    std::map<const void *, std::shared_ptr<C2Buffer>> mBuffers;
+    std::list<std::unique_ptr<uint8_t[]>> mFreeConversionBuffers;
+    std::list<std::unique_ptr<uint8_t[]>> mConversionBuffersInUse;
+
+    void initEncParams();
+    c2_status_t initEncoder();
+    c2_status_t releaseEncoder();
+
+    c2_status_t setFrameType(IV_PICTURE_CODING_TYPE_T  e_frame_type);
+    c2_status_t setQp();
+    c2_status_t setEncMode(IVE_ENC_MODE_T e_enc_mode);
+    c2_status_t setDimensions();
+    c2_status_t setNumCores();
+    c2_status_t setFrameRate();
+    c2_status_t setIpeParams();
+    c2_status_t setBitRate();
+    c2_status_t setAirParams();
+    c2_status_t setMeParams();
+    c2_status_t setGopParams();
+    c2_status_t setProfileParams();
+    c2_status_t setDeblockParams();
+    c2_status_t setVbvParams();
+    void logVersion();
+    c2_status_t setEncodeArgs(
+            ive_video_encode_ip_t *ps_encode_ip,
+            ive_video_encode_op_t *ps_encode_op,
+            const C2GraphicView *const input,
+            uint8_t *base,
+            uint32_t capacity,
+            uint64_t timestamp);
+
+    DISALLOW_EVIL_CONSTRUCTORS(C2SoftAvcEnc);
+};
+
+#ifdef FILE_DUMP_ENABLE
+
+#define INPUT_DUMP_PATH     "/sdcard/media/avce_input"
+#define INPUT_DUMP_EXT      "yuv"
+#define OUTPUT_DUMP_PATH    "/sdcard/media/avce_output"
+#define OUTPUT_DUMP_EXT     "h264"
+
+#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);                            \
+    strcpy(mOutFile, "");                               \
+    sprintf(mOutFile, "%s_%ld.%ld.%s", OUTPUT_DUMP_PATH,\
+            mTimeStart.tv_sec, mTimeStart.tv_usec,      \
+            OUTPUT_DUMP_EXT);                           \
+}
+
+#define CREATE_DUMP_FILE(m_filename) {                  \
+    FILE *fp = fopen(m_filename, "wb");                 \
+    if (fp != NULL) {                                   \
+        ALOGD("Opened file %s", m_filename);            \
+        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) {                  \
+        int i;                                          \
+        i = fwrite(m_buf, 1, m_size, fp);               \
+        ALOGD("fwrite ret %d to write %d", i, m_size);  \
+        if (i != (int)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);\
+        if (fp != NULL)                                 \
+            fclose(fp);                                 \
+    }                                                   \
+}
+#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 */
+
+}  // namespace android
+
+#endif  // C2_SOFT_AVC_ENC_H__
diff --git a/media/libstagefright/codecs/cmds/codec2.cpp b/media/libstagefright/codecs/cmds/codec2.cpp
index 78fb527..d95bb07 100644
--- a/media/libstagefright/codecs/cmds/codec2.cpp
+++ b/media/libstagefright/codecs/cmds/codec2.cpp
@@ -83,7 +83,7 @@
     ~SimplePlayer();
 
     void onWorkDone(std::weak_ptr<C2Component> component,
-                    std::vector<std::unique_ptr<C2Work>> workItems);
+                    std::list<std::unique_ptr<C2Work>> workItems);
     void onTripped(std::weak_ptr<C2Component> component,
                    std::vector<std::shared_ptr<C2SettingResult>> settingResult);
     void onError(std::weak_ptr<C2Component> component, uint32_t errorCode);
@@ -120,7 +120,7 @@
     virtual ~Listener() = default;
 
     virtual void onWorkDone_nb(std::weak_ptr<C2Component> component,
-                            std::vector<std::unique_ptr<C2Work>> workItems) override {
+                            std::list<std::unique_ptr<C2Work>> workItems) override {
         mThis->onWorkDone(component, std::move(workItems));
     }
 
@@ -174,7 +174,7 @@
 }
 
 void SimplePlayer::onWorkDone(
-        std::weak_ptr<C2Component> component, std::vector<std::unique_ptr<C2Work>> workItems) {
+        std::weak_ptr<C2Component> component, std::list<std::unique_ptr<C2Work>> workItems) {
     ALOGV("SimplePlayer::onWorkDone");
     (void) component;
     ULock l(mProcessedLock);
@@ -247,7 +247,7 @@
             }
             int slot;
             sp<Fence> fence;
-            ALOGV("Render: Frame #%" PRId64, work->worklets.front()->output.ordinal.frame_index);
+            ALOGV("Render: Frame #%lld", work->worklets.front()->output.ordinal.frameIndex.peekll());
             const std::shared_ptr<C2Buffer> &output = work->worklets.front()->output.buffers[0];
             if (output) {
                 const C2ConstGraphicBlock &block = output->data().graphicBlocks().front();
@@ -266,7 +266,7 @@
                 status_t err = igbp->attachBuffer(&slot, buffer);
 
                 IGraphicBufferProducer::QueueBufferInput qbi(
-                        work->worklets.front()->output.ordinal.timestamp * 1000ll,
+                        (work->worklets.front()->output.ordinal.timestamp * 1000ll).peekll(),
                         false,
                         HAL_DATASPACE_UNKNOWN,
                         Rect(block.width(), block.height()),
@@ -338,9 +338,9 @@
                 mQueueCondition.wait_for(l, 100ms);
             }
         }
-        work->input.flags = (C2BufferPack::flags_t)0;
+        work->input.flags = (C2FrameData::flags_t)0;
         work->input.ordinal.timestamp = timestamp;
-        work->input.ordinal.frame_index = numFrames;
+        work->input.ordinal.frameIndex = numFrames;
 
         std::shared_ptr<C2LinearBlock> block;
         mLinearPool->fetchLinearBlock(
diff --git a/media/libstagefright/codecs/mp3dec/Android.bp b/media/libstagefright/codecs/mp3dec/Android.bp
index e6eb32b..9e41d17 100644
--- a/media/libstagefright/codecs/mp3dec/Android.bp
+++ b/media/libstagefright/codecs/mp3dec/Android.bp
@@ -119,6 +119,50 @@
     compile_multilib: "32",
 }
 
+cc_library_shared {
+    name: "libstagefright_soft_c2mp3dec",
+//    vendor_available: true,
+//    vndk: {
+//        enabled: true,
+//    },
+
+    srcs: ["C2SoftMP3.cpp"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    local_include_dirs: [
+        "src",
+        "include",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+            "unsigned-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+
+    static_libs: [
+        "libstagefright_mp3dec"
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libutils",
+        "libstagefright_codec2",
+        "libstagefright_codec2_vndk",
+        "libstagefright_foundation",
+        "libstagefright_simple_c2component",
+    ],
+}
+
 //###############################################################################
 cc_test {
     name: "libstagefright_mp3dec_test",
diff --git a/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp
new file mode 100644
index 0000000..c34a6f0
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/C2SoftMP3.cpp
@@ -0,0 +1,431 @@
+/*
+ * 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 "C2SoftMP3"
+#include <utils/Log.h>
+
+#include "pvmp3decoder_api.h"
+
+#include "C2SoftMP3.h"
+
+#include <C2PlatformSupport.h>
+#include <SimpleC2Interface.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+
+#include <numeric>
+
+namespace android {
+
+C2SoftMP3::C2SoftMP3(const char *name, c2_node_id_t id)
+    : SimpleC2Component(
+            SimpleC2Interface::Builder(name, id)
+            .inputFormat(C2FormatCompressed)
+            .outputFormat(C2FormatAudio)
+            .build()),
+      mConfig(nullptr),
+      mDecoderBuf(nullptr) {
+}
+
+C2SoftMP3::~C2SoftMP3() {
+    onRelease();
+}
+
+c2_status_t C2SoftMP3::onInit() {
+    status_t err = initDecoder();
+    return err == OK ? C2_OK : C2_NO_MEMORY;
+}
+
+c2_status_t C2SoftMP3::onStop() {
+    // Make sure that the next buffer output does not still
+    // depend on fragments from the last one decoded.
+    pvmp3_InitDecoder(mConfig, mDecoderBuf);
+    mSignalledError = false;
+    mIsFirst = true;
+    mSignalledOutputEos = false;
+
+    return C2_OK;
+}
+
+void C2SoftMP3::onReset() {
+    (void)onStop();
+}
+
+void C2SoftMP3::onRelease() {
+    if (mDecoderBuf) {
+        free(mDecoderBuf);
+        mDecoderBuf = nullptr;
+    }
+
+    if (mConfig) {
+        delete mConfig;
+        mConfig = nullptr;
+    }
+}
+
+status_t C2SoftMP3::initDecoder() {
+    mConfig = new tPVMP3DecoderExternal{};
+    if (!mConfig) return NO_MEMORY;
+    mConfig->equalizerType = flat;
+    mConfig->crcEnabled = false;
+
+    size_t memRequirements = pvmp3_decoderMemRequirements();
+    mDecoderBuf = malloc(memRequirements);
+    if (!mDecoderBuf) return NO_MEMORY;
+
+    pvmp3_InitDecoder(mConfig, mDecoderBuf);
+
+    mNumChannels = 2;
+    mSamplingRate = 44100;
+    mIsFirst = true;
+    mSignalledError = false;
+    mSignalledOutputEos = false;
+
+    return OK;
+}
+
+/* The below code is borrowed from ./test/mp3reader.cpp */
+static bool parseMp3Header(uint32_t header, size_t *frame_size,
+                           uint32_t *out_sampling_rate = nullptr,
+                           uint32_t *out_channels = nullptr,
+                           uint32_t *out_bitrate = nullptr,
+                           uint32_t *out_num_samples = nullptr) {
+    *frame_size = 0;
+    if (out_sampling_rate) *out_sampling_rate = 0;
+    if (out_channels) *out_channels = 0;
+    if (out_bitrate) *out_bitrate = 0;
+    if (out_num_samples) *out_num_samples = 1152;
+
+    if ((header & 0xffe00000) != 0xffe00000) return false;
+
+    unsigned version = (header >> 19) & 3;
+    if (version == 0x01) return false;
+
+    unsigned layer = (header >> 17) & 3;
+    if (layer == 0x00) return false;
+
+    unsigned bitrate_index = (header >> 12) & 0x0f;
+    if (bitrate_index == 0 || bitrate_index == 0x0f) return false;
+
+    unsigned sampling_rate_index = (header >> 10) & 3;
+    if (sampling_rate_index == 3) return false;
+
+    static const int kSamplingRateV1[] = { 44100, 48000, 32000 };
+    int sampling_rate = kSamplingRateV1[sampling_rate_index];
+    if (version == 2 /* V2 */) {
+        sampling_rate /= 2;
+    } else if (version == 0 /* V2.5 */) {
+        sampling_rate /= 4;
+    }
+
+    unsigned padding = (header >> 9) & 1;
+
+    if (layer == 3) { // layer I
+        static const int kBitrateV1[] =
+        {
+            32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448
+        };
+        static const int kBitrateV2[] =
+        {
+            32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256
+        };
+
+        int bitrate = (version == 3 /* V1 */) ? kBitrateV1[bitrate_index - 1] :
+                kBitrateV2[bitrate_index - 1];
+
+        if (out_bitrate) {
+            *out_bitrate = bitrate;
+        }
+        *frame_size = (12000 * bitrate / sampling_rate + padding) * 4;
+        if (out_num_samples) {
+            *out_num_samples = 384;
+        }
+    } else { // layer II or III
+        static const int kBitrateV1L2[] =
+        {
+            32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384
+        };
+
+        static const int kBitrateV1L3[] =
+        {
+            32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320
+        };
+
+        static const int kBitrateV2[] =
+        {
+            8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160
+        };
+
+        int bitrate;
+        if (version == 3 /* V1 */) {
+            bitrate = (layer == 2 /* L2 */) ? kBitrateV1L2[bitrate_index - 1] :
+                    kBitrateV1L3[bitrate_index - 1];
+
+            if (out_num_samples) {
+                *out_num_samples = 1152;
+            }
+        } else { // V2 (or 2.5)
+            bitrate = kBitrateV2[bitrate_index - 1];
+            if (out_num_samples) {
+                *out_num_samples = (layer == 1 /* L3 */) ? 576 : 1152;
+            }
+        }
+
+        if (out_bitrate) {
+            *out_bitrate = bitrate;
+        }
+
+        if (version == 3 /* V1 */) {
+            *frame_size = 144000 * bitrate / sampling_rate + padding;
+        } else { // V2 or V2.5
+            size_t tmp = (layer == 1 /* L3 */) ? 72000 : 144000;
+            *frame_size = tmp * bitrate / sampling_rate + padding;
+        }
+    }
+
+    if (out_sampling_rate) {
+        *out_sampling_rate = sampling_rate;
+    }
+
+    if (out_channels) {
+        int channel_mode = (header >> 6) & 3;
+
+        *out_channels = (channel_mode == 3) ? 1 : 2;
+    }
+
+    return true;
+}
+
+static uint32_t U32_AT(const uint8_t *ptr) {
+    return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
+}
+
+static status_t calculateOutSize(uint8 *header, size_t inSize,
+                                 std::vector<size_t> *decodedSizes) {
+    uint32_t channels;
+    uint32_t numSamples;
+    size_t frameSize;
+    size_t totalInSize = 0;
+
+    while (totalInSize + 4 < inSize) {
+        if (!parseMp3Header(U32_AT(header + totalInSize), &frameSize,
+                            nullptr, &channels, nullptr, &numSamples)) {
+            ALOGE("Error in parse mp3 header during outSize estimation");
+            return UNKNOWN_ERROR;
+        }
+        totalInSize += frameSize;
+        decodedSizes->push_back(numSamples * channels * sizeof(int16_t));
+    }
+
+    if (decodedSizes->empty()) return UNKNOWN_ERROR;
+
+    return OK;
+}
+
+c2_status_t C2SoftMP3::onFlush_sm() {
+    return onStop();
+}
+
+c2_status_t C2SoftMP3::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;
+}
+
+// TODO: Can overall error checking be improved? As in the check for validity of
+//       work, pool ptr, work->input.buffers.size() == 1, ...
+// TODO: Gapless playback: decoder has a delay of 529 samples. For the first
+//       frame we intend to remove 529 samples worth of data. When this is
+//       done it is going to effect the timestamps of future frames. This
+//       timestamp correction is handled by the client or plugin? Soft omx mp3
+//       plugin also has this problem
+// TODO: Blind removal of 529 samples from the output may not work. Because
+//       mpeg layer 1 frame size is 384 samples per frame. This should introduce
+//       negative values and can cause SEG faults. Soft omx mp3 plugin can have
+//       this problem (CHECK!)
+void C2SoftMP3::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 = inBuffer.map().get();
+    if (inSize && rView.error()) {
+        ALOGE("read view map failed %d", rView.error());
+        work->result = C2_CORRUPTED;
+        return;
+    }
+
+    if (inSize == 0) {
+        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;
+        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());
+
+    size_t calOutSize;
+    std::vector<size_t> decodedSizes;
+    if (OK != calculateOutSize(const_cast<uint8 *>(rView.data() + inOffset),
+                               inSize, &decodedSizes)) {
+        work->result = C2_CORRUPTED;
+        return;
+    }
+    calOutSize = std::accumulate(decodedSizes.begin(), decodedSizes.end(), 0);
+    std::shared_ptr<C2LinearBlock> block;
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    c2_status_t err = pool->fetchLinearBlock(calOutSize, 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 outSize = 0;
+    int outOffset = 0;
+    auto it = decodedSizes.begin();
+    while (inOffset < inSize) {
+        if (it == decodedSizes.end()) {
+            ALOGE("unexpected trailing bytes, ignoring them");
+            break;
+        }
+
+        mConfig->pInputBuffer = const_cast<uint8 *>(rView.data() + inOffset);
+        mConfig->inputBufferCurrentLength = (inSize - inOffset);
+        mConfig->inputBufferMaxLength = 0;
+        mConfig->inputBufferUsedLength = 0;
+        mConfig->outputFrameSize = (calOutSize - outSize);
+        mConfig->pOutputBuffer = reinterpret_cast<int16_t *> (wView.data() + outSize);
+
+        ERROR_CODE decoderErr;
+        if ((decoderErr = pvmp3_framedecoder(mConfig, mDecoderBuf))
+                != NO_DECODING_ERROR) {
+            ALOGE("mp3 decoder returned error %d", decoderErr);
+            if (decoderErr != NO_ENOUGH_MAIN_DATA_ERROR
+                    && decoderErr != SIDE_INFO_ERROR) {
+                mSignalledError = true;
+                work->result = C2_CORRUPTED;
+                return;
+            }
+
+            // This is recoverable, just ignore the current frame and
+            // play silence instead.
+            ALOGV("ignoring error and sending silence");
+            if (mConfig->outputFrameSize == 0) {
+                mConfig->outputFrameSize = *it / sizeof(int16_t);
+            }
+            memset(mConfig->pOutputBuffer, 0, mConfig->outputFrameSize * sizeof(int16_t));
+        } else if (mConfig->samplingRate != mSamplingRate
+                || mConfig->num_channels != mNumChannels) {
+            mSamplingRate = mConfig->samplingRate;
+            mNumChannels = mConfig->num_channels;
+        }
+        if (*it != mConfig->outputFrameSize * sizeof(int16_t)) {
+            ALOGE("panic, parsed size does not match decoded size");
+            mSignalledError = true;
+            work->result = C2_CORRUPTED;
+            return;
+        }
+        outSize += mConfig->outputFrameSize * sizeof(int16_t);
+        inOffset += mConfig->inputBufferUsedLength;
+        it++;
+    }
+    if (mIsFirst) {
+        mIsFirst = false;
+        // The decoder delay is 529 samples, so trim that many samples off
+        // the start of the first output buffer. This essentially makes this
+        // decoder have zero delay, which the rest of the pipeline assumes.
+        outOffset = kPVMP3DecoderDelay * mNumChannels * sizeof(int16_t);
+    }
+    ALOGV("out buffer attr. offset %d size %d", outOffset, outSize);
+    decodedSizes.clear();
+    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;
+    if (eos) {
+        mSignalledOutputEos = true;
+        ALOGV("signalled EOS");
+    }
+}
+
+class C2SoftMp3DecFactory : public C2ComponentFactory {
+public:
+    virtual c2_status_t createComponent(
+            c2_node_id_t id, std::shared_ptr<C2Component>* const component,
+            std::function<void(::android::C2Component*)> deleter) override {
+        *component = std::shared_ptr<C2Component>(new C2SoftMP3("mp3", id), deleter);
+        return C2_OK;
+    }
+
+    virtual c2_status_t createInterface(
+            c2_node_id_t id, std::shared_ptr<C2ComponentInterface>* const interface,
+            std::function<void(::android::C2ComponentInterface*)> deleter) override {
+        *interface =
+                SimpleC2Interface::Builder("mp3", id, deleter)
+                .inputFormat(C2FormatCompressed)
+                .outputFormat(C2FormatAudio)
+                .build();
+        return C2_OK;
+    }
+
+    virtual ~C2SoftMp3DecFactory() override = default;
+};
+
+}  // namespace android
+
+extern "C" ::android::C2ComponentFactory* CreateCodec2Factory() {
+    ALOGV("in %s", __func__);
+    return new ::android::C2SoftMp3DecFactory();
+}
+
+extern "C" void DestroyCodec2Factory(::android::C2ComponentFactory* factory) {
+    ALOGV("in %s", __func__);
+    delete factory;
+}
+
diff --git a/media/libstagefright/codecs/mp3dec/C2SoftMP3.h b/media/libstagefright/codecs/mp3dec/C2SoftMP3.h
new file mode 100644
index 0000000..ad974bd
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/C2SoftMP3.h
@@ -0,0 +1,72 @@
+/*
+ * 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_MP3_H_
+#define C2_SOFT_MP3_H_
+
+#include <SimpleC2Component.h>
+
+#include <media/stagefright/foundation/ABase.h>
+
+struct tPVMP3DecoderExternal;
+
+bool parseMp3Header(uint32_t header, size_t *frame_size,
+                    uint32_t *out_sampling_rate = nullptr,
+                    uint32_t *out_channels = nullptr,
+                    uint32_t *out_bitrate = nullptr,
+                    uint32_t *out_num_samples = nullptr);
+
+namespace android {
+
+struct C2SoftMP3 : public SimpleC2Component {
+    C2SoftMP3(const char *name, c2_node_id_t id);
+    virtual ~C2SoftMP3();
+
+    // 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 {
+        kPVMP3DecoderDelay = 529 // samples
+    };
+
+    tPVMP3DecoderExternal *mConfig;
+    void *mDecoderBuf;
+
+    int32_t mNumChannels;
+    int32_t mSamplingRate;
+    bool mIsFirst;
+    bool mSignalledError;
+    bool mSignalledOutputEos;
+
+    status_t initDecoder();
+
+    DISALLOW_EVIL_CONSTRUCTORS(C2SoftMP3);
+};
+
+}  // namespace android
+
+#endif  // C2_SOFT_MP3_H_
diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp
index c7f9001..eae73fc 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -64,7 +64,7 @@
                     || mDstFormat == OMX_COLOR_Format32bitBGRA8888;
 
         case OMX_COLOR_FormatYUV420Planar16:
-            return mDstFormat == OMX_COLOR_Format32BitRGBA1010102;
+            return mDstFormat == OMX_COLOR_FormatYUV444Y410;
 
         case OMX_COLOR_FormatCbYCrY:
         case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
@@ -77,6 +77,12 @@
     }
 }
 
+bool ColorConverter::isDstRGB() const {
+    return mDstFormat == OMX_COLOR_Format16bitRGB565
+            || mDstFormat == OMX_COLOR_Format32BitRGBA8888
+            || mDstFormat == OMX_COLOR_Format32bitBGRA8888;
+}
+
 ColorConverter::BitmapParams::BitmapParams(
         void *bits,
         size_t width, size_t height,
@@ -99,7 +105,7 @@
 
     case OMX_COLOR_Format32bitBGRA8888:
     case OMX_COLOR_Format32BitRGBA8888:
-    case OMX_COLOR_Format32BitRGBA1010102:
+    case OMX_COLOR_FormatYUV444Y410:
         mBpp = 4;
         mStride = 4 * mWidth;
         break;
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index fca9c09..c9ebbdc 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -134,6 +134,10 @@
             }
             case OMX_COLOR_FormatYUV420Planar16:
             {
+                // Here we would convert OMX_COLOR_FormatYUV420Planar16 into
+                // OMX_COLOR_FormatYUV444Y410, and put it inside a buffer with
+                // format HAL_PIXEL_FORMAT_RGBA_1010102. Surfaceflinger will
+                // use render engine to convert it to RGB if needed.
                 halFormat = HAL_PIXEL_FORMAT_RGBA_1010102;
                 bufWidth = (mCropWidth + 1) & ~1;
                 bufHeight = (mCropHeight + 1) & ~1;
@@ -152,7 +156,7 @@
         CHECK(mConverter->isValid());
     } else if (mColorFormat == OMX_COLOR_FormatYUV420Planar16) {
         mConverter = new ColorConverter(
-                mColorFormat, OMX_COLOR_Format32BitRGBA1010102);
+                mColorFormat, OMX_COLOR_FormatYUV444Y410);
         CHECK(mConverter->isValid());
     }
 
@@ -380,7 +384,7 @@
     if (format->findInt32("android._dataspace", (int32_t *)&dataSpace) && dataSpace != mDataSpace) {
         mDataSpace = dataSpace;
 
-        if (mConverter != NULL) {
+        if (mConverter != NULL && mConverter->isDstRGB()) {
             // graphics only supports full range RGB. ColorConverter should have
             // converted any YUV to full range.
             dataSpace = (android_dataspace)
diff --git a/media/libstagefright/foundation/Android.bp b/media/libstagefright/foundation/Android.bp
index 2258e2c..b343c16 100644
--- a/media/libstagefright/foundation/Android.bp
+++ b/media/libstagefright/foundation/Android.bp
@@ -64,7 +64,6 @@
         "ColorUtils.cpp",
         "MediaDefs.cpp",
         "MediaKeys.cpp",
-        "MetaData.cpp",
         "ParsedMessage.cpp",
         "avc_utils.cpp",
         "base64.cpp",
diff --git a/media/libstagefright/foundation/avc_utils.cpp b/media/libstagefright/foundation/avc_utils.cpp
index bfaeb21..b58474c 100644
--- a/media/libstagefright/foundation/avc_utils.cpp
+++ b/media/libstagefright/foundation/avc_utils.cpp
@@ -383,7 +383,9 @@
     }
 }
 
-sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit) {
+sp<ABuffer> MakeAVCCodecSpecificData(
+        const sp<ABuffer> &accessUnit, int32_t *width, int32_t *height,
+        int32_t *sarWidth, int32_t *sarHeight) {
     const uint8_t *data = accessUnit->data();
     size_t size = accessUnit->size();
 
@@ -392,10 +394,8 @@
         return NULL;
     }
 
-    int32_t width, height;
-    int32_t sarWidth, sarHeight;
     FindAVCDimensions(
-            seqParamSet, &width, &height, &sarWidth, &sarHeight);
+            seqParamSet, width, height, sarWidth, sarHeight);
 
     sp<ABuffer> picParamSet = FindNAL(data, size, 8);
     CHECK(picParamSet != NULL);
@@ -434,38 +434,32 @@
     hexdump(seqParamSet->data(), seqParamSet->size());
 #endif
 
-    sp<MetaData> meta = new MetaData;
-    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
 
-    meta->setData(kKeyAVCC, kTypeAVCC, csd->data(), csd->size());
-    meta->setInt32(kKeyWidth, width);
-    meta->setInt32(kKeyHeight, height);
-
-    if ((sarWidth > 0 && sarHeight > 0) && (sarWidth != 1 || sarHeight != 1)) {
-        // We treat *:0 and 0:* (unspecified) as 1:1.
-
-        meta->setInt32(kKeySARWidth, sarWidth);
-        meta->setInt32(kKeySARHeight, sarHeight);
-
-        ALOGI("found AVC codec config (%d x %d, %s-profile level %d.%d) "
-              "SAR %d : %d",
-             width,
-             height,
-             AVCProfileToString(profile),
-             level / 10,
-             level % 10,
-             sarWidth,
-             sarHeight);
-    } else {
-        ALOGI("found AVC codec config (%d x %d, %s-profile level %d.%d)",
-             width,
-             height,
-             AVCProfileToString(profile),
-             level / 10,
-             level % 10);
+    if (sarWidth != nullptr && sarHeight != nullptr) {
+        if ((*sarWidth > 0 && *sarHeight > 0) && (*sarWidth != 1 || *sarHeight != 1)) {
+            ALOGI("found AVC codec config (%d x %d, %s-profile level %d.%d) "
+                    "SAR %d : %d",
+                    *width,
+                    *height,
+                    AVCProfileToString(profile),
+                    level / 10,
+                    level % 10,
+                    *sarWidth,
+                    *sarHeight);
+        } else {
+            // We treat *:0 and 0:* (unspecified) as 1:1.
+            *sarWidth = 0;
+            *sarHeight = 0;
+            ALOGI("found AVC codec config (%d x %d, %s-profile level %d.%d)",
+                    *width,
+                    *height,
+                    AVCProfileToString(profile),
+                    level / 10,
+                    level % 10);
+        }
     }
 
-    return meta;
+    return csd;
 }
 
 bool IsIDR(const uint8_t *data, size_t size) {
@@ -543,19 +537,17 @@
     return layerId;
 }
 
-sp<MetaData> MakeAACCodecSpecificData(
+sp<ABuffer> MakeAACCodecSpecificData(
         unsigned profile, unsigned sampling_freq_index,
-        unsigned channel_configuration) {
-    sp<MetaData> meta = new MetaData;
-    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
-
+        unsigned channel_configuration, int32_t *sampleRate,
+        int32_t *channelCount) {
     CHECK_LE(sampling_freq_index, 11u);
     static const int32_t kSamplingFreq[] = {
         96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
         16000, 12000, 11025, 8000
     };
-    meta->setInt32(kKeySampleRate, kSamplingFreq[sampling_freq_index]);
-    meta->setInt32(kKeyChannelCount, channel_configuration);
+    *sampleRate = kSamplingFreq[sampling_freq_index];
+    *channelCount = channel_configuration;
 
     static const uint8_t kStaticESDS[] = {
         0x03, 22,
@@ -585,9 +577,7 @@
     csd->data()[sizeof(kStaticESDS) + 1] =
         ((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3);
 
-    meta->setData(kKeyESDS, 0, csd->data(), csd->size());
-
-    return meta;
+    return csd;
 }
 
 bool ExtractDimensionsFromVOLHeader(
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/AUtils.h b/media/libstagefright/foundation/include/media/stagefright/foundation/AUtils.h
index 255a0f4..af6b357 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/AUtils.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/AUtils.h
@@ -22,28 +22,28 @@
 
 /* T must be integer type, den must not be 0 */
 template<class T>
-inline static const T divRound(const T &nom, const T &den) {
-    if ((nom >= 0) ^ (den >= 0)) {
-        return (nom - den / 2) / den;
+inline static const T divRound(const T &num, const T &den) {
+    if ((num >= 0) ^ (den >= 0)) {
+        return (num - den / 2) / den;
     } else {
-        return (nom + den / 2) / den;
+        return (num + den / 2) / den;
     }
 }
 
-/* == ceil(nom / den). T must be integer type, den must not be 0 */
+/* == ceil(num / den). T must be integer type, den must not be 0 */
 template<class T>
-inline static const T divUp(const T &nom, const T &den) {
+inline static const T divUp(const T &num, const T &den) {
     if (den < 0) {
-        return (nom < 0 ? nom + den + 1 : nom) / den;
+        return (num < 0 ? num + den + 1 : num) / den;
     } else {
-        return (nom < 0 ? nom : nom + den - 1) / den;
+        return (num < 0 ? num : num + den - 1) / den;
     }
 }
 
-/* == ceil(nom / den) * den. T must be integer type, alignment must be positive power of 2 */
+/* == ceil(num / den) * den. T must be integer type, alignment must be positive power of 2 */
 template<class T, class U>
-inline static const T align(const T &nom, const U &den) {
-    return (nom + (T)(den - 1)) & (T)~(den - 1);
+inline static const T align(const T &num, const U &den) {
+    return (num + (T)(den - 1)) & (T)~(den - 1);
 }
 
 template<class T>
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/ByteUtils.h b/media/libstagefright/foundation/include/media/stagefright/foundation/ByteUtils.h
index dc4125f..a434f81 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/ByteUtils.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/ByteUtils.h
@@ -22,8 +22,20 @@
 
 namespace android {
 
-#define FOURCC(c1, c2, c3, c4) \
-    ((c1) << 24 | (c2) << 16 | (c3) << 8 | (c4))
+constexpr int FOURCC(unsigned char c1, unsigned char c2, unsigned char c3, unsigned char c4) {
+    return ((c1) << 24 | (c2) << 16 | (c3) << 8 | (c4));
+}
+
+template <size_t N>
+constexpr int32_t FOURCC(const char (&s) [N]) {
+    static_assert(N == 5, "fourcc: wrong length");
+    return
+            (unsigned char) s[0] << 24 |
+            (unsigned char) s[1] << 16 |
+            (unsigned char) s[2] << 8 |
+            (unsigned char) s[3] << 0;
+}
+
 
 uint16_t U16_AT(const uint8_t *ptr);
 uint32_t U32_AT(const uint8_t *ptr);
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/avc_utils.h b/media/libstagefright/foundation/include/media/stagefright/foundation/avc_utils.h
index a939f12..2ca66fb 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/avc_utils.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/avc_utils.h
@@ -80,8 +80,9 @@
         const uint8_t **nalStart, size_t *nalSize,
         bool startCodeFollows = false);
 
-class MetaData;
-sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit);
+sp<ABuffer> MakeAVCCodecSpecificData(
+        const sp<ABuffer> &accessUnit, int32_t *width, int32_t *height,
+        int32_t *sarWidth = nullptr, int32_t *sarHeight = nullptr);
 
 bool IsIDR(const uint8_t *data, size_t size);
 bool IsAVCReferenceFrame(const sp<ABuffer> &accessUnit);
@@ -89,9 +90,10 @@
 
 const char *AVCProfileToString(uint8_t profile);
 
-sp<MetaData> MakeAACCodecSpecificData(
+sp<ABuffer> MakeAACCodecSpecificData(
         unsigned profile, unsigned sampling_freq_index,
-        unsigned channel_configuration);
+        unsigned channel_configuration, int32_t *sampleRate,
+        int32_t *channelCount);
 
 // Given an MPEG4 video VOL-header chunk (starting with 0x00 0x00 0x01 0x2?)
 // parse it and fill in dimensions, returns true iff successful.
diff --git a/media/libstagefright/httplive/Android.bp b/media/libstagefright/httplive/Android.bp
index de560b6..f64e437 100644
--- a/media/libstagefright/httplive/Android.bp
+++ b/media/libstagefright/httplive/Android.bp
@@ -47,6 +47,7 @@
 
     static_libs: [
         "libstagefright_id3",
+        "libstagefright_metadatautils",
         "libstagefright_mpeg2support",
     ],
 
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index b46d923..55fc680 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -35,6 +35,7 @@
 #include <media/stagefright/foundation/avc_utils.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MetaData.h>
+#include <media/stagefright/MetaDataUtils.h>
 #include <media/stagefright/Utils.h>
 
 #include <ctype.h>
diff --git a/media/libstagefright/include/CCodecBufferChannel.h b/media/libstagefright/include/CCodecBufferChannel.h
index c5062d6..57a1374 100644
--- a/media/libstagefright/include/CCodecBufferChannel.h
+++ b/media/libstagefright/include/CCodecBufferChannel.h
@@ -27,6 +27,7 @@
 #include <C2Component.h>
 
 #include <media/stagefright/foundation/Mutexed.h>
+#include <media/stagefright/gbs/GraphicBufferSource.h>
 #include <media/stagefright/CodecBase.h>
 #include <media/ICrypto.h>
 
@@ -35,134 +36,9 @@
 /**
  * BufferChannelBase implementation for CCodec.
  */
-class CCodecBufferChannel : public BufferChannelBase {
+class CCodecBufferChannel
+    : public BufferChannelBase, public std::enable_shared_from_this<CCodecBufferChannel> {
 public:
-    /**
-     * Base class for representation of buffers at one port.
-     */
-    class Buffers {
-    public:
-        Buffers() = default;
-        virtual ~Buffers() = default;
-
-        /**
-         * Set format for MediaCodec-facing buffers.
-         */
-        inline void setFormat(const sp<AMessage> &format) { mFormat = format; }
-
-        /**
-         * Returns true if the buffers are operating under array mode.
-         */
-        virtual bool isArrayMode() { return false; }
-
-        /**
-         * Fills the vector with MediaCodecBuffer's if in array mode; otherwise,
-         * no-op.
-         */
-        virtual void getArray(Vector<sp<MediaCodecBuffer>> *) {}
-
-    protected:
-        // Format to be used for creating MediaCodec-facing buffers.
-        sp<AMessage> mFormat;
-
-    private:
-        DISALLOW_EVIL_CONSTRUCTORS(Buffers);
-    };
-
-    class InputBuffers : public Buffers {
-    public:
-        using Buffers::Buffers;
-        virtual ~InputBuffers() = default;
-
-        /**
-         * Set a block pool to obtain input memory blocks.
-         */
-        inline void setPool(const std::shared_ptr<C2BlockPool> &pool) { mPool = pool; }
-
-        /**
-         * Get a new MediaCodecBuffer for input and its corresponding index.
-         * Returns false if no new buffer can be obtained at the moment.
-         */
-        virtual bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) = 0;
-
-        /**
-         * Release the buffer obtained from requestNewBuffer() and get the
-         * associated C2Buffer object back. Returns empty shared_ptr if the
-         * buffer is not on file.
-         */
-        virtual std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) = 0;
-
-        /**
-         * Flush internal state. After this call, no index or buffer previously
-         * returned from requestNewBuffer() is valid.
-         */
-        virtual void flush() = 0;
-
-        /**
-         * Return array-backed version of input buffers. The returned object
-         * shall retain the internal state so that it will honor index and
-         * buffer from previous calls of requestNewBuffer().
-         */
-        virtual std::unique_ptr<InputBuffers> toArrayMode() = 0;
-
-    protected:
-        // Pool to obtain blocks for input buffers.
-        std::shared_ptr<C2BlockPool> mPool;
-
-    private:
-        DISALLOW_EVIL_CONSTRUCTORS(InputBuffers);
-    };
-
-    class OutputBuffers : public Buffers {
-    public:
-        using Buffers::Buffers;
-        virtual ~OutputBuffers() = default;
-
-        /**
-         * Register output C2Buffer from the component and obtain corresponding
-         * index and MediaCodecBuffer object. Returns false if registration
-         * fails.
-         */
-        virtual bool registerBuffer(
-                const std::shared_ptr<C2Buffer> &buffer,
-                size_t *index,
-                sp<MediaCodecBuffer> *codecBuffer) = 0;
-
-        /**
-         * Register codec specific data as a buffer to be consistent with
-         * MediaCodec behavior.
-         */
-        virtual bool registerCsd(
-                const C2StreamCsdInfo::output * /* csd */,
-                size_t * /* index */,
-                sp<MediaCodecBuffer> * /* codecBuffer */) {
-            return false;
-        }
-
-        /**
-         * Release the buffer obtained from registerBuffer() and get the
-         * associated C2Buffer object back. Returns empty shared_ptr if the
-         * buffer is not on file.
-         */
-        virtual std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) = 0;
-
-        /**
-         * Flush internal state. After this call, no index or buffer previously
-         * returned from registerBuffer() is valid.
-         */
-        virtual void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) = 0;
-
-        /**
-         * Return array-backed version of output buffers. The returned object
-         * shall retain the internal state so that it will honor index and
-         * buffer from previous calls of registerBuffer().
-         */
-        virtual std::unique_ptr<OutputBuffers> toArrayMode() = 0;
-
-    private:
-        DISALLOW_EVIL_CONSTRUCTORS(OutputBuffers);
-    };
-
     CCodecBufferChannel(const std::function<void(status_t, enum ActionCode)> &onError);
     virtual ~CCodecBufferChannel();
 
@@ -186,23 +62,21 @@
 
     // Methods below are interface for CCodec to use.
 
+    /**
+     * Set the component object for buffer processing.
+     */
     void setComponent(const std::shared_ptr<C2Component> &component);
+
+    /**
+     * Set output graphic surface for rendering.
+     */
     status_t setSurface(const sp<Surface> &surface);
 
     /**
-     * Set C2BlockPool for input buffers.
-     *
-     * TODO: start timestamp?
+     * Set GraphicBufferSource object from which the component extracts input
+     * buffers.
      */
-    void setInputBufferAllocator(const sp<C2BlockPool> &inAlloc);
-
-    /**
-     * Set C2BlockPool for output buffers. This object shall never use the
-     * allocator itself; it's just passed
-     *
-     * TODO: start timestamp?
-     */
-    void setOutputBufferAllocator(const sp<C2BlockPool> &outAlloc);
+    status_t setGraphicBufferSource(const sp<GraphicBufferSource> &source);
 
     /**
      * Start queueing buffers to the component. This object should never queue
@@ -219,11 +93,17 @@
     void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork);
 
     /**
-     * Notify MediaCodec about work done.
+     * Notify input client about work done.
      *
-     * @param workItems   finished work items.
+     * @param workItems   finished work item.
      */
-    void onWorkDone(std::vector<std::unique_ptr<C2Work>> workItems);
+    void onWorkDone(const std::unique_ptr<C2Work> &work);
+
+    // Internal classes
+    class Buffers;
+    class InputBuffers;
+    class OutputBuffers;
+    class InputBufferClient;
 
 private:
     class QueueGuard;
@@ -276,12 +156,17 @@
         bool mRunning;
     };
 
+    class C2ComponentWrapper;
+
+    void feedInputBufferIfAvailable();
+
     QueueSync mSync;
     sp<MemoryDealer> mDealer;
     sp<IMemory> mDecryptDestination;
     int32_t mHeapSeqNum;
 
     std::shared_ptr<C2Component> mComponent;
+    std::shared_ptr<InputBufferClient> mInputClient;
     std::function<void(status_t, enum ActionCode)> mOnError;
     std::shared_ptr<C2BlockPool> mInputAllocator;
     QueueSync mQueueSync;
diff --git a/media/libstagefright/include/FrameDecoder.h b/media/libstagefright/include/FrameDecoder.h
index d7c074c..c40324b 100644
--- a/media/libstagefright/include/FrameDecoder.h
+++ b/media/libstagefright/include/FrameDecoder.h
@@ -17,6 +17,9 @@
 #ifndef FRAME_DECODER_H_
 #define FRAME_DECODER_H_
 
+#include <memory>
+#include <vector>
+
 #include <media/stagefright/foundation/AString.h>
 #include <media/stagefright/foundation/ABase.h>
 #include <media/MediaSource.h>
@@ -27,6 +30,7 @@
 
 struct AMessage;
 class MediaCodecBuffer;
+class IMediaSource;
 class VideoFrame;
 
 struct FrameDecoder {
diff --git a/media/libstagefright/include/HTTPBase.h b/media/libstagefright/include/HTTPBase.h
index 26d7e8a..a924197 100644
--- a/media/libstagefright/include/HTTPBase.h
+++ b/media/libstagefright/include/HTTPBase.h
@@ -21,6 +21,7 @@
 #include <media/DataSource.h>
 #include <media/stagefright/foundation/ABase.h>
 #include <media/stagefright/MediaErrors.h>
+#include <utils/List.h>
 #include <utils/threads.h>
 
 namespace android {
diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h
index 3196b10..fa22003 100644
--- a/media/libstagefright/include/media/stagefright/ACodec.h
+++ b/media/libstagefright/include/media/stagefright/ACodec.h
@@ -495,7 +495,7 @@
     status_t verifySupportForProfileAndLevel(int32_t profile, int32_t level);
 
     status_t configureBitrate(
-            int32_t bitrate, OMX_VIDEO_CONTROLRATETYPE bitrateMode);
+            OMX_VIDEO_CONTROLRATETYPE bitrateMode, int32_t bitrate, int32_t quality = 0);
     void configureEncoderLatency(const sp<AMessage> &msg);
 
     status_t setupErrorCorrectionParameters();
diff --git a/media/libstagefright/include/media/stagefright/AudioSource.h b/media/libstagefright/include/media/stagefright/AudioSource.h
index 9414aab..f66b92d 100644
--- a/media/libstagefright/include/media/stagefright/AudioSource.h
+++ b/media/libstagefright/include/media/stagefright/AudioSource.h
@@ -21,11 +21,14 @@
 #include <media/AudioRecord.h>
 #include <media/AudioSystem.h>
 #include <media/MediaSource.h>
+#include <media/MicrophoneInfo.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <utils/List.h>
 
 #include <system/audio.h>
 
+#include <vector>
+
 namespace android {
 
 class AudioRecord;
@@ -64,6 +67,9 @@
     status_t addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
     status_t removeAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
 
+    status_t getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones);
+
+
 protected:
     virtual ~AudioSource();
 
diff --git a/media/libstagefright/include/media/stagefright/CCodec.h b/media/libstagefright/include/media/stagefright/CCodec.h
index 3e24bbe..fa19c6e 100644
--- a/media/libstagefright/include/media/stagefright/CCodec.h
+++ b/media/libstagefright/include/media/stagefright/CCodec.h
@@ -24,6 +24,7 @@
 #include <android/native_window.h>
 #include <media/hardware/MetadataBufferType.h>
 #include <media/stagefright/foundation/Mutexed.h>
+#include <media/stagefright/gbs/GraphicBufferSource.h>
 #include <media/stagefright/CodecBase.h>
 #include <media/stagefright/FrameRenderTracker.h>
 #include <media/stagefright/MediaDefs.h>
@@ -58,6 +59,7 @@
     virtual void signalRequestIDRFrame() override;
 
     void initiateReleaseIfStuck();
+    void onWorkDone(std::list<std::unique_ptr<C2Work>> &workItems);
 
 protected:
     virtual ~CCodec();
@@ -77,6 +79,10 @@
     void flush();
     void release(bool sendCallback);
 
+    void createInputSurface();
+    void setInputSurface(const sp<PersistentSurface> &surface);
+    status_t setupInputSurface(const sp<GraphicBufferSource> &source);
+
     void setDeadline(const TimePoint &deadline);
 
     enum {
@@ -86,6 +92,9 @@
         kWhatFlush,
         kWhatStop,
         kWhatRelease,
+        kWhatCreateInputSurface,
+        kWhatSetInputSurface,
+        kWhatWorkDone,
     };
 
     enum {
@@ -104,14 +113,17 @@
 
     struct State {
         inline State() : mState(RELEASED) {}
+        inline int get() const { return mState; }
+        inline void set(int newState) { mState = newState; }
 
+        std::shared_ptr<C2Component> comp;
+    private:
         int mState;
-        std::shared_ptr<C2Component> mComp;
     };
 
     struct Formats {
-        sp<AMessage> mInputFormat;
-        sp<AMessage> mOutputFormat;
+        sp<AMessage> inputFormat;
+        sp<AMessage> outputFormat;
     };
 
     Mutexed<State> mState;
@@ -119,6 +131,7 @@
     std::shared_ptr<C2Component::Listener> mListener;
     Mutexed<TimePoint> mDeadline;
     Mutexed<Formats> mFormats;
+    Mutexed<std::list<std::unique_ptr<C2Work>>> mWorkDoneQueue;
 
     DISALLOW_EVIL_CONSTRUCTORS(CCodec);
 };
diff --git a/media/libstagefright/include/media/stagefright/CallbackMediaSource.h b/media/libstagefright/include/media/stagefright/CallbackMediaSource.h
index 3459de1..944d951 100644
--- a/media/libstagefright/include/media/stagefright/CallbackMediaSource.h
+++ b/media/libstagefright/include/media/stagefright/CallbackMediaSource.h
@@ -22,6 +22,8 @@
 
 namespace android {
 
+class IMediaSource;
+
 // A stagefright MediaSource that wraps a binder IMediaSource.
 class CallbackMediaSource : public MediaSource {
 public:
diff --git a/media/libstagefright/include/media/stagefright/ColorConverter.h b/media/libstagefright/include/media/stagefright/ColorConverter.h
index f6bd353..a6c8981 100644
--- a/media/libstagefright/include/media/stagefright/ColorConverter.h
+++ b/media/libstagefright/include/media/stagefright/ColorConverter.h
@@ -33,6 +33,8 @@
 
     bool isValid() const;
 
+    bool isDstRGB() const;
+
     status_t convert(
             const void *srcBits,
             size_t srcWidth, size_t srcHeight,
diff --git a/media/libstagefright/include/media/stagefright/InterfaceUtils.h b/media/libstagefright/include/media/stagefright/InterfaceUtils.h
index 224c1f1..ceeaf31 100644
--- a/media/libstagefright/include/media/stagefright/InterfaceUtils.h
+++ b/media/libstagefright/include/media/stagefright/InterfaceUtils.h
@@ -17,13 +17,22 @@
 #ifndef INTERFACE_UTILS_H_
 #define INTERFACE_UTILS_H_
 
+#include <utils/RefBase.h>
 #include <media/MediaExtractor.h>
+#include <media/stagefright/RemoteMediaExtractor.h>
 #include <media/MediaSource.h>
 #include <media/IMediaExtractor.h>
 #include <media/IMediaSource.h>
 
 namespace android {
 
+class DataSource;
+class MediaExtractor;
+struct MediaSource;
+class IDataSource;
+class IMediaExtractor;
+class IMediaSource;
+
 // Creates a DataSource which wraps the given IDataSource object.
 sp<DataSource> CreateDataSourceFromIDataSource(const sp<IDataSource> &source);
 
@@ -32,14 +41,15 @@
 
 // Creates an IMediaExtractor wrapper to the given MediaExtractor.
 sp<IMediaExtractor> CreateIMediaExtractorFromMediaExtractor(
-        const sp<MediaExtractor> &extractor, const sp<RefBase> &plugin);
+        MediaExtractor *extractor, const sp<RefBase> &plugin);
 
 // Creates a MediaSource which wraps the given IMediaSource object.
 sp<MediaSource> CreateMediaSourceFromIMediaSource(const sp<IMediaSource> &source);
 
 // Creates an IMediaSource wrapper to the given MediaSource.
-sp<IMediaSource> CreateIMediaSourceFromMediaSource(
-        const sp<MediaSource> &source, const sp<RefBase> &plugin);
+sp<IMediaSource> CreateIMediaSourceFromMediaSourceBase(
+        const sp<RemoteMediaExtractor> &extractor,
+        MediaSourceBase *source, const sp<RefBase> &plugin);
 
 }  // namespace android
 
diff --git a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
index e5c67e1..4610359 100644
--- a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
+++ b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
@@ -22,6 +22,7 @@
 
 #include <media/IMediaExtractor.h>
 #include <media/MediaExtractor.h>
+#include <utils/List.h>
 
 namespace android {
 
diff --git a/media/libstagefright/include/media/stagefright/MetaData.h b/media/libstagefright/include/media/stagefright/MetaData.h
new file mode 120000
index 0000000..160f8d3
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/MetaData.h
@@ -0,0 +1 @@
+../../../../libmediaextractor/include/media/stagefright/MetaData.h
\ No newline at end of file
diff --git a/media/libstagefright/include/media/stagefright/MetaDataUtils.h b/media/libstagefright/include/media/stagefright/MetaDataUtils.h
new file mode 100644
index 0000000..7c18bc2
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/MetaDataUtils.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef META_DATA_UTILS_H_
+
+#define META_DATA_UTILS_H_
+
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+struct ABuffer;
+sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit);
+sp<MetaData> MakeAACCodecSpecificData(unsigned profile, unsigned sampling_freq_index,
+        unsigned channel_configuration);
+
+}  // namespace android
+
+#endif  // META_DATA_UTILS_H_
diff --git a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
index eed0f05..6a2e39b 100644
--- a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
+++ b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
@@ -85,6 +85,7 @@
     // readSampleData() reads the sample with the lowest timestamp.
     status_t readSampleData(const sp<ABuffer> &buffer);
 
+    status_t getSampleSize(size_t *sampleSize);
     status_t getSampleTrackIndex(size_t *trackIndex);
     status_t getSampleTime(int64_t *sampleTimeUs);
     status_t getSampleMeta(sp<MetaData> *sampleMeta);
diff --git a/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h b/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h
index 2bd71ee..78f3a84 100644
--- a/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h
+++ b/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h
@@ -19,6 +19,7 @@
 
 #include <media/IMediaExtractor.h>
 #include <media/MediaExtractor.h>
+#include <media/stagefright/foundation/ABase.h>
 
 namespace android {
 
@@ -27,7 +28,7 @@
 // IMediaExtractor wrapper to the MediaExtractor.
 class RemoteMediaExtractor : public BnMediaExtractor {
 public:
-    static sp<IMediaExtractor> wrap(const sp<MediaExtractor> &extractor, const sp<RefBase> &plugin);
+    static sp<IMediaExtractor> wrap(MediaExtractor *extractor, const sp<RefBase> &plugin);
 
     virtual ~RemoteMediaExtractor();
     virtual size_t countTracks();
@@ -43,12 +44,12 @@
     virtual void release();
 
 private:
-    sp<MediaExtractor> mExtractor;
+    MediaExtractor *mExtractor;
     sp<RefBase> mExtractorPlugin;
 
     MediaAnalyticsItem *mAnalyticsItem;
 
-    explicit RemoteMediaExtractor(const sp<MediaExtractor> &extractor, const sp<RefBase> &plugin);
+    explicit RemoteMediaExtractor(MediaExtractor *extractor, const sp<RefBase> &plugin);
 
     DISALLOW_EVIL_CONSTRUCTORS(RemoteMediaExtractor);
 };
diff --git a/media/libstagefright/include/media/stagefright/RemoteMediaSource.h b/media/libstagefright/include/media/stagefright/RemoteMediaSource.h
index cb222cc..d1afa6a 100644
--- a/media/libstagefright/include/media/stagefright/RemoteMediaSource.h
+++ b/media/libstagefright/include/media/stagefright/RemoteMediaSource.h
@@ -26,7 +26,10 @@
 // IMediaSrouce wrapper to the MediaSource.
 class RemoteMediaSource : public BnMediaSource {
 public:
-    static sp<IMediaSource> wrap(const sp<MediaSource> &source, const sp<RefBase> &plugin);
+    static sp<IMediaSource> wrap(
+            const sp<RemoteMediaExtractor> &extractor,
+            MediaSourceBase *source,
+            const sp<RefBase> &plugin);
     virtual ~RemoteMediaSource();
     virtual status_t start(MetaData *params = NULL);
     virtual status_t stop();
@@ -38,10 +41,14 @@
     virtual status_t setStopTimeUs(int64_t stopTimeUs);
 
 private:
-    sp<MediaSource> mSource;
+    sp<RemoteMediaExtractor> mExtractor;
+    MediaSourceBase *mSource;
     sp<RefBase> mExtractorPlugin;
 
-    explicit RemoteMediaSource(const sp<MediaSource> &source, const sp<RefBase> &plugin);
+    explicit RemoteMediaSource(
+            const sp<RemoteMediaExtractor> &extractor,
+            MediaSourceBase *source,
+            const sp<RefBase> &plugin);
 
     DISALLOW_EVIL_CONSTRUCTORS(RemoteMediaSource);
 };
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 464ee90..9b12b2d 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -23,6 +23,8 @@
 #include "ESQueue.h"
 
 #include <android/hardware/cas/native/1.0/IDescrambler.h>
+#include <binder/IMemory.h>
+#include <binder/MemoryDealer.h>
 #include <cutils/native_handle.h>
 #include <media/stagefright/foundation/ABitReader.h>
 #include <media/stagefright/foundation/ABuffer.h>
@@ -77,7 +79,7 @@
 
     void signalEOS(status_t finalResult);
 
-    sp<MediaSource> getSource(SourceType type);
+    sp<AnotherPacketSource> getSource(SourceType type);
     bool hasSource(SourceType type) const;
 
     int64_t convertPTSToTimestamp(uint64_t PTS);
@@ -170,7 +172,7 @@
     void signalEOS(status_t finalResult);
 
     SourceType getSourceType();
-    sp<MediaSource> getSource(SourceType type);
+    sp<AnotherPacketSource> getSource(SourceType type);
 
     bool isAudio() const;
     bool isVideo() const;
@@ -274,7 +276,7 @@
 ATSParser::SyncEvent::SyncEvent(off64_t offset)
     : mHasReturnedData(false), mOffset(offset), mTimeUs(0) {}
 
-void ATSParser::SyncEvent::init(off64_t offset, const sp<MediaSource> &source,
+void ATSParser::SyncEvent::init(off64_t offset, const sp<AnotherPacketSource> &source,
         int64_t timeUs, SourceType type) {
     mHasReturnedData = true;
     mOffset = offset;
@@ -641,9 +643,9 @@
     return mLastRecoveredPTS;
 }
 
-sp<MediaSource> ATSParser::Program::getSource(SourceType type) {
+sp<AnotherPacketSource> ATSParser::Program::getSource(SourceType type) {
     for (size_t i = 0; i < mStreams.size(); ++i) {
-        sp<MediaSource> source = mStreams.editValueAt(i)->getSource(type);
+        sp<AnotherPacketSource> source = mStreams.editValueAt(i)->getSource(type);
         if (source != NULL) {
             return source;
         }
@@ -1607,7 +1609,7 @@
     return NUM_SOURCE_TYPES;
 }
 
-sp<MediaSource> ATSParser::Stream::getSource(SourceType type) {
+sp<AnotherPacketSource> ATSParser::Stream::getSource(SourceType type) {
     switch (type) {
         case VIDEO:
         {
@@ -2042,11 +2044,11 @@
     return err;
 }
 
-sp<MediaSource> ATSParser::getSource(SourceType type) {
-    sp<MediaSource> firstSourceFound;
+sp<AnotherPacketSource> ATSParser::getSource(SourceType type) {
+    sp<AnotherPacketSource> firstSourceFound;
     for (size_t i = 0; i < mPrograms.size(); ++i) {
         const sp<Program> &program = mPrograms.editItemAt(i);
-        sp<MediaSource> source = program->getSource(type);
+        sp<AnotherPacketSource> source = program->getSource(type);
         if (source == NULL) {
             continue;
         }
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 6079afc..45ca06b 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -81,13 +81,13 @@
     struct SyncEvent {
         explicit SyncEvent(off64_t offset);
 
-        void init(off64_t offset, const sp<MediaSource> &source,
+        void init(off64_t offset, const sp<AnotherPacketSource> &source,
                 int64_t timeUs, SourceType type);
 
         bool hasReturnedData() const { return mHasReturnedData; }
         void reset();
         off64_t getOffset() const { return mOffset; }
-        const sp<MediaSource> &getMediaSource() const { return mMediaSource; }
+        const sp<AnotherPacketSource> &getMediaSource() const { return mMediaSource; }
         int64_t getTimeUs() const { return mTimeUs; }
         SourceType getType() const { return mType; }
 
@@ -100,7 +100,7 @@
          */
         off64_t mOffset;
         /* The media source object for this event. */
-        sp<MediaSource> mMediaSource;
+        sp<AnotherPacketSource> mMediaSource;
         /* The timestamp of the sync frame. */
         int64_t mTimeUs;
         SourceType mType;
@@ -126,7 +126,7 @@
 
     void signalEOS(status_t finalResult);
 
-    sp<MediaSource> getSource(SourceType type);
+    sp<AnotherPacketSource> getSource(SourceType type);
     bool hasSource(SourceType type) const;
 
     bool PTSTimeDeltaEstablished();
diff --git a/media/libstagefright/mpeg2ts/Android.bp b/media/libstagefright/mpeg2ts/Android.bp
index 7654eb3..0b2a48f 100644
--- a/media/libstagefright/mpeg2ts/Android.bp
+++ b/media/libstagefright/mpeg2ts/Android.bp
@@ -37,4 +37,8 @@
         "android.hardware.cas.native@1.0",
         "android.hidl.memory@1.0",
     ],
+
+    whole_static_libs: [
+        "libstagefright_metadatautils",
+    ],
 }
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index b621fd0..850face 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -29,6 +29,7 @@
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MetaData.h>
+#include <media/stagefright/MetaDataUtils.h>
 #include <media/cas/DescramblerAPI.h>
 #include <media/hardware/CryptoAPI.h>
 
diff --git a/media/libstagefright/tests/Android.bp b/media/libstagefright/tests/Android.bp
index 35119c2..e67a949 100644
--- a/media/libstagefright/tests/Android.bp
+++ b/media/libstagefright/tests/Android.bp
@@ -15,6 +15,7 @@
         "libcutils",
         "libgui",
         "libmedia",
+        "libmediaextractor",
         "libstagefright",
         "libstagefright_foundation",
         "libstagefright_omx",
diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp
index cea2f9e..4a36681 100644
--- a/media/ndk/Android.bp
+++ b/media/ndk/Android.bp
@@ -37,6 +37,7 @@
     srcs: [
         "NdkMediaCodec.cpp",
         "NdkMediaCrypto.cpp",
+        "NdkMediaDataSource.cpp",
         "NdkMediaExtractor.cpp",
         "NdkMediaFormat.cpp",
         "NdkMediaMuxer.cpp",
@@ -70,6 +71,7 @@
         "libmedia_omx",
         "libmedia_jni",
         "libmediadrm",
+        "libmediaextractor",
         "libstagefright",
         "libstagefright_foundation",
         "liblog",
diff --git a/media/ndk/NdkMediaDataSource.cpp b/media/ndk/NdkMediaDataSource.cpp
new file mode 100644
index 0000000..0cae6f4
--- /dev/null
+++ b/media/ndk/NdkMediaDataSource.cpp
@@ -0,0 +1,115 @@
+/*
+ * 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 "NdkMediaDataSource"
+
+#include "NdkMediaDataSourcePriv.h"
+
+#include <inttypes.h>
+#include <jni.h>
+#include <unistd.h>
+
+#include <binder/IServiceManager.h>
+#include <cutils/properties.h>
+#include <utils/Log.h>
+#include <utils/StrongPointer.h>
+#include <media/NdkMediaError.h>
+#include <media/NdkMediaDataSource.h>
+#include <media/stagefright/InterfaceUtils.h>
+
+#include "../../libstagefright/include/HTTPBase.h"
+#include "../../libstagefright/include/NuCachedSource2.h"
+
+using namespace android;
+
+struct AMediaDataSource {
+    void *userdata;
+    AMediaDataSourceReadAt readAt;
+    AMediaDataSourceGetSize getSize;
+};
+
+NdkDataSource::NdkDataSource(AMediaDataSource *dataSource)
+    : mDataSource(dataSource) {
+}
+
+status_t NdkDataSource::initCheck() const {
+    return OK;
+}
+
+ssize_t NdkDataSource::readAt(off64_t offset, void *data, size_t size) {
+    Mutex::Autolock l(mLock);
+    if (mDataSource->getSize == NULL || mDataSource->userdata == NULL) {
+        return -1;
+    }
+    return mDataSource->readAt(mDataSource->userdata, offset, data, size);
+}
+
+status_t NdkDataSource::getSize(off64_t *size) {
+    Mutex::Autolock l(mLock);
+    if (mDataSource->getSize == NULL || mDataSource->userdata == NULL) {
+        return NO_INIT;
+    }
+    if (size != NULL) {
+        *size = mDataSource->getSize(mDataSource->userdata);
+    }
+    return OK;
+}
+
+String8 NdkDataSource::toString() {
+    return String8::format("NdkDataSource(pid %d, uid %d)", getpid(), getuid());
+}
+
+String8 NdkDataSource::getMIMEType() const {
+    return String8("application/octet-stream");
+}
+
+extern "C" {
+
+EXPORT
+AMediaDataSource* AMediaDataSource_new() {
+    AMediaDataSource *mSource = new AMediaDataSource();
+    mSource->userdata = NULL;
+    mSource->readAt = NULL;
+    mSource->getSize = NULL;
+    return mSource;
+}
+
+EXPORT
+void AMediaDataSource_delete(AMediaDataSource *mSource) {
+    ALOGV("dtor");
+    if (mSource != NULL) {
+        delete mSource;
+    }
+}
+
+EXPORT
+void AMediaDataSource_setUserdata(AMediaDataSource *mSource, void *userdata) {
+    mSource->userdata = userdata;
+}
+
+EXPORT
+void AMediaDataSource_setReadAt(AMediaDataSource *mSource, AMediaDataSourceReadAt readAt) {
+    mSource->readAt = readAt;
+}
+
+EXPORT
+void AMediaDataSource_setGetSize(AMediaDataSource *mSource, AMediaDataSourceGetSize getSize) {
+    mSource->getSize = getSize;
+}
+
+} // extern "C"
+
diff --git a/media/ndk/NdkMediaDataSourcePriv.h b/media/ndk/NdkMediaDataSourcePriv.h
new file mode 100644
index 0000000..a1cb331
--- /dev/null
+++ b/media/ndk/NdkMediaDataSourcePriv.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+
+/*
+ * This file defines an NDK API.
+ * Do not remove methods.
+ * Do not change method signatures.
+ * Do not change the value of constants.
+ * Do not change the size of any of the classes defined in here.
+ * Do not reference types that are not part of the NDK.
+ * Do not #include files that aren't part of the NDK.
+ */
+
+#ifndef _NDK_MEDIA_DATASOURCE_PRIV_H
+#define _NDK_MEDIA_DATASOURCE_PRIV_H
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <media/DataSource.h>
+#include <media/NdkMediaDataSource.h>
+#include <utils/Mutex.h>
+#include <utils/String8.h>
+
+using namespace android;
+
+struct NdkDataSource : public DataSource {
+
+    NdkDataSource(AMediaDataSource *);
+
+    virtual status_t initCheck() const;
+    virtual ssize_t readAt(off64_t offset, void *data, size_t size);
+    virtual status_t getSize(off64_t *);
+    virtual String8 toString();
+    virtual String8 getMIMEType() const;
+
+private:
+
+    Mutex mLock;
+    AMediaDataSource *mDataSource;
+
+};
+
+#endif // _NDK_MEDIA_DATASOURCE_PRIV_H
+
diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp
index e677d00..ea43d2e 100644
--- a/media/ndk/NdkMediaExtractor.cpp
+++ b/media/ndk/NdkMediaExtractor.cpp
@@ -20,6 +20,7 @@
 
 #include <media/NdkMediaError.h>
 #include <media/NdkMediaExtractor.h>
+#include "NdkMediaDataSourcePriv.h"
 #include "NdkMediaFormatPriv.h"
 
 
@@ -50,7 +51,6 @@
 struct AMediaExtractor {
     sp<NuMediaExtractor> mImpl;
     sp<ABuffer> mPsshBuf;
-
 };
 
 extern "C" {
@@ -121,6 +121,18 @@
 }
 
 EXPORT
+media_status_t AMediaExtractor_setDataSourceCustom(AMediaExtractor* mData, AMediaDataSource *src) {
+    return translate_error(mData->mImpl->setDataSource(new NdkDataSource(src)));
+}
+
+EXPORT
+AMediaFormat* AMediaExtractor_getFileFormat(AMediaExtractor *mData) {
+    sp<AMessage> format;
+    mData->mImpl->getFileFormat(&format);
+    return AMediaFormat_fromMsg(&format);
+}
+
+EXPORT
 size_t AMediaExtractor_getTrackCount(AMediaExtractor *mData) {
     return mData->mImpl->countTracks();
 }
@@ -182,6 +194,16 @@
 }
 
 EXPORT
+ssize_t AMediaExtractor_getSampleSize(AMediaExtractor *mData) {
+    size_t sampleSize;
+    status_t err = mData->mImpl->getSampleSize(&sampleSize);
+    if (err != OK) {
+        return -1;
+    }
+    return sampleSize;
+}
+
+EXPORT
 uint32_t AMediaExtractor_getSampleFlags(AMediaExtractor *mData) {
     int sampleFlags = 0;
     sp<MetaData> meta;
@@ -379,6 +401,15 @@
             (size_t*) crypteddata);
 }
 
+EXPORT
+int64_t AMediaExtractor_getCachedDuration(AMediaExtractor *ex) {
+    bool eos;
+    int64_t durationUs;
+    if (ex->mImpl->getCachedDuration(&durationUs, &eos)) {
+        return durationUs;
+    }
+    return -1;
+}
 
 } // extern "C"
 
diff --git a/media/ndk/include/media/NdkMediaDataSource.h b/media/ndk/include/media/NdkMediaDataSource.h
new file mode 100644
index 0000000..752b684
--- /dev/null
+++ b/media/ndk/include/media/NdkMediaDataSource.h
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+
+/*
+ * This file defines an NDK API.
+ * Do not remove methods.
+ * Do not change method signatures.
+ * Do not change the value of constants.
+ * Do not change the size of any of the classes defined in here.
+ * Do not reference types that are not part of the NDK.
+ * Do not #include files that aren't part of the NDK.
+ */
+
+#ifndef _NDK_MEDIA_DATASOURCE_H
+#define _NDK_MEDIA_DATASOURCE_H
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <media/NdkMediaError.h>
+
+__BEGIN_DECLS
+
+struct AMediaDataSource;
+typedef struct AMediaDataSource AMediaDataSource;
+
+#if __ANDROID_API__ >= 28
+
+/*
+ * AMediaDataSource's callbacks will be invoked on an implementation-defined thread
+ * or thread pool. No guarantees are provided about which thread(s) will be used for
+ * callbacks. However, it is guaranteed that AMediaDataSource's callbacks will only
+ * ever be invoked by a single thread at a time.
+ *
+ * There will be a thread synchronization point between each call to ensure that
+ * modifications to the state of your AMediaDataSource are visible to future
+ * calls. This means you don't need to do your own synchronization unless you're
+ * modifying the AMediaDataSource from another thread while it's being used by the
+ * framework.
+ */
+
+/**
+ * Called to request data from the given |offset|.
+ *
+ * Implementations should should write up to |size| bytes into
+ * |buffer|, and return the number of bytes written.
+ *
+ * Return 0 if size is zero (thus no bytes are read).
+ *
+ * Return -1 to indicate that end of stream is reached.
+ */
+typedef ssize_t (*AMediaDataSourceReadAt)(
+        void *userdata, off64_t offset, void * buffer, size_t size);
+
+/**
+ * Called to get the size of the data source.
+ *
+ * Return the size of data source in bytes, or -1 if the size is unknown.
+ */
+typedef ssize_t (*AMediaDataSourceGetSize)(void *userdata);
+
+/**
+ * Create new media data source. Returns NULL if memory allocation
+ * for the new data source object fails.
+ */
+AMediaDataSource* AMediaDataSource_new();
+
+/**
+ * Delete a previously created media data source.
+ */
+void AMediaDataSource_delete(AMediaDataSource*);
+
+/**
+ * Set an user provided opaque handle. This opaque handle is passed as
+ * the first argument to the data source callbacks.
+ */
+void AMediaDataSource_setUserdata(
+        AMediaDataSource*, void *userdata);
+
+/**
+ * Set a custom callback for supplying random access media data to the
+ * NDK media framework.
+ *
+ * Implement this if your app has special requirements for the way media
+ * data is obtained, or if you need a callback when data is read by the
+ * NDK media framework.
+ *
+ * Please refer to the definition of AMediaDataSourceReadAt for
+ * additional details.
+ */
+void AMediaDataSource_setReadAt(
+        AMediaDataSource*,
+        AMediaDataSourceReadAt);
+
+/**
+ * Set a custom callback for supplying the size of the data source to the
+ * NDK media framework.
+ *
+ * Please refer to the definition of AMediaDataSourceGetSize for
+ * additional details.
+ */
+void AMediaDataSource_setGetSize(
+        AMediaDataSource*,
+        AMediaDataSourceGetSize);
+
+#endif  /*__ANDROID_API__ >= 28 */
+
+__END_DECLS
+
+#endif // _NDK_MEDIA_DATASOURCE_H
diff --git a/media/ndk/include/media/NdkMediaExtractor.h b/media/ndk/include/media/NdkMediaExtractor.h
index bf0e46d..3c9e23d 100644
--- a/media/ndk/include/media/NdkMediaExtractor.h
+++ b/media/ndk/include/media/NdkMediaExtractor.h
@@ -32,6 +32,7 @@
 #include <sys/types.h>
 
 #include "NdkMediaCodec.h"
+#include "NdkMediaDataSource.h"
 #include "NdkMediaFormat.h"
 #include "NdkMediaCrypto.h"
 
@@ -64,6 +65,15 @@
 media_status_t AMediaExtractor_setDataSource(AMediaExtractor*, const char *location);
         // TODO support headers
 
+#if __ANDROID_API__ >= 28
+
+/**
+ * Set the custom data source implementation from which the extractor will read.
+ */
+media_status_t AMediaExtractor_setDataSourceCustom(AMediaExtractor*, AMediaDataSource *src);
+
+#endif /* __ANDROID_API__ >= 28 */
+
 /**
  * Return the number of tracks in the previously specified media file
  */
@@ -152,12 +162,49 @@
 
 AMediaCodecCryptoInfo *AMediaExtractor_getSampleCryptoInfo(AMediaExtractor *);
 
-
 enum {
     AMEDIAEXTRACTOR_SAMPLE_FLAG_SYNC = 1,
     AMEDIAEXTRACTOR_SAMPLE_FLAG_ENCRYPTED = 2,
 };
 
+#if __ANDROID_API__ >= 28
+
+/**
+ * Returns the format of the extractor. The caller must free the returned format
+ * using AMediaFormat_delete(format).
+ *
+ * This function will always return a format; however, the format could be empty
+ * (no key-value pairs) if the media container does not provide format information.
+ */
+AMediaFormat* AMediaExtractor_getFileFormat(AMediaExtractor*);
+
+/**
+ * Returns the size of the current sample in bytes, or -1 when no samples are
+ * available (end of stream). This API can be used in in conjunction with
+ * AMediaExtractor_readSampleData:
+ *
+ * ssize_t sampleSize = AMediaExtractor_getSampleSize(ex);
+ * uint8_t *buf = new uint8_t[sampleSize];
+ * AMediaExtractor_readSampleData(ex, buf, sampleSize);
+ *
+ */
+ssize_t AMediaExtractor_getSampleSize(AMediaExtractor*);
+
+/**
+ * Returns the duration of cached media samples downloaded from a network data source
+ * (AMediaExtractor_setDataSource with a "http(s)" URI) in microseconds.
+ *
+ * This information is calculated using total bitrate; if total bitrate is not in the
+ * media container it is calculated using total duration and file size.
+ *
+ * Returns -1 when the extractor is not reading from a network data source, or when the
+ * cached duration cannot be calculated (bitrate, duration, and file size information
+ * not available).
+ */
+int64_t AMediaExtractor_getCachedDuration(AMediaExtractor *);
+
+#endif /* __ANDROID_API__ >= 28 */
+
 #endif /* __ANDROID_API__ >= 21 */
 
 __END_DECLS
diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt
index f2d97cd..37c557a 100644
--- a/media/ndk/libmediandk.map.txt
+++ b/media/ndk/libmediandk.map.txt
@@ -123,6 +123,11 @@
     AMediaCrypto_isCryptoSchemeSupported;
     AMediaCrypto_new;
     AMediaCrypto_requiresSecureDecoderComponent;
+    AMediaDataSource_delete;      # introduced=28
+    AMediaDataSource_new;         # introduced=28
+    AMediaDataSource_setGetSize;  # introduced=28
+    AMediaDataSource_setReadAt;   # introduced=28
+    AMediaDataSource_setUserdata; # introduced=28
     AMediaDrm_closeSession;
     AMediaDrm_createByUUID;
     AMediaDrm_decrypt;
@@ -148,9 +153,12 @@
     AMediaDrm_verify;
     AMediaExtractor_advance;
     AMediaExtractor_delete;
+    AMediaExtractor_getCachedDuration; # introduced=28
+    AMediaExtractor_getFileFormat;     # introduced=28
     AMediaExtractor_getPsshInfo;
     AMediaExtractor_getSampleCryptoInfo;
     AMediaExtractor_getSampleFlags;
+    AMediaExtractor_getSampleSize;     # introduced=28
     AMediaExtractor_getSampleTime;
     AMediaExtractor_getSampleTrackIndex;
     AMediaExtractor_getTrackCount;
@@ -160,6 +168,7 @@
     AMediaExtractor_seekTo;
     AMediaExtractor_selectTrack;
     AMediaExtractor_setDataSource;
+    AMediaExtractor_setDataSourceCustom; # introduced=28
     AMediaExtractor_setDataSourceFd;
     AMediaExtractor_unselectTrack;
     AMediaFormat_delete;
diff --git a/packages/MediaComponents/Android.mk b/packages/MediaComponents/Android.mk
index 61d73a0..7c7718e 100644
--- a/packages/MediaComponents/Android.mk
+++ b/packages/MediaComponents/Android.mk
@@ -27,7 +27,10 @@
 # TODO: Use System SDK once public APIs are approved
 # LOCAL_SDK_VERSION := system_current
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src) \
+    $(call all-Iaidl-files-under, src)
+
 LOCAL_PROGUARD_FLAG_FILES := proguard.cfg
 
 LOCAL_MULTILIB := first
diff --git a/packages/MediaComponents/res/layout/media_controller.xml b/packages/MediaComponents/res/layout/media_controller.xml
index 4d05546..dd56e7c 100644
--- a/packages/MediaComponents/res/layout/media_controller.xml
+++ b/packages/MediaComponents/res/layout/media_controller.xml
@@ -51,6 +51,7 @@
         <view class="com.android.support.mediarouter.app.MediaRouteButton" android:id="@+id/cast"
             android:layout_alignParentEnd="true"
             android:layout_centerVertical="true"
+            android:visibility="gone"
             style="@style/TitleBarButton" />
 
     </RelativeLayout>
@@ -136,7 +137,13 @@
             android:layout_height="wrap_content"
             android:layout_centerVertical="true"
             android:visibility="gone"
-            android:orientation="horizontal" >
+            android:orientation="horizontal"
+            android:gravity="center">
+
+            <LinearLayout
+                android:id="@+id/custom_buttons"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
 
             <ImageButton
                 android:id="@+id/mute"
diff --git a/packages/MediaComponents/res/values/attrs.xml b/packages/MediaComponents/res/values/attrs.xml
index 6175b11..e37285b 100644
--- a/packages/MediaComponents/res/values/attrs.xml
+++ b/packages/MediaComponents/res/values/attrs.xml
@@ -41,5 +41,4 @@
     <attr name="mediaRouteControlPanelThemeOverlay" format="reference" />
 
     <attr name="mediaRouteTheme" format="reference" />
-    <attr name="enableControlView" format="boolean" />
 </resources>
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
new file mode 100644
index 0000000..d5bd354
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media;
+
+import android.os.Bundle;
+import android.os.ResultReceiver;
+
+import com.android.media.IMediaSession2Callback;
+
+/**
+ * Interface to MediaSession2.
+ * <p>
+ * Keep this interface oneway. Otherwise a malicious app may implement fake version of this,
+ * and holds calls from session to make session owner(s) frozen.
+ */
+// TODO: Consider to make some methods oneway
+interface IMediaSession2 {
+    // TODO(jaewan): add onCommand() to send private command
+    // TODO(jaewan): Due to the nature of oneway calls, APIs can be called in out of order
+    //               Add id for individual calls to address this.
+
+    // TODO(jaewan): We may consider to add another binder just for the connection
+    //               not to expose other methods to the controller whose connection wasn't accepted.
+    //               But this would be enough for now because it's the same as existing
+    //               MediaBrowser and MediaBrowserService.
+    void connect(String callingPackage, IMediaSession2Callback callback);
+    void release(IMediaSession2Callback caller);
+
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    // send command
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    void sendCommand(IMediaSession2Callback caller, in Bundle command, in Bundle args);
+    void sendTransportControlCommand(IMediaSession2Callback caller,
+            int commandCode, in Bundle args);
+    void sendCustomCommand(IMediaSession2Callback caller, in Bundle command, in Bundle args,
+            in ResultReceiver receiver);
+
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    // Get library service specific
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    void getBrowserRoot(IMediaSession2Callback callback, in Bundle rootHints);
+}
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
new file mode 100644
index 0000000..aabbc69
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media;
+
+import android.os.Bundle;
+import android.os.ResultReceiver;
+
+import com.android.media.IMediaSession2;
+
+/**
+ * Interface from MediaSession2 to MediaSession2Record.
+ * <p>
+ * Keep this interface oneway. Otherwise a malicious app may implement fake version of this,
+ * and holds calls from session to make session owner(s) frozen.
+ */
+oneway interface IMediaSession2Callback {
+    void onPlaybackStateChanged(in Bundle state);
+    void onPlaylistChanged(in List<Bundle> playlist);
+    void onPlaylistParamsChanged(in Bundle params);
+
+    /**
+     * Called only when the controller is created with service's token.
+     *
+     * @param sessionBinder {@code null} if the connect is rejected or is disconnected. a session
+     *     binder if the connect is accepted.
+     * @param commands initially allowed commands.
+     */
+    // TODO(jaewan): Also need to pass flags for allowed actions for permission check.
+    //               For example, a media can allow setRating only for whitelisted apps
+    //               it's better for controller to know such information in advance.
+    //               Follow-up TODO: Add similar functions to the session.
+    // TODO(jaewan): Is term 'accepted/rejected' correct? For permission, 'grant' is used.
+    void onConnectionChanged(IMediaSession2 sessionBinder, in Bundle commandGroup);
+
+    void onCustomLayoutChanged(in List<Bundle> commandButtonlist);
+
+    void sendCustomCommand(in Bundle command, in Bundle args, in ResultReceiver receiver);
+
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    // Browser sepcific
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    void onGetRootResult(in Bundle rootHints, String rootMediaId, in Bundle rootExtra);
+}
diff --git a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
index f159398..3e6d98f 100644
--- a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
@@ -17,11 +17,10 @@
 package com.android.media;
 
 import android.content.Context;
-import android.media.IMediaSession2;
 import android.media.MediaBrowser2;
 import android.media.MediaBrowser2.BrowserCallback;
 import android.media.MediaSession2.CommandButton;
-import android.media.SessionToken;
+import android.media.SessionToken2;
 import android.media.update.MediaBrowser2Provider;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -37,15 +36,15 @@
     private final MediaBrowser2 mInstance;
     private final MediaBrowser2.BrowserCallback mCallback;
 
-    public MediaBrowser2Impl(MediaBrowser2 instance, Context context, SessionToken token,
-            BrowserCallback callback, Executor executor) {
-        super(instance, context, token, callback, executor);
+    public MediaBrowser2Impl(Context context, MediaBrowser2 instance, SessionToken2 token,
+            Executor executor, BrowserCallback callback) {
+        super(context, instance, token, executor, callback);
         mInstance = instance;
         mCallback = callback;
     }
 
     @Override
-    public void getBrowserRoot_impl(Bundle rootHints) {
+    public void getLibraryRoot_impl(Bundle rootHints) {
         final IMediaSession2 binder = getSessionBinder();
         if (binder != null) {
             try {
@@ -61,6 +60,31 @@
         }
     }
 
+    @Override
+    public void subscribe_impl(String parentId, Bundle options) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void unsubscribe_impl(String parentId, Bundle options) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void getItem_impl(String mediaId) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void getChildren_impl(String parentId, int page, int pageSize, Bundle options) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void search_impl(String query, int page, int pageSize, Bundle extras) {
+        // TODO(jaewan): Implement
+    }
+
     public void onGetRootResult(
             final Bundle rootHints, final String rootMediaId, final Bundle rootExtra) {
         getCallbackExecutor().execute(() -> {
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index 08a7165..41dfd00 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -16,30 +16,35 @@
 
 package com.android.media;
 
+import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.media.IMediaSession2;
-import android.media.IMediaSession2Callback;
+import android.media.MediaController2.PlaybackInfo;
+import android.media.MediaItem2;
 import android.media.MediaSession2;
 import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandButton;
 import android.media.MediaSession2.CommandGroup;
 import android.media.MediaController2;
 import android.media.MediaController2.ControllerCallback;
-import android.media.MediaPlayerBase;
+import android.media.MediaSession2.PlaylistParams;
 import android.media.MediaSessionService2;
-import android.media.SessionToken;
-import android.media.session.PlaybackState;
+import android.media.PlaybackState2;
+import android.media.Rating2;
+import android.media.SessionToken2;
 import android.media.update.MediaController2Provider;
+import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
 import android.support.annotation.GuardedBy;
 import android.util.Log;
 
+import com.android.media.MediaSession2Impl.CommandButtonImpl;
+
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
@@ -50,29 +55,25 @@
     private static final boolean DEBUG = true; // TODO(jaewan): Change
 
     private final MediaController2 mInstance;
-
-    /**
-     * Flag used by MediaController2Record to filter playback callback.
-     */
-    static final int CALLBACK_FLAG_PLAYBACK = 0x1;
-
-    static final int REQUEST_CODE_ALL = 0;
-
+    private final Context mContext;
     private final Object mLock = new Object();
 
-    private final Context mContext;
     private final MediaSession2CallbackStub mSessionCallbackStub;
-    private final SessionToken mToken;
+    private final SessionToken2 mToken;
     private final ControllerCallback mCallback;
     private final Executor mCallbackExecutor;
     private final IBinder.DeathRecipient mDeathRecipient;
 
     @GuardedBy("mLock")
-    private final List<PlaybackListenerHolder> mPlaybackListeners = new ArrayList<>();
-    @GuardedBy("mLock")
     private SessionServiceConnection mServiceConnection;
     @GuardedBy("mLock")
     private boolean mIsReleased;
+    @GuardedBy("mLock")
+    private PlaybackState2 mPlaybackState;
+    @GuardedBy("mLock")
+    private List<MediaItem2> mPlaylist;
+    @GuardedBy("mLock")
+    private PlaylistParams mPlaylistParams;
 
     // Assignment should be used with the lock hold, but should be used without a lock to prevent
     // potential deadlock.
@@ -84,10 +85,9 @@
 
     // TODO(jaewan): Require session activeness changed listener, because controller can be
     //               available when the session's player is null.
-    public MediaController2Impl(MediaController2 instance, Context context, SessionToken token,
-            ControllerCallback callback, Executor executor) {
+    public MediaController2Impl(Context context, MediaController2 instance, SessionToken2 token,
+            Executor executor, ControllerCallback callback) {
         mInstance = instance;
-
         if (context == null) {
             throw new IllegalArgumentException("context shouldn't be null");
         }
@@ -110,21 +110,28 @@
         };
 
         mSessionBinder = null;
+    }
 
-        if (token.getSessionBinder() == null) {
+    @Override
+    public void initialize() {
+        SessionToken2Impl impl = SessionToken2Impl.from(mToken);
+        // TODO(jaewan): More sanity checks.
+        if (impl.getSessionBinder() == null) {
+            // Session service
             mServiceConnection = new SessionServiceConnection();
             connectToService();
         } else {
+            // Session
             mServiceConnection = null;
-            connectToSession(token.getSessionBinder());
+            connectToSession(impl.getSessionBinder());
         }
     }
 
-    // Should be only called by constructor.
     private void connectToService() {
         // Service. Needs to get fresh binder whenever connection is needed.
+        SessionToken2Impl impl = SessionToken2Impl.from(mToken);
         final Intent intent = new Intent(MediaSessionService2.SERVICE_INTERFACE);
-        intent.setClassName(mToken.getPackageName(), mToken.getServiceName());
+        intent.setClassName(mToken.getPackageName(), impl.getServiceName());
 
         // Use bindService() instead of startForegroundService() to start session service for three
         // reasons.
@@ -161,7 +168,7 @@
     @Override
     public void close_impl() {
         if (DEBUG) {
-            Log.d(TAG, "relese from " + mToken);
+            Log.d(TAG, "release from " + mToken);
         }
         final IMediaSession2 binder;
         synchronized (mLock) {
@@ -174,7 +181,6 @@
                 mContext.unbindService(mServiceConnection);
                 mServiceConnection = null;
             }
-            mPlaybackListeners.clear();
             binder = mSessionBinder;
             mSessionBinder = null;
             mSessionCallbackStub.destroy();
@@ -204,8 +210,12 @@
         return mCallbackExecutor;
     }
 
+    Context getContext() {
+      return mContext;
+    }
+
     @Override
-    public SessionToken getSessionToken_impl() {
+    public SessionToken2 getSessionToken_impl() {
         return mToken;
     }
 
@@ -217,38 +227,38 @@
 
     @Override
     public void play_impl() {
-        sendCommand(MediaSession2.COMMAND_CODE_PLAYBACK_START);
+        sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_PLAY);
     }
 
     @Override
     public void pause_impl() {
-        sendCommand(MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE);
+        sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE);
     }
 
     @Override
     public void stop_impl() {
-        sendCommand(MediaSession2.COMMAND_CODE_PLAYBACK_STOP);
+        sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_STOP);
     }
 
     @Override
     public void skipToPrevious_impl() {
-        sendCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM);
+        sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM);
     }
 
     @Override
     public void skipToNext_impl() {
-        sendCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM);
+        sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM);
     }
 
-    private void sendCommand(int code) {
-        // TODO(jaewan): optimization) Cache Command objects?
-        Command command = new Command(code);
-        // TODO(jaewan): Check if the command is in the allowed group.
+    private void sendTransportControlCommand(int commandCode) {
+        sendTransportControlCommand(commandCode, null);
+    }
 
+    private void sendTransportControlCommand(int commandCode, Bundle args) {
         final IMediaSession2 binder = mSessionBinder;
         if (binder != null) {
             try {
-                binder.sendCommand(mSessionCallbackStub, command.toBundle(), null);
+                binder.sendTransportControlCommand(mSessionCallbackStub, commandCode, args);
             } catch (RemoteException e) {
                 Log.w(TAG, "Cannot connect to the service or the session is gone", e);
             }
@@ -257,92 +267,205 @@
         }
     }
 
+    //////////////////////////////////////////////////////////////////////////////////////
+    // TODO(jaewan): Implement follows
+    //////////////////////////////////////////////////////////////////////////////////////
     @Override
-    public PlaybackState getPlaybackState_impl() {
-        final IMediaSession2 binder = mSessionBinder;
-        if (binder != null) {
-            try {
-                return binder.getPlaybackState();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
-            }
-        } else {
-            Log.w(TAG, "Session isn't active", new IllegalStateException());
-        }
-        // TODO(jaewan): What to return for error case?
+    public PendingIntent getSessionActivity_impl() {
+        // TODO(jaewan): Implement
         return null;
     }
 
     @Override
-    public void addPlaybackListener_impl(
-            MediaPlayerBase.PlaybackListener listener, Handler handler) {
-        if (listener == null) {
-            throw new IllegalArgumentException("listener shouldn't be null");
+    public int getRatingType_impl() {
+        // TODO(jaewan): Implement
+        return 0;
+    }
+
+    @Override
+    public void setVolumeTo_impl(int value, int flags) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void adjustVolume_impl(int direction, int flags) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public PlaybackInfo getPlaybackInfo_impl() {
+        // TODO(jaewan): Implement
+        return null;
+    }
+
+    @Override
+    public void prepareFromUri_impl(Uri uri, Bundle extras) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void prepareFromSearch_impl(String query, Bundle extras) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void prepareMediaId_impl(String mediaId, Bundle extras) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void playFromSearch_impl(String query, Bundle extras) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void playFromUri_impl(String uri, Bundle extras) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void playFromMediaId_impl(String mediaId, Bundle extras) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void setRating_impl(Rating2 rating) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void sendCustomCommand_impl(Command command, Bundle args, ResultReceiver cb) {
+        if (command == null) {
+            throw new IllegalArgumentException("command shouldn't be null");
         }
-        if (handler == null) {
-            throw new IllegalArgumentException("handler shouldn't be null");
-        }
-        boolean registerCallback;
-        synchronized (mLock) {
-            if (PlaybackListenerHolder.contains(mPlaybackListeners, listener)) {
-                throw new IllegalArgumentException("listener is already added. Ignoring.");
+        // TODO(jaewan): Also check if the command is allowed.
+        final IMediaSession2 binder = mSessionBinder;
+        if (binder != null) {
+            try {
+                binder.sendCustomCommand(mSessionCallbackStub, command.toBundle(), args, cb);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
             }
-            registerCallback = mPlaybackListeners.isEmpty();
-            mPlaybackListeners.add(new PlaybackListenerHolder(listener, handler));
-        }
-        if (registerCallback) {
-            registerCallbackForPlaybackNotLocked();
+        } else {
+            Log.w(TAG, "Session isn't active", new IllegalStateException());
         }
     }
 
     @Override
-    public void removePlaybackListener_impl(MediaPlayerBase.PlaybackListener listener) {
-        if (listener == null) {
-            throw new IllegalArgumentException("listener shouldn't be null");
-        }
-        boolean unregisterCallback;
+    public List<MediaItem2> getPlaylist_impl() {
         synchronized (mLock) {
-            int idx = PlaybackListenerHolder.indexOf(mPlaybackListeners, listener);
-            if (idx >= 0) {
-                mPlaybackListeners.get(idx).removeCallbacksAndMessages(null);
-                mPlaybackListeners.remove(idx);
-            }
-            unregisterCallback = mPlaybackListeners.isEmpty();
+            return mPlaylist;
         }
-        if (unregisterCallback) {
-            final IMediaSession2 binder = mSessionBinder;
-            if (binder != null) {
-                // Lazy unregister
-                try {
-                    binder.unregisterCallback(mSessionCallbackStub, CALLBACK_FLAG_PLAYBACK);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Cannot connect to the service or the session is gone", e);
-                }
-            }
+    }
+
+    @Override
+    public void prepare_impl() {
+        sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_PREPARE);
+    }
+
+    @Override
+    public void fastForward_impl() {
+        sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_FAST_FORWARD);
+    }
+
+    @Override
+    public void rewind_impl() {
+        sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_REWIND);
+    }
+
+    @Override
+    public void seekTo_impl(long pos) {
+        Bundle args = new Bundle();
+        args.putLong(MediaSession2Stub.ARGUMENT_KEY_POSITION, pos);
+        sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SEEK_TO, args);
+    }
+
+    @Override
+    public void setCurrentPlaylistItem_impl(int index) {
+        Bundle args = new Bundle();
+        args.putInt(MediaSession2Stub.ARGUMENT_KEY_ITEM_INDEX, index);
+        sendTransportControlCommand(
+                MediaSession2.COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM, args);
+    }
+
+    @Override
+    public PlaybackState2 getPlaybackState_impl() {
+        synchronized (mLock) {
+            return mPlaybackState;
         }
     }
 
+    @Override
+    public void removePlaylistItem_impl(MediaItem2 index) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void addPlaylistItem_impl(int index, MediaItem2 item) {
+    // TODO(jaewan): Implement
+    }
+
+    @Override
+    public PlaylistParams getPlaylistParams_impl() {
+        synchronized (mLock) {
+            return mPlaylistParams;
+        }
+    }
+
+    @Override
+    public void setPlaylistParams_impl(PlaylistParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException("PlaylistParams should not be null!");
+        }
+        Bundle args = new Bundle();
+        args.putBundle(MediaSession2Stub.ARGUMENT_KEY_PLAYLIST_PARAMS, params.toBundle());
+        sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS, args);
+    }
+
     ///////////////////////////////////////////////////
     // Protected or private methods
     ///////////////////////////////////////////////////
-    // Should be used without a lock to prevent potential deadlock.
-    private void registerCallbackForPlaybackNotLocked() {
-        final IMediaSession2 binder = mSessionBinder;
-        if (binder != null) {
-            try {
-                binder.registerCallback(mSessionCallbackStub,
-                        CALLBACK_FLAG_PLAYBACK, REQUEST_CODE_ALL);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Cannot connect to the service or the session is gone", e);
-            }
+    private void pushPlaybackStateChanges(final PlaybackState2 state) {
+        synchronized (mLock) {
+            mPlaybackState = state;
         }
+        mCallbackExecutor.execute(() -> {
+            if (!mInstance.isConnected()) {
+                return;
+            }
+            mCallback.onPlaybackStateChanged(state);
+        });
     }
 
-    private void pushPlaybackStateChanges(final PlaybackState state) {
+    private void pushPlaylistParamsChanges(final PlaylistParams params) {
         synchronized (mLock) {
-            for (int i = 0; i < mPlaybackListeners.size(); i++) {
-                mPlaybackListeners.get(i).postPlaybackChange(state);
+            mPlaylistParams = params;
+        }
+        mCallbackExecutor.execute(() -> {
+            if (!mInstance.isConnected()) {
+                return;
             }
+            mCallback.onPlaylistParamsChanged(params);
+        });
+    }
+
+    private void pushPlaylistChanges(final List<Bundle> list) {
+        final List<MediaItem2> playlist = new ArrayList<>();
+        for (int i = 0; i < list.size(); i++) {
+            MediaItem2 item = MediaItem2.fromBundle(mContext, list.get(i));
+            if (item != null) {
+                playlist.add(item);
+            }
+        }
+
+        synchronized (mLock) {
+            mPlaylist = playlist;
+            mCallbackExecutor.execute(() -> {
+                if (!mInstance.isConnected()) {
+                    return;
+                }
+                mCallback.onPlaylistChanged(playlist);
+            });
         }
     }
 
@@ -361,7 +484,6 @@
                 release = true;
                 return;
             }
-            boolean registerCallbackForPlaybackNeeded;
             synchronized (mLock) {
                 if (mIsReleased) {
                     return;
@@ -384,15 +506,11 @@
                     release = true;
                     return;
                 }
-                registerCallbackForPlaybackNeeded = !mPlaybackListeners.isEmpty();
             }
             // TODO(jaewan): Keep commands to prevents illegal API calls.
             mCallbackExecutor.execute(() -> {
                 mCallback.onConnected(commandGroup);
             });
-            if (registerCallbackForPlaybackNeeded) {
-                registerCallbackForPlaybackNotLocked();
-            }
         } finally {
             if (release) {
                 // Trick to call release() without holding the lock, to prevent potential deadlock
@@ -402,6 +520,17 @@
         }
     }
 
+    private void onCustomCommand(final Command command, final Bundle args,
+            final ResultReceiver receiver) {
+        if (DEBUG) {
+            Log.d(TAG, "onCustomCommand cmd=" + command);
+        }
+        mCallbackExecutor.execute(() -> {
+            // TODO(jaewan): Double check if the controller exists.
+            mCallback.onCustomCommand(command, args, receiver);
+        });
+    }
+
     // TODO(jaewan): Pull out this from the controller2, and rename it to the MediaController2Stub
     //               or MediaBrowser2Stub.
     static class MediaSession2CallbackStub extends IMediaSession2Callback.Stub {
@@ -433,9 +562,44 @@
         }
 
         @Override
-        public void onPlaybackStateChanged(PlaybackState state) throws RuntimeException {
-            final MediaController2Impl controller = getController();
-            controller.pushPlaybackStateChanges(state);
+        public void onPlaybackStateChanged(Bundle state) throws RuntimeException {
+            final MediaController2Impl controller;
+            try {
+                controller = getController();
+            } catch (IllegalStateException e) {
+                Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+                return;
+            }
+            controller.pushPlaybackStateChanges(
+                    PlaybackState2.fromBundle(controller.getContext(), state));
+        }
+
+        @Override
+        public void onPlaylistChanged(List<Bundle> playlist) throws RuntimeException {
+            final MediaController2Impl controller;
+            try {
+                controller = getController();
+            } catch (IllegalStateException e) {
+                Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+                return;
+            }
+            if (playlist == null) {
+                return;
+            }
+            controller.pushPlaylistChanges(playlist);
+        }
+
+        @Override
+        public void onPlaylistParamsChanged(Bundle params) throws RuntimeException {
+            final MediaController2Impl controller;
+            try {
+                controller = getController();
+            } catch (IllegalStateException e) {
+                Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+                return;
+            }
+            controller.pushPlaylistParamsChanges(
+                    PlaylistParams.fromBundle(controller.getContext(), params));
         }
 
         @Override
@@ -449,7 +613,7 @@
                 return;
             }
             controller.onConnectionChangedNotLocked(
-                    sessionBinder, CommandGroup.fromBundle(commandGroup));
+                    sessionBinder, CommandGroup.fromBundle(controller.getContext(), commandGroup));
         }
 
         @Override
@@ -488,13 +652,30 @@
             }
             List<CommandButton> layout = new ArrayList<>();
             for (int i = 0; i < commandButtonlist.size(); i++) {
-                CommandButton button = CommandButton.fromBundle(commandButtonlist.get(i));
+                CommandButton button = CommandButtonImpl.fromBundle(
+                        browser.getContext(), commandButtonlist.get(i));
                 if (button != null) {
                     layout.add(button);
                 }
             }
             browser.onCustomLayoutChanged(layout);
         }
+
+        @Override
+        public void sendCustomCommand(Bundle commandBundle, Bundle args, ResultReceiver receiver) {
+            final MediaController2Impl controller;
+            try {
+                controller = getController();
+            } catch (IllegalStateException e) {
+                Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+                return;
+            }
+            Command command = Command.fromBundle(controller.getContext(), commandBundle);
+            if (command == null) {
+                return;
+            }
+            controller.onCustomCommand(command, args, receiver);
+        }
     }
 
     // This will be called on the main thread.
diff --git a/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java b/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
new file mode 100644
index 0000000..f51e246
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media;
+
+import static android.media.MediaItem2.FLAG_BROWSABLE;
+import static android.media.MediaItem2.FLAG_PLAYABLE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.DataSourceDesc;
+import android.media.MediaItem2;
+import android.media.MediaItem2.Flags;
+import android.media.MediaMetadata2;
+import android.media.update.MediaItem2Provider;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+public class MediaItem2Impl implements MediaItem2Provider {
+    private static final String KEY_ID = "android.media.mediaitem2.id";
+    private static final String KEY_FLAGS = "android.media.mediaitem2.flags";
+    private static final String KEY_METADATA = "android.media.mediaitem2.metadata";
+
+    private final Context mContext;
+    private final MediaItem2 mInstance;
+    private final String mId;
+    private final int mFlags;
+    private MediaMetadata2 mMetadata;
+    private DataSourceDesc mDataSourceDesc;
+
+    // From the public API
+    public MediaItem2Impl(Context context, MediaItem2 instance, String mediaId,
+            DataSourceDesc dsd, MediaMetadata2 metadata, @Flags int flags) {
+        if (mediaId == null) {
+            throw new IllegalArgumentException("mediaId shouldn't be null");
+        }
+        if (dsd == null) {
+            throw new IllegalArgumentException("dsd shouldn't be null");
+        }
+        if (metadata != null && !TextUtils.equals(mediaId, metadata.getMediaId())) {
+            throw new IllegalArgumentException("metadata's id should be match with the mediaid");
+        }
+
+        mContext = context;
+        mInstance = instance;
+
+        mId = mediaId;
+        mDataSourceDesc = dsd;
+        mMetadata = metadata;
+        mFlags = flags;
+    }
+
+    // Create anonymized version
+    public MediaItem2Impl(Context context, String mediaId, MediaMetadata2 metadata,
+            @Flags int flags) {
+        if (mediaId == null) {
+            throw new IllegalArgumentException("mediaId shouldn't be null");
+        }
+        if (metadata != null && !TextUtils.equals(mediaId, metadata.getMediaId())) {
+            throw new IllegalArgumentException("metadata's id should be match with the mediaid");
+        }
+        mContext =context;
+        mId = mediaId;
+        mMetadata = metadata;
+        mFlags = flags;
+        mInstance = new MediaItem2(this);
+    }
+
+    /**
+     * Return this object as a bundle to share between processes.
+     *
+     * @return a new bundle instance
+     */
+    public Bundle toBundle_impl() {
+        Bundle bundle = new Bundle();
+        bundle.putString(KEY_ID, mId);
+        bundle.putInt(KEY_FLAGS, mFlags);
+        if (mMetadata != null) {
+            bundle.putBundle(KEY_METADATA, mMetadata.toBundle());
+        }
+        return bundle;
+    }
+
+    public static MediaItem2 fromBundle(Context context, Bundle bundle) {
+        if (bundle == null) {
+            return null;
+        }
+        final String id = bundle.getString(KEY_ID);
+        final Bundle metadataBundle = bundle.getBundle(KEY_METADATA);
+        final MediaMetadata2 metadata = metadataBundle != null
+                ? MediaMetadata2.fromBundle(context, metadataBundle) : null;
+        final int flags = bundle.getInt(KEY_FLAGS);
+        return new MediaItem2Impl(context, id, metadata, flags).getInstance();
+    }
+
+    private MediaItem2 getInstance() {
+        return mInstance;
+    }
+
+    @Override
+    public String toString_impl() {
+        final StringBuilder sb = new StringBuilder("MediaItem2{");
+        sb.append("mFlags=").append(mFlags);
+        sb.append(", mMetadata=").append(mMetadata);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public @Flags int getFlags_impl() {
+        return mFlags;
+    }
+
+    @Override
+    public boolean isBrowsable_impl() {
+        return (mFlags & FLAG_BROWSABLE) != 0;
+    }
+
+    @Override
+    public boolean isPlayable_impl() {
+        return (mFlags & FLAG_PLAYABLE) != 0;
+    }
+
+    @Override
+    public void setMetadata_impl(@NonNull MediaMetadata2 metadata) {
+        if (metadata == null) {
+            throw new IllegalArgumentException("metadata shouldn't be null");
+        }
+        if (TextUtils.isEmpty(metadata.getMediaId())) {
+            throw new IllegalArgumentException("metadata must have a non-empty media id");
+        }
+        mMetadata = metadata;
+    }
+
+    @Override
+    public MediaMetadata2 getMetadata_impl() {
+        return mMetadata;
+    }
+
+    @Override
+    public @Nullable String getMediaId_impl() {
+        return mMetadata.getMediaId();
+    }
+
+    @Override
+    public @Nullable DataSourceDesc getDataSourceDesc_impl() {
+        return mDataSourceDesc;
+    }
+}
diff --git a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
index 430ab4c..54c8d41 100644
--- a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
@@ -16,12 +16,25 @@
 
 package com.android.media;
 
-import android.content.Intent;
+import android.app.PendingIntent;
+import android.content.Context;
 import android.media.MediaLibraryService2;
+import android.media.MediaLibraryService2.LibraryRoot;
 import android.media.MediaLibraryService2.MediaLibrarySession;
+import android.media.MediaLibraryService2.MediaLibrarySessionBuilder;
+import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
+import android.media.MediaPlayerInterface;
 import android.media.MediaSession2;
+import android.media.MediaSession2.ControllerInfo;
 import android.media.MediaSessionService2;
+import android.media.SessionToken2;
+import android.media.VolumeProvider;
 import android.media.update.MediaLibraryService2Provider;
+import android.os.Bundle;
+
+import com.android.media.MediaSession2Impl.BuilderBaseImpl;
+
+import java.util.concurrent.Executor;
 
 public class MediaLibraryService2Impl extends MediaSessionService2Impl implements
         MediaLibraryService2Provider {
@@ -46,9 +59,85 @@
     }
 
     @Override
-    Intent createServiceIntent() {
-        Intent serviceIntent = new Intent(mInstance, mInstance.getClass());
-        serviceIntent.setAction(MediaLibraryService2.SERVICE_INTERFACE);
-        return serviceIntent;
+    int getSessionType() {
+        return SessionToken2.TYPE_LIBRARY_SERVICE;
     }
-}
+
+    public static class MediaLibrarySessionImpl extends MediaSession2Impl
+            implements MediaLibrarySessionProvider {
+        private final MediaLibrarySessionCallback mCallback;
+
+        public MediaLibrarySessionImpl(Context context,
+                MediaPlayerInterface player, String id, VolumeProvider volumeProvider,
+                int ratingType, PendingIntent sessionActivity, Executor callbackExecutor,
+                MediaLibrarySessionCallback callback) {
+            super(context, player, id, volumeProvider, ratingType, sessionActivity,
+                    callbackExecutor, callback);
+            mCallback = callback;
+        }
+
+        @Override
+        MediaLibrarySession createInstance() {
+            return new MediaLibrarySession(this);
+        }
+
+        @Override
+        MediaLibrarySession getInstance() {
+            return (MediaLibrarySession) super.getInstance();
+        }
+
+        @Override
+        public void notifyChildrenChanged_impl(ControllerInfo controller, String parentId,
+                Bundle options) {
+            // TODO(jaewan): Implements
+        }
+
+        @Override
+        public void notifyChildrenChanged_impl(String parentId, Bundle options) {
+            // TODO(jaewan): Implements
+        }
+    }
+
+    public static class BuilderImpl
+            extends BuilderBaseImpl<MediaLibrarySession, MediaLibrarySessionCallback> {
+        public BuilderImpl(Context context, MediaLibrarySessionBuilder instance,
+                MediaPlayerInterface player, Executor callbackExecutor,
+                MediaLibrarySessionCallback callback) {
+            super(context, player);
+            setSessionCallback_impl(callbackExecutor, callback);
+        }
+
+        @Override
+        public MediaLibrarySession build_impl() {
+            return new MediaLibrarySessionImpl(mContext, mPlayer, mId, mVolumeProvider, mRatingType,
+                    mSessionActivity, mCallbackExecutor, mCallback).getInstance();
+        }
+    }
+
+    public static final class LibraryRootImpl implements LibraryRootProvider {
+        private final LibraryRoot mInstance;
+        private final String mRootId;
+        private final Bundle mExtras;
+
+        public LibraryRootImpl(Context context, LibraryRoot instance, String rootId,
+                Bundle extras) {
+            if (rootId == null) {
+                throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " +
+                        "Use null for BrowserRoot instead.");
+            }
+            mInstance = instance;
+            mRootId = rootId;
+            mExtras = extras;
+        }
+
+        @Override
+        public String getRootId_impl() {
+            return mRootId;
+        }
+
+        @Override
+        public Bundle getExtras_impl() {
+            return mExtras;
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/MediaComponents/src/com/android/media/MediaMetadata2Impl.java b/packages/MediaComponents/src/com/android/media/MediaMetadata2Impl.java
new file mode 100644
index 0000000..9088029
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/MediaMetadata2Impl.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media;
+
+import static android.media.MediaMetadata2.*;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.media.MediaMetadata2;
+import android.media.MediaMetadata2.BitmapKey;
+import android.media.MediaMetadata2.Builder;
+import android.media.MediaMetadata2.LongKey;
+import android.media.MediaMetadata2.RatingKey;
+import android.media.MediaMetadata2.TextKey;
+import android.media.Rating2;
+import android.media.update.MediaMetadata2Provider;
+import android.os.Bundle;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import java.util.Set;
+
+public class MediaMetadata2Impl implements MediaMetadata2Provider {
+    private static final String TAG = "MediaMetadata2";
+
+    /**
+     * A {@link Bundle} extra.
+     * @hide
+     */
+    public static final String METADATA_KEY_EXTRA = "android.media.metadata.EXTRA";
+
+    static final int METADATA_TYPE_LONG = 0;
+    static final int METADATA_TYPE_TEXT = 1;
+    static final int METADATA_TYPE_BITMAP = 2;
+    static final int METADATA_TYPE_RATING = 3;
+    static final ArrayMap<String, Integer> METADATA_KEYS_TYPE;
+
+    static {
+        METADATA_KEYS_TYPE = new ArrayMap<String, Integer>();
+        METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DURATION, METADATA_TYPE_LONG);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_AUTHOR, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_WRITER, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_COMPOSER, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_COMPILATION, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DATE, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_YEAR, METADATA_TYPE_LONG);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_TRACK_NUMBER, METADATA_TYPE_LONG);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_NUM_TRACKS, METADATA_TYPE_LONG);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ARTIST, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_ART_URI, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART, METADATA_TYPE_BITMAP);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART_URI, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_USER_RATING, METADATA_TYPE_RATING);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_RATING, METADATA_TYPE_RATING);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_TITLE, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_SUBTITLE, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON, METADATA_TYPE_BITMAP);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON_URI, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_ID, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_BT_FOLDER_TYPE, METADATA_TYPE_LONG);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_URI, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_ADVERTISEMENT, METADATA_TYPE_LONG);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DOWNLOAD_STATUS, METADATA_TYPE_LONG);
+    }
+
+    private static final @TextKey
+    String[] PREFERRED_DESCRIPTION_ORDER = {
+            METADATA_KEY_TITLE,
+            METADATA_KEY_ARTIST,
+            METADATA_KEY_ALBUM,
+            METADATA_KEY_ALBUM_ARTIST,
+            METADATA_KEY_WRITER,
+            METADATA_KEY_AUTHOR,
+            METADATA_KEY_COMPOSER
+    };
+
+    private static final @BitmapKey
+    String[] PREFERRED_BITMAP_ORDER = {
+            METADATA_KEY_DISPLAY_ICON,
+            METADATA_KEY_ART,
+            METADATA_KEY_ALBUM_ART
+    };
+
+    private static final @TextKey
+    String[] PREFERRED_URI_ORDER = {
+            METADATA_KEY_DISPLAY_ICON_URI,
+            METADATA_KEY_ART_URI,
+            METADATA_KEY_ALBUM_ART_URI
+    };
+
+    private final Context mContext;
+    private final MediaMetadata2 mInstance;
+    private final Bundle mBundle;
+
+    public MediaMetadata2Impl(Context context, Bundle bundle) {
+        mContext = context;
+        mInstance = new MediaMetadata2(this);
+        mBundle = bundle;
+    }
+
+    public MediaMetadata2 getInstance() {
+        return mInstance;
+    }
+
+    @Override
+    public boolean containsKey_impl(String key) {
+        return mBundle.containsKey(key);
+    }
+
+    @Override
+    public CharSequence getText_impl(@TextKey String key) {
+        return mBundle.getCharSequence(key);
+    }
+
+    @Override
+    public @Nullable String getMediaId_impl() {
+        return mInstance.getString(METADATA_KEY_MEDIA_ID);
+    }
+
+    @Override
+    public String getString_impl(@TextKey String key) {
+        CharSequence text = mBundle.getCharSequence(key);
+        if (text != null) {
+            return text.toString();
+        }
+        return null;
+    }
+
+    @Override
+    public long getLong_impl(@LongKey String key) {
+        return mBundle.getLong(key, 0);
+    }
+
+    @Override
+    public Rating2 getRating_impl(@RatingKey String key) {
+        // TODO(jaewan): Add backward compatibility
+        Rating2 rating = null;
+        try {
+            rating = Rating2.fromBundle(mContext, mBundle.getBundle(key));
+        } catch (Exception e) {
+            // ignore, value was not a rating
+            Log.w(TAG, "Failed to retrieve a key as Rating.", e);
+        }
+        return rating;
+    }
+
+    @Override
+    public Bitmap getBitmap_impl(@BitmapKey String key) {
+        Bitmap bmp = null;
+        try {
+            bmp = mBundle.getParcelable(key);
+        } catch (Exception e) {
+            // ignore, value was not a bitmap
+            Log.w(TAG, "Failed to retrieve a key as Bitmap.", e);
+        }
+        return bmp;
+    }
+
+    @Override
+    public Bundle getExtra_impl() {
+        try {
+            return mBundle.getBundle(METADATA_KEY_EXTRA);
+        } catch (Exception e) {
+            // ignore, value was not an bundle
+            Log.w(TAG, "Failed to retrieve an extra");
+        }
+        return null;
+    }
+
+    @Override
+    public int size_impl() {
+        return mBundle.size();
+    }
+
+    @Override
+    public Set<String> keySet_impl() {
+        return mBundle.keySet();
+    }
+
+    @Override
+    public Bundle toBundle_impl() {
+        return mBundle;
+    }
+
+    public static MediaMetadata2 fromBundle(Context context, Bundle bundle) {
+        return new MediaMetadata2Impl(context, bundle).getInstance();
+    }
+
+    public static final class BuilderImpl implements MediaMetadata2Provider.BuilderProvider {
+        private final Context mContext;
+        private final MediaMetadata2.Builder mInstance;
+        private final Bundle mBundle;
+
+        public BuilderImpl(Context context, MediaMetadata2.Builder instance) {
+            mContext = context;
+            mInstance = instance;
+            mBundle = new Bundle();
+        }
+
+        public BuilderImpl(Context context, MediaMetadata2.Builder instance, MediaMetadata2 source) {
+            if (source == null) {
+                throw new IllegalArgumentException("source shouldn't be null");
+            }
+            mContext = context;
+            mInstance = instance;
+            mBundle = new Bundle(source.toBundle());
+        }
+
+        public BuilderImpl(Context context, int maxBitmapSize) {
+            mContext = context;
+            mInstance = new MediaMetadata2.Builder(this);
+            mBundle = new Bundle();
+
+            for (String key : mBundle.keySet()) {
+                Object value = mBundle.get(key);
+                if (value instanceof Bitmap) {
+                    Bitmap bmp = (Bitmap) value;
+                    if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) {
+                        mInstance.putBitmap(key, scaleBitmap(bmp, maxBitmapSize));
+                    }
+                }
+            }
+        }
+
+        @Override
+        public Builder putText_impl(@TextKey String key, CharSequence value) {
+            if (METADATA_KEYS_TYPE.containsKey(key)) {
+                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
+                    throw new IllegalArgumentException("The " + key
+                            + " key cannot be used to put a CharSequence");
+                }
+            }
+            mBundle.putCharSequence(key, value);
+            return mInstance;
+        }
+
+        @Override
+        public Builder putString_impl(@TextKey String key, String value) {
+            if (METADATA_KEYS_TYPE.containsKey(key)) {
+                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
+                    throw new IllegalArgumentException("The " + key
+                            + " key cannot be used to put a String");
+                }
+            }
+            mBundle.putCharSequence(key, value);
+            return mInstance;
+        }
+
+        @Override
+        public Builder putLong_impl(@LongKey String key, long value) {
+            if (METADATA_KEYS_TYPE.containsKey(key)) {
+                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_LONG) {
+                    throw new IllegalArgumentException("The " + key
+                            + " key cannot be used to put a long");
+                }
+            }
+            mBundle.putLong(key, value);
+            return mInstance;
+        }
+
+        @Override
+        public Builder putRating_impl(@RatingKey String key, Rating2 value) {
+            if (METADATA_KEYS_TYPE.containsKey(key)) {
+                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_RATING) {
+                    throw new IllegalArgumentException("The " + key
+                            + " key cannot be used to put a Rating");
+                }
+            }
+            mBundle.putBundle(key, value.toBundle());
+            return mInstance;
+        }
+
+        @Override
+        public Builder putBitmap_impl(@BitmapKey String key, Bitmap value) {
+            if (METADATA_KEYS_TYPE.containsKey(key)) {
+                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) {
+                    throw new IllegalArgumentException("The " + key
+                            + " key cannot be used to put a Bitmap");
+                }
+            }
+            mBundle.putParcelable(key, value);
+            return mInstance;
+        }
+
+        @Override
+        public Builder setExtra_impl(Bundle bundle) {
+            mBundle.putBundle(METADATA_KEY_EXTRA, bundle);
+            return mInstance;
+        }
+
+        @Override
+        public MediaMetadata2 build_impl() {
+            return new MediaMetadata2Impl(mContext, mBundle).getInstance();
+        }
+
+        private Bitmap scaleBitmap(Bitmap bmp, int maxSize) { float maxSizeF = maxSize;
+            float widthScale = maxSizeF / bmp.getWidth();
+            float heightScale = maxSizeF / bmp.getHeight();
+            float scale = Math.min(widthScale, heightScale);
+            int height = (int) (bmp.getHeight() * scale);
+            int width = (int) (bmp.getWidth() * scale);
+            return Bitmap.createScaledBitmap(bmp, width, height, true);
+        }
+    }
+}
+
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index 8c276cd..f820cdc 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -16,48 +16,80 @@
 
 package com.android.media;
 
+import static android.media.MediaSession2.COMMAND_CODE_CUSTOM;
+import static android.media.SessionToken2.TYPE_LIBRARY_SERVICE;
+import static android.media.SessionToken2.TYPE_SESSION;
+import static android.media.SessionToken2.TYPE_SESSION_SERVICE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.Manifest.permission;
+import android.app.PendingIntent;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.media.AudioAttributes;
-import android.media.IMediaSession2Callback;
-import android.media.MediaController2;
-import android.media.MediaPlayerBase;
+import android.content.pm.ResolveInfo;
+import android.media.MediaItem2;
+import android.media.MediaLibraryService2;
+import android.media.MediaMetadata2;
+import android.media.MediaPlayerInterface;
+import android.media.MediaPlayerInterface.PlaybackListener;
 import android.media.MediaSession2;
 import android.media.MediaSession2.Builder;
+import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandButton;
+import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.PlaylistParams;
+import android.media.MediaSession2.PlaylistParams.RepeatMode;
+import android.media.MediaSession2.PlaylistParams.ShuffleMode;
 import android.media.MediaSession2.SessionCallback;
-import android.media.SessionToken;
+import android.media.MediaSessionService2;
+import android.media.PlaybackState2;
+import android.media.SessionToken2;
+import android.media.VolumeProvider;
 import android.media.session.MediaSessionManager;
-import android.media.session.PlaybackState;
 import android.media.update.MediaSession2Provider;
-import android.os.Handler;
+import android.media.update.MediaSession2Provider.CommandButtonProvider;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.Process;
 import android.os.IBinder;
-import android.os.Looper;
+import android.os.ResultReceiver;
+import android.support.annotation.GuardedBy;
+import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Log;
+
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 public class MediaSession2Impl implements MediaSession2Provider {
     private static final String TAG = "MediaSession2";
     private static final boolean DEBUG = true;//Log.isLoggable(TAG, Log.DEBUG);
 
-    private final MediaSession2 mInstance;
+    private final Object mLock = new Object();
 
+    private final MediaSession2 mInstance;
     private final Context mContext;
     private final String mId;
-    private final Handler mHandler;
+    private final Executor mCallbackExecutor;
+    private final SessionCallback mCallback;
     private final MediaSession2Stub mSessionStub;
-    private final SessionToken mSessionToken;
-
-    private MediaPlayerBase mPlayer;
-
+    private final SessionToken2 mSessionToken;
     private final List<PlaybackListenerHolder> mListeners = new ArrayList<>();
+
+    @GuardedBy("mLock")
+    private MediaPlayerInterface mPlayer;
+    @GuardedBy("mLock")
     private MyPlaybackListener mListener;
-    private MediaSession2 instance;
+    @GuardedBy("mLock")
+    private PlaylistParams mPlaylistParams;
+    @GuardedBy("mLock")
+    private List<MediaItem2> mPlaylist;
 
     /**
      * Can be only called by the {@link Builder#build()}.
@@ -67,70 +99,120 @@
      * @param player
      * @param id
      * @param callback
+     * @param volumeProvider
+     * @param ratingType
+     * @param sessionActivity
      */
-    public MediaSession2Impl(MediaSession2 instance, Context context, MediaPlayerBase player,
-            String id, SessionCallback callback) {
-        mInstance = instance;
+    public MediaSession2Impl(Context context, MediaPlayerInterface player,
+            String id, VolumeProvider volumeProvider, int ratingType, PendingIntent sessionActivity,
+            Executor callbackExecutor, SessionCallback callback) {
+        // TODO(jaewan): Keep other params.
+        mInstance = createInstance();
 
         // Argument checks are done by builder already.
         // Initialize finals first.
         mContext = context;
         mId = id;
-        mHandler = new Handler(Looper.myLooper());
-        mSessionStub = new MediaSession2Stub(this, callback);
-        // Ask server to create session token for following reasons.
-        //   1. Make session ID unique per package.
-        //      Server can only know if the package has another process and has another session
-        //      with the same id. Let server check this.
-        //      Note that 'ID is unique per package' is important for controller to distinguish
-        //      a session in another package.
-        //   2. Easier to know the type of session.
-        //      Session created here can be the session service token. In order distinguish,
-        //      we need to iterate AndroidManifest.xml but it's already done by the server.
-        //      Let server to create token with the type.
+        mCallback = callback;
+        mCallbackExecutor = callbackExecutor;
+        mSessionStub = new MediaSession2Stub(this);
+
+        // Infer type from the id and package name.
+        String libraryService = getServiceName(context, MediaLibraryService2.SERVICE_INTERFACE, id);
+        String sessionService = getServiceName(context, MediaSessionService2.SERVICE_INTERFACE, id);
+        if (sessionService != null && libraryService != null) {
+            throw new IllegalArgumentException("Ambiguous session type. Multiple"
+                    + " session services define the same id=" + id);
+        } else if (libraryService != null) {
+            mSessionToken = new SessionToken2Impl(context, Process.myUid(), TYPE_LIBRARY_SERVICE,
+                    mContext.getPackageName(), libraryService, id, mSessionStub).getInstance();
+        } else if (sessionService != null) {
+            mSessionToken = new SessionToken2Impl(context, Process.myUid(), TYPE_SESSION_SERVICE,
+                    mContext.getPackageName(), sessionService, id, mSessionStub).getInstance();
+        } else {
+            mSessionToken = new SessionToken2Impl(context, Process.myUid(), TYPE_SESSION,
+                    mContext.getPackageName(), null, id, mSessionStub).getInstance();
+        }
+
+        setPlayerLocked(player);
+
+        // Ask server for the sanity check, and starts
+        // Sanity check for making session ID unique 'per package' cannot be done in here.
+        // Server can only know if the package has another process and has another session with the
+        // same id. Note that 'ID is unique per package' is important for controller to distinguish
+        // a session in another package.
         MediaSessionManager manager =
                 (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
-        mSessionToken = manager.createSessionToken(mContext.getPackageName(), mId, mSessionStub);
-        if (mSessionToken == null) {
+        if (!manager.onSessionCreated(mSessionToken)) {
             throw new IllegalStateException("Session with the same id is already used by"
                     + " another process. Use MediaController2 instead.");
         }
+    }
 
-        setPlayerInternal(player);
+    MediaSession2 createInstance() {
+        return new MediaSession2(this);
+    }
+
+    private static String getServiceName(Context context, String serviceAction, String id) {
+        PackageManager manager = context.getPackageManager();
+        Intent serviceIntent = new Intent(serviceAction);
+        serviceIntent.setPackage(context.getPackageName());
+        List<ResolveInfo> services = manager.queryIntentServices(serviceIntent,
+                PackageManager.GET_META_DATA);
+        String serviceName = null;
+        if (services != null) {
+            for (int i = 0; i < services.size(); i++) {
+                String serviceId = SessionToken2Impl.getSessionId(services.get(i));
+                if (serviceId != null && TextUtils.equals(id, serviceId)) {
+                    if (services.get(i).serviceInfo == null) {
+                        continue;
+                    }
+                    if (serviceName != null) {
+                        throw new IllegalArgumentException("Ambiguous session type. Multiple"
+                                + " session services define the same id=" + id);
+                    }
+                    serviceName = services.get(i).serviceInfo.name;
+                }
+            }
+        }
+        return serviceName;
     }
 
     // TODO(jaewan): Add explicit release() and do not remove session object with the
     //               setPlayer(null). Token can be available when player is null, and
     //               controller can also attach to session.
     @Override
-    public void setPlayer_impl(MediaPlayerBase player) throws IllegalArgumentException {
+    public void setPlayer_impl(MediaPlayerInterface player, VolumeProvider volumeProvider)
+            throws IllegalArgumentException {
         ensureCallingThread();
         if (player == null) {
             throw new IllegalArgumentException("player shouldn't be null");
         }
-        setPlayerInternal(player);
-    }
-
-    private void setPlayerInternal(MediaPlayerBase player) {
-        if (mPlayer == player) {
-            // Player didn't changed. No-op.
+        if (player == mPlayer) {
             return;
         }
-        mHandler.removeCallbacksAndMessages(null);
+        synchronized (mLock) {
+            setPlayerLocked(player);
+        }
+    }
+
+    private void setPlayerLocked(MediaPlayerInterface player) {
         if (mPlayer != null && mListener != null) {
             // This might not work for a poorly implemented player.
             mPlayer.removePlaybackListener(mListener);
         }
-        mListener = new MyPlaybackListener(this, player);
-        player.addPlaybackListener(mListener, mHandler);
-        notifyPlaybackStateChanged(player.getPlaybackState());
         mPlayer = player;
+        mListener = new MyPlaybackListener(this, player);
+        player.addPlaybackListener(mCallbackExecutor, mListener);
     }
 
     @Override
     public void close_impl() {
-        // Flush any pending messages.
-        mHandler.removeCallbacksAndMessages(null);
+        // Stop system service from listening this session first.
+        MediaSessionManager manager =
+                (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
+        manager.onSessionDestroyed(mSessionToken);
+
         if (mSessionStub != null) {
             if (DEBUG) {
                 Log.d(TAG, "session is now unavailable, id=" + mId);
@@ -138,16 +220,23 @@
             // Invalidate previously published session stub.
             mSessionStub.destroyNotLocked();
         }
+        synchronized (mLock) {
+            if (mPlayer != null) {
+                // close can be called multiple times
+                mPlayer.removePlaybackListener(mListener);
+                mPlayer = null;
+            }
+        }
     }
 
     @Override
-    public MediaPlayerBase getPlayer_impl() {
+    public MediaPlayerInterface getPlayer_impl() {
         return getPlayer();
     }
 
     // TODO(jaewan): Change this to @NonNull
     @Override
-    public SessionToken getToken_impl() {
+    public SessionToken2 getToken_impl() {
         return mSessionToken;
     }
 
@@ -157,11 +246,6 @@
     }
 
     @Override
-    public void setAudioAttributes_impl(AudioAttributes attributes) {
-        // implement
-    }
-
-    @Override
     public void setAudioFocusRequest_impl(int focusGain) {
         // implement
     }
@@ -202,43 +286,6 @@
     }
 
     @Override
-    public PlaybackState getPlaybackState_impl() {
-        ensureCallingThread();
-        ensurePlayer();
-        return mPlayer.getPlaybackState();
-    }
-
-    @Override
-    public void addPlaybackListener_impl(
-            MediaPlayerBase.PlaybackListener listener, Handler handler) {
-        if (listener == null) {
-            throw new IllegalArgumentException("listener shouldn't be null");
-        }
-        if (handler == null) {
-            throw new IllegalArgumentException("handler shouldn't be null");
-        }
-        ensureCallingThread();
-        if (PlaybackListenerHolder.contains(mListeners, listener)) {
-            Log.w(TAG, "listener is already added. Ignoring.");
-            return;
-        }
-        mListeners.add(new PlaybackListenerHolder(listener, handler));
-    }
-
-    @Override
-    public void removePlaybackListener_impl(MediaPlayerBase.PlaybackListener listener) {
-        if (listener == null) {
-            throw new IllegalArgumentException("listener shouldn't be null");
-        }
-        ensureCallingThread();
-        int idx = PlaybackListenerHolder.indexOf(mListeners, listener);
-        if (idx >= 0) {
-            mListeners.get(idx).removeCallbacksAndMessages(null);
-            mListeners.remove(idx);
-        }
-    }
-
-    @Override
     public void setCustomLayout_impl(ControllerInfo controller, List<CommandButton> layout) {
         ensureCallingThread();
         if (controller == null) {
@@ -250,6 +297,149 @@
         mSessionStub.notifyCustomLayoutNotLocked(controller, layout);
     }
 
+    @Override
+    public void setPlaylistParams_impl(PlaylistParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException("PlaylistParams should not be null!");
+        }
+        ensureCallingThread();
+        ensurePlayer();
+        synchronized (mLock) {
+            mPlaylistParams = params;
+        }
+        mPlayer.setPlaylistParams(params);
+        mSessionStub.notifyPlaylistParamsChanged(params);
+    }
+
+    @Override
+    public PlaylistParams getPlaylistParams_impl() {
+        // TODO: Do we need to synchronize here for preparing Controller2.setPlaybackParams?
+        return mPlaylistParams;
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////////
+    // TODO(jaewan): Implement follows
+    //////////////////////////////////////////////////////////////////////////////////////
+    @Override
+    public void setPlayer_impl(MediaPlayerInterface player) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void setAllowedCommands_impl(ControllerInfo controller, CommandGroup commands) {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void notifyMetadataChanged_impl() {
+        // TODO(jaewan): Implement
+    }
+
+    @Override
+    public void sendCustomCommand_impl(ControllerInfo controller, Command command, Bundle args,
+            ResultReceiver receiver) {
+        mSessionStub.sendCustomCommand(controller, command, args, receiver);
+    }
+
+    @Override
+    public void sendCustomCommand_impl(Command command, Bundle args) {
+        mSessionStub.sendCustomCommand(command, args);
+    }
+
+    @Override
+    public void setPlaylist_impl(List<MediaItem2> playlist) {
+        if (playlist == null) {
+            throw new IllegalArgumentException("Playlist should not be null!");
+        }
+        ensureCallingThread();
+        ensurePlayer();
+        synchronized (mLock) {
+            mPlaylist = playlist;
+        }
+        mPlayer.setPlaylist(playlist);
+        mSessionStub.notifyPlaylistChanged(playlist);
+    }
+
+    @Override
+    public List<MediaItem2> getPlaylist_impl() {
+        synchronized (mLock) {
+            return mPlaylist;
+        }
+    }
+
+    @Override
+    public void prepare_impl() {
+        ensureCallingThread();
+        ensurePlayer();
+        mPlayer.prepare();
+    }
+
+    @Override
+    public void fastForward_impl() {
+        ensureCallingThread();
+        ensurePlayer();
+        mPlayer.fastForward();
+    }
+
+    @Override
+    public void rewind_impl() {
+        ensureCallingThread();
+        ensurePlayer();
+        mPlayer.rewind();
+    }
+
+    @Override
+    public void seekTo_impl(long pos) {
+        ensureCallingThread();
+        ensurePlayer();
+        mPlayer.seekTo(pos);
+    }
+
+    @Override
+    public void setCurrentPlaylistItem_impl(int index) {
+        ensureCallingThread();
+        ensurePlayer();
+        mPlayer.setCurrentPlaylistItem(index);
+    }
+
+    @Override
+    public void addPlaybackListener_impl(Executor executor, PlaybackListener listener) {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor shouldn't be null");
+        }
+        if (listener == null) {
+            throw new IllegalArgumentException("listener shouldn't be null");
+        }
+        ensureCallingThread();
+        if (PlaybackListenerHolder.contains(mListeners, listener)) {
+            Log.w(TAG, "listener is already added. Ignoring.");
+            return;
+        }
+        mListeners.add(new PlaybackListenerHolder(executor, listener));
+        executor.execute(() -> listener.onPlaybackChanged(getInstance().getPlaybackState()));
+    }
+
+    @Override
+    public void removePlaybackListener_impl(PlaybackListener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("listener shouldn't be null");
+        }
+        ensureCallingThread();
+        int idx = PlaybackListenerHolder.indexOf(mListeners, listener);
+        if (idx >= 0) {
+            mListeners.remove(idx);
+        }
+    }
+
+    @Override
+    public PlaybackState2 getPlaybackState_impl() {
+        ensureCallingThread();
+        ensurePlayer();
+        // TODO(jaewan): Is it safe to be called on any thread?
+        //               Otherwise we should cache the result from listener.
+        return mPlayer.getPlaybackState();
+    }
+
     ///////////////////////////////////////////////////
     // Protected or private methods
     ///////////////////////////////////////////////////
@@ -266,11 +456,12 @@
     //               1. Allow calls from random threads for all methods.
     //               2. Allow calls from random threads for all methods, except for the
     //                  {@link #setPlayer()}.
-    // TODO(jaewan): Should we pend command instead of exception?
     private void ensureCallingThread() {
+        // TODO(jaewan): Uncomment or remove
+        /*
         if (mHandler.getLooper() != Looper.myLooper()) {
             throw new IllegalStateException("Run this on the given thread");
-        }
+        }*/
     }
 
     private void ensurePlayer() {
@@ -281,14 +472,14 @@
         }
     }
 
-    Handler getHandler() {
-        return mHandler;
-    }
-
-    private void notifyPlaybackStateChanged(PlaybackState state) {
+    private void notifyPlaybackStateChangedNotLocked(PlaybackState2 state) {
+        List<PlaybackListenerHolder> listeners = new ArrayList<>();
+        synchronized (mLock) {
+            listeners.addAll(mListeners);
+        }
         // Notify to listeners added directly to this session
-        for (int i = 0; i < mListeners.size(); i++) {
-            mListeners.get(i).postPlaybackChange(state);
+        for (int i = 0; i < listeners.size(); i++) {
+            listeners.get(i).postPlaybackChange(state);
         }
         // Notify to controllers as well.
         mSessionStub.notifyPlaybackStateChangedNotLocked(state);
@@ -302,29 +493,223 @@
         return mInstance;
     }
 
-    MediaPlayerBase getPlayer() {
+    MediaPlayerInterface getPlayer() {
         return mPlayer;
     }
 
-    private static class MyPlaybackListener implements MediaPlayerBase.PlaybackListener {
-        private final WeakReference<MediaSession2Impl> mSession;
-        private final MediaPlayerBase mPlayer;
+    Executor getCallbackExecutor() {
+        return mCallbackExecutor;
+    }
 
-        private MyPlaybackListener(MediaSession2Impl session, MediaPlayerBase player) {
+    SessionCallback getCallback() {
+        return mCallback;
+    }
+
+    private static class MyPlaybackListener implements MediaPlayerInterface.PlaybackListener {
+        private final WeakReference<MediaSession2Impl> mSession;
+        private final MediaPlayerInterface mPlayer;
+
+        private MyPlaybackListener(MediaSession2Impl session, MediaPlayerInterface player) {
             mSession = new WeakReference<>(session);
             mPlayer = player;
         }
 
         @Override
-        public void onPlaybackChanged(PlaybackState state) {
+        public void onPlaybackChanged(PlaybackState2 state) {
             MediaSession2Impl session = mSession.get();
-            if (session == null || session.getHandler().getLooper() != Looper.myLooper()
-                    || mPlayer != session.mInstance.getPlayer()) {
+            if (mPlayer != session.mInstance.getPlayer()) {
                 Log.w(TAG, "Unexpected playback state change notifications. Ignoring.",
                         new IllegalStateException());
                 return;
             }
-            session.notifyPlaybackStateChanged(state);
+            session.notifyPlaybackStateChangedNotLocked(state);
+        }
+    }
+
+    public static final class CommandImpl implements CommandProvider {
+        private static final String KEY_COMMAND_CODE
+                = "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 final Command mInstance;
+        private final int mCommandCode;
+        // Nonnull if it's custom command
+        private final String mCustomCommand;
+        private final Bundle mExtra;
+
+        public CommandImpl(Command instance, int commandCode) {
+            mInstance = instance;
+            mCommandCode = commandCode;
+            mCustomCommand = null;
+            mExtra = null;
+        }
+
+        public CommandImpl(Command instance, @NonNull String action, @Nullable Bundle extra) {
+            if (action == null) {
+                throw new IllegalArgumentException("action shouldn't be null");
+            }
+            mInstance = instance;
+            mCommandCode = COMMAND_CODE_CUSTOM;
+            mCustomCommand = action;
+            mExtra = extra;
+        }
+
+        public int getCommandCode_impl() {
+            return mCommandCode;
+        }
+
+        public @Nullable String getCustomCommand_impl() {
+            return mCustomCommand;
+        }
+
+        public @Nullable Bundle getExtra_impl() {
+            return mExtra;
+        }
+
+        /**
+         * @ 7return a new Bundle instance from the Command
+         */
+        public Bundle toBundle_impl() {
+            Bundle bundle = new Bundle();
+            bundle.putInt(KEY_COMMAND_CODE, mCommandCode);
+            bundle.putString(KEY_COMMAND_CUSTOM_COMMAND, mCustomCommand);
+            bundle.putBundle(KEY_COMMAND_EXTRA, mExtra);
+            return bundle;
+        }
+
+        /**
+         * @return a new Command instance from the Bundle
+         */
+        public static Command fromBundle_impl(Context context, Bundle command) {
+            int code = command.getInt(KEY_COMMAND_CODE);
+            if (code != COMMAND_CODE_CUSTOM) {
+                return new Command(context, code);
+            } else {
+                String customCommand = command.getString(KEY_COMMAND_CUSTOM_COMMAND);
+                if (customCommand == null) {
+                    return null;
+                }
+                return new Command(context, customCommand, command.getBundle(KEY_COMMAND_EXTRA));
+            }
+        }
+
+        @Override
+        public boolean equals_impl(Object obj) {
+            if (!(obj instanceof CommandImpl)) {
+                return false;
+            }
+            CommandImpl other = (CommandImpl) obj;
+            // TODO(jaewan): Should we also compare contents in bundle?
+            //               It may not be possible if the bundle contains private class.
+            return mCommandCode == other.mCommandCode
+                    && TextUtils.equals(mCustomCommand, other.mCustomCommand);
+        }
+
+        @Override
+        public int hashCode_impl() {
+            final int prime = 31;
+            return ((mCustomCommand != null)
+                    ? mCustomCommand.hashCode() : 0) * prime + mCommandCode;
+        }
+    }
+
+    /**
+     * Represent set of {@link Command}.
+     */
+    public static class CommandGroupImpl implements CommandGroupProvider {
+        private static final String KEY_COMMANDS =
+                "android.media.mediasession2.commandgroup.commands";
+        private ArraySet<Command> mCommands = new ArraySet<>();
+        private final Context mContext;
+        private final CommandGroup mInstance;
+
+        public CommandGroupImpl(Context context, CommandGroup instance, Object other) {
+            mContext = context;
+            mInstance = instance;
+            if (other != null && other instanceof CommandGroupImpl) {
+                mCommands.addAll(((CommandGroupImpl) other).mCommands);
+            }
+        }
+
+        @Override
+        public void addCommand_impl(Command command) {
+            mCommands.add(command);
+        }
+
+        @Override
+        public void addAllPredefinedCommands_impl() {
+            final int COMMAND_CODE_MAX = 22;
+            for (int i = 1; i <= COMMAND_CODE_MAX; i++) {
+                mCommands.add(new Command(mContext, i));
+            }
+        }
+
+        @Override
+        public void removeCommand_impl(Command command) {
+            mCommands.remove(command);
+        }
+
+        @Override
+        public boolean hasCommand_impl(Command command) {
+            return mCommands.contains(command);
+        }
+
+        @Override
+        public boolean hasCommand_impl(int code) {
+            if (code == COMMAND_CODE_CUSTOM) {
+                throw new IllegalArgumentException("Use hasCommand(Command) for custom command");
+            }
+            for (int i = 0; i < mCommands.size(); i++) {
+                if (mCommands.valueAt(i).getCommandCode() == code) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * @return new bundle from the CommandGroup
+         * @hide
+         */
+        @Override
+        public Bundle toBundle_impl() {
+            ArrayList<Bundle> list = new ArrayList<>();
+            for (int i = 0; i < mCommands.size(); i++) {
+                list.add(mCommands.valueAt(i).toBundle());
+            }
+            Bundle bundle = new Bundle();
+            bundle.putParcelableArrayList(KEY_COMMANDS, list);
+            return bundle;
+        }
+
+        /**
+         * @return new instance of CommandGroup from the bundle
+         * @hide
+         */
+        public static @Nullable CommandGroup fromBundle_impl(Context context, Bundle commands) {
+            if (commands == null) {
+                return null;
+            }
+            List<Parcelable> list = commands.getParcelableArrayList(KEY_COMMANDS);
+            if (list == null) {
+                return null;
+            }
+            CommandGroup commandGroup = new CommandGroup(context);
+            for (int i = 0; i < list.size(); i++) {
+                Parcelable parcelable = list.get(i);
+                if (!(parcelable instanceof Bundle)) {
+                    continue;
+                }
+                Bundle commandBundle = (Bundle) parcelable;
+                Command command = Command.fromBundle(context, commandBundle);
+                if (command != null) {
+                    commandGroup.addCommand(command);
+                }
+            }
+            return commandGroup;
         }
     }
 
@@ -335,12 +720,7 @@
         private final boolean mIsTrusted;
         private final IMediaSession2Callback mControllerBinder;
 
-        // Flag to indicate which callbacks should be returned for the controller binder.
-        // Either 0 or combination of {@link #CALLBACK_FLAG_PLAYBACK},
-        // {@link #CALLBACK_FLAG_SESSION_ACTIVENESS}
-        private int mFlag;
-
-        public ControllerInfoImpl(ControllerInfo instance, Context context, int uid,
+        public ControllerInfoImpl(Context context, ControllerInfo instance, int uid,
                 int pid, String packageName, IMediaSession2Callback callback) {
             mInstance = instance;
             mUid = uid;
@@ -423,20 +803,310 @@
             return mControllerBinder;
         }
 
-        public boolean containsFlag(int flag) {
-            return (mFlag & flag) != 0;
-        }
-
-        public void addFlag(int flag) {
-            mFlag |= flag;
-        }
-
-        public void removeFlag(int flag) {
-            mFlag &= ~flag;
-        }
-
         public static ControllerInfoImpl from(ControllerInfo controller) {
             return (ControllerInfoImpl) controller.getProvider();
         }
     }
+
+    public static class PlaylistParamsImpl implements PlaylistParamsProvider {
+        /**
+         * Keys used for converting a PlaylistParams object to a bundle object and vice versa.
+         */
+        private static final String KEY_REPEAT_MODE =
+                "android.media.session2.playlistparams2.repeat_mode";
+        private static final String KEY_SHUFFLE_MODE =
+                "android.media.session2.playlistparams2.shuffle_mode";
+        private static final String KEY_MEDIA_METADATA2_BUNDLE =
+                "android.media.session2.playlistparams2.metadata2_bundle";
+
+        private Context mContext;
+        private PlaylistParams mInstance;
+        private @RepeatMode int mRepeatMode;
+        private @ShuffleMode int mShuffleMode;
+        private MediaMetadata2 mPlaylistMetadata;
+
+        public PlaylistParamsImpl(Context context, PlaylistParams instance,
+                @RepeatMode int repeatMode, @ShuffleMode int shuffleMode,
+                MediaMetadata2 playlistMetadata) {
+            // TODO(jaewan): Sanity check
+            mContext = context;
+            mInstance = instance;
+            mRepeatMode = repeatMode;
+            mShuffleMode = shuffleMode;
+            mPlaylistMetadata = playlistMetadata;
+        }
+
+        public @RepeatMode int getRepeatMode_impl() {
+            return mRepeatMode;
+        }
+
+        public @ShuffleMode int getShuffleMode_impl() {
+            return mShuffleMode;
+        }
+
+        public MediaMetadata2 getPlaylistMetadata_impl() {
+            return mPlaylistMetadata;
+        }
+
+        @Override
+        public Bundle toBundle_impl() {
+            Bundle bundle = new Bundle();
+            bundle.putInt(KEY_REPEAT_MODE, mRepeatMode);
+            bundle.putInt(KEY_SHUFFLE_MODE, mShuffleMode);
+            if (mPlaylistMetadata != null) {
+                bundle.putBundle(KEY_MEDIA_METADATA2_BUNDLE, mPlaylistMetadata.toBundle());
+            }
+            return bundle;
+        }
+
+        public static PlaylistParams fromBundle(Context context, Bundle bundle) {
+            if (bundle == null) {
+                return null;
+            }
+            if (!bundle.containsKey(KEY_REPEAT_MODE) || !bundle.containsKey(KEY_SHUFFLE_MODE)) {
+                return null;
+            }
+
+            Bundle metadataBundle = bundle.getBundle(KEY_MEDIA_METADATA2_BUNDLE);
+            MediaMetadata2 metadata = metadataBundle == null
+                    ? null : MediaMetadata2.fromBundle(context, metadataBundle);
+
+            return new PlaylistParams(context,
+                    bundle.getInt(KEY_REPEAT_MODE),
+                    bundle.getInt(KEY_SHUFFLE_MODE),
+                    metadata);
+        }
+    }
+
+    public static class CommandButtonImpl implements CommandButtonProvider {
+        private static final String KEY_COMMAND
+                = "android.media.media_session2.command_button.command";
+        private static final String KEY_ICON_RES_ID
+                = "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_ENABLED
+                = "android.media.media_session2.command_button.enabled";
+
+        private final CommandButton mInstance;
+        private Command mCommand;
+        private int mIconResId;
+        private String mDisplayName;
+        private Bundle mExtra;
+        private boolean mEnabled;
+
+        public CommandButtonImpl(Context context, @Nullable Command command, int iconResId,
+                @Nullable String displayName, Bundle extra, boolean enabled) {
+            mCommand = command;
+            mIconResId = iconResId;
+            mDisplayName = displayName;
+            mExtra = extra;
+            mEnabled = enabled;
+            mInstance = new CommandButton(this);
+        }
+
+        @Override
+        public @Nullable Command getCommand_impl() {
+            return mCommand;
+        }
+
+        @Override
+        public int getIconResId_impl() {
+            return mIconResId;
+        }
+
+        @Override
+        public @Nullable String getDisplayName_impl() {
+            return mDisplayName;
+        }
+
+        @Override
+        public @Nullable Bundle getExtra_impl() {
+            return mExtra;
+        }
+
+        @Override
+        public boolean isEnabled_impl() {
+            return mEnabled;
+        }
+
+        public @NonNull Bundle toBundle() {
+            Bundle bundle = new Bundle();
+            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.putBoolean(KEY_ENABLED, mEnabled);
+            return bundle;
+        }
+
+        public static @Nullable CommandButton fromBundle(Context context, Bundle bundle) {
+            if (bundle == null) {
+                return null;
+            }
+            CommandButton.Builder builder = new CommandButton.Builder(context);
+            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.setEnabled(bundle.getBoolean(KEY_ENABLED));
+            try {
+                return builder.build();
+            } catch (IllegalStateException e) {
+                // Malformed or version mismatch. Return null for now.
+                return null;
+            }
+        }
+
+        /**
+         * Builder for {@link CommandButton}.
+         */
+        public static class BuilderImpl implements CommandButtonProvider.BuilderProvider {
+            private final Context mContext;
+            private final CommandButton.Builder mInstance;
+            private Command mCommand;
+            private int mIconResId;
+            private String mDisplayName;
+            private Bundle mExtra;
+            private boolean mEnabled;
+
+            public BuilderImpl(Context context, CommandButton.Builder instance) {
+                mContext = context;
+                mInstance = instance;
+                mEnabled = true;
+            }
+
+            @Override
+            public CommandButton.Builder setCommand_impl(Command command) {
+                mCommand = command;
+                return mInstance;
+            }
+
+            @Override
+            public CommandButton.Builder setIconResId_impl(int resId) {
+                mIconResId = resId;
+                return mInstance;
+            }
+
+            @Override
+            public CommandButton.Builder setDisplayName_impl(String displayName) {
+                mDisplayName = displayName;
+                return mInstance;
+            }
+
+            @Override
+            public CommandButton.Builder setEnabled_impl(boolean enabled) {
+                mEnabled = enabled;
+                return mInstance;
+            }
+
+            @Override
+            public CommandButton.Builder setExtra_impl(Bundle extra) {
+                mExtra = extra;
+                return mInstance;
+            }
+
+            @Override
+            public CommandButton build_impl() {
+                if (mEnabled && mCommand == null) {
+                    throw new IllegalStateException("Enabled button needs Command"
+                            + " for controller to invoke the command");
+                }
+                if (mCommand != null && mCommand.getCommandCode() == COMMAND_CODE_CUSTOM
+                        && (mIconResId == 0 || TextUtils.isEmpty(mDisplayName))) {
+                    throw new IllegalStateException("Custom commands needs icon and"
+                            + " and name to display");
+                }
+                return new CommandButtonImpl(
+                        mContext, mCommand, mIconResId, mDisplayName, mExtra, mEnabled).mInstance;
+            }
+        }
+    }
+
+    public static abstract class BuilderBaseImpl<T extends MediaSession2, C extends SessionCallback>
+            implements BuilderBaseProvider<T, C> {
+        final Context mContext;
+        final MediaPlayerInterface mPlayer;
+        String mId;
+        Executor mCallbackExecutor;
+        C mCallback;
+        VolumeProvider mVolumeProvider;
+        int mRatingType;
+        PendingIntent mSessionActivity;
+
+        /**
+         * Constructor.
+         *
+         * @param context a context
+         * @param player a player to handle incoming command from any controller.
+         * @throws IllegalArgumentException if any parameter is null, or the player is a
+         *      {@link MediaSession2} or {@link MediaController2}.
+         */
+        // TODO(jaewan): Also need executor
+        public BuilderBaseImpl(Context context, MediaPlayerInterface player) {
+            if (context == null) {
+                throw new IllegalArgumentException("context shouldn't be null");
+            }
+            if (player == null) {
+                throw new IllegalArgumentException("player shouldn't be null");
+            }
+            mContext = context;
+            mPlayer = player;
+            // Ensure non-null
+            mId = "";
+        }
+
+        public void setVolumeProvider_impl(VolumeProvider volumeProvider) {
+            mVolumeProvider = volumeProvider;
+        }
+
+        public void setRatingType_impl(int type) {
+            mRatingType = type;
+        }
+
+        public void setSessionActivity_impl(PendingIntent pi) {
+            mSessionActivity = pi;
+        }
+
+        public void setId_impl(String id) {
+            if (id == null) {
+                throw new IllegalArgumentException("id shouldn't be null");
+            }
+            mId = id;
+        }
+
+        public void setSessionCallback_impl(Executor executor, C callback) {
+            if (executor == null) {
+                throw new IllegalArgumentException("executor shouldn't be null");
+            }
+            if (callback == null) {
+                throw new IllegalArgumentException("callback shouldn't be null");
+            }
+            mCallbackExecutor = executor;
+            mCallback = callback;
+        }
+
+        public abstract T build_impl();
+    }
+
+    public static class BuilderImpl extends BuilderBaseImpl<MediaSession2, SessionCallback> {
+        public BuilderImpl(Context context, Builder instance, MediaPlayerInterface player) {
+            super(context, player);
+        }
+
+        @Override
+        public MediaSession2 build_impl() {
+            if (mCallbackExecutor == null) {
+                mCallbackExecutor = mContext.getMainExecutor();
+            }
+            if (mCallback == null) {
+                mCallback = new SessionCallback(mContext);
+            }
+
+            return new MediaSession2Impl(mContext, mPlayer, mId, mVolumeProvider, mRatingType,
+                    mSessionActivity, mCallbackExecutor, mCallback).getInstance();
+        }
+    }
 }
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index f2772ed..1f71187 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -16,64 +16,58 @@
 
 package com.android.media;
 
-import static com.android.media.MediaController2Impl.CALLBACK_FLAG_PLAYBACK;
-
 import android.content.Context;
-import android.media.IMediaSession2;
-import android.media.IMediaSession2Callback;
+import android.media.MediaItem2;
+import android.media.MediaLibraryService2.LibraryRoot;
 import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
 import android.media.MediaSession2;
 import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandButton;
 import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.SessionCallback;
-import android.media.session.PlaybackState;
+import android.media.MediaSession2.PlaylistParams;
+import android.media.PlaybackState2;
+import android.media.update.MediaSession2Provider.CommandButtonProvider;
 import android.os.Binder;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
 import android.os.RemoteException;
-import android.service.media.MediaBrowserService.BrowserRoot;
+import android.os.ResultReceiver;
 import android.support.annotation.GuardedBy;
 import android.util.ArrayMap;
 import android.util.Log;
+
+import com.android.media.MediaSession2Impl.CommandButtonImpl;
 import com.android.media.MediaSession2Impl.ControllerInfoImpl;
+
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
 
 public class MediaSession2Stub extends IMediaSession2.Stub {
+
+    static final String ARGUMENT_KEY_POSITION = "android.media.media_session2.key_position";
+    static final String ARGUMENT_KEY_ITEM_INDEX = "android.media.media_session2.key_item_index";
+    static final String ARGUMENT_KEY_PLAYLIST_PARAMS =
+            "android.media.media_session2.key_playlist_params";
+
     private static final String TAG = "MediaSession2Stub";
     private static final boolean DEBUG = true; // TODO(jaewan): Rename.
 
     private final Object mLock = new Object();
-    private final CommandHandler mCommandHandler;
     private final WeakReference<MediaSession2Impl> mSession;
-    private final Context mContext;
-    private final SessionCallback mSessionCallback;
-    private final MediaLibrarySessionCallback mLibraryCallback;
 
     @GuardedBy("mLock")
     private final ArrayMap<IBinder, ControllerInfo> mControllers = new ArrayMap<>();
 
-    public MediaSession2Stub(MediaSession2Impl session, SessionCallback callback) {
+    public MediaSession2Stub(MediaSession2Impl session) {
         mSession = new WeakReference<>(session);
-        mContext = session.getContext();
-        // TODO(jaewan): Should be executor from the session builder
-        mCommandHandler = new CommandHandler(session.getHandler().getLooper());
-        mSessionCallback = callback;
-        mLibraryCallback = (callback instanceof MediaLibrarySessionCallback)
-                ? (MediaLibrarySessionCallback) callback : null;
     }
 
     public void destroyNotLocked() {
         final List<ControllerInfo> list;
         synchronized (mLock) {
             mSession.clear();
-            mCommandHandler.removeCallbacksAndMessages(null);
             list = getControllers();
             mControllers.clear();
         }
@@ -98,14 +92,61 @@
     }
 
     @Override
-    public void connect(String callingPackage, IMediaSession2Callback callback) {
-        if (callback == null) {
-            // Requesting connect without callback to receive result.
-            return;
-        }
-        ControllerInfo request = new ControllerInfo(mContext,
+    public void connect(String callingPackage, final IMediaSession2Callback callback)
+            throws RuntimeException {
+        final MediaSession2Impl sessionImpl = getSession();
+        final Context context = sessionImpl.getContext();
+        final ControllerInfo request = new ControllerInfo(context,
                 Binder.getCallingUid(), Binder.getCallingPid(), callingPackage, callback);
-        mCommandHandler.postConnect(request);
+        sessionImpl.getCallbackExecutor().execute(() -> {
+            final MediaSession2Impl session = mSession.get();
+            if (session == null) {
+                return;
+            }
+            CommandGroup allowedCommands = session.getCallback().onConnect(request);
+            // Don't reject connection for the request from trusted app.
+            // Otherwise server will fail to retrieve session's information to dispatch
+            // media keys to.
+            boolean accept = allowedCommands != null || request.isTrusted();
+            ControllerInfoImpl impl = ControllerInfoImpl.from(request);
+            if (accept) {
+                synchronized (mLock) {
+                    mControllers.put(impl.getId(), request);
+                }
+                if (allowedCommands == null) {
+                    // For trusted apps, send non-null allowed commands to keep connection.
+                    allowedCommands = new CommandGroup(context);
+                }
+            }
+            if (DEBUG) {
+                Log.d(TAG, "onConnectResult, request=" + request
+                        + " accept=" + accept);
+            }
+            try {
+                callback.onConnectionChanged(
+                        accept ? MediaSession2Stub.this : null,
+                        allowedCommands == null ? null : allowedCommands.toBundle());
+            } catch (RemoteException e) {
+                // Controller may be died prematurely.
+            }
+            if (accept) {
+                // If connection is accepted, notify the current state to the controller.
+                // It's needed because we cannot call synchronous calls between session/controller.
+                // Note: We're doing this after the onConnectionChanged(), but there's no guarantee
+                //       that events here are notified after the onConnected() because
+                //       IMediaSession2Callback is oneway (i.e. async call) and CallbackStub will
+                //       use thread poll for incoming calls.
+                // TODO(jaewan): Should we protect getting playback state?
+                final PlaybackState2 state = session.getInstance().getPlaybackState();
+                final Bundle bundle = state != null ? state.toBundle() : null;
+                try {
+                    callback.onPlaybackStateChanged(bundle);
+                } catch (RemoteException e) {
+                    // TODO(jaewan): Handle this.
+                    // Controller may be died prematurely.
+                }
+            }
+        });
     }
 
     @Override
@@ -121,22 +162,108 @@
     @Override
     public void sendCommand(IMediaSession2Callback caller, Bundle command, Bundle args)
             throws RuntimeException {
-        ControllerInfo controller = getController(caller);
+        // TODO(jaewan): Generic command
+    }
+
+    @Override
+    public void sendTransportControlCommand(IMediaSession2Callback caller,
+            int commandCode, Bundle args) throws RuntimeException {
+        final MediaSession2Impl sessionImpl = getSession();
+        final ControllerInfo controller = getController(caller);
         if (controller == null) {
             if (DEBUG) {
                 Log.d(TAG, "Command from a controller that hasn't connected. Ignore");
             }
             return;
         }
-        mCommandHandler.postCommand(controller, Command.fromBundle(command), args);
+        sessionImpl.getCallbackExecutor().execute(() -> {
+            final MediaSession2Impl session = mSession.get();
+            if (session == null) {
+                return;
+            }
+            // TODO(jaewan): Sanity check.
+            Command command = new Command(session.getContext(), commandCode);
+            boolean accepted = session.getCallback().onCommandRequest(controller, command);
+            if (!accepted) {
+                // Don't run rejected command.
+                if (DEBUG) {
+                    Log.d(TAG, "Command " + commandCode + " from "
+                            + controller + " was rejected by " + session);
+                }
+                return;
+            }
+
+            switch (commandCode) {
+                case MediaSession2.COMMAND_CODE_PLAYBACK_PLAY:
+                    session.getInstance().play();
+                    break;
+                case MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE:
+                    session.getInstance().pause();
+                    break;
+                case MediaSession2.COMMAND_CODE_PLAYBACK_STOP:
+                    session.getInstance().stop();
+                    break;
+                case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM:
+                    session.getInstance().skipToPrevious();
+                    break;
+                case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM:
+                    session.getInstance().skipToNext();
+                    break;
+                case MediaSession2.COMMAND_CODE_PLAYBACK_PREPARE:
+                    session.getInstance().prepare();
+                    break;
+                case MediaSession2.COMMAND_CODE_PLAYBACK_FAST_FORWARD:
+                    session.getInstance().fastForward();
+                    break;
+                case MediaSession2.COMMAND_CODE_PLAYBACK_REWIND:
+                    session.getInstance().rewind();
+                    break;
+                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:
+                    session.getInstance().setCurrentPlaylistItem(
+                            args.getInt(ARGUMENT_KEY_ITEM_INDEX));
+                    break;
+                case MediaSession2.COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS:
+                    session.getInstance().setPlaylistParams(
+                            PlaylistParams.fromBundle(session.getContext(),
+                                    args.getBundle(ARGUMENT_KEY_PLAYLIST_PARAMS)));
+                    break;
+                default:
+                    // TODO(jaewan): Resend unknown (new) commands through the custom command.
+            }
+        });
+    }
+
+    @Override
+    public void sendCustomCommand(final IMediaSession2Callback caller, final Bundle commandBundle,
+            final Bundle args, final ResultReceiver receiver) {
+        final MediaSession2Impl sessionImpl = getSession();
+        final ControllerInfo controller = getController(caller);
+        if (controller == null) {
+            if (DEBUG) {
+                Log.d(TAG, "Command from a controller that hasn't connected. Ignore");
+            }
+            return;
+        }
+        sessionImpl.getCallbackExecutor().execute(() -> {
+            final MediaSession2Impl session = mSession.get();
+            if (session == null) {
+                return;
+            }
+            final Command command = Command.fromBundle(session.getContext(), commandBundle);
+            session.getCallback().onCustomCommand(controller, command, args, receiver);
+        });
     }
 
     @Override
     public void getBrowserRoot(IMediaSession2Callback caller, Bundle rootHints)
             throws RuntimeException {
-        if (mLibraryCallback == null) {
+        final MediaSession2Impl sessionImpl = getSession();
+        if (!(sessionImpl.getCallback() instanceof MediaLibrarySessionCallback)) {
             if (DEBUG) {
-                Log.d(TAG, "Session cannot hand getBrowserRoot()");
+                Log.d(TAG, "Session cannot hand getLibraryRoot()");
             }
             return;
         }
@@ -147,51 +274,36 @@
             }
             return;
         }
-        mCommandHandler.postOnGetRoot(controller, rootHints);
-    }
-
-    @Deprecated
-    @Override
-    public PlaybackState getPlaybackState() throws RemoteException {
-        MediaSession2Impl session = getSession();
-        // TODO(jaewan): Check if mPlayer.getPlaybackState() is safe here.
-        return session.getInstance().getPlayer().getPlaybackState();
-    }
-
-    @Deprecated
-    @Override
-    public void registerCallback(final IMediaSession2Callback callbackBinder,
-            final int callbackFlag, final int requestCode) throws RemoteException {
-        // TODO(jaewan): Call onCommand() here. To do so, you should pend message.
-        synchronized (mLock) {
-            ControllerInfo controllerInfo = getController(callbackBinder);
-            if (controllerInfo == null) {
+        sessionImpl.getCallbackExecutor().execute(() -> {
+            final MediaSession2Impl session = mSession.get();
+            if (session == null) {
                 return;
             }
-            ControllerInfoImpl.from(controllerInfo).addFlag(callbackFlag);
-        }
-    }
-
-    @Deprecated
-    @Override
-    public void unregisterCallback(IMediaSession2Callback callbackBinder, int callbackFlag)
-            throws RemoteException {
-        // TODO(jaewan): Call onCommand() here. To do so, you should pend message.
-        synchronized (mLock) {
-            ControllerInfo controllerInfo = getController(callbackBinder);
-            if (controllerInfo == null) {
-                return;
+            final MediaLibrarySessionCallback libraryCallback =
+                    (MediaLibrarySessionCallback) session.getCallback();
+            final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
+            LibraryRoot root = libraryCallback.onGetRoot(controller, rootHints);
+            try {
+                controllerImpl.getControllerBinder().onGetRootResult(rootHints,
+                        root == null ? null : root.getRootId(),
+                        root == null ? null : root.getExtras());
+            } catch (RemoteException e) {
+                // Controller may be died prematurely.
+                // TODO(jaewan): Handle this.
             }
-            ControllerInfoImpl.from(controllerInfo).removeFlag(callbackFlag);
-        }
+        });
     }
 
     private ControllerInfo getController(IMediaSession2Callback caller) {
+        // TODO(jaewan): Device a way to return connection-in-progress-controller
+        //               to be included here, because session owner may want to send some datas
+        //               while onConnected() hasn't returned.
         synchronized (mLock) {
             return mControllers.get(caller.asBinder());
         }
     }
 
+    // TODO(jaewan): Need a way to get controller with permissions
     public List<ControllerInfo> getControllers() {
         ArrayList<ControllerInfo> controllers = new ArrayList<>();
         synchronized (mLock) {
@@ -202,27 +314,15 @@
         return controllers;
     }
 
-    public List<ControllerInfo> getControllersWithFlag(int flag) {
-        ArrayList<ControllerInfo> controllers = new ArrayList<>();
-        synchronized (mLock) {
-            for (int i = 0; i < mControllers.size(); i++) {
-                ControllerInfo controllerInfo = mControllers.valueAt(i);
-                if (ControllerInfoImpl.from(controllerInfo).containsFlag(flag)) {
-                    controllers.add(controllerInfo);
-                }
-            }
-        }
-        return controllers;
-    }
-
     // Should be used without a lock to prevent potential deadlock.
-    public void notifyPlaybackStateChangedNotLocked(PlaybackState state) {
-        final List<ControllerInfo> list = getControllersWithFlag(CALLBACK_FLAG_PLAYBACK);
+    public void notifyPlaybackStateChangedNotLocked(PlaybackState2 state) {
+        final List<ControllerInfo> list = getControllers();
         for (int i = 0; i < list.size(); i++) {
             IMediaSession2Callback callbackBinder =
                     ControllerInfoImpl.from(list.get(i)).getControllerBinder();
             try {
-                callbackBinder.onPlaybackStateChanged(state);
+                final Bundle bundle = state != null ? state.toBundle() : null;
+                callbackBinder.onPlaybackStateChanged(bundle);
             } catch (RemoteException e) {
                 Log.w(TAG, "Controller is gone", e);
                 // TODO(jaewan): What to do when the controller is gone?
@@ -238,7 +338,7 @@
         try {
             List<Bundle> layoutBundles = new ArrayList<>();
             for (int i = 0; i < layout.size(); i++) {
-                Bundle bundle = layout.get(i).toBundle();
+                Bundle bundle = ((CommandButtonImpl) layout.get(i).getProvider()).toBundle();
                 if (bundle != null) {
                     layoutBundles.add(bundle);
                 }
@@ -250,130 +350,83 @@
         }
     }
 
-    // TODO(jaewan): Remove this. We should use Executor given by the session builder.
-    private class CommandHandler extends Handler {
-        public static final int MSG_CONNECT = 1000;
-        public static final int MSG_COMMAND = 1001;
-        public static final int MSG_ON_GET_ROOT = 2000;
-
-        public CommandHandler(Looper looper) {
-            super(looper);
+    public void notifyPlaylistChanged(List<MediaItem2> playlist) {
+        if (playlist == null) {
+            return;
         }
-
-        @Override
-        public void handleMessage(Message msg) {
-            final MediaSession2Impl session = MediaSession2Stub.this.mSession.get();
-            if (session == null || session.getPlayer() == null) {
-                return;
-            }
-
-            switch (msg.what) {
-                case MSG_CONNECT: {
-                    ControllerInfo request = (ControllerInfo) msg.obj;
-                    CommandGroup allowedCommands = mSessionCallback.onConnect(request);
-                    // Don't reject connection for the request from trusted app.
-                    // Otherwise server will fail to retrieve session's information to dispatch
-                    // media keys to.
-                    boolean accept = allowedCommands != null || request.isTrusted();
-                    ControllerInfoImpl impl = ControllerInfoImpl.from(request);
-                    if (accept) {
-                        synchronized (mLock) {
-                            mControllers.put(impl.getId(), request);
-                        }
-                        if (allowedCommands == null) {
-                            // For trusted apps, send non-null allowed commands to keep connection.
-                            allowedCommands = new CommandGroup();
-                        }
-                    }
-                    if (DEBUG) {
-                        Log.d(TAG, "onConnectResult, request=" + request
-                                + " accept=" + accept);
-                    }
-                    try {
-                        impl.getControllerBinder().onConnectionChanged(
-                                accept ? MediaSession2Stub.this : null,
-                                allowedCommands == null ? null : allowedCommands.toBundle());
-                    } catch (RemoteException e) {
-                        // Controller may be died prematurely.
-                    }
-                    break;
-                }
-                case MSG_COMMAND: {
-                    CommandParam param = (CommandParam) msg.obj;
-                    Command command = param.command;
-                    boolean accepted = mSessionCallback.onCommandRequest(
-                            param.controller, command);
-                    if (!accepted) {
-                        // Don't run rejected command.
-                        if (DEBUG) {
-                            Log.d(TAG, "Command " + command + " from "
-                                    + param.controller + " was rejected by " + session);
-                        }
-                        return;
-                    }
-
-                    switch (param.command.getCommandCode()) {
-                        case MediaSession2.COMMAND_CODE_PLAYBACK_START:
-                            session.getInstance().play();
-                            break;
-                        case MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE:
-                            session.getInstance().pause();
-                            break;
-                        case MediaSession2.COMMAND_CODE_PLAYBACK_STOP:
-                            session.getInstance().stop();
-                            break;
-                        case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM:
-                            session.getInstance().skipToPrevious();
-                            break;
-                        case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM:
-                            session.getInstance().skipToNext();
-                            break;
-                        default:
-                            // TODO(jaewan): Handle custom command.
-                    }
-                    break;
-                }
-                case MSG_ON_GET_ROOT: {
-                    final CommandParam param = (CommandParam) msg.obj;
-                    final ControllerInfoImpl controller = ControllerInfoImpl.from(param.controller);
-                    BrowserRoot root = mLibraryCallback.onGetRoot(param.controller, param.args);
-                    try {
-                        controller.getControllerBinder().onGetRootResult(param.args,
-                                root == null ? null : root.getRootId(),
-                                root == null ? null : root.getExtras());
-                    } catch (RemoteException e) {
-                        // Controller may be died prematurely.
-                        // TODO(jaewan): Handle this.
-                    }
-                    break;
+        final List<Bundle> bundleList = new ArrayList<>();
+        for (int i = 0; i < playlist.size(); i++) {
+            if (playlist.get(i) != null) {
+                Bundle bundle = playlist.get(i).toBundle();
+                if (bundle != null) {
+                    bundleList.add(bundle);
                 }
             }
         }
-
-        public void postConnect(ControllerInfo request) {
-            obtainMessage(MSG_CONNECT, request).sendToTarget();
-        }
-
-        public void postCommand(ControllerInfo controller, Command command, Bundle args) {
-            CommandParam param = new CommandParam(controller, command, args);
-            obtainMessage(MSG_COMMAND, param).sendToTarget();
-        }
-
-        public void postOnGetRoot(ControllerInfo controller, Bundle rootHints) {
-            CommandParam param = new CommandParam(controller, null, rootHints);
-            obtainMessage(MSG_ON_GET_ROOT, param).sendToTarget();
+        final List<ControllerInfo> list = getControllers();
+        for (int i = 0; i < list.size(); i++) {
+            IMediaSession2Callback callbackBinder =
+                    ControllerInfoImpl.from(list.get(i)).getControllerBinder();
+            try {
+                callbackBinder.onPlaylistChanged(bundleList);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Controller is gone", e);
+                // TODO(jaewan): What to do when the controller is gone?
+            }
         }
     }
 
-    private static class CommandParam {
-        public final ControllerInfo controller;
-        public final Command command;
-        public final Bundle args;
+    public void notifyPlaylistParamsChanged(MediaSession2.PlaylistParams params) {
+        final List<ControllerInfo> list = getControllers();
+        for (int i = 0; i < list.size(); i++) {
+            IMediaSession2Callback callbackBinder =
+                    ControllerInfoImpl.from(list.get(i)).getControllerBinder();
+            try {
+                callbackBinder.onPlaylistParamsChanged(params.toBundle());
+            } catch (RemoteException e) {
+                Log.w(TAG, "Controller is gone", e);
+                // TODO(jaewan): What to do when the controller is gone?
+            }
+        }
+    }
 
-        private CommandParam(ControllerInfo controller, Command command, Bundle args) {
-            this.controller = controller;
-            this.command = command;
-            this.args = args;
+    public void sendCustomCommand(ControllerInfo controller, Command command, Bundle args,
+            ResultReceiver receiver) {
+        if (receiver != null && controller == null) {
+            throw new IllegalArgumentException("Controller shouldn't be null if result receiver is"
+                    + " specified");
+        }
+        if (command == null) {
+            throw new IllegalArgumentException("command shouldn't be null");
+        }
+        final IMediaSession2Callback callbackBinder =
+                ControllerInfoImpl.from(controller).getControllerBinder();
+        if (getController(callbackBinder) == null) {
+            throw new IllegalArgumentException("Controller is gone");
+        }
+        sendCustomCommandInternal(controller, command, args, receiver);
+    }
+
+    public void sendCustomCommand(Command command, Bundle args) {
+        if (command == null) {
+            throw new IllegalArgumentException("command shouldn't be null");
+        }
+        final List<ControllerInfo> controllers = getControllers();
+        for (int i = 0; i < controllers.size(); i++) {
+            sendCustomCommand(controllers.get(i), command, args, null);
+        }
+    }
+
+    private void sendCustomCommandInternal(ControllerInfo controller, Command command, Bundle args,
+            ResultReceiver receiver) {
+        final IMediaSession2Callback callbackBinder =
+                ControllerInfoImpl.from(controller).getControllerBinder();
+        try {
+            Bundle commandBundle = command.toBundle();
+            callbackBinder.sendCustomCommand(commandBundle, args, receiver);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Controller is gone", e);
+            // TODO(jaewan): What to do when the controller is gone?
         }
     }
 }
diff --git a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
index 773a06f..8773df4 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
@@ -18,18 +18,20 @@
 
 import static android.content.Context.NOTIFICATION_SERVICE;
 
+import android.app.Notification;
 import android.app.NotificationManager;
+import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.media.MediaPlayerBase.PlaybackListener;
+import android.media.MediaPlayerInterface.PlaybackListener;
 import android.media.MediaSession2;
 import android.media.MediaSessionService2;
 import android.media.MediaSessionService2.MediaNotification;
+import android.media.PlaybackState2;
+import android.media.SessionToken2;
+import android.media.SessionToken2.TokenType;
 import android.media.session.PlaybackState;
 import android.media.update.MediaSessionService2Provider;
 import android.os.IBinder;
-import android.os.Looper;
 import android.support.annotation.GuardedBy;
 import android.util.Log;
 
@@ -70,7 +72,7 @@
     }
 
     @Override
-    public MediaNotification onUpdateNotification_impl(PlaybackState state) {
+    public MediaNotification onUpdateNotification_impl(PlaybackState2 state) {
         // Provide default notification UI later.
         return null;
     }
@@ -81,44 +83,32 @@
                 NOTIFICATION_SERVICE);
         mStartSelfIntent = new Intent(mInstance, mInstance.getClass());
 
-        Intent serviceIntent = createServiceIntent();
-        ResolveInfo resolveInfo = mInstance.getPackageManager()
-                .resolveService(serviceIntent, PackageManager.GET_META_DATA);
-        String id;
-        if (resolveInfo == null || resolveInfo.serviceInfo == null) {
-            throw new IllegalArgumentException("service " + mInstance + " doesn't implement"
-                    + serviceIntent.getAction());
-        } else if (resolveInfo.serviceInfo.metaData == null) {
-            if (DEBUG) {
-                Log.d(TAG, "Failed to resolve ID for " + mInstance + ". Using empty id");
-            }
-            id = "";
-        } else {
-            id = resolveInfo.serviceInfo.metaData.getString(
-                    MediaSessionService2.SERVICE_META_DATA, "");
+        SessionToken2 token = new SessionToken2(mInstance, mInstance.getPackageName(),
+                mInstance.getClass().getName());
+        if (token.getType() != getSessionType()) {
+            throw new RuntimeException("Expected session service, but was " + token.getType());
         }
-        mSession = mInstance.onCreateSession(id);
-        if (mSession == null || !id.equals(mSession.getToken().getId())) {
-            throw new RuntimeException("Expected session with id " + id + ", but got " + mSession);
+        mSession = mInstance.onCreateSession(token.getId());
+        if (mSession == null || !token.getId().equals(mSession.getToken().getId())) {
+            throw new RuntimeException("Expected session with id " + token.getId()
+                    + ", but got " + mSession);
         }
         // TODO(jaewan): Uncomment here.
         // mSession.addPlaybackListener(mListener, mSession.getExecutor());
     }
 
-    Intent createServiceIntent() {
-        Intent serviceIntent = new Intent(mInstance, mInstance.getClass());
-        serviceIntent.setAction(MediaSessionService2.SERVICE_INTERFACE);
-        return serviceIntent;
+    @TokenType int getSessionType() {
+        return SessionToken2.TYPE_SESSION_SERVICE;
     }
 
     public IBinder onBind_impl(Intent intent) {
         if (MediaSessionService2.SERVICE_INTERFACE.equals(intent.getAction())) {
-            return mSession.getToken().getSessionBinder().asBinder();
+            return SessionToken2Impl.from(mSession.getToken()).getSessionBinder().asBinder();
         }
         return null;
     }
 
-    private void updateNotification(PlaybackState state) {
+    private void updateNotification(PlaybackState2 state) {
         MediaNotification mediaNotification = mInstance.onUpdateNotification(state);
         if (mediaNotification == null) {
             return;
@@ -128,7 +118,8 @@
                 if (!mIsRunningForeground) {
                     mIsRunningForeground = true;
                     mInstance.startForegroundService(mStartSelfIntent);
-                    mInstance.startForeground(mediaNotification.id, mediaNotification.notification);
+                    mInstance.startForeground(mediaNotification.getNotificationId(),
+                            mediaNotification.getNotification());
                     return;
                 }
                 break;
@@ -140,23 +131,43 @@
                 }
                 break;
         }
-        mNotificationManager.notify(mediaNotification.id, mediaNotification.notification);
+        mNotificationManager.notify(mediaNotification.getNotificationId(),
+                mediaNotification.getNotification());
     }
 
     private class SessionServicePlaybackListener implements PlaybackListener {
         @Override
-        public void onPlaybackChanged(PlaybackState state) {
+        public void onPlaybackChanged(PlaybackState2 state) {
             if (state == null) {
                 Log.w(TAG, "Ignoring null playback state");
                 return;
             }
             MediaSession2Impl impl = (MediaSession2Impl) mSession.getProvider();
-            if (impl.getHandler().getLooper() != Looper.myLooper()) {
-                Log.w(TAG, "Ignoring " + state + ". Expected " + impl.getHandler().getLooper()
-                        + " but " + Looper.myLooper());
-                return;
-            }
             updateNotification(state);
         }
     }
+
+    public static class MediaNotificationImpl implements MediaNotificationProvider {
+        private int mNotificationId;
+        private Notification mNotification;
+
+        public MediaNotificationImpl(Context context, MediaNotification instance,
+                int notificationId, Notification notification) {
+            if (notification == null) {
+                throw new IllegalArgumentException("notification shouldn't be null");
+            }
+            mNotificationId = notificationId;
+            mNotification = notification;
+        }
+
+        @Override
+        public int getNotificationId_impl() {
+            return mNotificationId;
+        }
+
+        @Override
+        public Notification getNotification_impl() {
+            return mNotification;
+        }
+    }
 }
diff --git a/packages/MediaComponents/src/com/android/media/PlaybackInfoImpl.java b/packages/MediaComponents/src/com/android/media/PlaybackInfoImpl.java
new file mode 100644
index 0000000..0782cf1
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/PlaybackInfoImpl.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media;
+
+import android.content.Context;
+import android.media.AudioAttributes;
+import android.media.MediaController2.PlaybackInfo;
+import android.media.update.PlaybackInfoProvider;
+import android.os.Bundle;
+
+public final class PlaybackInfoImpl implements PlaybackInfoProvider {
+
+    private static final String KEY_PLAYBACK_TYPE =
+            "android.media.playbackinfo_impl.playback_type";
+    private static final String KEY_CONTROL_TYPE =
+            "android.media.playbackinfo_impl.control_type";
+    private static final String KEY_MAX_VOLUME =
+            "android.media.playbackinfo_impl.max_volume";
+    private static final String KEY_CURRENT_VOLUME =
+            "android.media.playbackinfo_impl.current_volume";
+    private static final String KEY_AUDIO_ATTRIBUTES =
+            "android.media.playbackinfo_impl.audio_attrs";
+
+    private final Context mContext;
+    private final PlaybackInfo mInstance;
+
+    private final int mPlaybackType;
+    private final int mControlType;
+    private final int mMaxVolume;
+    private final int mCurrentVolume;
+    private final AudioAttributes mAudioAttrs;
+
+    private PlaybackInfoImpl(Context context, int playbackType, AudioAttributes attrs,
+            int controlType, int max, int current) {
+        mContext = context;
+        mPlaybackType = playbackType;
+        mAudioAttrs = attrs;
+        mControlType = controlType;
+        mMaxVolume = max;
+        mCurrentVolume = current;
+        mInstance = new PlaybackInfo(this);
+    }
+
+    @Override
+    public int getPlaybackType_impl() {
+        return mPlaybackType;
+    }
+
+    @Override
+    public AudioAttributes getAudioAttributes_impl() {
+        return mAudioAttrs;
+    }
+
+    @Override
+    public int getControlType_impl() {
+        return mControlType;
+    }
+
+    @Override
+    public int getMaxVolume_impl() {
+        return mMaxVolume;
+    }
+
+    @Override
+    public int getCurrentVolume_impl() {
+        return mCurrentVolume;
+    }
+
+    public PlaybackInfo getInstance() {
+        return mInstance;
+    }
+
+    public Bundle toBundle() {
+        Bundle bundle = new Bundle();
+        bundle.putInt(KEY_PLAYBACK_TYPE, mPlaybackType);
+        bundle.putInt(KEY_CONTROL_TYPE, mControlType);
+        bundle.putInt(KEY_MAX_VOLUME, mMaxVolume);
+        bundle.putInt(KEY_CURRENT_VOLUME, mCurrentVolume);
+        bundle.putParcelable(KEY_AUDIO_ATTRIBUTES, mAudioAttrs);
+        return bundle;
+    }
+
+    public static PlaybackInfo createPlaybackInfo(Context context, int playbackType,
+            AudioAttributes attrs, int controlType, int max, int current) {
+        return new PlaybackInfoImpl(context, playbackType, attrs, controlType, max, current)
+                .getInstance();
+    }
+
+    public static PlaybackInfo fromBundle(Context context, Bundle bundle) {
+        if (bundle == null) {
+            return null;
+        }
+        final int volumeType = bundle.getInt(KEY_PLAYBACK_TYPE);
+        final int volumeControl = bundle.getInt(KEY_CONTROL_TYPE);
+        final int maxVolume = bundle.getInt(KEY_MAX_VOLUME);
+        final int currentVolume = bundle.getInt(KEY_CURRENT_VOLUME);
+        final AudioAttributes attrs = bundle.getParcelable(KEY_AUDIO_ATTRIBUTES);
+
+        return createPlaybackInfo(
+                context, volumeType, attrs, volumeControl, maxVolume, currentVolume);
+    }
+}
diff --git a/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java b/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java
index 4d06463..4241f85 100644
--- a/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java
+++ b/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java
@@ -16,39 +16,28 @@
 
 package com.android.media;
 
-import android.media.MediaPlayerBase;
-import android.media.MediaPlayerBase.PlaybackListener;
-import android.media.session.PlaybackState;
+import android.media.MediaPlayerInterface.PlaybackListener;
+import android.media.PlaybackState2;
 import android.os.Handler;
-import android.os.Message;
 import android.support.annotation.NonNull;
+
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
- * Holds {@link android.media.MediaPlayerBase.PlaybackListener} with the {@link Handler}.
+ * Holds {@link PlaybackListener} with the {@link Handler}.
  */
-public class PlaybackListenerHolder extends Handler {
-    private static final int ON_PLAYBACK_CHANGED = 1;
+public class PlaybackListenerHolder {
+    public final Executor executor;
+    public final PlaybackListener listener;
 
-    public final MediaPlayerBase.PlaybackListener listener;
-
-    public PlaybackListenerHolder(
-            @NonNull MediaPlayerBase.PlaybackListener listener, @NonNull Handler handler) {
-        super(handler.getLooper());
+    public PlaybackListenerHolder(Executor executor, @NonNull PlaybackListener listener) {
+        this.executor = executor;
         this.listener = listener;
     }
 
-    @Override
-    public void handleMessage(Message msg) {
-        switch (msg.what) {
-            case ON_PLAYBACK_CHANGED:
-                listener.onPlaybackChanged((PlaybackState) msg.obj);
-                break;
-        }
-    }
-
-    public void postPlaybackChange(PlaybackState state) {
-        obtainMessage(ON_PLAYBACK_CHANGED, state).sendToTarget();
+    public void postPlaybackChange(final PlaybackState2 state) {
+        executor.execute(() -> listener.onPlaybackChanged(state));
     }
 
     /**
diff --git a/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java b/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java
new file mode 100644
index 0000000..5eb1129
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media;
+
+import android.content.Context;
+import android.media.PlaybackState2;
+import android.media.update.PlaybackState2Provider;
+import android.os.Bundle;
+
+public final class PlaybackState2Impl implements PlaybackState2Provider {
+    /**
+     * Keys used for converting a PlaybackState2 to a bundle object and vice versa.
+     */
+    private static final String KEY_STATE = "android.media.playbackstate2.state";
+    private static final String KEY_POSITION = "android.media.playbackstate2.position";
+    private static final String KEY_BUFFERED_POSITION =
+            "android.media.playbackstate2.buffered_position";
+    private static final String KEY_SPEED = "android.media.playbackstate2.speed";
+    private static final String KEY_ERROR_MESSAGE = "android.media.playbackstate2.error_message";
+    private static final String KEY_UPDATE_TIME = "android.media.playbackstate2.update_time";
+    private static final String KEY_ACTIVE_ITEM_ID = "android.media.playbackstate2.active_item_id";
+
+    private final Context mContext;
+    private final PlaybackState2 mInstance;
+    private final int mState;
+    private final long mPosition;
+    private final long mUpdateTime;
+    private final float mSpeed;
+    private final long mBufferedPosition;
+    private final long mActiveItemId;
+    private final CharSequence mErrorMessage;
+
+    public PlaybackState2Impl(Context context, PlaybackState2 instance, int state, long position,
+            long updateTime, float speed, long bufferedPosition, long activeItemId,
+            CharSequence error) {
+        mContext = context;
+        mInstance = instance;
+        mState = state;
+        mPosition = position;
+        mSpeed = speed;
+        mUpdateTime = updateTime;
+        mBufferedPosition = bufferedPosition;
+        mActiveItemId = activeItemId;
+        mErrorMessage = error;
+    }
+
+    @Override
+    public String toString_impl() {
+        StringBuilder bob = new StringBuilder("PlaybackState {");
+        bob.append("state=").append(mState);
+        bob.append(", position=").append(mPosition);
+        bob.append(", buffered position=").append(mBufferedPosition);
+        bob.append(", speed=").append(mSpeed);
+        bob.append(", updated=").append(mUpdateTime);
+        bob.append(", active item id=").append(mActiveItemId);
+        bob.append(", error=").append(mErrorMessage);
+        bob.append("}");
+        return bob.toString();
+    }
+
+    @Override
+    public int getState_impl() {
+        return mState;
+    }
+
+    @Override
+    public long getPosition_impl() {
+        return mPosition;
+    }
+
+    @Override
+    public long getBufferedPosition_impl() {
+        return mBufferedPosition;
+    }
+
+    @Override
+    public float getPlaybackSpeed_impl() {
+        return mSpeed;
+    }
+
+    @Override
+    public CharSequence getErrorMessage_impl() {
+        return mErrorMessage;
+    }
+
+    @Override
+    public long getLastPositionUpdateTime_impl() {
+        return mUpdateTime;
+    }
+
+    @Override
+    public long getCurrentPlaylistItemIndex_impl() {
+        return mActiveItemId;
+    }
+
+    @Override
+    public Bundle toBundle_impl() {
+        Bundle bundle = new Bundle();
+        bundle.putInt(KEY_STATE, mState);
+        bundle.putLong(KEY_POSITION, mPosition);
+        bundle.putLong(KEY_UPDATE_TIME, mUpdateTime);
+        bundle.putFloat(KEY_SPEED, mSpeed);
+        bundle.putLong(KEY_BUFFERED_POSITION, mBufferedPosition);
+        bundle.putLong(KEY_ACTIVE_ITEM_ID, mActiveItemId);
+        bundle.putCharSequence(KEY_ERROR_MESSAGE, mErrorMessage);
+        return bundle;
+    }
+
+    public static PlaybackState2 fromBundle(Context context, Bundle bundle) {
+        if (bundle == null) {
+            return null;
+        }
+        if (!bundle.containsKey(KEY_STATE)
+                || !bundle.containsKey(KEY_POSITION)
+                || !bundle.containsKey(KEY_UPDATE_TIME)
+                || !bundle.containsKey(KEY_SPEED)
+                || !bundle.containsKey(KEY_BUFFERED_POSITION)
+                || !bundle.containsKey(KEY_ACTIVE_ITEM_ID)
+                || !bundle.containsKey(KEY_ERROR_MESSAGE)) {
+            return null;
+        }
+
+        return new PlaybackState2(context,
+                bundle.getInt(KEY_STATE),
+                bundle.getLong(KEY_POSITION),
+                bundle.getLong(KEY_UPDATE_TIME),
+                bundle.getFloat(KEY_SPEED),
+                bundle.getLong(KEY_BUFFERED_POSITION),
+                bundle.getLong(KEY_ACTIVE_ITEM_ID),
+                bundle.getCharSequence(KEY_ERROR_MESSAGE));
+    }
+}
\ No newline at end of file
diff --git a/packages/MediaComponents/src/com/android/media/Rating2Impl.java b/packages/MediaComponents/src/com/android/media/Rating2Impl.java
new file mode 100644
index 0000000..68e104a
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/Rating2Impl.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media;
+
+import static android.media.Rating2.*;
+
+import android.content.Context;
+import android.media.Rating2;
+import android.media.Rating2.Style;
+import android.media.update.Rating2Provider;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.util.Objects;
+
+public final class Rating2Impl implements Rating2Provider {
+    private static final String TAG = "Rating2";
+
+    private static final String KEY_STYLE = "android.media.rating2.style";
+    private static final String KEY_VALUE = "android.media.rating2.value";
+
+    private final static float RATING_NOT_RATED = -1.0f;
+
+    private final Rating2 mInstance;
+    private final int mRatingStyle;
+    private final float mRatingValue;
+
+    private Rating2Impl(Context context, @Style int ratingStyle, float rating) {
+        mRatingStyle = ratingStyle;
+        mRatingValue = rating;
+        mInstance = new Rating2(this);
+    }
+
+    @Override
+    public String toString_impl() {
+        return "Rating2:style=" + mRatingStyle + " rating="
+                + (mRatingValue < 0.0f ? "unrated" : String.valueOf(mRatingValue));
+    }
+
+    @Override
+    public boolean equals_impl(Object obj) {
+        if (!(obj instanceof Rating2)) {
+            return false;
+        }
+        Rating2Impl other = (Rating2Impl) ((Rating2) obj).getProvider();
+        return mRatingStyle == other.mRatingStyle
+                && mRatingValue == other.mRatingValue;
+    }
+
+    @Override
+    public int hashCode_impl() {
+        return Objects.hash(mRatingStyle, mRatingValue);
+    }
+
+    public Rating2 getInstance() {
+        return mInstance;
+    }
+
+    public static Rating2 fromBundle(Context context, Bundle bundle) {
+        if (bundle == null) {
+            return null;
+        }
+        return new Rating2Impl(context, bundle.getInt(KEY_STYLE), bundle.getFloat(KEY_VALUE))
+                .getInstance();
+    }
+
+    public Bundle toBundle_impl() {
+        Bundle bundle = new Bundle();
+        bundle.putInt(KEY_STYLE, mRatingStyle);
+        bundle.putFloat(KEY_VALUE, mRatingValue);
+        return bundle;
+    }
+
+    public static Rating2 newUnratedRating(Context context, @Style int ratingStyle) {
+        switch(ratingStyle) {
+            case RATING_HEART:
+            case RATING_THUMB_UP_DOWN:
+            case RATING_3_STARS:
+            case RATING_4_STARS:
+            case RATING_5_STARS:
+            case RATING_PERCENTAGE:
+                return new Rating2Impl(context, ratingStyle, RATING_NOT_RATED).getInstance();
+            default:
+                return null;
+        }
+    }
+
+    public static Rating2 newHeartRating(Context context, boolean hasHeart) {
+        return new Rating2Impl(context, RATING_HEART, hasHeart ? 1.0f : 0.0f).getInstance();
+    }
+
+    public static Rating2 newThumbRating(Context context, boolean thumbIsUp) {
+        return new Rating2Impl(context, RATING_THUMB_UP_DOWN, thumbIsUp ? 1.0f : 0.0f)
+                .getInstance();
+    }
+
+    public static Rating2 newStarRating(Context context, int starRatingStyle, float starRating) {
+        float maxRating = RATING_NOT_RATED;
+        switch(starRatingStyle) {
+            case RATING_3_STARS:
+                maxRating = 3.0f;
+                break;
+            case RATING_4_STARS:
+                maxRating = 4.0f;
+                break;
+            case RATING_5_STARS:
+                maxRating = 5.0f;
+                break;
+            default:
+                Log.e(TAG, "Invalid rating style (" + starRatingStyle + ") for a star rating");
+                return null;
+        }
+        if ((starRating < 0.0f) || (starRating > maxRating)) {
+            Log.e(TAG, "Trying to set out of range star-based rating");
+            return null;
+        }
+        return new Rating2Impl(context, starRatingStyle, starRating).getInstance();
+    }
+
+    public static Rating2 newPercentageRating(Context context, float percent) {
+        if ((percent < 0.0f) || (percent > 100.0f)) {
+            Log.e(TAG, "Invalid percentage-based rating value");
+            return null;
+        } else {
+            return new Rating2Impl(context, RATING_PERCENTAGE, percent).getInstance();
+        }
+    }
+
+    @Override
+    public boolean isRated_impl() {
+        return mRatingValue >= 0.0f;
+    }
+
+    @Override
+    public int getRatingStyle_impl() {
+        return mRatingStyle;
+    }
+
+    @Override
+    public boolean hasHeart_impl() {
+        if (mRatingStyle != RATING_HEART) {
+            return false;
+        } else {
+            return (mRatingValue == 1.0f);
+        }
+    }
+
+    @Override
+    public boolean isThumbUp_impl() {
+        if (mRatingStyle != RATING_THUMB_UP_DOWN) {
+            return false;
+        } else {
+            return (mRatingValue == 1.0f);
+        }
+    }
+
+    @Override
+    public float getStarRating_impl() {
+        switch (mRatingStyle) {
+            case RATING_3_STARS:
+            case RATING_4_STARS:
+            case RATING_5_STARS:
+                if (mInstance.isRated()) {
+                    return mRatingValue;
+                }
+            default:
+                return -1.0f;
+        }
+    }
+
+    @Override
+    public float getPercentRating_impl() {
+        if ((mRatingStyle != RATING_PERCENTAGE) || !mInstance.isRated()) {
+            return -1.0f;
+        } else {
+            return mRatingValue;
+        }
+    }
+}
diff --git a/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java b/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java
new file mode 100644
index 0000000..b2b7959
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media;
+
+import static android.media.SessionToken2.TYPE_SESSION;
+import static android.media.SessionToken2.TYPE_SESSION_SERVICE;
+import static android.media.SessionToken2.TYPE_LIBRARY_SERVICE;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.media.MediaLibraryService2;
+import android.media.MediaSessionService2;
+import android.media.SessionToken2;
+import android.media.SessionToken2.TokenType;
+import android.media.update.SessionToken2Provider;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.text.TextUtils;
+
+public class SessionToken2Impl implements SessionToken2Provider {
+    private static final String KEY_UID = "android.media.token.uid";
+    private static final String KEY_TYPE = "android.media.token.type";
+    private static final String KEY_PACKAGE_NAME = "android.media.token.package_name";
+    private static final String KEY_SERVICE_NAME = "android.media.token.service_name";
+    private static final String KEY_ID = "android.media.token.id";
+    private static final String KEY_SESSION_BINDER = "android.media.token.session_binder";
+
+    private final SessionToken2 mInstance;
+    private final int mUid;
+    private final @TokenType int mType;
+    private final String mPackageName;
+    private final String mServiceName;
+    private final String mId;
+    private final IMediaSession2 mSessionBinder;
+
+    /**
+     * Public constructor for the legacy support (i.e. browser can try connecting to any browser service
+     * if it knows the service name)
+     */
+    public SessionToken2Impl(Context context, SessionToken2 instance,
+            String packageName, String serviceName, int uid) {
+        if (TextUtils.isEmpty(packageName)) {
+            throw new IllegalArgumentException("package name shouldn't be null");
+        }
+        if (TextUtils.isEmpty(serviceName)) {
+            throw new IllegalArgumentException("service name shouldn't be null");
+        }
+        mInstance = instance;
+        // Calculate uid if it's not specified.
+        final PackageManager manager = context.getPackageManager();
+        if (uid < 0) {
+            try {
+                uid = manager.getPackageUid(packageName, 0);
+            } catch (NameNotFoundException e) {
+                throw new IllegalArgumentException("Cannot find package " + packageName);
+            }
+        }
+        mUid = uid;
+        // calculate id and type
+        Intent serviceIntent = new Intent(MediaLibraryService2.SERVICE_INTERFACE);
+        serviceIntent.setClassName(packageName, serviceName);
+        String id = getSessionId(manager.resolveService(serviceIntent,
+                PackageManager.GET_META_DATA));
+        int type = TYPE_LIBRARY_SERVICE;
+        if (id == null) {
+            // retry with session service
+            serviceIntent.setClassName(packageName, serviceName);
+            id = getSessionId(manager.resolveService(serviceIntent,
+                    PackageManager.GET_META_DATA));
+            type = TYPE_SESSION_SERVICE;
+        }
+        if (id == null) {
+            throw new IllegalArgumentException("service " + serviceName + " doesn't implement"
+                    + " session service nor library service");
+        }
+        mId = id;
+        mType = type;
+        mPackageName = packageName;
+        mServiceName = serviceName;
+        mSessionBinder = null;
+    }
+
+    public SessionToken2Impl(Context context, int uid, int type,
+            String packageName, String serviceName, String id, IMediaSession2 sessionBinder) {
+        // TODO(jaewan): Add sanity check
+        mUid = uid;
+        mType = type;
+        mPackageName = packageName;
+        mServiceName = serviceName;
+        mId = id;
+        mSessionBinder = sessionBinder;
+        mInstance = new SessionToken2(this);
+    }
+
+    public static String getSessionId(ResolveInfo resolveInfo) {
+        if (resolveInfo == null || resolveInfo.serviceInfo == null) {
+            return null;
+        } else if (resolveInfo.serviceInfo.metaData == null) {
+            return "";
+        } else {
+            return resolveInfo.serviceInfo.metaData.getString(
+                    MediaSessionService2.SERVICE_META_DATA, "");
+        }
+    }
+
+    public SessionToken2 getInstance() {
+        return mInstance;
+    }
+
+    @Override
+    public String getPackageName_impl() {
+        return mPackageName;
+    }
+
+    @Override
+    public int getUid_impl() {
+        return mUid;
+    }
+
+    @Override
+    public String getId_imp() {
+        return mId;
+    }
+
+    @Override
+    public int getType_impl() {
+        return mType;
+    }
+
+    public String getServiceName() {
+        return mServiceName;
+    }
+
+    public IMediaSession2 getSessionBinder() {
+        return mSessionBinder;
+    }
+
+    public static SessionToken2 fromBundle_impl(Context context, Bundle bundle) {
+        if (bundle == null) {
+            return null;
+        }
+        final int uid = bundle.getInt(KEY_UID);
+        final @TokenType int type = bundle.getInt(KEY_TYPE, -1);
+        final String packageName = bundle.getString(KEY_PACKAGE_NAME);
+        final String serviceName = bundle.getString(KEY_SERVICE_NAME);
+        final String id = bundle.getString(KEY_ID);
+        final IBinder sessionBinder = bundle.getBinder(KEY_SESSION_BINDER);
+
+        // Sanity check.
+        switch (type) {
+            case TYPE_SESSION:
+                if (sessionBinder == null) {
+                    throw new IllegalArgumentException("Unexpected sessionBinder for session,"
+                            + " binder=" + sessionBinder);
+                }
+                break;
+            case TYPE_SESSION_SERVICE:
+            case TYPE_LIBRARY_SERVICE:
+                if (TextUtils.isEmpty(serviceName)) {
+                    throw new IllegalArgumentException("Session service needs service name");
+                }
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid type");
+        }
+        if (TextUtils.isEmpty(packageName) || id == null) {
+            throw new IllegalArgumentException("Package name nor ID cannot be null.");
+        }
+        // TODO(jaewan): Revisit here when we add connection callback to the session for individual
+        //               controller's permission check. With it, sessionBinder should be available
+        //               if and only if for session, not session service.
+        return new SessionToken2Impl(context, uid, type, packageName, serviceName, id,
+                sessionBinder != null ? IMediaSession2.Stub.asInterface(sessionBinder) : null)
+                .getInstance();
+    }
+
+    @Override
+    public Bundle toBundle_impl() {
+        Bundle bundle = new Bundle();
+        bundle.putInt(KEY_UID, mUid);
+        bundle.putString(KEY_PACKAGE_NAME, mPackageName);
+        bundle.putString(KEY_SERVICE_NAME, mServiceName);
+        bundle.putString(KEY_ID, mId);
+        bundle.putInt(KEY_TYPE, mType);
+        bundle.putBinder(KEY_SESSION_BINDER,
+                mSessionBinder != null ? mSessionBinder.asBinder() : null);
+        return bundle;
+    }
+
+    @Override
+    public int hashCode_impl() {
+        final int prime = 31;
+        return mType
+                + prime * (mUid
+                + prime * (mPackageName.hashCode()
+                + prime * (mId.hashCode()
+                + prime * ((mServiceName != null ? mServiceName.hashCode() : 0)
+                + prime * (mSessionBinder != null ? mSessionBinder.asBinder().hashCode() : 0)))));
+    }
+
+    @Override
+    public boolean equals_impl(Object obj) {
+        if (!(obj instanceof SessionToken2)) {
+            return false;
+        }
+        SessionToken2Impl other = from((SessionToken2) obj);
+        if (mUid != other.mUid
+                || !TextUtils.equals(mPackageName, other.mPackageName)
+                || !TextUtils.equals(mServiceName, other.mServiceName)
+                || !TextUtils.equals(mId, other.mId)
+                || mType != other.mType) {
+            return false;
+        }
+        if (mSessionBinder == other.mSessionBinder) {
+            return true;
+        } else if (mSessionBinder == null || other.mSessionBinder == null) {
+            return false;
+        }
+        return mSessionBinder.asBinder().equals(other.mSessionBinder.asBinder());
+    }
+
+    @Override
+    public String toString_impl() {
+        return "SessionToken {pkg=" + mPackageName + " id=" + mId + " type=" + mType
+                + " service=" + mServiceName + " binder=" + mSessionBinder + "}";
+    }
+
+    public static SessionToken2Impl from(SessionToken2 token) {
+        return ((SessionToken2Impl) token.getProvider());
+    }
+}
diff --git a/packages/MediaComponents/src/com/android/media/VolumeProvider2Impl.java b/packages/MediaComponents/src/com/android/media/VolumeProvider2Impl.java
new file mode 100644
index 0000000..5af3ddf
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/VolumeProvider2Impl.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.media;
+
+import android.content.Context;
+import android.media.VolumeProvider2;
+import android.media.update.VolumeProvider2Provider;
+
+public class VolumeProvider2Impl implements VolumeProvider2Provider {
+
+    private final Context mContext;
+    private final VolumeProvider2 mInstance;
+    private final int mControlType;
+    private final int mMaxVolume;
+
+    private int mCurrentVolume;
+    private Callback mCallback;
+
+    public VolumeProvider2Impl(Context context, VolumeProvider2 instance,
+            @VolumeProvider2.ControlType int controlType, int maxVolume, int currentVolume) {
+        mContext = context;
+        mInstance = instance;
+        mControlType = controlType;
+        mMaxVolume = maxVolume;
+        mCurrentVolume = currentVolume;
+    }
+
+    @Override
+    public int getControlType_impl() {
+        return mControlType;
+    }
+
+    @Override
+    public int getMaxVolume_impl() {
+        return mMaxVolume;
+    }
+
+    @Override
+    public int getCurrentVolume_impl() {
+        return mCurrentVolume;
+    }
+
+    @Override
+    public void setCurrentVolume_impl(int currentVolume) {
+        mCurrentVolume = currentVolume;
+        if (mCallback != null) {
+            mCallback.onVolumeChanged(mInstance);
+        }
+    }
+
+    /**
+     * Sets a callback to receive volume changes.
+     */
+    public void setCallback(Callback callback) {
+        mCallback = callback;
+    }
+
+    /**
+     * Listens for changes to the volume.
+     */
+    public static abstract class Callback {
+        public abstract void onVolumeChanged(VolumeProvider2 volumeProvider);
+    }
+}
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
index 07b565e..661e252 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
@@ -16,38 +16,77 @@
 
 package com.android.media.update;
 
+import android.app.Notification;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
+import android.media.DataSourceDesc;
 import android.media.MediaBrowser2;
 import android.media.MediaBrowser2.BrowserCallback;
 import android.media.MediaController2;
+import android.media.MediaController2.ControllerCallback;
+import android.media.MediaItem2;
 import android.media.MediaLibraryService2;
-import android.media.MediaPlayerBase;
+import android.media.MediaLibraryService2.LibraryRoot;
+import android.media.MediaLibraryService2.MediaLibrarySession;
+import android.media.MediaLibraryService2.MediaLibrarySessionBuilder;
+import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
+import android.media.MediaMetadata2;
+import android.media.MediaPlayerInterface;
 import android.media.MediaSession2;
+import android.media.MediaSession2.Command;
+import android.media.MediaSession2.CommandButton.Builder;
+import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.PlaylistParams;
 import android.media.MediaSession2.SessionCallback;
 import android.media.MediaSessionService2;
-import android.media.IMediaSession2Callback;
-import android.media.SessionToken;
+import android.media.MediaSessionService2.MediaNotification;
+import android.media.PlaybackState2;
+import android.media.Rating2;
+import android.media.SessionPlayer2;
+import android.media.SessionToken2;
+import android.media.VolumeProvider2;
 import android.media.update.MediaBrowser2Provider;
 import android.media.update.MediaControlView2Provider;
 import android.media.update.MediaController2Provider;
+import android.media.update.MediaItem2Provider;
+import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
+import android.media.update.MediaMetadata2Provider;
 import android.media.update.MediaSession2Provider;
+import android.media.update.MediaSession2Provider.BuilderBaseProvider;
+import android.media.update.MediaSession2Provider.CommandButtonProvider.BuilderProvider;
+import android.media.update.MediaSession2Provider.PlaylistParamsProvider;
 import android.media.update.MediaSessionService2Provider;
-import android.media.update.VideoView2Provider;
+import android.media.update.MediaSessionService2Provider.MediaNotificationProvider;
+import android.media.update.PlaybackState2Provider;
+import android.media.update.SessionPlayer2Provider;
+import android.media.update.SessionToken2Provider;
 import android.media.update.StaticProvider;
+import android.media.update.VideoView2Provider;
 import android.media.update.ViewProvider;
+import android.media.update.VolumeProvider2Provider;
+import android.os.Bundle;
+import android.os.IInterface;
 import android.support.annotation.Nullable;
 import android.util.AttributeSet;
 import android.widget.MediaControlView2;
 import android.widget.VideoView2;
 
+import com.android.media.IMediaSession2Callback;
 import com.android.media.MediaBrowser2Impl;
 import com.android.media.MediaController2Impl;
+import com.android.media.MediaItem2Impl;
 import com.android.media.MediaLibraryService2Impl;
+import com.android.media.MediaLibraryService2Impl.LibraryRootImpl;
+import com.android.media.MediaMetadata2Impl;
 import com.android.media.MediaSession2Impl;
+import com.android.media.MediaSession2Impl.PlaylistParamsImpl;
 import com.android.media.MediaSessionService2Impl;
+import com.android.media.PlaybackState2Impl;
+import com.android.media.Rating2Impl;
+import com.android.media.SessionToken2Impl;
+import com.android.media.VolumeProvider2Impl;
 import com.android.widget.MediaControlView2Impl;
 import com.android.widget.VideoView2Impl;
 
@@ -62,29 +101,73 @@
 
     @Override
     public MediaController2Provider createMediaController2(
-            MediaController2 instance, Context context, SessionToken token,
-            MediaController2.ControllerCallback callback, Executor executor) {
-        return new MediaController2Impl(instance, context, token, callback, executor);
+            Context context, MediaController2 instance, SessionToken2 token,
+            Executor executor, ControllerCallback callback) {
+        return new MediaController2Impl(context, instance, token, executor, callback);
     }
 
     @Override
-    public MediaBrowser2Provider createMediaBrowser2(MediaBrowser2 instance, Context context,
-            SessionToken token, BrowserCallback callback, Executor executor) {
-        return new MediaBrowser2Impl(instance, context, token, callback, executor);
+    public MediaBrowser2Provider createMediaBrowser2(Context context, MediaBrowser2 instance,
+            SessionToken2 token, Executor executor, BrowserCallback callback) {
+        return new MediaBrowser2Impl(context, instance, token, executor, callback);
     }
 
     @Override
-    public MediaSession2Provider createMediaSession2(MediaSession2 instance, Context context,
-            MediaPlayerBase player, String id, SessionCallback callback) {
-        return new MediaSession2Impl(instance, context, player, id, callback);
+    public MediaSession2Provider.CommandProvider createMediaSession2Command(
+            Command instance, int commandCode, String action, Bundle extra) {
+        if (action == null && extra == null) {
+            return new MediaSession2Impl.CommandImpl(instance, commandCode);
+        }
+        return new MediaSession2Impl.CommandImpl(instance, action, extra);
     }
 
     @Override
-    public MediaSession2Provider.ControllerInfoProvider createMediaSession2ControllerInfoProvider(
-            ControllerInfo instance, Context context, int uid, int pid, String packageName,
-            IMediaSession2Callback callback) {
-        return new MediaSession2Impl.ControllerInfoImpl(
-                instance, context, uid, pid, packageName, callback);
+    public Command fromBundle_MediaSession2Command(Context context, Bundle command) {
+        return MediaSession2Impl.CommandImpl.fromBundle_impl(context, command);
+    }
+
+    @Override
+    public MediaSession2Provider.CommandGroupProvider createMediaSession2CommandGroup(
+            Context context, CommandGroup instance, CommandGroup other) {
+        return new MediaSession2Impl.CommandGroupImpl(context, instance,
+                (other == null) ? null : other.getProvider());
+    }
+
+    @Override
+    public CommandGroup fromBundle_MediaSession2CommandGroup(Context context, Bundle commands) {
+        return MediaSession2Impl.CommandGroupImpl.fromBundle_impl(context, commands);
+    }
+
+    @Override
+    public MediaSession2Provider.ControllerInfoProvider createMediaSession2ControllerInfo(
+            Context context, ControllerInfo instance, int uid, int pid, String packageName,
+            IInterface callback) {
+        return new MediaSession2Impl.ControllerInfoImpl(context,
+                instance, uid, pid, packageName, (IMediaSession2Callback) callback);
+    }
+
+    @Override
+    public PlaylistParamsProvider createMediaSession2PlaylistParams(Context context,
+            PlaylistParams playlistParams, int repeatMode, int shuffleMode,
+            MediaMetadata2 playlistMetadata) {
+        return new PlaylistParamsImpl(context, playlistParams, repeatMode, shuffleMode,
+                playlistMetadata);
+    }
+
+    @Override
+    public PlaylistParams fromBundle_PlaylistParams(Context context, Bundle bundle) {
+        return PlaylistParamsImpl.fromBundle(context, bundle);
+    }
+
+    @Override
+    public BuilderProvider createMediaSession2CommandButtonBuilder(Context context,
+            Builder instance) {
+        return new MediaSession2Impl.CommandButtonImpl.BuilderImpl(context, instance);
+    }
+
+    public BuilderBaseProvider<MediaSession2, SessionCallback> createMediaSession2Builder(
+            Context context, MediaSession2.Builder instance, MediaPlayerInterface player) {
+        return new MediaSession2Impl.BuilderImpl(context, instance, player);
     }
 
     @Override
@@ -94,12 +177,34 @@
     }
 
     @Override
+    public MediaNotificationProvider createMediaSessionService2MediaNotification(Context context,
+            MediaNotification instance, int notificationId, Notification notification) {
+        return new MediaSessionService2Impl.MediaNotificationImpl(
+                context, instance, notificationId, notification);
+    }
+
+    @Override
     public MediaSessionService2Provider createMediaLibraryService2(
             MediaLibraryService2 instance) {
         return new MediaLibraryService2Impl(instance);
     }
 
     @Override
+    public BuilderBaseProvider<MediaLibrarySession, MediaLibrarySessionCallback>
+        createMediaLibraryService2Builder(
+            Context context, MediaLibrarySessionBuilder instance, MediaPlayerInterface player,
+            Executor callbackExecutor, MediaLibrarySessionCallback callback) {
+        return new MediaLibraryService2Impl.BuilderImpl(context, instance, player, callbackExecutor,
+                callback);
+    }
+
+    @Override
+    public LibraryRootProvider createMediaLibraryService2LibraryRoot(Context context,
+            LibraryRoot instance, String rootId, Bundle extras) {
+        return new LibraryRootImpl(context, instance, rootId, extras);
+    }
+
+    @Override
     public MediaControlView2Provider createMediaControlView2(
             MediaControlView2 instance, ViewProvider superProvider) {
         return new MediaControlView2Impl(instance, superProvider);
@@ -111,4 +216,98 @@
             @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         return new VideoView2Impl(instance, superProvider, attrs, defStyleAttr, defStyleRes);
     }
+
+    @Override
+    public SessionToken2Provider createSessionToken2(Context context, SessionToken2 instance,
+            String packageName, String serviceName, int uid) {
+        return new SessionToken2Impl(context, instance, packageName, serviceName, uid);
+    }
+
+    @Override
+    public SessionToken2 SessionToken2_fromBundle(Context context, Bundle bundle) {
+        return SessionToken2Impl.fromBundle_impl(context, bundle);
+    }
+
+    @Override
+    public SessionPlayer2Provider createSessionPlayer2(Context context, SessionPlayer2 instance) {
+        // TODO(jaewan): Implement this
+        return null;
+    }
+
+    @Override
+    public MediaItem2Provider createMediaItem2(Context context, MediaItem2 instance,
+            String mediaId, DataSourceDesc dsd, MediaMetadata2 metadata, int flags) {
+        return new MediaItem2Impl(context, instance, mediaId, dsd, metadata, flags);
+    }
+
+    @Override
+    public MediaItem2 fromBundle_MediaItem2(Context context, Bundle bundle) {
+        return MediaItem2Impl.fromBundle(context, bundle);
+    }
+
+    @Override
+    public VolumeProvider2Provider createVolumeProvider2(Context context, VolumeProvider2 instance,
+            int controlType, int maxVolume, int currentVolume) {
+        return new VolumeProvider2Impl(context, instance, controlType, maxVolume, currentVolume);
+    }
+
+    @Override
+    public MediaMetadata2 fromBundle_MediaMetadata2(Context context, Bundle bundle) {
+        return MediaMetadata2Impl.fromBundle(context, bundle);
+    }
+
+    @Override
+    public MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
+            Context context, MediaMetadata2.Builder builder) {
+        return new MediaMetadata2Impl.BuilderImpl(context, builder);
+    }
+
+    @Override
+    public MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
+            Context context, MediaMetadata2.Builder builder, MediaMetadata2 source) {
+        return new MediaMetadata2Impl.BuilderImpl(context, builder, source);
+    }
+
+    @Override
+    public Rating2 fromBundle_Rating2(Context context, Bundle bundle) {
+        return Rating2Impl.fromBundle(context, bundle);
+    }
+
+    @Override
+    public Rating2 newUnratedRating_Rating2(Context context, int ratingStyle) {
+        return Rating2Impl.newUnratedRating(context, ratingStyle);
+    }
+
+    @Override
+    public Rating2 newHeartRating_Rating2(Context context, boolean hasHeart) {
+        return Rating2Impl.newHeartRating(context, hasHeart);
+    }
+
+    @Override
+    public Rating2 newThumbRating_Rating2(Context context, boolean thumbIsUp) {
+        return Rating2Impl.newThumbRating(context, thumbIsUp);
+    }
+
+    @Override
+    public Rating2 newStarRating_Rating2(Context context, int starRatingStyle, float starRating) {
+        return Rating2Impl.newStarRating(context, starRatingStyle, starRating);
+    }
+
+    @Override
+    public Rating2 newPercentageRating_Rating2(Context context, float percent) {
+        return Rating2Impl.newPercentageRating(context, percent);
+    }
+
+    @Override
+    public PlaybackState2Provider createPlaybackState2(Context context, PlaybackState2 instance,
+            int state, long position, long updateTime, float speed, long bufferedPosition,
+            long activeItemId, CharSequence error) {
+        return new PlaybackState2Impl(context, instance, state, position, updateTime, speed,
+                bufferedPosition, activeItemId, error);
+    }
+
+    @Override
+    public PlaybackState2 fromBundle_PlaybackState2(Context context, Bundle bundle) {
+        return PlaybackState2Impl.fromBundle(context, bundle);
+    }
 }
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java
index 65fc88c..7fdcfe4 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteButton.java
@@ -90,6 +90,7 @@
     private final MediaRouterCallback mCallback;
 
     private MediaRouteSelector mSelector = MediaRouteSelector.EMPTY;
+    private int mRouteCallbackFlags;
     private MediaRouteDialogFactory mDialogFactory = MediaRouteDialogFactory.getDefault();
 
     private boolean mAttachedToWindow;
@@ -174,23 +175,38 @@
      * Sets the media route selector for filtering the routes that the user can
      * select using the media route chooser dialog.
      *
-     * @param selector The selector, must not be null.
+     * @param selector The selector.
      */
     public void setRouteSelector(MediaRouteSelector selector) {
-        if (selector == null) {
-            throw new IllegalArgumentException("selector must not be null");
+        setRouteSelector(selector, 0);
+    }
+
+    /**
+     * Sets the media route selector for filtering the routes that the user can
+     * select using the media route chooser dialog.
+     *
+     * @param selector The selector.
+     * @param flags Flags to control the behavior of the callback. May be zero or a combination of
+     *              {@link #MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and
+     *              {@link #MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS}.
+     */
+    public void setRouteSelector(MediaRouteSelector selector, int flags) {
+        if (mSelector.equals(selector) && mRouteCallbackFlags == flags) {
+            return;
+        }
+        if (!mSelector.isEmpty()) {
+            mRouter.removeCallback(mCallback);
+        }
+        if (selector == null || selector.isEmpty()) {
+            mSelector = MediaRouteSelector.EMPTY;
+            return;
         }
 
-        if (!mSelector.equals(selector)) {
-            if (mAttachedToWindow) {
-                if (!mSelector.isEmpty()) {
-                    mRouter.removeCallback(mCallback);
-                }
-                if (!selector.isEmpty()) {
-                    mRouter.addCallback(selector, mCallback);
-                }
-            }
-            mSelector = selector;
+        mSelector = selector;
+        mRouteCallbackFlags = flags;
+
+        if (mAttachedToWindow) {
+            mRouter.addCallback(selector, mCallback, flags);
             refreshRoute();
         }
     }
@@ -411,7 +427,7 @@
 
         mAttachedToWindow = true;
         if (!mSelector.isEmpty()) {
-            mRouter.addCallback(mSelector, mCallback);
+            mRouter.addCallback(mSelector, mCallback, mRouteCallbackFlags);
         }
         refreshRoute();
     }
diff --git a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
index 303efb9..138232e 100644
--- a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
@@ -24,11 +24,11 @@
 import android.media.update.ViewProvider;
 import android.os.Bundle;
 import android.util.Log;
-import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityManager;
+import android.widget.Button;
 import android.widget.ImageButton;
 import android.widget.MediaControlView2;
 import android.widget.ProgressBar;
@@ -38,8 +38,12 @@
 
 import com.android.media.update.ApiHelper;
 import com.android.media.update.R;
+import com.android.support.mediarouter.app.MediaRouteButton;
+import com.android.support.mediarouter.media.MediaRouter;
+import com.android.support.mediarouter.media.MediaRouteSelector;
 
 import java.util.Formatter;
+import java.util.List;
 import java.util.Locale;
 
 public class MediaControlView2Impl implements MediaControlView2Provider {
@@ -48,10 +52,6 @@
     private final MediaControlView2 mInstance;
     private final ViewProvider mSuperProvider;
 
-    static final String COMMAND_SHOW_SUBTITLE = "showSubtitle";
-    static final String COMMAND_HIDE_SUBTITLE = "hideSubtitle";
-    static final String COMMAND_SET_FULLSCREEN = "setFullscreen";
-
     static final String ARGUMENT_KEY_FULLSCREEN = "fullScreen";
 
     static final String KEY_STATE_CONTAINS_SUBTITLE = "StateContainsSubtitle";
@@ -59,7 +59,6 @@
 
     private static final int MAX_PROGRESS = 1000;
     private static final int DEFAULT_PROGRESS_UPDATE_TIME_MS = 1000;
-    private static final int DEFAULT_TIMEOUT_MS = 2000;
 
     private static final int REWIND_TIME_MS = 10000;
     private static final int FORWARD_TIME_MS = 30000;
@@ -75,6 +74,8 @@
     private TextView mTitleView;
     private int mDuration;
     private int mPrevState;
+    private int mCurrentVisibility;
+    private long mTimeout;
     private long mPlaybackActions;
     private boolean mShowing;
     private boolean mDragging;
@@ -84,7 +85,6 @@
     private boolean mSubtitleIsEnabled;
     private boolean mContainsSubtitle;
     private boolean mSeekAvailable;
-    private View.OnClickListener mNextListener, mPrevListener;
     private ImageButton mPlayPauseButton;
     private ImageButton mFfwdButton;
     private ImageButton mRewButton;
@@ -97,6 +97,7 @@
     private ImageButton mOverflowButtonRight;
 
     private ViewGroup mExtraControls;
+    private ViewGroup mCustomButtons;
     private ImageButton mOverflowButtonLeft;
     private ImageButton mMuteButton;
     private ImageButton mAspectRationButton;
@@ -109,6 +110,9 @@
     private StringBuilder mFormatBuilder;
     private Formatter mFormatter;
 
+    private MediaRouteButton mRouteButton;
+    private MediaRouteSelector mRouteSelector;
+
     public MediaControlView2Impl(
             MediaControlView2 instance, ViewProvider superProvider) {
         mInstance = instance;
@@ -118,6 +122,9 @@
         // Inflate MediaControlView2 from XML
         View root = makeControllerView();
         mInstance.addView(root);
+
+        // Set default timeout
+        mTimeout = 2000;
     }
 
     @Override
@@ -137,192 +144,70 @@
     }
 
     @Override
-    public void show_impl() {
-        mInstance.show(DEFAULT_TIMEOUT_MS);
-    }
-
-    @Override
-    public void show_impl(int timeout) {
-        if (!mShowing) {
-            setProgress();
-            if (mPlayPauseButton != null) {
-                mPlayPauseButton.requestFocus();
-            }
-            disableUnsupportedButtons();
-            mInstance.setVisibility(View.VISIBLE);
-            mShowing = true;
-        }
-        // cause the progress bar to be updated even if mShowing
-        // was already true.  This happens, for example, if we're
-        // paused with the progress bar showing the user hits play.
-        mInstance.post(mShowProgress);
-
-        if (timeout != 0 && !mAccessibilityManager.isTouchExplorationEnabled()) {
-            mInstance.removeCallbacks(mFadeOut);
-            mInstance.postDelayed(mFadeOut, timeout);
-        }
-    }
-
-    @Override
     public boolean isShowing_impl() {
-        return mShowing;
+        return (mInstance.getVisibility() == View.VISIBLE) ? true : false;
     }
 
     @Override
-    public void hide_impl() {
-        if (mShowing) {
-            try {
-                mInstance.removeCallbacks(mShowProgress);
-                // Remove existing call to mFadeOut to avoid from being called later.
-                mInstance.removeCallbacks(mFadeOut);
-                mInstance.setVisibility(View.GONE);
-            } catch (IllegalArgumentException ex) {
-                Log.w(TAG, "already removed");
-            }
-            mShowing = false;
-        }
-    }
-
-    @Override
-    public boolean isPlaying_impl() {
-        if (mPlaybackState != null) {
-            return mPlaybackState.getState() == PlaybackState.STATE_PLAYING;
-        }
-        return false;
-    }
-
-    @Override
-    public int getCurrentPosition_impl() {
-        mPlaybackState = mController.getPlaybackState();
-        if (mPlaybackState != null) {
-            return (int) mPlaybackState.getPosition();
-        }
-        return 0;
-    }
-
-    @Override
-    public int getBufferPercentage_impl() {
-        if (mDuration == 0) {
-            return 0;
-        }
-        mPlaybackState = mController.getPlaybackState();
-        if (mPlaybackState != null) {
-            return (int) (mPlaybackState.getBufferedPosition() * 100) / mDuration;
-        }
-        return 0;
-    }
-
-    @Override
-    public boolean canPause_impl() {
-        if (mPlaybackState != null) {
-            return (mPlaybackState.getActions() & PlaybackState.ACTION_PAUSE) != 0;
-        }
-        return true;
-    }
-
-    @Override
-    public boolean canSeekBackward_impl() {
-        if (mPlaybackState!= null) {
-            return (mPlaybackState.getActions() & PlaybackState.ACTION_REWIND) != 0;
-        }
-        return true;
-    }
-
-    @Override
-    public boolean canSeekForward_impl() {
-        if (mPlaybackState != null) {
-            return (mPlaybackState.getActions() & PlaybackState.ACTION_FAST_FORWARD) != 0;
-        }
-        return true;
-    }
-
-    @Override
-    public void showSubtitle_impl() {
-        mController.sendCommand(COMMAND_SHOW_SUBTITLE, null, null);
-    }
-
-    @Override
-    public void hideSubtitle_impl() {
-        mController.sendCommand(COMMAND_HIDE_SUBTITLE, null, null);
-    }
-
-    @Override
-    public void setPrevNextListeners_impl(View.OnClickListener next, View.OnClickListener prev) {
-        mNextListener = next;
-        mPrevListener = prev;
-
-        if (mNextButton != null) {
-            mNextButton.setOnClickListener(mNextListener);
-            mNextButton.setEnabled(mNextListener != null);
-            mNextButton.setVisibility(View.VISIBLE);
-        }
-        if (mPrevButton != null) {
-            mPrevButton.setOnClickListener(mPrevListener);
-            mPrevButton.setEnabled(mPrevListener != null);
-            mPrevButton.setVisibility(View.VISIBLE);
-        }
-    }
-
-    @Override
-    public void setButtonVisibility_impl(int button, boolean visible) {
+    public void setButtonVisibility_impl(int button, int visibility) {
         switch (button) {
             case MediaControlView2.BUTTON_PLAY_PAUSE:
-                if (mPlayPauseButton != null && mInstance.canPause()) {
-                    mPlayPauseButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                if (mPlayPauseButton != null && canPause()) {
+                    mPlayPauseButton.setVisibility(visibility);
                 }
                 break;
             case MediaControlView2.BUTTON_FFWD:
-                if (mFfwdButton != null && mInstance.canSeekForward()) {
-                    mFfwdButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                if (mFfwdButton != null && canSeekForward()) {
+                    mFfwdButton.setVisibility(visibility);
                 }
                 break;
             case MediaControlView2.BUTTON_REW:
-                if (mRewButton != null && mInstance.canSeekBackward()) {
-                    mRewButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                if (mRewButton != null && canSeekBackward()) {
+                    mRewButton.setVisibility(visibility);
                 }
                 break;
             case MediaControlView2.BUTTON_NEXT:
                 // TODO: this button is not visible unless its listener is manually set. Should this
                 // function still be provided?
                 if (mNextButton != null) {
-                    mNextButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                    mNextButton.setVisibility(visibility);
                 }
                 break;
             case MediaControlView2.BUTTON_PREV:
                 // TODO: this button is not visible unless its listener is manually set. Should this
                 // function still be provided?
                 if (mPrevButton != null) {
-                    mPrevButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                    mPrevButton.setVisibility(visibility);
                 }
                 break;
             case MediaControlView2.BUTTON_SUBTITLE:
                 if (mSubtitleButton != null && mContainsSubtitle) {
-                    mSubtitleButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                    mSubtitleButton.setVisibility(visibility);
                 }
                 break;
             case MediaControlView2.BUTTON_FULL_SCREEN:
                 if (mFullScreenButton != null) {
-                    mFullScreenButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                    mFullScreenButton.setVisibility(visibility);
                 }
                 break;
             case MediaControlView2.BUTTON_OVERFLOW:
                 if (mOverflowButtonRight != null) {
-                    mOverflowButtonRight.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                    mOverflowButtonRight.setVisibility(visibility);
                 }
                 break;
             case MediaControlView2.BUTTON_MUTE:
                 if (mMuteButton != null) {
-                    mMuteButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                    mMuteButton.setVisibility(visibility);
                 }
                 break;
             case MediaControlView2.BUTTON_ASPECT_RATIO:
                 if (mAspectRationButton != null) {
-                    mAspectRationButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                    mAspectRationButton.setVisibility(visibility);
                 }
                 break;
             case MediaControlView2.BUTTON_SETTINGS:
                 if (mSettingsButton != null) {
-                    mSettingsButton.setVisibility((visible) ? View.VISIBLE : View.GONE);
+                    mSettingsButton.setVisibility(visibility);
                 }
                 break;
             default:
@@ -331,6 +216,46 @@
     }
 
     @Override
+    public void requestPlayButtonFocus_impl() {
+        if (mPlayPauseButton != null) {
+            mPlayPauseButton.requestFocus();
+        }
+    }
+
+    @Override
+    public void setTimeout_impl(long timeout) {
+        mTimeout = timeout;
+    }
+
+    @Override
+    public long getTimeout_impl() {
+        return mTimeout;
+    }
+
+    @Override
+    public void onVisibilityAggregated_impl(boolean invisible) {
+        int visibility = mInstance.getVisibility();
+        if (mCurrentVisibility != visibility) {
+            mInstance.setVisibility(visibility);
+            mCurrentVisibility = visibility;
+
+            if (visibility == View.VISIBLE) {
+                setProgress();
+                disableUnsupportedButtons();
+                // cause the progress bar to be updated even if mShowing
+                // was already true.  This happens, for example, if we're
+                // paused with the progress bar showing the user hits play.
+                mInstance.post(mShowProgress);
+                resetFadeOutRunnable();
+            } else if (visibility == View.GONE) {
+                mInstance.removeCallbacks(mShowProgress);
+                // Remove existing call to mFadeOut to avoid from being called later.
+                mInstance.removeCallbacks(mFadeOut);
+            }
+        }
+    }
+
+    @Override
     public void onAttachedToWindow_impl() {
         mSuperProvider.onAttachedToWindow_impl();
     }
@@ -353,67 +278,16 @@
     // TODO: Should this function be removed?
     @Override
     public boolean onTrackballEvent_impl(MotionEvent ev) {
-        mInstance.show(DEFAULT_TIMEOUT_MS);
+        resetFadeOutRunnable();
         return false;
     }
 
     @Override
-    public boolean onKeyDown_impl(int keyCode, KeyEvent event) {
-        return mSuperProvider.onKeyDown_impl(keyCode, event);
-    }
-
-    @Override
     public void onFinishInflate_impl() {
         mSuperProvider.onFinishInflate_impl();
     }
 
     @Override
-    public boolean dispatchKeyEvent_impl(KeyEvent event) {
-        int keyCode = event.getKeyCode();
-        final boolean uniqueDown = event.getRepeatCount() == 0
-                && event.getAction() == KeyEvent.ACTION_DOWN;
-        if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK
-                || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
-                || keyCode == KeyEvent.KEYCODE_SPACE) {
-            if (uniqueDown) {
-                togglePausePlayState();
-                mInstance.show(DEFAULT_TIMEOUT_MS);
-                if (mPlayPauseButton != null) {
-                    mPlayPauseButton.requestFocus();
-                }
-            }
-            return true;
-        } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
-            if (uniqueDown && !mInstance.isPlaying()) {
-                togglePausePlayState();
-                mInstance.show(DEFAULT_TIMEOUT_MS);
-            }
-            return true;
-        } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
-                || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
-            if (uniqueDown && mInstance.isPlaying()) {
-                togglePausePlayState();
-                mInstance.show(DEFAULT_TIMEOUT_MS);
-            }
-            return true;
-        } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
-                || keyCode == KeyEvent.KEYCODE_VOLUME_UP
-                || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE
-                || keyCode == KeyEvent.KEYCODE_CAMERA) {
-            // don't show the controls for volume adjustment
-            return mSuperProvider.dispatchKeyEvent_impl(event);
-        } else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) {
-            if (uniqueDown) {
-                mInstance.hide();
-            }
-            return true;
-        }
-
-        mInstance.show(DEFAULT_TIMEOUT_MS);
-        return mSuperProvider.dispatchKeyEvent_impl(event);
-    }
-
-    @Override
     public void setEnabled_impl(boolean enabled) {
         if (mPlayPauseButton != null) {
             mPlayPauseButton.setEnabled(enabled);
@@ -437,10 +311,68 @@
         mSuperProvider.setEnabled_impl(enabled);
     }
 
+    public void setRouteSelector(MediaRouteSelector selector) {
+        mRouteSelector = selector;
+        if (mRouteSelector != null && !mRouteSelector.isEmpty()) {
+            mRouteButton.setRouteSelector(selector, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
+            mRouteButton.setVisibility(View.VISIBLE);
+        } else {
+            mRouteButton.setRouteSelector(MediaRouteSelector.EMPTY);
+            mRouteButton.setVisibility(View.GONE);
+        }
+    }
+
     ///////////////////////////////////////////////////
     // Protected or private methods
     ///////////////////////////////////////////////////
 
+    private boolean isPlaying() {
+        if (mPlaybackState != null) {
+            return mPlaybackState.getState() == PlaybackState.STATE_PLAYING;
+        }
+        return false;
+    }
+
+    private int getCurrentPosition() {
+        mPlaybackState = mController.getPlaybackState();
+        if (mPlaybackState != null) {
+            return (int) mPlaybackState.getPosition();
+        }
+        return 0;
+    }
+
+    private int getBufferPercentage() {
+        if (mDuration == 0) {
+            return 0;
+        }
+        mPlaybackState = mController.getPlaybackState();
+        if (mPlaybackState != null) {
+            return (int) (mPlaybackState.getBufferedPosition() * 100) / mDuration;
+        }
+        return 0;
+    }
+
+    private boolean canPause() {
+        if (mPlaybackState != null) {
+            return (mPlaybackState.getActions() & PlaybackState.ACTION_PAUSE) != 0;
+        }
+        return true;
+    }
+
+    private boolean canSeekBackward() {
+        if (mPlaybackState != null) {
+            return (mPlaybackState.getActions() & PlaybackState.ACTION_REWIND) != 0;
+        }
+        return true;
+    }
+
+    private boolean canSeekForward() {
+        if (mPlaybackState != null) {
+            return (mPlaybackState.getActions() & PlaybackState.ACTION_FAST_FORWARD) != 0;
+        }
+        return true;
+    }
+
     /**
      * Create the view that holds the widgets that control playback.
      * Derived classes can override this to create their own.
@@ -460,6 +392,8 @@
         mPauseDescription = res.getText(R.string.lockscreen_pause_button_content_description);
         mReplayDescription = res.getText(R.string.lockscreen_replay_button_content_description);
 
+        mRouteButton = v.findViewById(R.id.cast);
+
         mPlayPauseButton = v.findViewById(R.id.pause);
         if (mPlayPauseButton != null) {
             mPlayPauseButton.requestFocus();
@@ -481,11 +415,11 @@
         }
         mNextButton = v.findViewById(R.id.next);
         if (mNextButton != null) {
-            mNextButton.setVisibility(View.GONE);
+            mNextButton.setOnClickListener(mNextListener);
         }
         mPrevButton = v.findViewById(R.id.prev);
         if (mPrevButton != null) {
-            mPrevButton.setVisibility(View.GONE);
+            mPrevButton.setOnClickListener(mPrevListener);
         }
 
         mBasicControls = v.findViewById(R.id.basic_controls);
@@ -507,6 +441,7 @@
 
         // TODO: should these buttons be shown as default?
         mExtraControls = v.findViewById(R.id.extra_controls);
+        mCustomButtons = v.findViewById(R.id.custom_buttons);
         mOverflowButtonLeft = v.findViewById(R.id.overflow_left);
         if (mOverflowButtonLeft != null) {
             mOverflowButtonLeft.setOnClickListener(mOverflowLeftListener);
@@ -538,13 +473,13 @@
      */
     private void disableUnsupportedButtons() {
         try {
-            if (mPlayPauseButton != null && !mInstance.canPause()) {
+            if (mPlayPauseButton != null && !canPause()) {
                 mPlayPauseButton.setEnabled(false);
             }
-            if (mRewButton != null && !mInstance.canSeekBackward()) {
+            if (mRewButton != null && !canSeekBackward()) {
                 mRewButton.setEnabled(false);
             }
-            if (mFfwdButton != null && !mInstance.canSeekForward()) {
+            if (mFfwdButton != null && !canSeekForward()) {
                 mFfwdButton.setEnabled(false);
             }
             // TODO What we really should do is add a canSeek to the MediaPlayerControl interface;
@@ -554,7 +489,7 @@
             // However, currently the flags SEEK_BACKWARD_AVAILABLE, SEEK_FORWARD_AVAILABLE,
             // and SEEK_AVAILABLE are all (un)set together; as such the aforementioned issue
             // shouldn't arise in existing applications.
-            if (mProgress != null && !mInstance.canSeekBackward() && !mInstance.canSeekForward()) {
+            if (mProgress != null && !canSeekBackward() && !canSeekForward()) {
                 mProgress.setEnabled(false);
             }
         } catch (IncompatibleClassChangeError ex) {
@@ -568,8 +503,8 @@
     private final Runnable mFadeOut = new Runnable() {
         @Override
         public void run() {
-            if (mInstance.isPlaying()) {
-                mInstance.hide();
+            if (isPlaying()) {
+                mInstance.setVisibility(View.GONE);
             }
         }
     };
@@ -578,13 +513,20 @@
         @Override
         public void run() {
             int pos = setProgress();
-            if (!mDragging && mShowing && mInstance.isPlaying()) {
+            if (!mDragging && mShowing && isPlaying()) {
                 mInstance.postDelayed(mShowProgress,
                         DEFAULT_PROGRESS_UPDATE_TIME_MS - (pos % DEFAULT_PROGRESS_UPDATE_TIME_MS));
             }
         }
     };
 
+    private void resetFadeOutRunnable() {
+        if (mTimeout != 0 && !mAccessibilityManager.isTouchExplorationEnabled()) {
+            mInstance.removeCallbacks(mFadeOut);
+            mInstance.postDelayed(mFadeOut, mTimeout);
+        }
+    }
+
     private String stringForTime(int timeMs) {
         int totalSeconds = timeMs / 1000;
 
@@ -605,13 +547,13 @@
             return 0;
         }
         int positionOnProgressBar = 0;
-        int currentPosition = mInstance.getCurrentPosition();
+        int currentPosition = getCurrentPosition();
         if (mDuration > 0) {
             positionOnProgressBar = (int) (MAX_PROGRESS * (long) currentPosition / mDuration);
         }
         if (mProgress != null && currentPosition != mDuration) {
             mProgress.setProgress(positionOnProgressBar);
-            mProgress.setSecondaryProgress(mInstance.getBufferPercentage() * 10);
+            mProgress.setSecondaryProgress(getBufferPercentage() * 10);
         }
 
         if (mEndTime != null) {
@@ -626,7 +568,7 @@
     }
 
     private void togglePausePlayState() {
-        if (mInstance.isPlaying()) {
+        if (isPlaying()) {
             mControls.pause();
             mPlayPauseButton.setImageDrawable(
                     ApiHelper.getLibResources().getDrawable(
@@ -658,9 +600,9 @@
             if (!mSeekAvailable) {
                 return;
             }
-            mInstance.show(3600000);
 
             mDragging = true;
+            mInstance.removeCallbacks(mFadeOut);
 
             // By removing these pending progress messages we make sure
             // that a) we won't update the progress while the user adjusts
@@ -708,7 +650,8 @@
             mDragging = false;
 
             setProgress();
-            mInstance.show(DEFAULT_TIMEOUT_MS);
+
+            resetFadeOutRunnable();
 
             // Ensure that progress is properly updated in the future,
             // the call to show() does not guarantee this because it is a
@@ -721,29 +664,45 @@
         @Override
         public void onClick(View v) {
             togglePausePlayState();
-            mInstance.show(DEFAULT_TIMEOUT_MS);
+            resetFadeOutRunnable();
         }
     };
 
     private final View.OnClickListener mRewListener = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
-            int pos = mInstance.getCurrentPosition() - REWIND_TIME_MS;
+            int pos = getCurrentPosition() - REWIND_TIME_MS;
             mControls.seekTo(pos);
             setProgress();
 
-            mInstance.show(DEFAULT_TIMEOUT_MS);
+            resetFadeOutRunnable();
         }
     };
 
     private final View.OnClickListener mFfwdListener = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
-            int pos = mInstance.getCurrentPosition() + FORWARD_TIME_MS;
+            int pos = getCurrentPosition() + FORWARD_TIME_MS;
             mControls.seekTo(pos);
             setProgress();
 
-            mInstance.show(DEFAULT_TIMEOUT_MS);
+            resetFadeOutRunnable();
+        }
+    };
+
+    private final View.OnClickListener mNextListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            mControls.skipToNext();
+            resetFadeOutRunnable();
+        }
+    };
+
+    private final View.OnClickListener mPrevListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            mControls.skipToPrevious();
+            resetFadeOutRunnable();
         }
     };
 
@@ -754,16 +713,16 @@
                 mSubtitleButton.setImageDrawable(
                         ApiHelper.getLibResources().getDrawable(
                                 R.drawable.ic_media_subtitle_enabled, null));
-                mInstance.showSubtitle();
+                mController.sendCommand(MediaControlView2.COMMAND_SHOW_SUBTITLE, null, null);
                 mSubtitleIsEnabled = true;
             } else {
                 mSubtitleButton.setImageDrawable(
                         ApiHelper.getLibResources().getDrawable(
                                 R.drawable.ic_media_subtitle_disabled, null));
-                mInstance.hideSubtitle();
+                mController.sendCommand(MediaControlView2.COMMAND_HIDE_SUBTITLE, null, null);
                 mSubtitleIsEnabled = false;
             }
-            mInstance.show(DEFAULT_TIMEOUT_MS);
+            resetFadeOutRunnable();
         }
     };
 
@@ -782,10 +741,10 @@
             }
             Bundle args = new Bundle();
             args.putBoolean(ARGUMENT_KEY_FULLSCREEN, isEnteringFullScreen);
-            mController.sendCommand(COMMAND_SET_FULLSCREEN, args, null);
+            mController.sendCommand(MediaControlView2.COMMAND_SET_FULLSCREEN, args, null);
 
             mIsFullScreen = isEnteringFullScreen;
-            mInstance.show(DEFAULT_TIMEOUT_MS);
+            resetFadeOutRunnable();
         }
     };
 
@@ -794,7 +753,7 @@
         public void onClick(View v) {
             mBasicControls.setVisibility(View.GONE);
             mExtraControls.setVisibility(View.VISIBLE);
-            mInstance.show(DEFAULT_TIMEOUT_MS);
+            resetFadeOutRunnable();
         }
     };
 
@@ -803,6 +762,7 @@
         public void onClick(View v) {
             mBasicControls.setVisibility(View.VISIBLE);
             mExtraControls.setVisibility(View.GONE);
+            resetFadeOutRunnable();
         }
     };
 
@@ -881,6 +841,31 @@
                 }
                 mPlaybackActions = newActions;
             }
+
+            // Add buttons if custom actions are present.
+            List<PlaybackState.CustomAction> customActions = mPlaybackState.getCustomActions();
+            mCustomButtons.removeAllViews();
+            if (customActions.size() > 0) {
+                for (PlaybackState.CustomAction action : customActions) {
+                    ImageButton button = new ImageButton(mInstance.getContext(),
+                            null /* AttributeSet */, 0 /* Style */);
+                    // TODO: Apply R.style.BottomBarButton to this button using library context.
+                    // Refer Constructor with argument (int defStyleRes) of View.java
+                    button.setImageResource(action.getIcon());
+                    button.setTooltipText(action.getName());
+                    final String actionString = action.getAction().toString();
+                    button.setOnClickListener(new View.OnClickListener() {
+                        @Override
+                        public void onClick(View v) {
+                            // TODO: Currently, we are just sending extras that came from session.
+                            // Is it the right behavior?
+                            mControls.sendCustomAction(actionString, action.getExtras());
+                            mInstance.setVisibility(View.VISIBLE);
+                        }
+                    });
+                    mCustomButtons.addView(button);
+                }
+            }
         }
 
         @Override
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index 4c312f8..7218150 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
@@ -16,6 +16,7 @@
 
 package com.android.widget;
 
+import android.annotation.NonNull;
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -25,16 +26,16 @@
 import android.media.AudioManager;
 import android.media.MediaMetadata;
 import android.media.MediaPlayer;
-import android.media.MediaPlayerBase;
+import android.media.MediaPlayerInterface;
 import android.media.Cea708CaptionRenderer;
 import android.media.ClosedCaptionRenderer;
-import android.media.MediaMetadata;
-import android.media.MediaPlayer;
 import android.media.Metadata;
 import android.media.PlaybackParams;
 import android.media.SubtitleController;
 import android.media.TtmlRenderer;
 import android.media.WebVttRenderer;
+import android.media.session.MediaController;
+import android.media.session.MediaController.PlaybackInfo;
 import android.media.session.MediaSession;
 import android.media.session.PlaybackState;
 import android.media.update.VideoView2Provider;
@@ -46,7 +47,6 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Gravity;
-import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.FrameLayout.LayoutParams;
@@ -55,11 +55,17 @@
 
 import com.android.media.update.ApiHelper;
 import com.android.media.update.R;
+import com.android.support.mediarouter.media.MediaRouter;
+import com.android.support.mediarouter.media.MediaRouteSelector;
+import com.android.support.mediarouter.media.RemotePlaybackClient;
+import com.android.support.mediarouter.media.MediaItemStatus;
+import com.android.support.mediarouter.media.MediaSessionStatus;
 
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Executor;
 
 public class VideoView2Impl implements VideoView2Provider, VideoViewInterface.SurfaceListener {
     private static final String TAG = "VideoView2";
@@ -79,14 +85,14 @@
     private final AudioManager mAudioManager;
     private AudioAttributes mAudioAttributes;
     private int mAudioFocusType = AudioManager.AUDIOFOCUS_GAIN; // legacy focus gain
-    private int mAudioSession;
 
+    private VideoView2.OnCustomActionListener mOnCustomActionListener;
     private VideoView2.OnPreparedListener mOnPreparedListener;
     private VideoView2.OnCompletionListener mOnCompletionListener;
     private VideoView2.OnErrorListener mOnErrorListener;
     private VideoView2.OnInfoListener mOnInfoListener;
     private VideoView2.OnViewTypeChangedListener mOnViewTypeChangedListener;
-    private VideoView2.OnFullScreenChangedListener mOnFullScreenChangedListener;
+    private VideoView2.OnFullScreenRequestListener mOnFullScreenRequestListener;
 
     private VideoViewInterface mCurrentView;
     private VideoTextureView mTextureView;
@@ -95,14 +101,19 @@
     private MediaPlayer mMediaPlayer;
     private MediaControlView2 mMediaControlView;
     private MediaSession mMediaSession;
+    private MediaController mMediaController;
+    private MediaSession.Callback mRouteSessionCallback = new RouteSessionCallback();
+    private MediaRouter mMediaRouter;
+    private MediaRouteSelector mRouteSelector;
     private Metadata mMetadata;
     private String mTitle;
 
     private PlaybackState.Builder mStateBuilder;
+    private List<PlaybackState.CustomAction> mCustomActionList;
     private int mTargetState = STATE_IDLE;
     private int mCurrentState = STATE_IDLE;
     private int mCurrentBufferPercentage;
-    private int mSeekWhenPrepared;  // recording the seek position while preparing
+    private long mSeekWhenPrepared;  // recording the seek position while preparing
 
     private int mVideoWidth;
     private int mVideoHeight;
@@ -162,16 +173,38 @@
 
         // TODO: Need a common namespace for attributes those are defined in updatable library.
         boolean enableControlView = (attrs == null) || attrs.getAttributeBooleanValue(
-                "http://schemas.android.com/apk/com.android.media.update",
+                "http://schemas.android.com/apk/res/android",
                 "enableControlView", true);
         if (enableControlView) {
             mMediaControlView = new MediaControlView2(mInstance.getContext());
         }
+        boolean showSubtitle = (attrs == null) || attrs.getAttributeBooleanValue(
+                "http://schemas.android.com/apk/res/android",
+                "showSubtitle", true);
+        if (showSubtitle) {
+            Log.d(TAG, "showSubtitle attribute is true.");
+            // TODO: implement
+        }
+        int viewType = (attrs == null) ? VideoView2.VIEW_TYPE_SURFACEVIEW
+                : attrs.getAttributeIntValue(
+                "http://schemas.android.com/apk/res/android",
+                "viewType", 0);
+        if (viewType == 0) {
+            Log.d(TAG, "viewType attribute is surfaceView.");
+            // TODO: implement
+        } else if (viewType == 1) {
+            Log.d(TAG, "viewType attribute is textureView.");
+            // TODO: implement
+        }
     }
 
     @Override
     public void setMediaControlView2_impl(MediaControlView2 mediaControlView) {
         mMediaControlView = mediaControlView;
+        if (mRouteSelector != null) {
+            ((MediaControlView2Impl) mMediaControlView.getProvider())
+                    .setRouteSelector(mRouteSelector);
+        }
 
         if (mInstance.isAttachedToWindow()) {
             attachMediaControlView();
@@ -179,123 +212,46 @@
     }
 
     @Override
+    public MediaController getMediaController_impl() {
+        if (mMediaSession == null) {
+            throw new IllegalStateException("MediaSession instance is not available.");
+        }
+        return mMediaController;
+    }
+
+    @Override
     public MediaControlView2 getMediaControlView2_impl() {
         return mMediaControlView;
     }
 
     @Override
-    public void start_impl() {
-        if (isInPlaybackState() && mCurrentView.hasAvailableSurface()) {
-            applySpeed();
-            mMediaPlayer.start();
-            mCurrentState = STATE_PLAYING;
-            updatePlaybackState();
-        }
-        mTargetState = STATE_PLAYING;
-        if (DEBUG) {
-            Log.d(TAG, "start(). mCurrentState=" + mCurrentState
-                    + ", mTargetState=" + mTargetState);
-        }
-    }
+    public void showSubtitle_impl(boolean show) {
+        if (show) {
+            // Retrieve all tracks that belong to the current video.
+            MediaPlayer.TrackInfo[] trackInfos = mMediaPlayer.getTrackInfo();
 
-    @Override
-    public void pause_impl() {
-        if (isInPlaybackState()) {
-            if (mMediaPlayer.isPlaying()) {
-                mMediaPlayer.pause();
-                mCurrentState = STATE_PAUSED;
-                updatePlaybackState();
+            List<Integer> subtitleTrackIndices = new ArrayList<>();
+            for (int i = 0; i < trackInfos.length; ++i) {
+                int trackType = trackInfos[i].getTrackType();
+                if (trackType == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) {
+                    subtitleTrackIndices.add(i);
+                }
             }
-        }
-        mTargetState = STATE_PAUSED;
-        if (DEBUG) {
-            Log.d(TAG, "pause(). mCurrentState=" + mCurrentState
-                    + ", mTargetState=" + mTargetState);
-        }
-    }
-
-    @Override
-    public int getDuration_impl() {
-        if (isInPlaybackState()) {
-            return mMediaPlayer.getDuration();
-        }
-        return -1;
-    }
-
-    @Override
-    public int getCurrentPosition_impl() {
-        if (isInPlaybackState()) {
-            return mMediaPlayer.getCurrentPosition();
-        }
-        return 0;
-    }
-
-    @Override
-    public void seekTo_impl(int msec) {
-        if (isInPlaybackState()) {
-            mMediaPlayer.seekTo(msec);
-            mSeekWhenPrepared = 0;
-            updatePlaybackState();
+            if (subtitleTrackIndices.size() > 0) {
+                // Select first subtitle track
+                mCCEnabled = true;
+                mSelectedTrackIndex = subtitleTrackIndices.get(0);
+                mMediaPlayer.selectTrack(mSelectedTrackIndex);
+            }
         } else {
-            mSeekWhenPrepared = msec;
-        }
-    }
-
-    @Override
-    public boolean isPlaying_impl() {
-        return (isInPlaybackState()) && mMediaPlayer.isPlaying();
-    }
-
-    @Override
-    public int getBufferPercentage_impl() {
-        return mCurrentBufferPercentage;
-    }
-
-    @Override
-    public int getAudioSessionId_impl() {
-        if (mAudioSession == 0) {
-            MediaPlayer foo = new MediaPlayer();
-            mAudioSession = foo.getAudioSessionId();
-            foo.release();
-        }
-        return mAudioSession;
-    }
-
-    @Override
-    public void showSubtitle_impl() {
-        // Retrieve all tracks that belong to the current video.
-        MediaPlayer.TrackInfo[] trackInfos = mMediaPlayer.getTrackInfo();
-
-        List<Integer> subtitleTrackIndices = new ArrayList<>();
-        for (int i = 0; i < trackInfos.length; ++i) {
-            int trackType = trackInfos[i].getTrackType();
-            if (trackType == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) {
-                subtitleTrackIndices.add(i);
+            if (mCCEnabled) {
+                mMediaPlayer.deselectTrack(mSelectedTrackIndex);
+                mCCEnabled = false;
             }
         }
-        if (subtitleTrackIndices.size() > 0) {
-            // Select first subtitle track
-            mCCEnabled = true;
-            mSelectedTrackIndex = subtitleTrackIndices.get(0);
-            mMediaPlayer.selectTrack(mSelectedTrackIndex);
-        }
     }
 
-    @Override
-    public void hideSubtitle_impl() {
-        if (mCCEnabled) {
-            mMediaPlayer.deselectTrack(mSelectedTrackIndex);
-            mCCEnabled = false;
-        }
-    }
-
-    @Override
-    public void setFullScreen_impl(boolean fullScreen) {
-        if (mOnFullScreenChangedListener != null) {
-            mOnFullScreenChangedListener.onFullScreenChanged(fullScreen);
-        }
-    }
-
+    // TODO: remove setSpeed_impl once MediaController2 is ready.
     @Override
     public void setSpeed_impl(float speed) {
         if (speed <= 0.0f) {
@@ -306,20 +262,7 @@
         if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
             applySpeed();
         }
-    }
-
-    @Override
-    public float getSpeed_impl() {
-        if (DEBUG) {
-            if (mMediaPlayer != null) {
-                float speed = mMediaPlayer.getPlaybackParams().getSpeed();
-                if (speed != mSpeed) {
-                    Log.w(TAG, "VideoView2's speed : " + mSpeed + " is different from "
-                            + "MediaPlayer's speed : " + speed);
-                }
-            }
-        }
-        return mSpeed;
+        updatePlaybackState();
     }
 
     @Override
@@ -343,22 +286,41 @@
     }
 
     @Override
-    public void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerBase player) {
+    public void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerInterface player) {
         // TODO: implement this.
     }
 
     @Override
+    public void setRouteAttributes_impl(@NonNull List<String> routeCategories,
+            MediaSession.Callback sessionPlayer) {
+        MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder();
+        for (String category : routeCategories) {
+            builder.addControlCategory(category);
+        }
+        mRouteSelector = builder.build();
+        if (mMediaControlView != null) {
+            ((MediaControlView2Impl) mMediaControlView.getProvider())
+                    .setRouteSelector(mRouteSelector);
+        }
+        mMediaRouter = MediaRouter.getInstance(mInstance.getContext());
+        mRouteSessionCallback = sessionPlayer;
+        if (mMediaSession != null) {
+            mMediaRouter.setMediaSession(mMediaSession);
+        }
+    }
+
+    @Override
     public void setVideoPath_impl(String path) {
-        mInstance.setVideoURI(Uri.parse(path));
+        mInstance.setVideoUri(Uri.parse(path));
     }
 
     @Override
-    public void setVideoURI_impl(Uri uri) {
-        mInstance.setVideoURI(uri, null);
+    public void setVideoUri_impl(Uri uri) {
+        mInstance.setVideoUri(uri, null);
     }
 
     @Override
-    public void setVideoURI_impl(Uri uri, Map<String, String> headers) {
+    public void setVideoUri_impl(Uri uri, Map<String, String> headers) {
         mSeekWhenPrepared = 0;
         openVideo(uri, headers);
     }
@@ -388,39 +350,49 @@
         return mCurrentView.getViewType();
     }
 
+    // TODO: Handle executor properly for all the set listener methods.
     @Override
-    public void stopPlayback_impl() {
-        resetPlayer();
+    public void setCustomActions_impl(
+            List<PlaybackState.CustomAction> actionList,
+            Executor executor, VideoView2.OnCustomActionListener listener) {
+        mCustomActionList = actionList;
+        mOnCustomActionListener = listener;
+
+        // Create a new playback builder in order to clear existing the custom actions.
+        mStateBuilder = null;
+        updatePlaybackState();
     }
 
     @Override
-    public void setOnPreparedListener_impl(VideoView2.OnPreparedListener l) {
+    public void setOnPreparedListener_impl(Executor executor, VideoView2.OnPreparedListener l) {
         mOnPreparedListener = l;
     }
 
     @Override
-    public void setOnCompletionListener_impl(VideoView2.OnCompletionListener l) {
+    public void setOnCompletionListener_impl(Executor executor, VideoView2.OnCompletionListener l) {
         mOnCompletionListener = l;
     }
 
     @Override
-    public void setOnErrorListener_impl(VideoView2.OnErrorListener l) {
+    public void setOnErrorListener_impl(Executor executor, VideoView2.OnErrorListener l) {
         mOnErrorListener = l;
     }
 
     @Override
-    public void setOnInfoListener_impl(VideoView2.OnInfoListener l) {
+    public void setOnInfoListener_impl(Executor executor, VideoView2.OnInfoListener l) {
         mOnInfoListener = l;
     }
 
     @Override
-    public void setOnViewTypeChangedListener_impl(VideoView2.OnViewTypeChangedListener l) {
+    public void setOnViewTypeChangedListener_impl(Executor executor,
+            VideoView2.OnViewTypeChangedListener l) {
         mOnViewTypeChangedListener = l;
     }
 
     @Override
-    public void setFullScreenChangedListener_impl(VideoView2.OnFullScreenChangedListener l) {
-        mOnFullScreenChangedListener = l;
+    public void setFullScreenRequestListener_impl(Executor executor,
+            VideoView2.OnFullScreenRequestListener l) {
+        mOnFullScreenRequestListener = l;
     }
 
     @Override
@@ -430,15 +402,21 @@
         // Create MediaSession
         mMediaSession = new MediaSession(mInstance.getContext(), "VideoView2MediaSession");
         mMediaSession.setCallback(new MediaSessionCallback());
+        mMediaController = mMediaSession.getController();
+        if (mMediaRouter != null) {
+            mMediaRouter.setMediaSession(mMediaSession);
+        }
 
         attachMediaControlView();
     }
 
     @Override
     public void onDetachedFromWindow_impl() {
+        Log.e(TAG, ".... Debugging. onDetachedFromWindow_impl()");
         mSuperProvider.onDetachedFromWindow_impl();
         mMediaSession.release();
         mMediaSession = null;
+        mMediaController = null;
     }
 
     @Override
@@ -469,58 +447,11 @@
     }
 
     @Override
-    public boolean onKeyDown_impl(int keyCode, KeyEvent event) {
-        Log.v(TAG, "onKeyDown_impl: " + keyCode);
-        boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK
-                && keyCode != KeyEvent.KEYCODE_VOLUME_UP
-                && keyCode != KeyEvent.KEYCODE_VOLUME_DOWN
-                && keyCode != KeyEvent.KEYCODE_VOLUME_MUTE
-                && keyCode != KeyEvent.KEYCODE_MENU
-                && keyCode != KeyEvent.KEYCODE_CALL
-                && keyCode != KeyEvent.KEYCODE_ENDCALL;
-        if (isInPlaybackState() && isKeyCodeSupported && mMediaControlView != null) {
-            if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK
-                    || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
-                if (mMediaPlayer.isPlaying()) {
-                    mInstance.pause();
-                    mMediaControlView.show();
-                } else {
-                    mInstance.start();
-                    mMediaControlView.hide();
-                }
-                return true;
-            } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
-                if (!mMediaPlayer.isPlaying()) {
-                    mInstance.start();
-                    mMediaControlView.hide();
-                }
-                return true;
-            } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
-                    || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
-                if (mMediaPlayer.isPlaying()) {
-                    mInstance.pause();
-                    mMediaControlView.show();
-                }
-                return true;
-            } else {
-                toggleMediaControlViewVisibility();
-            }
-        }
-
-        return mSuperProvider.onKeyDown_impl(keyCode, event);
-    }
-
-    @Override
     public void onFinishInflate_impl() {
         mSuperProvider.onFinishInflate_impl();
     }
 
     @Override
-    public boolean dispatchKeyEvent_impl(KeyEvent event) {
-        return mSuperProvider.dispatchKeyEvent_impl(event);
-    }
-
-    @Override
     public void setEnabled_impl(boolean enabled) {
         mSuperProvider.setEnabled_impl(enabled);
     }
@@ -537,7 +468,7 @@
                     + ", " + view.toString());
         }
         if (needToStart()) {
-            mInstance.start();
+            mMediaController.getTransportControls().play();
         }
     }
 
@@ -548,7 +479,7 @@
                     + ", mTargetState=" + mTargetState + ", " + view.toString());
         }
         if (mMediaControlView != null) {
-            mMediaControlView.hide();
+            mMediaControlView.setVisibility(View.GONE);
         }
     }
 
@@ -568,10 +499,10 @@
         }
         mCurrentView = view;
         if (mOnViewTypeChangedListener != null) {
-            mOnViewTypeChangedListener.onViewTypeChanged(view.getViewType());
+            mOnViewTypeChangedListener.onViewTypeChanged(mInstance, view.getViewType());
         }
         if (needToStart()) {
-            mInstance.start();
+            mMediaController.getTransportControls().play();
         }
     }
 
@@ -630,12 +561,6 @@
             controller.registerRenderer(new Cea708CaptionRenderer(context));
             controller.registerRenderer(new ClosedCaptionRenderer(context));
             mMediaPlayer.setSubtitleAnchor(controller, (SubtitleController.Anchor) mSubtitleView);
-
-            if (mAudioSession != 0) {
-                mMediaPlayer.setAudioSessionId(mAudioSession);
-            } else {
-                mAudioSession = mMediaPlayer.getAudioSessionId();
-            }
             mMediaPlayer.setOnPreparedListener(mPreparedListener);
             mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
             mMediaPlayer.setOnCompletionListener(mCompletionListener);
@@ -734,13 +659,17 @@
             }
             mStateBuilder = new PlaybackState.Builder();
             mStateBuilder.setActions(playbackActions);
-            mStateBuilder.addCustomAction(MediaControlView2Impl.COMMAND_SHOW_SUBTITLE, null, -1);
-            mStateBuilder.addCustomAction(MediaControlView2Impl.COMMAND_HIDE_SUBTITLE, null, -1);
+
+            if (mCustomActionList != null) {
+                for (PlaybackState.CustomAction action : mCustomActionList) {
+                    mStateBuilder.addCustomAction(action);
+                }
+            }
         }
         mStateBuilder.setState(getCorrespondingPlaybackState(),
-                mInstance.getCurrentPosition(), 1.0f);
+                mMediaPlayer.getCurrentPosition(), mSpeed);
         mStateBuilder.setBufferedPosition(
-                (long) (mCurrentBufferPercentage / 100.0) * mInstance.getDuration());
+                (long) (mCurrentBufferPercentage / 100.0) * mMediaPlayer.getDuration());
 
         // Set PlaybackState for MediaSession
         if (mMediaSession != null) {
@@ -772,9 +701,9 @@
 
     private void toggleMediaControlViewVisibility() {
         if (mMediaControlView.isShowing()) {
-            mMediaControlView.hide();
+            mMediaControlView.setVisibility(View.GONE);
         } else {
-            mMediaControlView.show();
+            mMediaControlView.setVisibility(View.VISIBLE);
         }
     }
 
@@ -801,6 +730,14 @@
         }
     }
 
+    private boolean isRemotePlayback() {
+        if (mMediaController == null) {
+            return false;
+        }
+        PlaybackInfo playbackInfo = mMediaController.getPlaybackInfo();
+        return (playbackInfo != null) && (playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE);
+    }
+
     MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener =
             new MediaPlayer.OnVideoSizeChangedListener() {
                 public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
@@ -828,7 +765,7 @@
             }
             mCurrentState = STATE_PREPARED;
             if (mOnPreparedListener != null) {
-                mOnPreparedListener.onPrepared();
+                mOnPreparedListener.onPrepared(mInstance);
             }
             if (mMediaControlView != null) {
                 mMediaControlView.setEnabled(true);
@@ -837,9 +774,9 @@
             int videoHeight = mp.getVideoHeight();
 
             // mSeekWhenPrepared may be changed after seekTo() call
-            int seekToPosition = mSeekWhenPrepared;
+            long seekToPosition = mSeekWhenPrepared;
             if (seekToPosition != 0) {
-                mInstance.seekTo(seekToPosition);
+                mMediaController.getTransportControls().seekTo(seekToPosition);
             }
 
             if (videoWidth != 0 && videoHeight != 0) {
@@ -859,23 +796,26 @@
                 }
 
                 if (needToStart()) {
-                    mInstance.start();
+                    mMediaController.getTransportControls().play();
                     if (mMediaControlView != null) {
-                        mMediaControlView.show();
+                        mMediaControlView.setVisibility(View.VISIBLE);
                     }
-                } else if (!mInstance.isPlaying() && (seekToPosition != 0
-                        || mInstance.getCurrentPosition() > 0)) {
+                } else if (!(isInPlaybackState() && mMediaPlayer.isPlaying())
+                        && (seekToPosition != 0 || mMediaPlayer.getCurrentPosition() > 0)) {
                     if (mMediaControlView != null) {
                         // Show the media controls when we're paused into a video and
                         // make them stick.
-                        mMediaControlView.show(0);
+                        long currTimeout = mMediaControlView.getTimeout();
+                        mMediaControlView.setTimeout(0L);
+                        mMediaControlView.setVisibility(View.VISIBLE);
+                        mMediaControlView.setTimeout(currTimeout);
                     }
                 }
             } else {
                 // We don't know the video size yet, but should start anyway.
                 // The video size might be reported to us later.
                 if (needToStart()) {
-                    mInstance.start();
+                    mMediaController.getTransportControls().play();
                 }
             }
             // Create and set playback state for MediaControlView2
@@ -887,7 +827,7 @@
                 mTitle = mMetadata.getString(Metadata.TITLE);
             }
             builder.putString(MediaMetadata.METADATA_KEY_TITLE, mTitle);
-            builder.putLong(MediaMetadata.METADATA_KEY_DURATION, mInstance.getDuration());
+            builder.putLong(MediaMetadata.METADATA_KEY_DURATION, mMediaPlayer.getDuration());
 
             if (mMediaSession != null) {
                 mMediaSession.setMetadata(builder.build());
@@ -903,7 +843,7 @@
                     updatePlaybackState();
 
                     if (mOnCompletionListener != null) {
-                        mOnCompletionListener.onCompletion();
+                        mOnCompletionListener.onCompletion(mInstance);
                     }
                     if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
                         mAudioManager.abandonAudioFocus(null);
@@ -915,7 +855,7 @@
             new MediaPlayer.OnInfoListener() {
                 public boolean onInfo(MediaPlayer mp, int what, int extra) {
                     if (mOnInfoListener != null) {
-                        mOnInfoListener.onInfo(what, extra);
+                        mOnInfoListener.onInfo(mInstance, what, extra);
                     }
                     return true;
                 }
@@ -932,12 +872,12 @@
                     updatePlaybackState();
 
                     if (mMediaControlView != null) {
-                        mMediaControlView.hide();
+                        mMediaControlView.setVisibility(View.GONE);
                     }
 
                     /* If an error handler has been supplied, use it and finish. */
                     if (mOnErrorListener != null) {
-                        if (mOnErrorListener.onError(frameworkErr, implErr)) {
+                        if (mOnErrorListener.onError(mInstance, frameworkErr, implErr)) {
                             return true;
                         }
                     }
@@ -968,7 +908,7 @@
                                                 * at least inform them that the video is over.
                                                 */
                                                 if (mOnCompletionListener != null) {
-                                                    mOnCompletionListener.onCompletion();
+                                                    mOnCompletionListener.onCompletion(mInstance);
                                                 }
                                             }
                                         })
@@ -990,33 +930,156 @@
     private class MediaSessionCallback extends MediaSession.Callback {
         @Override
         public void onCommand(String command, Bundle args, ResultReceiver receiver) {
-            switch (command) {
-                case MediaControlView2Impl.COMMAND_SHOW_SUBTITLE:
-                    mInstance.showSubtitle();
-                    break;
-                case MediaControlView2Impl.COMMAND_HIDE_SUBTITLE:
-                    mInstance.hideSubtitle();
-                    break;
-                case MediaControlView2Impl.COMMAND_SET_FULLSCREEN:
-                    mInstance.setFullScreen(
-                            args.getBoolean(MediaControlView2Impl.ARGUMENT_KEY_FULLSCREEN));
-                    break;
+            if (isRemotePlayback()) {
+                mRouteSessionCallback.onCommand(command, args, receiver);
+            } else {
+                switch (command) {
+                    case MediaControlView2.COMMAND_SHOW_SUBTITLE:
+                        mInstance.showSubtitle(true);
+                        break;
+                    case MediaControlView2.COMMAND_HIDE_SUBTITLE:
+                        mInstance.showSubtitle(false);
+                        break;
+                    case MediaControlView2.COMMAND_SET_FULLSCREEN:
+                        if (mOnFullScreenRequestListener != null) {
+                            mOnFullScreenRequestListener.onFullScreenRequest(
+                                    mInstance,
+                                    args.getBoolean(MediaControlView2Impl.ARGUMENT_KEY_FULLSCREEN));
+                        }
+                        break;
+                }
             }
         }
 
         @Override
+        public void onCustomAction(String action, Bundle extras) {
+            mOnCustomActionListener.onCustomAction(action, extras);
+        }
+
+        @Override
         public void onPlay() {
-            mInstance.start();
+            if (isRemotePlayback()) {
+                mRouteSessionCallback.onPlay();
+            } else {
+                if (isInPlaybackState() && mCurrentView.hasAvailableSurface()) {
+                    applySpeed();
+                    mMediaPlayer.start();
+                    mCurrentState = STATE_PLAYING;
+                    updatePlaybackState();
+                }
+                mTargetState = STATE_PLAYING;
+                if (DEBUG) {
+                    Log.d(TAG, "onPlay(). mCurrentState=" + mCurrentState
+                            + ", mTargetState=" + mTargetState);
+                }
+            }
         }
 
         @Override
         public void onPause() {
-            mInstance.pause();
+            if (isRemotePlayback()) {
+                mRouteSessionCallback.onPause();
+            } else {
+                if (isInPlaybackState()) {
+                    if (mMediaPlayer.isPlaying()) {
+                        mMediaPlayer.pause();
+                        mCurrentState = STATE_PAUSED;
+                        updatePlaybackState();
+                    }
+                }
+                mTargetState = STATE_PAUSED;
+                if (DEBUG) {
+                    Log.d(TAG, "onPause(). mCurrentState=" + mCurrentState
+                            + ", mTargetState=" + mTargetState);
+                }
+            }
         }
 
         @Override
         public void onSeekTo(long pos) {
-            mInstance.seekTo((int) pos);
+            if (isRemotePlayback()) {
+                mRouteSessionCallback.onSeekTo(pos);
+            } else {
+                if (isInPlaybackState()) {
+                    mMediaPlayer.seekTo(pos, MediaPlayer.SEEK_PREVIOUS_SYNC);
+                    mSeekWhenPrepared = 0;
+                    updatePlaybackState();
+                } else {
+                    mSeekWhenPrepared = pos;
+                }
+            }
+        }
+
+        @Override
+        public void onStop() {
+            if (isRemotePlayback()) {
+                mRouteSessionCallback.onStop();
+            } else {
+                resetPlayer();
+            }
+        }
+    }
+
+    private class RouteSessionCallback extends MediaSession.Callback {
+        RemotePlaybackClient mClient;
+
+        RemotePlaybackClient.StatusCallback mStatusCallback =
+                new RemotePlaybackClient.StatusCallback() {
+            @Override
+            public void onItemStatusChanged(Bundle data,
+                    String sessionId, MediaSessionStatus sessionStatus,
+                    String itemId, MediaItemStatus itemStatus) {
+                // TODO: implement this
+            }
+
+            @Override
+            public void onSessionStatusChanged(Bundle data,
+                    String sessionId, MediaSessionStatus sessionStatus) {
+                // TODO: implement this
+            }
+
+            @Override
+            public void onSessionChanged(String sessionId) {
+                // TODO: implement this
+            }
+        };
+
+        @Override
+        public void onCommand(String command, Bundle args, ResultReceiver receiver) {
+            ensureClient();
+            // TODO: implement this
+        }
+
+        @Override
+        public void onPlay() {
+            ensureClient();
+            // TODO: implement this
+        }
+
+        @Override
+        public void onPause() {
+            ensureClient();
+            // TODO: implement this
+        }
+
+        @Override
+        public void onSeekTo(long pos) {
+            ensureClient();
+            // TODO: implement this
+        }
+
+        @Override
+        public void onStop() {
+            ensureClient();
+            // TODO: implement this
+        }
+
+        private void ensureClient() {
+            if (mClient == null) {
+                mClient = new RemotePlaybackClient(
+                        mInstance.getContext(), mMediaRouter.getSelectedRoute());
+                mClient.setStatusCallback(mStatusCallback);
+            }
         }
     }
 }
diff --git a/packages/MediaComponents/test/runtest.sh b/packages/MediaComponents/test/runtest.sh
index 5c0ef51..920fa96 100644
--- a/packages/MediaComponents/test/runtest.sh
+++ b/packages/MediaComponents/test/runtest.sh
@@ -129,11 +129,12 @@
       ${adb} root
       ${adb} remount
       ${adb} shell stop
+      ${adb} shell setprop log.tag.MediaSessionService DEBUG
       ${adb} sync
       ${adb} shell start
       ${adb} wait-for-device || break
       # Ensure package manager is loaded.
-      sleep 5
+      sleep 15
 
       # Install apks
       local install_failed="false"
diff --git a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
index fe8aeb9..38475a8 100644
--- a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
@@ -20,10 +20,14 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.media.MediaBrowser2.BrowserCallback;
+import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.PlaylistParams;
 import android.os.Bundle;
+import android.os.ResultReceiver;
 import android.support.annotation.CallSuper;
 import android.support.annotation.NonNull;
 import android.support.test.filters.SmallTest;
@@ -48,18 +52,26 @@
     private static final String TAG = "MediaBrowser2Test";
 
     @Override
-    TestControllerInterface onCreateController(@NonNull SessionToken token,
-            @NonNull TestControllerCallbackInterface callback) {
+    TestControllerInterface onCreateController(@NonNull SessionToken2 token,
+            @Nullable TestControllerCallbackInterface callback) {
+        if (callback == null) {
+            callback = new TestBrowserCallbackInterface() {};
+        }
         return new TestMediaBrowser(mContext, token, new TestBrowserCallback(callback));
     }
 
+    interface TestBrowserCallbackInterface extends TestControllerCallbackInterface {
+        // Browser specific callbacks
+        default void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {}
+    }
+
     @Test
-    public void testGetBrowserRoot() throws InterruptedException {
+    public void testGetLibraryRoot() throws InterruptedException {
         final Bundle param = new Bundle();
         param.putString(TAG, TAG);
 
         final CountDownLatch latch = new CountDownLatch(1);
-        final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
+        final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
             @Override
             public void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {
                 assertTrue(TestUtils.equals(param, rootHints));
@@ -69,10 +81,10 @@
             }
         };
 
-        final SessionToken token = MockMediaLibraryService2.getToken(mContext);
+        final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
         MediaBrowser2 browser =
                 (MediaBrowser2) createController(token,true, callback);
-        browser.getBrowserRoot(param);
+        browser.getLibraryRoot(param);
         assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
     }
 
@@ -83,6 +95,9 @@
         public final CountDownLatch disconnectLatch = new CountDownLatch(1);
 
         TestBrowserCallback(TestControllerCallbackInterface callbackProxy) {
+            if (callbackProxy == null) {
+                throw new IllegalArgumentException("Callback proxy shouldn't be null. Test bug");
+            }
             mCallbackProxy = callbackProxy;
         }
 
@@ -101,8 +116,30 @@
         }
 
         @Override
+        public void onPlaybackStateChanged(PlaybackState2 state) {
+            super.onPlaybackStateChanged(state);
+            mCallbackProxy.onPlaybackStateChanged(state);
+        }
+
+        @Override
+        public void onPlaylistParamsChanged(PlaylistParams params) {
+            super.onPlaylistParamsChanged(params);
+            mCallbackProxy.onPlaylistParamsChanged(params);
+        }
+
+        @Override
+        public void onCustomCommand(Command command, Bundle args, ResultReceiver receiver) {
+            super.onCustomCommand(command, args, receiver);
+            mCallbackProxy.onCustomCommand(command, args, receiver);
+        }
+
+        @Override
         public void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {
-            mCallbackProxy.onGetRootResult(rootHints, rootMediaId, rootExtra);
+            super.onGetRootResult(rootHints, rootMediaId, rootExtra);
+            if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+                ((TestBrowserCallbackInterface) mCallbackProxy)
+                        .onGetRootResult(rootHints, rootMediaId, rootExtra);
+            }
         }
 
         @Override
@@ -127,9 +164,9 @@
     public class TestMediaBrowser extends MediaBrowser2 implements TestControllerInterface {
         private final BrowserCallback mCallback;
 
-        public TestMediaBrowser(@NonNull Context context, @NonNull SessionToken token,
+        public TestMediaBrowser(@NonNull Context context, @NonNull SessionToken2 token,
                 @NonNull ControllerCallback callback) {
-            super(context, token, (BrowserCallback) callback, sHandlerExecutor);
+            super(context, token, sHandlerExecutor, (BrowserCallback) callback);
             mCallback = (BrowserCallback) callback;
         }
 
diff --git a/packages/MediaComponents/test/src/android/media/MediaController2Test.java b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
index ac0bd73..e8eaa20 100644
--- a/packages/MediaComponents/test/src/android/media/MediaController2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
@@ -16,14 +16,19 @@
 
 package android.media;
 
-import android.media.MediaPlayerBase.PlaybackListener;
+import android.media.MediaController2.ControllerCallback;
+import android.media.MediaPlayerInterface.PlaybackListener;
+import android.media.MediaSession2.Command;
 import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.PlaylistParams;
 import android.media.MediaSession2.SessionCallback;
 import android.media.TestUtils.SyncHandler;
 import android.media.session.PlaybackState;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Process;
+import android.os.ResultReceiver;
 import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -37,7 +42,8 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-import static android.media.TestUtils.createPlaybackState;
+import static android.media.TestUtils.ensurePlaylistParamsModeEquals;
+
 import static org.junit.Assert.*;
 
 /**
@@ -45,6 +51,7 @@
  */
 // TODO(jaewan): Implement host-side test so controller and session can run in different processes.
 // TODO(jaewan): Fix flaky failure -- see MediaController2Impl.getController()
+// TODO(jaeawn): Revisit create/close session in the sHandler. It's no longer necessary.
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 @FlakyTest
@@ -60,11 +67,10 @@
     public void setUp() throws Exception {
         super.setUp();
         // Create this test specific MediaSession2 to use our own Handler.
-        sHandler.postAndSync(()->{
-            mPlayer = new MockPlayer(1);
-            mSession = new MediaSession2.Builder(mContext, mPlayer).setId(TAG).build();
-        });
-
+        mPlayer = new MockPlayer(1);
+        mSession = new MediaSession2.Builder(mContext, mPlayer)
+                .setSessionCallback(sHandlerExecutor, new SessionCallback(mContext))
+                .setId(TAG).build();
         mController = createController(mSession.getToken());
         TestServiceRegistry.getInstance().setHandler(sHandler);
     }
@@ -73,11 +79,9 @@
     @Override
     public void cleanUp() throws Exception {
         super.cleanUp();
-        sHandler.postAndSync(() -> {
-            if (mSession != null) {
-                mSession.close();
-            }
-        });
+        if (mSession != null) {
+            mSession.close();
+        }
         TestServiceRegistry.getInstance().cleanUp();
     }
 
@@ -103,7 +107,6 @@
         assertTrue(mPlayer.mPauseCalled);
     }
 
-
     @Test
     public void testSkipToPrevious() throws InterruptedException {
         mController.skipToPrevious();
@@ -138,59 +141,146 @@
     }
 
     @Test
+    public void testPrepare() throws InterruptedException {
+        mController.prepare();
+        try {
+            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        assertTrue(mPlayer.mPrepareCalled);
+    }
+
+    @Test
+    public void testFastForward() throws InterruptedException {
+        mController.fastForward();
+        try {
+            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        assertTrue(mPlayer.mFastForwardCalled);
+    }
+
+    @Test
+    public void testRewind() throws InterruptedException {
+        mController.rewind();
+        try {
+            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        assertTrue(mPlayer.mRewindCalled);
+    }
+
+    @Test
+    public void testSeekTo() throws InterruptedException {
+        final long seekPosition = 12125L;
+        mController.seekTo(seekPosition);
+        try {
+            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        assertTrue(mPlayer.mSeekToCalled);
+        assertEquals(seekPosition, mPlayer.mSeekPosition);
+    }
+
+    @Test
+    public void testSetCurrentPlaylistItem() throws InterruptedException {
+        final int itemIndex = 9;
+        mController.setCurrentPlaylistItem(itemIndex);
+        try {
+            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        assertTrue(mPlayer.mSetCurrentPlaylistItemCalled);
+        assertEquals(itemIndex, mPlayer.mItemIndex);
+    }
+
+    @Test
+    public void testGetSetPlaylistParams() throws Exception {
+        final PlaylistParams params = new PlaylistParams(mContext,
+                PlaylistParams.REPEAT_MODE_ALL,
+                PlaylistParams.SHUFFLE_MODE_ALL,
+                null /* PlaylistMetadata */);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
+            @Override
+            public void onPlaylistParamsChanged(PlaylistParams givenParams) {
+                ensurePlaylistParamsModeEquals(params, givenParams);
+                latch.countDown();
+            }
+        };
+
+        final MediaController2 controller = createController(mSession.getToken(), true, callback);
+        controller.setPlaylistParams(params);
+
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        ensurePlaylistParamsModeEquals(params, mSession.getPlaylistParams());
+        ensurePlaylistParamsModeEquals(params, controller.getPlaylistParams());
+    }
+
+    @Test
     public void testGetPackageName() {
         assertEquals(mContext.getPackageName(), mController.getSessionToken().getPackageName());
     }
 
+    // This also tests testGetPlaybackState().
     @Test
-    public void testGetPlaybackState() throws InterruptedException {
-        final CountDownLatch latch = new CountDownLatch(1);
-        final MediaPlayerBase.PlaybackListener listener = (state) -> {
-            assertEquals(PlaybackState.STATE_BUFFERING, state.getState());
-            latch.countDown();
-        };
-        assertNull(mController.getPlaybackState());
-        mController.addPlaybackListener(listener, sHandler);
-
-        mPlayer.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_BUFFERING));
-        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-        assertEquals(PlaybackState.STATE_BUFFERING, mController.getPlaybackState().getState());
-    }
-
-    @Test
-    public void testAddPlaybackListener() throws InterruptedException {
+    public void testControllerCallback_onPlaybackStateChanged() throws InterruptedException {
         final CountDownLatch latch = new CountDownLatch(2);
-        final MediaPlayerBase.PlaybackListener listener = (state) -> {
-            switch ((int) latch.getCount()) {
-                case 2:
-                    assertEquals(PlaybackState.STATE_PLAYING, state.getState());
-                    break;
-                case 1:
-                    assertEquals(PlaybackState.STATE_PAUSED, state.getState());
-                    break;
+        final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
+            @Override
+            public void onPlaybackStateChanged(PlaybackState2 state) {
+                switch ((int) latch.getCount()) {
+                    case 2:
+                        assertEquals(PlaybackState.STATE_PLAYING, state.getState());
+                        break;
+                    case 1:
+                        assertEquals(PlaybackState.STATE_PAUSED, state.getState());
+                        break;
+                }
+                latch.countDown();
             }
-            latch.countDown();
         };
 
-        mController.addPlaybackListener(listener, sHandler);
-        sHandler.postAndSync(()->{
-            mPlayer.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_PLAYING));
-            mPlayer.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_PAUSED));
-        });
+        mPlayer.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_PLAYING));
+        mController = createController(mSession.getToken(), true, callback);
+        mPlayer.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_PAUSED));
         assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        assertEquals(PlaybackState.STATE_PAUSED, mController.getPlaybackState().getState());
     }
 
     @Test
-    public void testRemovePlaybackListener() throws InterruptedException {
+    public void testSendCustomCommand() throws InterruptedException {
+        // TODO(jaewan): Need to revisit with the permission.
+        final Command testCommand =
+                new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PREPARE);
+        final Bundle testArgs = new Bundle();
+        testArgs.putString("args", "testSendCustomAction");
+
         final CountDownLatch latch = new CountDownLatch(1);
-        final MediaPlayerBase.PlaybackListener listener = (state) -> {
-            fail();
-            latch.countDown();
+        final SessionCallback callback = new SessionCallback(mContext) {
+            @Override
+            public void onCustomCommand(ControllerInfo controller, Command customCommand,
+                    Bundle args, ResultReceiver cb) {
+                super.onCustomCommand(controller, customCommand, args, cb);
+                assertEquals(mContext.getPackageName(), controller.getPackageName());
+                assertEquals(testCommand, customCommand);
+                assertTrue(TestUtils.equals(testArgs, args));
+                assertNull(cb);
+                latch.countDown();
+            }
         };
-        mController.addPlaybackListener(listener, sHandler);
-        mController.removePlaybackListener(listener);
-        mPlayer.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_PLAYING));
-        assertFalse(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        mSession.close();
+        mSession = new MediaSession2.Builder(mContext, mPlayer)
+                .setSessionCallback(sHandlerExecutor, callback).setId(TAG).build();
+        final MediaController2 controller = createController(mSession.getToken());
+        controller.sendCustomCommand(testCommand, testArgs, null);
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
     }
 
     @Test
@@ -203,7 +293,7 @@
 
     @Test
     public void testControllerCallback_sessionRejects() throws InterruptedException {
-        final MediaSession2.SessionCallback sessionCallback = new SessionCallback() {
+        final MediaSession2.SessionCallback sessionCallback = new SessionCallback(mContext) {
             @Override
             public MediaSession2.CommandGroup onConnect(ControllerInfo controller) {
                 return null;
@@ -212,7 +302,7 @@
         sHandler.postAndSync(() -> {
             mSession.close();
             mSession = new MediaSession2.Builder(mContext, mPlayer)
-                    .setSessionCallback(sessionCallback).build();
+                    .setSessionCallback(sHandlerExecutor, sessionCallback).build();
         });
         MediaController2 controller =
                 createController(mSession.getToken(), false, null);
@@ -269,14 +359,12 @@
             final MockPlayer player = new MockPlayer(0);
             sessionHandler.postAndSync(() -> {
                 mSession = new MediaSession2.Builder(mContext, mPlayer)
+                        .setSessionCallback(sHandlerExecutor, new SessionCallback(mContext))
                         .setId("testDeadlock").build();
             });
             final MediaController2 controller = createController(mSession.getToken());
             testHandler.post(() -> {
-                controller.addPlaybackListener((state) -> {
-                    // no-op. Just to set a binder call path from session to controller.
-                }, sessionHandler);
-                final PlaybackState state = createPlaybackState(PlaybackState.STATE_ERROR);
+                final PlaybackState2 state = createPlaybackState(PlaybackState.STATE_ERROR);
                 for (int i = 0; i < 100; i++) {
                     // triggers call from session to controller.
                     player.notifyPlaybackState(state);
@@ -317,15 +405,14 @@
     @Ignore
     @Test
     public void testGetServiceToken() {
-        SessionToken token = TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID);
+        SessionToken2 token = TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID);
         assertNotNull(token);
         assertEquals(mContext.getPackageName(), token.getPackageName());
         assertEquals(MockMediaSessionService2.ID, token.getId());
-        assertNull(token.getSessionBinder());
-        assertEquals(SessionToken.TYPE_SESSION_SERVICE, token.getType());
+        assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
     }
 
-    private void connectToService(SessionToken token) throws InterruptedException {
+    private void connectToService(SessionToken2 token) throws InterruptedException {
         mController = createController(token);
         mSession = TestServiceRegistry.getInstance().getServiceInstance().getSession();
         mPlayer = (MockPlayer) mSession.getPlayer();
@@ -360,6 +447,8 @@
         assertTrue(mPlayer.mPlayCalled);
 
         // Test command from session service to controller
+        // TODO(jaewan): Add equivalent tests again
+        /*
         final CountDownLatch latch = new CountDownLatch(1);
         mController.addPlaybackListener((state) -> {
             assertNotNull(state);
@@ -369,6 +458,7 @@
         mPlayer.notifyPlaybackState(
                 TestUtils.createPlaybackState(PlaybackState.STATE_REWINDING));
         assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        */
     }
 
     @Test
@@ -456,7 +546,9 @@
         sHandler.postAndSync(() -> {
             // Recreated session has different session stub, so previously created controller
             // shouldn't be available.
-            mSession = new MediaSession2.Builder(mContext, mPlayer).setId(id).build();
+            mSession = new MediaSession2.Builder(mContext, mPlayer)
+                    .setSessionCallback(sHandlerExecutor, new SessionCallback(mContext))
+                    .setId(id).build();
         });
         testNoInteraction();
     }
@@ -467,10 +559,13 @@
             fail("Controller shouldn't be notified about change in session after the close.");
             latch.countDown();
         };
+        // TODO(jaewan): Add equivalent tests again
+        /*
         mController.addPlaybackListener(playbackListener, sHandler);
         mPlayer.notifyPlaybackState(TestUtils.createPlaybackState(PlaybackState.STATE_BUFFERING));
         assertFalse(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
         mController.removePlaybackListener(playbackListener);
+        */
     }
 
     // TODO(jaewan): Add  test for service connect rejection, when we differentiate session
diff --git a/packages/MediaComponents/test/src/android/media/MediaMetadata2Test.java b/packages/MediaComponents/test/src/android/media/MediaMetadata2Test.java
new file mode 100644
index 0000000..ea42651
--- /dev/null
+++ b/packages/MediaComponents/test/src/android/media/MediaMetadata2Test.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import android.content.Context;
+import android.media.MediaMetadata2.Builder;
+import android.os.Bundle;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MediaMetadata2Test {
+    private Context mContext;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    @Test
+    public void testBuilder() {
+        final Bundle extra = new Bundle();
+        extra.putString("MediaMetadata2Test", "testBuilder");
+        final String title = "title";
+        final long discNumber = 10;
+        final Rating2 rating = Rating2.newThumbRating(mContext, true);
+
+        MediaMetadata2.Builder builder = new Builder(mContext);
+        builder.setExtra(extra);
+        builder.putString(MediaMetadata2.METADATA_KEY_DISPLAY_TITLE, title);
+        builder.putLong(MediaMetadata2.METADATA_KEY_DISC_NUMBER, discNumber);
+        builder.putRating(MediaMetadata2.METADATA_KEY_USER_RATING, rating);
+
+        MediaMetadata2 metadata = builder.build();
+        assertTrue(TestUtils.equals(extra, metadata.getExtra()));
+        assertEquals(title, metadata.getString(MediaMetadata2.METADATA_KEY_DISPLAY_TITLE));
+        assertEquals(discNumber, metadata.getLong(MediaMetadata2.METADATA_KEY_DISC_NUMBER));
+        assertEquals(rating, metadata.getRating(MediaMetadata2.METADATA_KEY_USER_RATING));
+    }
+}
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
index c5408e8..ff8c2b7 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
@@ -16,27 +16,37 @@
 
 package android.media;
 
-import android.media.MediaPlayerBase.PlaybackListener;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static android.media.TestUtils.ensurePlaylistParamsModeEquals;
+
+import android.media.MediaPlayerInterface.PlaybackListener;
 import android.media.MediaSession2.Builder;
+import android.media.MediaSession2.Command;
 import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.PlaylistParams;
 import android.media.MediaSession2.SessionCallback;
-import android.media.session.PlaybackState;
-import android.os.Process;
+import android.os.Bundle;
 import android.os.Looper;
+import android.os.Process;
+import android.os.ResultReceiver;
 import android.support.annotation.NonNull;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.text.TextUtils;
 
 import java.util.ArrayList;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-import static android.media.TestUtils.createPlaybackState;
 import static org.junit.Assert.*;
 
 /**
@@ -54,19 +64,16 @@
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        sHandler.postAndSync(() -> {
-            mPlayer = new MockPlayer(0);
-            mSession = new MediaSession2.Builder(mContext, mPlayer).build();
-        });
+        mPlayer = new MockPlayer(0);
+        mSession = new MediaSession2.Builder(mContext, mPlayer)
+                .setSessionCallback(sHandlerExecutor, new SessionCallback(mContext)).build();
     }
 
     @After
     @Override
     public void cleanUp() throws Exception {
         super.cleanUp();
-        sHandler.postAndSync(() -> {
-            mSession.close();
-        });
+        mSession.close();
     }
 
     @Test
@@ -138,6 +145,56 @@
     }
 
     @Test
+    public void testSetPlaylist() throws Exception {
+        final List<MediaItem2> playlist = new ArrayList<>();
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
+            @Override
+            public void onPlaylistChanged(List<MediaItem2> givenList) {
+                assertMediaItemListEquals(playlist, givenList);
+                latch.countDown();
+            }
+        };
+
+        final MediaController2 controller = createController(mSession.getToken(), true, callback);
+        mSession.setPlaylist(playlist);
+
+        assertTrue(mPlayer.mSetPlaylistCalled);
+        assertMediaItemListEquals(playlist, mPlayer.mPlaylist);
+        assertMediaItemListEquals(playlist, mSession.getPlaylist());
+
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        assertMediaItemListEquals(playlist, controller.getPlaylist());
+    }
+
+    @Test
+    public void testSetPlaylistParams() throws Exception {
+        final PlaylistParams params = new PlaylistParams(mContext,
+                PlaylistParams.REPEAT_MODE_ALL,
+                PlaylistParams.SHUFFLE_MODE_ALL,
+                null /* PlaylistMetadata */);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
+            @Override
+            public void onPlaylistParamsChanged(PlaylistParams givenParams) {
+                ensurePlaylistParamsModeEquals(params, givenParams);
+                latch.countDown();
+            }
+        };
+
+        final MediaController2 controller = createController(mSession.getToken(), true, callback);
+        mSession.setPlaylistParams(params);
+        assertTrue(mPlayer.mSetPlaylistParamsCalled);
+        ensurePlaylistParamsModeEquals(params, mPlayer.mPlaylistParams);
+        ensurePlaylistParamsModeEquals(params, mSession.getPlaylistParams());
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    // TODO(jaewan): Re-enable test..
+    @Ignore
+    @Test
     public void testPlaybackStateChangedListener() throws InterruptedException {
         final CountDownLatch latch = new CountDownLatch(2);
         final MockPlayer player = new MockPlayer(0);
@@ -146,40 +203,41 @@
             assertNotNull(state);
             switch ((int) latch.getCount()) {
                 case 2:
-                    assertEquals(PlaybackState.STATE_PLAYING, state.getState());
+                    assertEquals(PlaybackState2.STATE_PLAYING, state.getState());
                     break;
                 case 1:
-                    assertEquals(PlaybackState.STATE_PAUSED, state.getState());
+                    assertEquals(PlaybackState2.STATE_PAUSED, state.getState());
                     break;
                 case 0:
                     fail();
             }
             latch.countDown();
         };
-        player.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_PLAYING));
+        player.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PLAYING));
         sHandler.postAndSync(() -> {
-            mSession.addPlaybackListener(listener, sHandler);
+            mSession.addPlaybackListener(sHandlerExecutor, listener);
             // When the player is set, listeners will be notified about the player's current state.
             mSession.setPlayer(player);
         });
-        player.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_PAUSED));
+        player.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PAUSED));
         assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
     }
 
     @Test
     public void testBadPlayer() throws InterruptedException {
+        // TODO(jaewan): Add equivalent tests again
         final CountDownLatch latch = new CountDownLatch(3); // expected call + 1
         final BadPlayer player = new BadPlayer(0);
         sHandler.postAndSync(() -> {
-            mSession.addPlaybackListener((state) -> {
+            mSession.addPlaybackListener(sHandlerExecutor, (state) -> {
                 // This will be called for every setPlayer() calls, but no more.
                 assertNull(state);
                 latch.countDown();
-            }, sHandler);
+            });
             mSession.setPlayer(player);
             mSession.setPlayer(mPlayer);
         });
-        player.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_PAUSED));
+        player.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PAUSED));
         assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
     }
 
@@ -202,7 +260,7 @@
             mSession.close();
             mPlayer = new MockPlayer(1);
             mSession = new MediaSession2.Builder(mContext, mPlayer)
-                    .setSessionCallback(callback).build();
+                    .setSessionCallback(sHandlerExecutor, callback).build();
         });
         MediaController2 controller = createController(mSession.getToken());
         controller.pause();
@@ -226,7 +284,7 @@
         sHandler.postAndSync(() -> {
             mSession.close();
             mSession = new MediaSession2.Builder(mContext, mPlayer)
-                    .setSessionCallback(sessionCallback).build();
+                    .setSessionCallback(sHandlerExecutor, sessionCallback).build();
         });
         MediaController2 controller =
                 createController(mSession.getToken(), false, null);
@@ -235,7 +293,53 @@
         waitForDisconnect(controller, true);
     }
 
+    @Test
+    public void testSendCustomAction() throws InterruptedException {
+        final Command testCommand =
+                new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PREPARE);
+        final Bundle testArgs = new Bundle();
+        testArgs.putString("args", "testSendCustomAction");
+
+        final CountDownLatch latch = new CountDownLatch(2);
+        final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
+            @Override
+            public void onCustomCommand(Command command, Bundle args, ResultReceiver receiver) {
+                assertEquals(testCommand, command);
+                assertTrue(TestUtils.equals(testArgs, args));
+                assertNull(receiver);
+                latch.countDown();
+            }
+        };
+        final MediaController2 controller =
+                createController(mSession.getToken(), true, callback);
+        // TODO(jaewan): Test with multiple controllers
+        mSession.sendCustomCommand(testCommand, testArgs);
+
+        ControllerInfo controllerInfo = getTestControllerInfo();
+        assertNotNull(controllerInfo);
+        // TODO(jaewan): Test receivers as well.
+        mSession.sendCustomCommand(controllerInfo, testCommand, testArgs, null);
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    private ControllerInfo getTestControllerInfo() {
+        List<ControllerInfo> controllers = mSession.getConnectedControllers();
+        assertNotNull(controllers);
+        final String packageName = mContext.getPackageName();
+        for (int i = 0; i < controllers.size(); i++) {
+            if (TextUtils.equals(packageName, controllers.get(i).getPackageName())) {
+                return controllers.get(i);
+            }
+        }
+        fail("Fails to get custom command");
+        return null;
+    }
+
     public class MockOnConnectCallback extends SessionCallback {
+        public MockOnConnectCallback() {
+            super(mContext);
+        }
+
         @Override
         public MediaSession2.CommandGroup onConnect(ControllerInfo controllerInfo) {
             if (Process.myUid() != controllerInfo.getUid()) {
@@ -252,6 +356,10 @@
     public class MockOnCommandCallback extends SessionCallback {
         public final ArrayList<MediaSession2.Command> commands = new ArrayList<>();
 
+        public MockOnCommandCallback() {
+            super(mContext);
+        }
+
         @Override
         public boolean onCommandRequest(ControllerInfo controllerInfo, MediaSession2.Command command) {
             assertEquals(mContext.getPackageName(), controllerInfo.getPackageName());
@@ -264,4 +372,28 @@
             return true;
         }
     }
+
+    private static void assertMediaItemListEquals(List<MediaItem2> a, List<MediaItem2> b) {
+        if (a == null || b == null) {
+            assertEquals(a, b);
+        }
+        assertEquals(a.size(), b.size());
+
+        for (int i = 0; i < a.size(); i++) {
+            MediaItem2 aItem = a.get(i);
+            MediaItem2 bItem = b.get(i);
+
+            if (aItem == null || bItem == null) {
+                assertEquals(aItem, bItem);
+                continue;
+            }
+
+            assertEquals(aItem.getMediaId(), bItem.getMediaId());
+            assertEquals(aItem.getFlags(), bItem.getFlags());
+            TestUtils.equals(aItem.getMetadata().toBundle(), bItem.getMetadata().toBundle());
+
+            // Note: Here it does not check whether DataSourceDesc are equal,
+            // since there DataSourceDec is not comparable.
+        }
+    }
 }
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
index 2965c82..5d94b17 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
@@ -21,13 +21,16 @@
 
 import android.content.Context;
 import android.media.MediaController2.ControllerCallback;
+import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandGroup;
 import android.os.Bundle;
 import android.os.HandlerThread;
+import android.os.ResultReceiver;
 import android.support.annotation.CallSuper;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.test.InstrumentationRegistry;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
@@ -57,10 +60,14 @@
     }
 
     interface TestControllerCallbackInterface {
-        // Currently empty. Add methods in ControllerCallback/BrowserCallback that you want to test.
+        // Add methods in ControllerCallback/BrowserCallback that you want to test.
+        default void onPlaylistChanged(List<MediaItem2> playlist) {}
+        default void onPlaylistParamsChanged(MediaSession2.PlaylistParams params) {}
 
-        // Browser specific callbacks
-        default void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {}
+        // Currently empty. Add methods in ControllerCallback/BrowserCallback that you want to test.
+        default void onPlaybackStateChanged(PlaybackState2 state) { }
+
+        default void onCustomCommand(Command command, Bundle args, ResultReceiver receiver) {}
     }
 
     interface WaitForConnectionInterface {
@@ -101,11 +108,22 @@
         }
     }
 
-    final MediaController2 createController(SessionToken token) throws InterruptedException {
+    /**
+     * Creates a {@link android.media.session.PlaybackState} with the given state.
+     *
+     * @param state one of the PlaybackState.STATE_xxx.
+     * @return a PlaybackState
+     */
+    public PlaybackState2 createPlaybackState(int state) {
+        return new PlaybackState2(mContext, state, 0, 0, 1.0f,
+                0, 0, null);
+    }
+
+    final MediaController2 createController(SessionToken2 token) throws InterruptedException {
         return createController(token, true, null);
     }
 
-    final MediaController2 createController(@NonNull SessionToken token,
+    final MediaController2 createController(@NonNull SessionToken2 token,
             boolean waitForConnect, @Nullable TestControllerCallbackInterface callback)
             throws InterruptedException {
         TestControllerInterface instance = onCreateController(token, callback);
@@ -145,8 +163,11 @@
         getWaitForConnectionInterface(controller).waitForDisconnect(expected);
     }
 
-    TestControllerInterface onCreateController(@NonNull SessionToken token,
-            @NonNull TestControllerCallbackInterface callback) {
+    TestControllerInterface onCreateController(@NonNull SessionToken2 token,
+            @Nullable TestControllerCallbackInterface callback) {
+        if (callback == null) {
+            callback = new TestControllerCallbackInterface() {};
+        }
         return new TestMediaController(mContext, token, new TestControllerCallback(callback));
     }
 
@@ -156,7 +177,10 @@
         public final CountDownLatch connectLatch = new CountDownLatch(1);
         public final CountDownLatch disconnectLatch = new CountDownLatch(1);
 
-        TestControllerCallback(TestControllerCallbackInterface callbackProxy) {
+        TestControllerCallback(@NonNull TestControllerCallbackInterface callbackProxy) {
+            if (callbackProxy == null) {
+                throw new IllegalArgumentException("Callback proxy shouldn't be null. Test bug");
+            }
             mCallbackProxy = callbackProxy;
         }
 
@@ -175,6 +199,18 @@
         }
 
         @Override
+        public void onPlaybackStateChanged(PlaybackState2 state) {
+            super.onPlaybackStateChanged(state);
+            mCallbackProxy.onPlaybackStateChanged(state);
+        }
+
+        @Override
+        public void onCustomCommand(Command command, Bundle args, ResultReceiver receiver) {
+            super.onCustomCommand(command, args, receiver);
+            mCallbackProxy.onCustomCommand(command, args, receiver);
+        }
+
+        @Override
         public void waitForConnect(boolean expect) throws InterruptedException {
             if (expect) {
                 assertTrue(connectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
@@ -191,14 +227,28 @@
                 assertFalse(disconnectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
             }
         }
+
+        @Override
+        public void onPlaylistChanged(List<MediaItem2> params) {
+            if (mCallbackProxy != null) {
+                mCallbackProxy.onPlaylistChanged(params);
+            }
+        }
+
+        @Override
+        public void onPlaylistParamsChanged(MediaSession2.PlaylistParams params) {
+            if (mCallbackProxy != null) {
+                mCallbackProxy.onPlaylistParamsChanged(params);
+            }
+        }
     }
 
     public class TestMediaController extends MediaController2 implements TestControllerInterface {
         private final ControllerCallback mCallback;
 
-        public TestMediaController(@NonNull Context context, @NonNull SessionToken token,
+        public TestMediaController(@NonNull Context context, @NonNull SessionToken2 token,
                 @NonNull ControllerCallback callback) {
-            super(context, token, callback, sHandlerExecutor);
+            super(context, token, sHandlerExecutor, callback);
             mCallback = callback;
         }
 
diff --git a/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
index bfed7d0..852f2fa 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
@@ -33,7 +33,6 @@
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
-import static android.media.TestUtils.createPlaybackState;
 import static org.junit.Assert.*;
 
 /**
@@ -59,9 +58,10 @@
         // 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);
-        sHandler.postAndSync(() -> {
-            mSession = new MediaSession2.Builder(mContext, player).setId(TAG).build();
-        });
+        mSession = new MediaSession2.Builder(mContext, player)
+                .setSessionCallback(sHandlerExecutor, new SessionCallback(mContext))
+                .setId(TAG)
+                .build();
         ensureChangeInSession();
     }
 
@@ -70,9 +70,7 @@
     public void cleanUp() throws Exception {
         super.cleanUp();
         sHandler.removeCallbacksAndMessages(null);
-        sHandler.postAndSync(() -> {
-            mSession.close();
-        });
+        mSession.close();
     }
 
     // TODO(jaewan): Make this host-side test to see per-user behavior.
@@ -82,13 +80,12 @@
         player.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_STOPPED));
 
         MediaController2 controller = null;
-        List<SessionToken> tokens = mManager.getActiveSessionTokens();
+        List<SessionToken2> tokens = mManager.getActiveSessionTokens();
         assertNotNull(tokens);
         for (int i = 0; i < tokens.size(); i++) {
-            SessionToken token = tokens.get(i);
+            SessionToken2 token = tokens.get(i);
             if (mContext.getPackageName().equals(token.getPackageName())
                     && TAG.equals(token.getId())) {
-                assertNotNull(token.getSessionBinder());
                 assertNull(controller);
                 controller = createController(token);
             }
@@ -113,7 +110,7 @@
         sHandler.postAndSync(() -> {
             mSession.close();
             mSession = new MediaSession2.Builder(mContext, new MockPlayer(0)).setId(TAG)
-                    .setSessionCallback(new SessionCallback() {
+                    .setSessionCallback(sHandlerExecutor, new SessionCallback(mContext) {
                         @Override
                         public MediaSession2.CommandGroup onConnect(ControllerInfo controller) {
                             // Reject all connection request.
@@ -124,10 +121,10 @@
         ensureChangeInSession();
 
         boolean foundSession = false;
-        List<SessionToken> tokens = mManager.getActiveSessionTokens();
+        List<SessionToken2> tokens = mManager.getActiveSessionTokens();
         assertNotNull(tokens);
         for (int i = 0; i < tokens.size(); i++) {
-            SessionToken token = tokens.get(i);
+            SessionToken2 token = tokens.get(i);
             if (mContext.getPackageName().equals(token.getPackageName())
                     && TAG.equals(token.getId())) {
                 assertFalse(foundSession);
@@ -147,9 +144,9 @@
 
         // When the mSession's player becomes null, it should lose binder connection between server.
         // So server will forget the session.
-        List<SessionToken> tokens = mManager.getActiveSessionTokens();
+        List<SessionToken2> tokens = mManager.getActiveSessionTokens();
         for (int i = 0; i < tokens.size(); i++) {
-            SessionToken token = tokens.get(i);
+            SessionToken2 token = tokens.get(i);
             assertFalse(mContext.getPackageName().equals(token.getPackageName())
                     && TAG.equals(token.getId()));
         }
@@ -159,20 +156,18 @@
     public void testGetMediaSessionService2Token() throws InterruptedException {
         boolean foundTestSessionService = false;
         boolean foundTestLibraryService = false;
-        List<SessionToken> tokens = mManager.getSessionServiceTokens();
+        List<SessionToken2> tokens = mManager.getSessionServiceTokens();
         for (int i = 0; i < tokens.size(); i++) {
-            SessionToken token = tokens.get(i);
+            SessionToken2 token = tokens.get(i);
             if (mContext.getPackageName().equals(token.getPackageName())
                     && MockMediaSessionService2.ID.equals(token.getId())) {
                 assertFalse(foundTestSessionService);
-                assertEquals(SessionToken.TYPE_SESSION_SERVICE, token.getType());
-                assertNull(token.getSessionBinder());
+                assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
                 foundTestSessionService = true;
             } else if (mContext.getPackageName().equals(token.getPackageName())
                     && MockMediaLibraryService2.ID.equals(token.getId())) {
                 assertFalse(foundTestLibraryService);
-                assertEquals(SessionToken.TYPE_LIBRARY_SERVICE, token.getType());
-                assertNull(token.getSessionBinder());
+                assertEquals(SessionToken2.TYPE_LIBRARY_SERVICE, token.getType());
                 foundTestLibraryService = true;
             }
         }
@@ -185,9 +180,9 @@
         boolean foundTestSession = false;
         boolean foundTestSessionService = false;
         boolean foundTestLibraryService = false;
-        List<SessionToken> tokens = mManager.getAllSessionTokens();
+        List<SessionToken2> tokens = mManager.getAllSessionTokens();
         for (int i = 0; i < tokens.size(); i++) {
-            SessionToken token = tokens.get(i);
+            SessionToken2 token = tokens.get(i);
             if (!mContext.getPackageName().equals(token.getPackageName())) {
                 continue;
             }
@@ -199,11 +194,11 @@
                 case MockMediaSessionService2.ID:
                     assertFalse(foundTestSessionService);
                     foundTestSessionService = true;
-                    assertEquals(SessionToken.TYPE_SESSION_SERVICE, token.getType());
+                    assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
                     break;
                 case MockMediaLibraryService2.ID:
                     assertFalse(foundTestLibraryService);
-                    assertEquals(SessionToken.TYPE_LIBRARY_SERVICE, token.getType());
+                    assertEquals(SessionToken2.TYPE_LIBRARY_SERVICE, token.getType());
                     foundTestLibraryService = true;
                     break;
                 default:
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
index 7a16127..ec69ff6 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
@@ -18,13 +18,14 @@
 
 import static junit.framework.Assert.fail;
 
+import static org.junit.Assert.assertEquals;
+
 import android.content.Context;
 import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.TestUtils.SyncHandler;
 import android.os.Bundle;
 import android.os.Process;
-import android.service.media.MediaBrowserService.BrowserRoot;
 
 import javax.annotation.concurrent.GuardedBy;
 
@@ -41,19 +42,19 @@
         EXTRA.putString(ROOT_ID, ROOT_ID);
     }
     @GuardedBy("MockMediaLibraryService2.class")
-    private static SessionToken sToken;
+    private static SessionToken2 sToken;
 
     private MediaLibrarySession mSession;
 
     @Override
     public MediaLibrarySession onCreateSession(String sessionId) {
         final MockPlayer player = new MockPlayer(1);
-        SyncHandler handler = (SyncHandler) TestServiceRegistry.getInstance().getHandler();
+        final SyncHandler handler = (SyncHandler) TestServiceRegistry.getInstance().getHandler();
         try {
             handler.postAndSync(() -> {
                 TestLibrarySessionCallback callback = new TestLibrarySessionCallback();
-                mSession = new MediaLibrarySessionBuilder(
-                        MockMediaLibraryService2.this, player, callback)
+                mSession = new MediaLibrarySessionBuilder(MockMediaLibraryService2.this,
+                        player, (runnable) -> handler.post(runnable), callback)
                         .setId(sessionId).build();
             });
         } catch (InterruptedException e) {
@@ -68,18 +69,22 @@
         super.onDestroy();
     }
 
-    public static SessionToken getToken(Context context) {
+    public static SessionToken2 getToken(Context context) {
         synchronized (MockMediaLibraryService2.class) {
             if (sToken == null) {
-                sToken = new SessionToken(SessionToken.TYPE_LIBRARY_SERVICE,
-                        context.getPackageName(), ID,
-                        MockMediaLibraryService2.class.getName(), null);
+                sToken = new SessionToken2(context, context.getPackageName(),
+                        MockMediaLibraryService2.class.getName());
+                assertEquals(SessionToken2.TYPE_LIBRARY_SERVICE, sToken.getType());
             }
             return sToken;
         }
     }
 
     private class TestLibrarySessionCallback extends MediaLibrarySessionCallback {
+        public TestLibrarySessionCallback() {
+            super(MockMediaLibraryService2.this);
+        }
+
         @Override
         public CommandGroup onConnect(ControllerInfo controller) {
             if (Process.myUid() != controller.getUid()) {
@@ -92,8 +97,8 @@
         }
 
         @Override
-        public BrowserRoot onGetRoot(ControllerInfo controller, Bundle rootHints) {
-            return new BrowserRoot(ROOT_ID, EXTRA);
+        public LibraryRoot onGetRoot(ControllerInfo controller, Bundle rootHints) {
+            return new LibraryRoot(MockMediaLibraryService2.this, ROOT_ID, EXTRA);
         }
     }
 }
\ No newline at end of file
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java b/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
index 9cf4911..c8ed184 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
@@ -28,6 +28,8 @@
 import android.media.session.PlaybackState;
 import android.os.Process;
 
+import java.util.concurrent.Executor;
+
 /**
  * Mock implementation of {@link android.media.MediaSessionService2} for testing.
  */
@@ -45,11 +47,13 @@
     @Override
     public MediaSession2 onCreateSession(String sessionId) {
         final MockPlayer player = new MockPlayer(1);
-        SyncHandler handler = (SyncHandler) TestServiceRegistry.getInstance().getHandler();
+        final SyncHandler handler = (SyncHandler) TestServiceRegistry.getInstance().getHandler();
+        final Executor executor = (runnable) -> handler.post(runnable);
         try {
             handler.postAndSync(() -> {
                 mSession = new MediaSession2.Builder(MockMediaSessionService2.this, player)
-                        .setId(sessionId).setSessionCallback(new MySessionCallback()).build();
+                        .setSessionCallback(executor, new MySessionCallback())
+                        .setId(sessionId).build();
             });
         } catch (InterruptedException e) {
             fail(e.toString());
@@ -70,7 +74,7 @@
     }
 
     @Override
-    public MediaNotification onUpdateNotification(PlaybackState state) {
+    public MediaNotification onUpdateNotification(PlaybackState2 state) {
         if (mDefaultNotificationChannel == null) {
             mDefaultNotificationChannel = new NotificationChannel(
                     DEFAULT_MEDIA_NOTIFICATION_CHANNEL_ID,
@@ -83,10 +87,14 @@
                 .setContentTitle(getPackageName())
                 .setContentText("Playback state: " + state.getState())
                 .setSmallIcon(android.R.drawable.sym_def_app_icon).build();
-        return MediaNotification.create(DEFAULT_MEDIA_NOTIFICATION_ID, notification);
+        return new MediaNotification(this, DEFAULT_MEDIA_NOTIFICATION_ID, notification);
     }
 
     private class MySessionCallback extends SessionCallback {
+        public MySessionCallback() {
+            super(MockMediaSessionService2.this);
+        }
+
         @Override
         public MediaSession2.CommandGroup onConnect(ControllerInfo controller) {
             if (Process.myUid() != controller.getUid()) {
diff --git a/packages/MediaComponents/test/src/android/media/MockPlayer.java b/packages/MediaComponents/test/src/android/media/MockPlayer.java
index b0d7a69..1faf0f4 100644
--- a/packages/MediaComponents/test/src/android/media/MockPlayer.java
+++ b/packages/MediaComponents/test/src/android/media/MockPlayer.java
@@ -16,19 +16,19 @@
 
 package android.media;
 
-import android.media.session.PlaybackState;
-import android.os.Handler;
+import android.media.MediaSession2.PlaylistParams;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
 
 /**
- * A mock implementation of {@link MediaPlayerBase} for testing.
+ * A mock implementation of {@link MediaPlayerInterface} for testing.
  */
-public class MockPlayer extends MediaPlayerBase {
+public class MockPlayer implements MediaPlayerInterface {
     public final CountDownLatch mCountDownLatch;
 
     public boolean mPlayCalled;
@@ -36,8 +36,22 @@
     public boolean mStopCalled;
     public boolean mSkipToPreviousCalled;
     public boolean mSkipToNextCalled;
+    public boolean mPrepareCalled;
+    public boolean mFastForwardCalled;
+    public boolean mRewindCalled;
+    public boolean mSeekToCalled;
+    public long mSeekPosition;
+    public boolean mSetCurrentPlaylistItemCalled;
+    public int mItemIndex;
+    public boolean mSetPlaylistCalled;
+    public boolean mSetPlaylistParamsCalled;
+
     public List<PlaybackListenerHolder> mListeners = new ArrayList<>();
-    private PlaybackState mLastPlaybackState;
+    public List<MediaItem2> mPlaylist;
+    public PlaylistParams mPlaylistParams;
+
+    private PlaybackState2 mLastPlaybackState;
+    private AudioAttributes mAudioAttributes;
 
     public MockPlayer(int count) {
         mCountDownLatch = (count > 0) ? new CountDownLatch(count) : null;
@@ -83,16 +97,58 @@
         }
     }
 
+    @Override
+    public void prepare() {
+        mPrepareCalled = true;
+        if (mCountDownLatch != null) {
+            mCountDownLatch.countDown();
+        }
+    }
+
+    @Override
+    public void fastForward() {
+        mFastForwardCalled = true;
+        if (mCountDownLatch != null) {
+            mCountDownLatch.countDown();
+        }
+    }
+
+    @Override
+    public void rewind() {
+        mRewindCalled = true;
+        if (mCountDownLatch != null) {
+            mCountDownLatch.countDown();
+        }
+    }
+
+    @Override
+    public void seekTo(long pos) {
+        mSeekToCalled = true;
+        mSeekPosition = pos;
+        if (mCountDownLatch != null) {
+            mCountDownLatch.countDown();
+        }
+    }
+
+    @Override
+    public void setCurrentPlaylistItem(int index) {
+        mSetCurrentPlaylistItemCalled = true;
+        mItemIndex = index;
+        if (mCountDownLatch != null) {
+            mCountDownLatch.countDown();
+        }
+    }
+
     @Nullable
     @Override
-    public PlaybackState getPlaybackState() {
+    public PlaybackState2 getPlaybackState() {
         return mLastPlaybackState;
     }
 
     @Override
-    public void addPlaybackListener(
-            @NonNull PlaybackListener listener, @NonNull Handler handler) {
-        mListeners.add(new PlaybackListenerHolder(listener, handler));
+    public void addPlaybackListener(@NonNull Executor executor,
+            @NonNull PlaybackListener listener) {
+        mListeners.add(new PlaybackListenerHolder(executor, listener));
     }
 
     @Override
@@ -103,10 +159,50 @@
         }
     }
 
-    public void notifyPlaybackState(final PlaybackState state) {
+    public void notifyPlaybackState(final PlaybackState2 state) {
         mLastPlaybackState = state;
         for (int i = 0; i < mListeners.size(); i++) {
             mListeners.get(i).postPlaybackChange(state);
         }
     }
+
+    @Override
+    public void setPlaylistParams(PlaylistParams params) {
+        mSetPlaylistParamsCalled = true;
+        mPlaylistParams = params;
+    }
+
+    @Override
+    public void addPlaylistItem(int index, MediaItem2 item) {
+    }
+
+    @Override
+    public void removePlaylistItem(MediaItem2 item) {
+    }
+
+    @Override
+    public PlaylistParams getPlaylistParams() {
+        return mPlaylistParams;
+    }
+
+    @Override
+    public void setAudioAttributes(AudioAttributes attributes) {
+        mAudioAttributes = attributes;
+    }
+
+    @Override
+    public AudioAttributes getAudioAttributes() {
+        return mAudioAttributes;
+    }
+
+    @Override
+    public void setPlaylist(List<MediaItem2> playlist) {
+        mSetPlaylistCalled = true;
+        mPlaylist = playlist;
+    }
+
+    @Override
+    public List<MediaItem2> getPlaylist() {
+        return mPlaylist;
+    }
 }
diff --git a/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java b/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java
index b0b87de..0f1644c 100644
--- a/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java
+++ b/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java
@@ -16,39 +16,27 @@
 
 package android.media;
 
-import android.media.MediaPlayerBase.PlaybackListener;
-import android.media.session.PlaybackState;
+import android.media.MediaPlayerInterface.PlaybackListener;
 import android.os.Handler;
-import android.os.Message;
 import android.support.annotation.NonNull;
 
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * Holds {@link PlaybackListener} with the {@link Handler}.
  */
-public class PlaybackListenerHolder extends Handler {
-    private static final int ON_PLAYBACK_CHANGED = 1;
-
+public class PlaybackListenerHolder {
+    public final Executor executor;
     public final PlaybackListener listener;
 
-    public PlaybackListenerHolder(
-            @NonNull PlaybackListener listener, @NonNull Handler handler) {
-        super(handler.getLooper());
+    public PlaybackListenerHolder(Executor executor, @NonNull PlaybackListener listener) {
+        this.executor = executor;
         this.listener = listener;
     }
 
-    @Override
-    public void handleMessage(Message msg) {
-        switch (msg.what) {
-            case ON_PLAYBACK_CHANGED:
-                listener.onPlaybackChanged((PlaybackState) msg.obj);
-                break;
-        }
-    }
-
-    public void postPlaybackChange(PlaybackState state) {
-        obtainMessage(ON_PLAYBACK_CHANGED, state).sendToTarget();
+    public void postPlaybackChange(final PlaybackState2 state) {
+        executor.execute(() -> listener.onPlaybackChanged(state));
     }
 
     /**
diff --git a/packages/MediaComponents/test/src/android/media/TestUtils.java b/packages/MediaComponents/test/src/android/media/TestUtils.java
index 1372f01..12b24c0 100644
--- a/packages/MediaComponents/test/src/android/media/TestUtils.java
+++ b/packages/MediaComponents/test/src/android/media/TestUtils.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.content.Context;
+import android.media.MediaSession2.PlaylistParams;
 import android.media.session.MediaSessionManager;
 import android.media.session.PlaybackState;
 import android.os.Bundle;
@@ -29,6 +30,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -40,16 +42,6 @@
     private static final int WAIT_SERVICE_TIME_MS = 5000;
 
     /**
-     * Creates a {@link android.media.session.PlaybackState} with the given state.
-     *
-     * @param state one of the PlaybackState.STATE_xxx.
-     * @return a PlaybackState
-     */
-    public static PlaybackState createPlaybackState(int state) {
-        return new PlaybackState.Builder().setState(state, 0, 1.0f).build();
-    }
-
-    /**
      * Finds the session with id in this test package.
      *
      * @param context
@@ -57,12 +49,12 @@
      * @return
      */
     // TODO(jaewan): Currently not working.
-    public static SessionToken getServiceToken(Context context, String id) {
+    public static SessionToken2 getServiceToken(Context context, String id) {
         MediaSessionManager manager =
                 (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
-        List<SessionToken> tokens = manager.getSessionServiceTokens();
+        List<SessionToken2> tokens = manager.getSessionServiceTokens();
         for (int i = 0; i < tokens.size(); i++) {
-            SessionToken token = tokens.get(i);
+            SessionToken2 token = tokens.get(i);
             if (context.getPackageName().equals(token.getPackageName())
                     && id.equals(token.getId())) {
                 return token;
@@ -99,6 +91,11 @@
         return true;
     }
 
+    public static void ensurePlaylistParamsModeEquals(PlaylistParams a, PlaylistParams b) {
+        assertEquals(a.getRepeatMode(), b.getRepeatMode());
+        assertEquals(a.getShuffleMode(), b.getShuffleMode());
+    }
+
     /**
      * Handler that always waits until the Runnable finishes.
      */
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 686e3e3..592273e 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -155,6 +155,8 @@
       mBtNrecIsOff(false),
       mIsLowRamDevice(true),
       mIsDeviceTypeKnown(false),
+      mTotalMemory(0),
+      mClientSharedHeapSize(kMinimumClientSharedHeapSizeBytes),
       mGlobalEffectEnableTime(0),
       mSystemReady(false)
 {
@@ -1512,17 +1514,9 @@
         mAudioFlinger(audioFlinger),
         mPid(pid)
 {
-    size_t heapSize = property_get_int32("ro.af.client_heap_size_kbyte", 0);
-    heapSize *= 1024;
-    if (!heapSize) {
-        heapSize = kClientSharedHeapSizeBytes;
-        // Increase heap size on non low ram devices to limit risk of reconnection failure for
-        // invalidated tracks
-        if (!audioFlinger->isLowRamDevice()) {
-            heapSize *= kClientSharedHeapSizeMultiplier;
-        }
-    }
-    mMemoryDealer = new MemoryDealer(heapSize, "AudioFlinger::Client");
+    mMemoryDealer = new MemoryDealer(
+            audioFlinger->getClientSharedHeapSize(),
+            (std::string("AudioFlinger::Client(") + std::to_string(pid) + ")").c_str());
 }
 
 // Client destructor must be called with AudioFlinger::mClientLock held
@@ -1860,7 +1854,7 @@
 
 // ----------------------------------------------------------------------------
 
-status_t AudioFlinger::setLowRamDevice(bool isLowRamDevice)
+status_t AudioFlinger::setLowRamDevice(bool isLowRamDevice, int64_t totalMemory)
 {
     uid_t uid = IPCThreadState::self()->getCallingUid();
     if (uid != AID_SYSTEM) {
@@ -1871,10 +1865,43 @@
         return INVALID_OPERATION;
     }
     mIsLowRamDevice = isLowRamDevice;
+    mTotalMemory = totalMemory;
+    // mIsLowRamDevice and mTotalMemory are obtained through ActivityManager;
+    // see ActivityManager.isLowRamDevice() and ActivityManager.getMemoryInfo().
+    // mIsLowRamDevice generally represent devices with less than 1GB of memory,
+    // though actual setting is determined through device configuration.
+    constexpr int64_t GB = 1024 * 1024 * 1024;
+    mClientSharedHeapSize =
+            isLowRamDevice ? kMinimumClientSharedHeapSizeBytes
+                    : mTotalMemory < 2 * GB ? 4 * kMinimumClientSharedHeapSizeBytes
+                    : mTotalMemory < 3 * GB ? 8 * kMinimumClientSharedHeapSizeBytes
+                    : mTotalMemory < 4 * GB ? 16 * kMinimumClientSharedHeapSizeBytes
+                    : 32 * kMinimumClientSharedHeapSizeBytes;
     mIsDeviceTypeKnown = true;
+
+    // TODO: Cache the client shared heap size in a persistent property.
+    // It's possible that a native process or Java service or app accesses audioserver
+    // after it is registered by system server, but before AudioService updates
+    // the memory info.  This would occur immediately after boot or an audioserver
+    // crash and restore. Before update from AudioService, the client would get the
+    // minimum heap size.
+
+    ALOGD("isLowRamDevice:%s totalMemory:%lld mClientSharedHeapSize:%zu",
+            (isLowRamDevice ? "true" : "false"),
+            (long long)mTotalMemory,
+            mClientSharedHeapSize.load());
     return NO_ERROR;
 }
 
+size_t AudioFlinger::getClientSharedHeapSize() const
+{
+    size_t heapSizeInBytes = property_get_int32("ro.af.client_heap_size_kbyte", 0) * 1024;
+    if (heapSizeInBytes != 0) { // read-only property overrides all.
+        return heapSizeInBytes;
+    }
+    return mClientSharedHeapSize;
+}
+
 audio_hw_sync_t AudioFlinger::getAudioHwSyncForSession(audio_session_t sessionId)
 {
     Mutex::Autolock _l(mLock);
@@ -1949,6 +1976,43 @@
     return NO_ERROR;
 }
 
+status_t AudioFlinger::getMicrophones(std::vector<media::MicrophoneInfo> *microphones)
+{
+    // Fake data
+    size_t fakeNum = 2;
+    audio_devices_t fakeTypes[] = { AUDIO_DEVICE_IN_BUILTIN_MIC, AUDIO_DEVICE_IN_BACK_MIC };
+    for (size_t i = 0; i < fakeNum; i++) {
+        struct audio_microphone_characteristic_t characteristics;
+        sprintf(characteristics.device_id, "microphone:%zu", i);
+        characteristics.type = fakeTypes[i];
+        sprintf(characteristics.address, "");
+        characteristics.location = AUDIO_MICROPHONE_LOCATION_MAINBODY;
+        characteristics.group = 0;
+        characteristics.index_in_the_group = i;
+        characteristics.sensitivity = 1.0f;
+        characteristics.max_spl = 100.0f;
+        characteristics.min_spl = 0.0f;
+        characteristics.directionality = AUDIO_MICROPHONE_DIRECTIONALITY_OMNI;
+        characteristics.num_frequency_responses = 5 - i;
+        for (size_t j = 0; j < characteristics.num_frequency_responses; j++) {
+            characteristics.frequency_responses[0][j] = 100.0f - j;
+            characteristics.frequency_responses[1][j] = 100.0f + j;
+        }
+        for (size_t j = 0; j < AUDIO_CHANNEL_COUNT_MAX; j++) {
+            characteristics.channel_mapping[j] = AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED;
+        }
+        characteristics.geometric_location.x = 0.1f;
+        characteristics.geometric_location.y = 0.2f;
+        characteristics.geometric_location.z = 0.3f;
+        characteristics.orientation.x = 0.0f;
+        characteristics.orientation.y = 1.0f;
+        characteristics.orientation.z = 0.0f;
+        media::MicrophoneInfo microphoneInfo = media::MicrophoneInfo(characteristics);
+        microphones->push_back(microphoneInfo);
+    }
+    return NO_ERROR;
+}
+
 // setAudioHwSyncForSession_l() must be called with AudioFlinger::mLock held
 void AudioFlinger::setAudioHwSyncForSession_l(PlaybackThread *thread, audio_session_t sessionId)
 {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index a1c3f36..7c38bcc 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -94,12 +94,6 @@
 
 static const nsecs_t kDefaultStandbyTimeInNsecs = seconds(3);
 
-
-// Max shared memory size for audio tracks and audio records per client process
-static const size_t kClientSharedHeapSizeBytes = 1024*1024;
-// Shared memory size multiplier for non low ram devices
-static const size_t kClientSharedHeapSizeMultiplier = 4;
-
 #define INCLUDING_FROM_AUDIOFLINGER_H
 
 class AudioFlinger :
@@ -227,7 +221,7 @@
     virtual uint32_t getPrimaryOutputSamplingRate();
     virtual size_t getPrimaryOutputFrameCount();
 
-    virtual status_t setLowRamDevice(bool isLowRamDevice);
+    virtual status_t setLowRamDevice(bool isLowRamDevice, int64_t totalMemory) override;
 
     /* List available audio ports and their attributes */
     virtual status_t listAudioPorts(unsigned int *num_ports,
@@ -256,6 +250,8 @@
     /* Indicate JAVA services are ready (scheduling, power management ...) */
     virtual status_t systemReady();
 
+    virtual status_t getMicrophones(std::vector<media::MicrophoneInfo> *microphones);
+
     virtual     status_t    onTransact(
                                 uint32_t code,
                                 const Parcel& data,
@@ -563,6 +559,8 @@
         virtual binder::Status    start(int /*AudioSystem::sync_event_t*/ event,
                 int /*audio_session_t*/ triggerSession);
         virtual binder::Status   stop();
+        virtual binder::Status   getActiveMicrophones(
+                std::vector<media::MicrophoneInfo>* activeMicrophones);
     private:
         const sp<RecordThread::RecordTrack> mRecordTrack;
 
@@ -829,15 +827,18 @@
     static const size_t kTeeSinkTrackFramesDefault = 0x200000;
 #endif
 
-    // This method reads from a variable without mLock, but the variable is updated under mLock.  So
-    // we might read a stale value, or a value that's inconsistent with respect to other variables.
-    // In this case, it's safe because the return value isn't used for making an important decision.
-    // The reason we don't want to take mLock is because it could block the caller for a long time.
+    // These methods read variables atomically without mLock,
+    // though the variables are updated with mLock.
     bool    isLowRamDevice() const { return mIsLowRamDevice; }
+    size_t getClientSharedHeapSize() const;
 
 private:
-    bool    mIsLowRamDevice;
+    std::atomic<bool> mIsLowRamDevice;
     bool    mIsDeviceTypeKnown;
+    int64_t mTotalMemory;
+    std::atomic<size_t> mClientSharedHeapSize;
+    static constexpr size_t kMinimumClientSharedHeapSizeBytes = 1024 * 1024; // 1MB
+
     nsecs_t mGlobalEffectEnableTime;  // when a global effect was last enabled
 
     sp<PatchPanel> mPatchPanel;
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 63a3d98..1733ef5 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -66,6 +66,8 @@
             void        setSilenced(bool silenced) { mSilenced = silenced; }
             bool        isSilenced() const { return mSilenced; }
 
+            status_t    getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones);
+
 private:
     friend class AudioFlinger;  // for mState
 
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 70ac32c..14d7e2e 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -4874,7 +4874,7 @@
 {
     PlaybackThread::dumpInternals(fd, args);
     dprintf(fd, "  Thread throttle time (msecs): %u\n", mThreadThrottleTimeMs);
-    dprintf(fd, "  AudioMixer tracks: 0x%08x\n", mAudioMixer->trackNames());
+    dprintf(fd, "  AudioMixer tracks: %s\n", mAudioMixer->trackNames().c_str());
     dprintf(fd, "  Master mono: %s\n", mMasterMono ? "on" : "off");
 
     if (hasFastMixer()) {
@@ -7043,6 +7043,49 @@
 #endif
 }
 
+status_t AudioFlinger::RecordThread::getActiveMicrophones(
+        std::vector<media::MicrophoneInfo>* activeMicrophones)
+{
+    ALOGV("RecordThread::getActiveMicrophones");
+    AutoMutex _l(mLock);
+    // Fake data
+    struct audio_microphone_characteristic_t characteristic;
+    sprintf(characteristic.device_id, "builtin_mic");
+    characteristic.type = AUDIO_DEVICE_IN_BUILTIN_MIC;
+    sprintf(characteristic.address, "");
+    characteristic.location = AUDIO_MICROPHONE_LOCATION_MAINBODY;
+    characteristic.group = 0;
+    characteristic.index_in_the_group = 0;
+    characteristic.sensitivity = 1.0f;
+    characteristic.max_spl = 100.0f;
+    characteristic.min_spl = 0.0f;
+    characteristic.directionality = AUDIO_MICROPHONE_DIRECTIONALITY_OMNI;
+    characteristic.num_frequency_responses = 5;
+    for (size_t i = 0; i < characteristic.num_frequency_responses; i++) {
+        characteristic.frequency_responses[0][i] = 100.0f - i;
+        characteristic.frequency_responses[1][i] = 100.0f + i;
+    }
+    for (size_t i = 0; i < AUDIO_CHANNEL_COUNT_MAX; i++) {
+        characteristic.channel_mapping[i] = AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED;
+    }
+    audio_microphone_channel_mapping_t channel_mappings[] = {
+        AUDIO_MICROPHONE_CHANNEL_MAPPING_DIRECT,
+        AUDIO_MICROPHONE_CHANNEL_MAPPING_PROCESSED,
+    };
+    for (size_t i = 0; i < mChannelCount; i++) {
+        characteristic.channel_mapping[i] = channel_mappings[i % 2];
+    }
+    characteristic.geometric_location.x = 0.1f;
+    characteristic.geometric_location.y = 0.2f;
+    characteristic.geometric_location.z = 0.3f;
+    characteristic.orientation.x = 0.0f;
+    characteristic.orientation.y = 1.0f;
+    characteristic.orientation.z = 0.0f;
+    media::MicrophoneInfo microphoneInfo = media::MicrophoneInfo(characteristic);
+    activeMicrophones->push_back(microphoneInfo);
+    return NO_ERROR;
+}
+
 // destroyTrack_l() must be called with ThreadBase::mLock held
 void AudioFlinger::RecordThread::destroyTrack_l(const sp<RecordTrack>& track)
 {
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 41d87a4..53cb8ad 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -623,8 +623,7 @@
     static const int8_t kMaxTrackRetriesOffload = 20;
     static const int8_t kMaxTrackStartupRetriesOffload = 100;
     static const int8_t kMaxTrackStopRetriesOffload = 2;
-    // 14 tracks max per client allows for 2 misbehaving application leaving 4 available tracks.
-    static const uint32_t kMaxTracksPerUid = 14;
+    static constexpr uint32_t kMaxTracksPerUid = 40;
 
     // Maximum delay (in nanoseconds) for upcoming buffers in suspend mode, otherwise
     // if delay is greater, the estimated time for timeLoopNextNs is reset.
@@ -1399,6 +1398,8 @@
             // Sets the UID records silence
             void        setRecordSilenced(uid_t uid, bool silenced);
 
+            status_t    getActiveMicrophones(std::vector<media::MicrophoneInfo>* activeMicrophones);
+
 private:
             // Enter standby if not already in standby, and set mStandby flag
             void    standbyIfNotAlreadyInStandby();
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index cdd8ca0..ce5c53b 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -1580,6 +1580,13 @@
     mRecordTrack->stop();
 }
 
+binder::Status AudioFlinger::RecordHandle::getActiveMicrophones(
+        std::vector<media::MicrophoneInfo>* activeMicrophones) {
+    ALOGV("RecordHandle::getActiveMicrophones()");
+    return binder::Status::fromStatusT(
+            mRecordTrack->getActiveMicrophones(activeMicrophones));
+}
+
 // ----------------------------------------------------------------------------
 
 // RecordTrack constructor must be called with AudioFlinger::mLock and ThreadBase::mLock held
@@ -1792,6 +1799,18 @@
     mServerProxy->setTimestamp(local);
 }
 
+status_t AudioFlinger::RecordThread::RecordTrack::getActiveMicrophones(
+        std::vector<media::MicrophoneInfo>* activeMicrophones)
+{
+    sp<ThreadBase> thread = mThread.promote();
+    if (thread != 0) {
+        RecordThread *recordThread = (RecordThread *)thread.get();
+        return recordThread->getActiveMicrophones(activeMicrophones);
+    } else {
+        return BAD_VALUE;
+    }
+}
+
 AudioFlinger::RecordThread::PatchRecord::PatchRecord(RecordThread *recordThread,
                                                      uint32_t sampleRate,
                                                      audio_channel_mask_t channelMask,
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index e058dc8..40e0199 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1427,7 +1427,7 @@
     // Explicit routing?
     sp<DeviceDescriptor> deviceDesc;
     if (*selectedDeviceId != AUDIO_PORT_HANDLE_NONE) {
-        deviceDesc = mAvailableOutputDevices.getDeviceFromId(*selectedDeviceId);
+        deviceDesc = mAvailableInputDevices.getDeviceFromId(*selectedDeviceId);
     }
     mInputRoutes.addRoute(session, SessionRoute::STREAM_TYPE_NA, inputSource, deviceDesc, uid);
 
@@ -2939,7 +2939,7 @@
                 sinkDeviceDesc->toAudioPortConfig(&newPatch.sinks[i], &patch->sinks[i]);
 
                 // create a software bridge in PatchPanel if:
-                // - source and sink devices are on differnt HW modules OR
+                // - source and sink devices are on different HW modules OR
                 // - audio HAL version is < 3.0
                 if (!srcDeviceDesc->hasSameHwModuleAs(sinkDeviceDesc) ||
                         (srcDeviceDesc->mModule->getHalVersionMajor() < 3)) {
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index e5aed9a..90a5a0f 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -273,7 +273,7 @@
 void AudioPolicyService::NotificationClient::onDynamicPolicyMixStateUpdate(
         const String8& regId, int32_t state)
 {
-    if (mAudioPolicyServiceClient != 0) {
+    if (mAudioPolicyServiceClient != 0 && mUid < AID_APP_START) {
         mAudioPolicyServiceClient->onDynamicPolicyMixStateUpdate(regId, state);
     }
 }
@@ -283,7 +283,7 @@
         const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig,
         audio_patch_handle_t patchHandle)
 {
-    if (mAudioPolicyServiceClient != 0) {
+    if (mAudioPolicyServiceClient != 0 && mUid < AID_APP_START) {
         mAudioPolicyServiceClient->onRecordingConfigurationUpdate(event, clientInfo,
                 clientConfig, deviceConfig, patchHandle);
     }
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 536b54d..e7609ed 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -2054,7 +2054,8 @@
         mp->prepare();
     } else {
         ALOGE("Failed to load CameraService sounds: %s", file);
-        return NULL;
+        delete mp;
+        return nullptr;
     }
     return mp;
 }
@@ -2066,9 +2067,19 @@
     LOG1("CameraService::loadSound ref=%d", mSoundRef);
     if (mSoundRef++) return;
 
-    mSoundPlayer[SOUND_SHUTTER] = newMediaPlayer("/system/media/audio/ui/camera_click.ogg");
-    mSoundPlayer[SOUND_RECORDING_START] = newMediaPlayer("/system/media/audio/ui/VideoRecord.ogg");
-    mSoundPlayer[SOUND_RECORDING_STOP] = newMediaPlayer("/system/media/audio/ui/VideoStop.ogg");
+    mSoundPlayer[SOUND_SHUTTER] = newMediaPlayer("/product/media/audio/ui/camera_click.ogg");
+    if (mSoundPlayer[SOUND_SHUTTER] == nullptr) {
+        mSoundPlayer[SOUND_SHUTTER] = newMediaPlayer("/system/media/audio/ui/camera_click.ogg");
+    }
+    mSoundPlayer[SOUND_RECORDING_START] = newMediaPlayer("/product/media/audio/ui/VideoRecord.ogg");
+    if (mSoundPlayer[SOUND_RECORDING_START] == nullptr) {
+        mSoundPlayer[SOUND_RECORDING_START] =
+                newMediaPlayer("/system/media/audio/ui/VideoRecord.ogg");
+    }
+    mSoundPlayer[SOUND_RECORDING_STOP] = newMediaPlayer("/product/media/audio/ui/VideoStop.ogg");
+    if (mSoundPlayer[SOUND_RECORDING_STOP] == nullptr) {
+        mSoundPlayer[SOUND_RECORDING_STOP] = newMediaPlayer("/system/media/audio/ui/VideoStop.ogg");
+    }
 }
 
 void CameraService::releaseSound() {
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index c36c7cf..1ebaea9 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -1697,7 +1697,8 @@
     // Thread-safe. No lock necessary.
     sp<hardware::camera2::ICameraDeviceCallbacks> remoteCb = mRemoteCallback;
     if (remoteCb != NULL) {
-        remoteCb->onResultReceived(result.mMetadata, result.mResultExtras);
+        remoteCb->onResultReceived(result.mMetadata, result.mResultExtras,
+                result.mPhysicalMetadatas);
     }
 }
 
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 999e258..9ba8bb7 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -39,6 +39,8 @@
 
 #include <inttypes.h>
 
+#include <utility>
+
 #include <utils/Log.h>
 #include <utils/Trace.h>
 #include <utils/Timers.h>
@@ -874,11 +876,9 @@
     return res;
 }
 
-// Only one processCaptureResult should be called at a time, so
-// the locks won't block. The locks are present here simply to enforce this.
-hardware::Return<void> Camera3Device::processCaptureResult(
+hardware::Return<void> Camera3Device::processCaptureResult_3_4(
         const hardware::hidl_vec<
-                hardware::camera::device::V3_2::CaptureResult>& results) {
+                hardware::camera::device::V3_4::CaptureResult>& results) {
     // Ideally we should grab mLock, but that can lead to deadlock, and
     // it's not super important to get up to date value of mStatus for this
     // warning print, hence skipping the lock here
@@ -902,45 +902,121 @@
         }
     }
     for (const auto& result : results) {
-        processOneCaptureResultLocked(result);
+        processOneCaptureResultLocked(result.v3_2, result.physicalCameraMetadata);
     }
     mProcessCaptureResultLock.unlock();
     return hardware::Void();
 }
 
+// Only one processCaptureResult should be called at a time, so
+// the locks won't block. The locks are present here simply to enforce this.
+hardware::Return<void> Camera3Device::processCaptureResult(
+        const hardware::hidl_vec<
+                hardware::camera::device::V3_2::CaptureResult>& results) {
+    hardware::hidl_vec<hardware::camera::device::V3_4::PhysicalCameraMetadata> noPhysMetadata;
+
+    // Ideally we should grab mLock, but that can lead to deadlock, and
+    // it's not super important to get up to date value of mStatus for this
+    // warning print, hence skipping the lock here
+    if (mStatus == STATUS_ERROR) {
+        // Per API contract, HAL should act as closed after device error
+        // But mStatus can be set to error by framework as well, so just log
+        // a warning here.
+        ALOGW("%s: received capture result in error state.", __FUNCTION__);
+    }
+
+    if (mProcessCaptureResultLock.tryLock() != OK) {
+        // This should never happen; it indicates a wrong client implementation
+        // that doesn't follow the contract. But, we can be tolerant here.
+        ALOGE("%s: callback overlapped! waiting 1s...",
+                __FUNCTION__);
+        if (mProcessCaptureResultLock.timedLock(1000000000 /* 1s */) != OK) {
+            ALOGE("%s: cannot acquire lock in 1s, dropping results",
+                    __FUNCTION__);
+            // really don't know what to do, so bail out.
+            return hardware::Void();
+        }
+    }
+    for (const auto& result : results) {
+        processOneCaptureResultLocked(result, noPhysMetadata);
+    }
+    mProcessCaptureResultLock.unlock();
+    return hardware::Void();
+}
+
+status_t Camera3Device::readOneCameraMetadataLocked(
+        uint64_t fmqResultSize, hardware::camera::device::V3_2::CameraMetadata& resultMetadata,
+        const hardware::camera::device::V3_2::CameraMetadata& result) {
+    if (fmqResultSize > 0) {
+        resultMetadata.resize(fmqResultSize);
+        if (mResultMetadataQueue == nullptr) {
+            return NO_MEMORY; // logged in initialize()
+        }
+        if (!mResultMetadataQueue->read(resultMetadata.data(), fmqResultSize)) {
+            ALOGE("%s: Cannot read camera metadata from fmq, size = %" PRIu64,
+                    __FUNCTION__, fmqResultSize);
+            return INVALID_OPERATION;
+        }
+    } else {
+        resultMetadata.setToExternal(const_cast<uint8_t *>(result.data()),
+                result.size());
+    }
+
+    if (resultMetadata.size() != 0) {
+        status_t res;
+        const camera_metadata_t* metadata =
+                reinterpret_cast<const camera_metadata_t*>(resultMetadata.data());
+        size_t expected_metadata_size = resultMetadata.size();
+        if ((res = validate_camera_metadata_structure(metadata, &expected_metadata_size)) != OK) {
+            ALOGE("%s: Invalid camera metadata received by camera service from HAL: %s (%d)",
+                    __FUNCTION__, strerror(-res), res);
+            return INVALID_OPERATION;
+        }
+    }
+
+    return OK;
+}
+
 void Camera3Device::processOneCaptureResultLocked(
-        const hardware::camera::device::V3_2::CaptureResult& result) {
+        const hardware::camera::device::V3_2::CaptureResult& result,
+        const hardware::hidl_vec<
+                hardware::camera::device::V3_4::PhysicalCameraMetadata> physicalCameraMetadatas) {
     camera3_capture_result r;
     status_t res;
     r.frame_number = result.frameNumber;
 
+    // Read and validate the result metadata.
     hardware::camera::device::V3_2::CameraMetadata resultMetadata;
-    if (result.fmqResultSize > 0) {
-        resultMetadata.resize(result.fmqResultSize);
-        if (mResultMetadataQueue == nullptr) {
-            return; // logged in initialize()
-        }
-        if (!mResultMetadataQueue->read(resultMetadata.data(), result.fmqResultSize)) {
-            ALOGE("%s: Frame %d: Cannot read camera metadata from fmq, size = %" PRIu64,
-                    __FUNCTION__, result.frameNumber, result.fmqResultSize);
-            return;
-        }
-    } else {
-        resultMetadata.setToExternal(const_cast<uint8_t *>(result.result.data()),
-                result.result.size());
+    res = readOneCameraMetadataLocked(result.fmqResultSize, resultMetadata, result.result);
+    if (res != OK) {
+        ALOGE("%s: Frame %d: Failed to read capture result metadata",
+                __FUNCTION__, result.frameNumber);
+        return;
     }
+    r.result = reinterpret_cast<const camera_metadata_t*>(resultMetadata.data());
 
-    if (resultMetadata.size() != 0) {
-        r.result = reinterpret_cast<const camera_metadata_t*>(resultMetadata.data());
-        size_t expected_metadata_size = resultMetadata.size();
-        if ((res = validate_camera_metadata_structure(r.result, &expected_metadata_size)) != OK) {
-            ALOGE("%s: Frame %d: Invalid camera metadata received by camera service from HAL: %s (%d)",
-                    __FUNCTION__, result.frameNumber, strerror(-res), res);
+    // Read and validate physical camera metadata
+    size_t physResultCount = physicalCameraMetadatas.size();
+    std::vector<const char*> physCamIds(physResultCount);
+    std::vector<const camera_metadata_t *> phyCamMetadatas(physResultCount);
+    std::vector<hardware::camera::device::V3_2::CameraMetadata> physResultMetadata;
+    physResultMetadata.resize(physResultCount);
+    for (size_t i = 0; i < physicalCameraMetadatas.size(); i++) {
+        res = readOneCameraMetadataLocked(physicalCameraMetadatas[i].fmqMetadataSize,
+                physResultMetadata[i], physicalCameraMetadatas[i].metadata);
+        if (res != OK) {
+            ALOGE("%s: Frame %d: Failed to read capture result metadata for camera %s",
+                    __FUNCTION__, result.frameNumber,
+                    physicalCameraMetadatas[i].physicalCameraId.c_str());
             return;
         }
-    } else {
-        r.result = nullptr;
+        physCamIds[i] = physicalCameraMetadatas[i].physicalCameraId.c_str();
+        phyCamMetadatas[i] = reinterpret_cast<const camera_metadata_t*>(
+                physResultMetadata[i].data());
     }
+    r.num_physcam_metadata = physResultCount;
+    r.physcam_ids = physCamIds.data();
+    r.physcam_metadata = phyCamMetadatas.data();
 
     std::vector<camera3_stream_buffer_t> outputBuffers(result.outputBuffers.size());
     std::vector<buffer_handle_t> outputBufferHandles(result.outputBuffers.size());
@@ -1806,6 +1882,7 @@
     CaptureResult &result = *(mResultQueue.begin());
     frame->mResultExtras = result.mResultExtras;
     frame->mMetadata.acquire(result.mMetadata);
+    frame->mPhysicalMetadatas = std::move(result.mPhysicalMetadatas);
     mResultQueue.erase(mResultQueue.begin());
 
     return OK;
@@ -2323,6 +2400,8 @@
 
     Vector<camera3_stream_t*> streams;
     streams.setCapacity(config.num_streams);
+    std::vector<uint32_t> outBufSizes(mOutputStreams.size(), 0);
+
 
     if (mInputStream != NULL) {
         camera3_stream_t *inputStream;
@@ -2353,6 +2432,12 @@
             return INVALID_OPERATION;
         }
         streams.add(outputStream);
+
+        if (outputStream->format == HAL_PIXEL_FORMAT_BLOB &&
+                outputStream->data_space == HAL_DATASPACE_V0_JFIF) {
+            outBufSizes[i] = static_cast<uint32_t>(
+                    getJpegBufferSize(outputStream->width, outputStream->height));
+        }
     }
 
     config.streams = streams.editArray();
@@ -2361,7 +2446,7 @@
     // max_buffers, usage, priv fields.
 
     const camera_metadata_t *sessionBuffer = sessionParams.getAndLock();
-    res = mInterface->configureStreams(sessionBuffer, &config);
+    res = mInterface->configureStreams(sessionBuffer, &config, outBufSizes);
     sessionParams.unlock(sessionBuffer);
 
     if (res == BAD_VALUE) {
@@ -2579,13 +2664,14 @@
 
 status_t Camera3Device::registerInFlight(uint32_t frameNumber,
         int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
-        bool hasAppCallback, nsecs_t maxExpectedDuration) {
+        bool hasAppCallback, nsecs_t maxExpectedDuration,
+        std::set<String8>& physicalCameraIds) {
     ATRACE_CALL();
     Mutex::Autolock l(mInFlightLock);
 
     ssize_t res;
     res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers, resultExtras, hasInput,
-            hasAppCallback, maxExpectedDuration));
+            hasAppCallback, maxExpectedDuration, physicalCameraIds));
     if (res < 0) return res;
 
     if (mInFlightMap.size() == 1) {
@@ -2825,7 +2911,8 @@
         CaptureResultExtras &resultExtras,
         CameraMetadata &collectedPartialResult,
         uint32_t frameNumber,
-        bool reprocess) {
+        bool reprocess,
+        const std::vector<PhysicalCaptureResultInfo>& physicalMetadatas) {
     ATRACE_CALL();
     if (pendingMetadata.isEmpty())
         return;
@@ -2854,6 +2941,7 @@
     CaptureResult captureResult;
     captureResult.mResultExtras = resultExtras;
     captureResult.mMetadata = pendingMetadata;
+    captureResult.mPhysicalMetadatas = physicalMetadatas;
 
     // Append any previous partials to form a complete result
     if (mUsePartialResult && !collectedPartialResult.isEmpty()) {
@@ -2869,6 +2957,15 @@
                 frameNumber);
         return;
     }
+    for (auto& physicalMetadata : captureResult.mPhysicalMetadatas) {
+        camera_metadata_entry timestamp =
+                physicalMetadata.mPhysicalCameraMetadata.find(ANDROID_SENSOR_TIMESTAMP);
+        if (timestamp.count == 0) {
+            SET_ERR("No timestamp provided by HAL for physical camera %s frame %d!",
+                    String8(physicalMetadata.mPhysicalCameraId).c_str(), frameNumber);
+            return;
+        }
+    }
 
     mTagMonitor.monitorMetadata(TagMonitor::RESULT,
             frameNumber, timestamp.data.i64[0], captureResult.mMetadata);
@@ -2904,7 +3001,6 @@
 
     bool isPartialResult = false;
     CameraMetadata collectedPartialResult;
-    CaptureResultExtras resultExtras;
     bool hasInputBufferInRequest = false;
 
     // Get shutter timestamp and resultExtras from list of in-flight requests,
@@ -2945,6 +3041,11 @@
                 return;
             }
             isPartialResult = (result->partial_result < mNumPartialResults);
+            if (isPartialResult && result->num_physcam_metadata) {
+                SET_ERR("Result is malformed for frame %d: partial_result not allowed for"
+                        " physical camera result", frameNumber);
+                return;
+            }
             if (isPartialResult) {
                 request.collectedPartialResult.append(result->result);
             }
@@ -2961,11 +3062,28 @@
 
         // Did we get the (final) result metadata for this capture?
         if (result->result != NULL && !isPartialResult) {
+            if (request.physicalCameraIds.size() != result->num_physcam_metadata) {
+                SET_ERR("Requested physical Camera Ids %d not equal to number of metadata %d",
+                        request.physicalCameraIds.size(), result->num_physcam_metadata);
+                return;
+            }
             if (request.haveResultMetadata) {
                 SET_ERR("Called multiple times with metadata for frame %d",
                         frameNumber);
                 return;
             }
+            for (uint32_t i = 0; i < result->num_physcam_metadata; i++) {
+                String8 physicalId(result->physcam_ids[i]);
+                std::set<String8>::iterator cameraIdIter =
+                        request.physicalCameraIds.find(physicalId);
+                if (cameraIdIter != request.physicalCameraIds.end()) {
+                    request.physicalCameraIds.erase(cameraIdIter);
+                } else {
+                    SET_ERR("Total result for frame %d has already returned for camera %s",
+                            frameNumber, physicalId.c_str());
+                    return;
+                }
+            }
             if (mUsePartialResult &&
                     !request.collectedPartialResult.isEmpty()) {
                 collectedPartialResult.acquire(
@@ -3010,15 +3128,21 @@
         }
 
         if (result->result != NULL && !isPartialResult) {
+            for (uint32_t i = 0; i < result->num_physcam_metadata; i++) {
+                CameraMetadata physicalMetadata;
+                physicalMetadata.append(result->physcam_metadata[i]);
+                request.physicalMetadatas.push_back({String16(result->physcam_ids[i]),
+                        physicalMetadata});
+            }
             if (shutterTimestamp == 0) {
                 request.pendingMetadata = result->result;
                 request.collectedPartialResult = collectedPartialResult;
-            } else if (request.hasCallback) {
+           } else if (request.hasCallback) {
                 CameraMetadata metadata;
                 metadata = result->result;
                 sendCaptureResult(metadata, request.resultExtras,
                     collectedPartialResult, frameNumber,
-                    hasInputBufferInRequest);
+                    hasInputBufferInRequest, request.physicalMetadatas);
             }
         }
 
@@ -3204,7 +3328,7 @@
                 // send pending result and buffers
                 sendCaptureResult(r.pendingMetadata, r.resultExtras,
                     r.collectedPartialResult, msg.frame_number,
-                    r.hasInputBuffer);
+                    r.hasInputBuffer, r.physicalMetadatas);
             }
             returnOutputBuffers(r.pendingOutputBuffers.array(),
                 r.pendingOutputBuffers.size(), r.shutterTimestamp);
@@ -3219,7 +3343,6 @@
     }
 }
 
-
 CameraMetadata Camera3Device::getLatestRequestLocked() {
     ALOGV("%s", __FUNCTION__);
 
@@ -3270,6 +3393,8 @@
 }
 
 void Camera3Device::HalInterface::clear() {
+    mHidlSession_3_4.clear();
+    mHidlSession_3_3.clear();
     mHidlSession.clear();
 }
 
@@ -3379,7 +3504,7 @@
 }
 
 status_t Camera3Device::HalInterface::configureStreams(const camera_metadata_t *sessionParams,
-        camera3_stream_configuration *config) {
+        camera3_stream_configuration *config, const std::vector<uint32_t>& outputBufferSizes) {
     ATRACE_NAME("CameraHal::configureStreams");
     if (!valid()) return INVALID_OPERATION;
     status_t res = OK;
@@ -3420,6 +3545,7 @@
         dst3_2.dataSpace = mapToHidlDataspace(src->data_space);
         dst3_2.rotation = mapToStreamRotation((camera3_stream_rotation_t) src->rotation);
         dst3_4.v3_2 = dst3_2;
+        dst3_4.bufferSize = outputBufferSizes[i];
         if (src->physical_camera_id != nullptr) {
             dst3_4.physicalCameraId = src->physical_camera_id;
         }
@@ -4657,6 +4783,7 @@
         outputBuffers->insertAt(camera3_stream_buffer_t(), 0,
                 captureRequest->mOutputStreams.size());
         halRequest->output_buffers = outputBuffers->array();
+        std::set<String8> requestedPhysicalCameras;
         for (size_t j = 0; j < captureRequest->mOutputStreams.size(); j++) {
             sp<Camera3OutputStreamInterface> outputStream = captureRequest->mOutputStreams.editItemAt(j);
 
@@ -4687,8 +4814,18 @@
 
                 return TIMED_OUT;
             }
-            halRequest->num_output_buffers++;
 
+            String8 physicalCameraId = outputStream->getPhysicalCameraId();
+
+            if (!physicalCameraId.isEmpty()) {
+                // Physical stream isn't supported for input request.
+                if (halRequest->input_buffer) {
+                    CLOGE("Physical stream is not supported for input request");
+                    return INVALID_OPERATION;
+                }
+                requestedPhysicalCameras.insert(physicalCameraId);
+            }
+            halRequest->num_output_buffers++;
         }
         totalNumBuffers += halRequest->num_output_buffers;
 
@@ -4711,7 +4848,8 @@
                 totalNumBuffers, captureRequest->mResultExtras,
                 /*hasInput*/halRequest->input_buffer != NULL,
                 hasCallback,
-                calculateMaxExpectedDuration(halRequest->settings));
+                calculateMaxExpectedDuration(halRequest->settings),
+                requestedPhysicalCameras);
         ALOGVV("%s: registered in flight requestId = %" PRId32 ", frameNumber = %" PRId64
                ", burstId = %" PRId32 ".",
                 __FUNCTION__,
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index bf2a577..ccd9d7a 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -19,6 +19,7 @@
 
 #include <utility>
 #include <unordered_map>
+#include <set>
 
 #include <utils/Condition.h>
 #include <utils/Errors.h>
@@ -33,6 +34,7 @@
 #include <android/hardware/camera/device/3.3/ICameraDeviceSession.h>
 #include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
 #include <android/hardware/camera/device/3.2/ICameraDeviceCallback.h>
+#include <android/hardware/camera/device/3.4/ICameraDeviceCallback.h>
 #include <fmq/MessageQueue.h>
 #include <hardware/camera3.h>
 
@@ -77,7 +79,7 @@
  */
 class Camera3Device :
             public CameraDeviceBase,
-            virtual public hardware::camera::device::V3_2::ICameraDeviceCallback,
+            virtual public hardware::camera::device::V3_4::ICameraDeviceCallback,
             private camera3_callback_ops {
   public:
 
@@ -280,7 +282,8 @@
         status_t constructDefaultRequestSettings(camera3_request_template_t templateId,
                 /*out*/ camera_metadata_t **requestTemplate);
         status_t configureStreams(const camera_metadata_t *sessionParams,
-                /*inout*/ camera3_stream_configuration *config);
+                /*inout*/ camera3_stream_configuration *config,
+                const std::vector<uint32_t>& outputBufferSizes);
         status_t processCaptureRequest(camera3_capture_request_t *request);
         status_t processBatchCaptureRequests(
                 std::vector<camera3_capture_request_t*>& requests,
@@ -472,9 +475,11 @@
 
 
     /**
-     * Implementation of android::hardware::camera::device::V3_2::ICameraDeviceCallback
+     * Implementation of android::hardware::camera::device::V3_4::ICameraDeviceCallback
      */
-
+    hardware::Return<void> processCaptureResult_3_4(
+            const hardware::hidl_vec<
+                    hardware::camera::device::V3_4::CaptureResult>& results) override;
     hardware::Return<void> processCaptureResult(
             const hardware::hidl_vec<
                     hardware::camera::device::V3_2::CaptureResult>& results) override;
@@ -484,7 +489,13 @@
 
     // Handle one capture result. Assume that mProcessCaptureResultLock is held.
     void processOneCaptureResultLocked(
-            const hardware::camera::device::V3_2::CaptureResult& results);
+            const hardware::camera::device::V3_2::CaptureResult& result,
+            const hardware::hidl_vec<
+            hardware::camera::device::V3_4::PhysicalCameraMetadata> physicalCameraMetadatas);
+    status_t readOneCameraMetadataLocked(uint64_t fmqResultSize,
+            hardware::camera::device::V3_2::CameraMetadata& resultMetadata,
+            const hardware::camera::device::V3_2::CameraMetadata& result);
+
     // Handle one notify message
     void notify(const hardware::camera::device::V3_2::NotifyMsg& msg);
 
@@ -965,6 +976,12 @@
         // REQUEST/RESULT error.
         bool skipResultMetadata;
 
+        // The physical camera ids being requested.
+        std::set<String8> physicalCameraIds;
+
+        // Map of physicalCameraId <-> Metadata
+        std::vector<PhysicalCaptureResultInfo> physicalMetadatas;
+
         // Default constructor needed by KeyedVector
         InFlightRequest() :
                 shutterTimestamp(0),
@@ -979,7 +996,8 @@
         }
 
         InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput,
-                bool hasAppCallback, nsecs_t maxDuration) :
+                bool hasAppCallback, nsecs_t maxDuration,
+                const std::set<String8>& physicalCameraIdSet) :
                 shutterTimestamp(0),
                 sensorTimestamp(0),
                 requestStatus(OK),
@@ -989,7 +1007,8 @@
                 hasInputBuffer(hasInput),
                 hasCallback(hasAppCallback),
                 maxExpectedDuration(maxDuration),
-                skipResultMetadata(false) {
+                skipResultMetadata(false),
+                physicalCameraIds(physicalCameraIdSet) {
         }
     };
 
@@ -1006,7 +1025,7 @@
 
     status_t registerInFlight(uint32_t frameNumber,
             int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
-            bool callback, nsecs_t maxExpectedDuration);
+            bool callback, nsecs_t maxExpectedDuration, std::set<String8>& physicalCameraIds);
 
     /**
      * Returns the maximum expected time it'll take for all currently in-flight
@@ -1127,7 +1146,9 @@
     void sendCaptureResult(CameraMetadata &pendingMetadata,
             CaptureResultExtras &resultExtras,
             CameraMetadata &collectedPartialResult, uint32_t frameNumber,
-            bool reprocess);
+            bool reprocess, const std::vector<PhysicalCaptureResultInfo>& physicalMetadatas);
+
+    bool isLastFullResult(const InFlightRequest& inFlightRequest);
 
     // Insert the result to the result queue after updating frame number and overriding AE
     // trigger cancel.
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
index 44eb68a..fb1ff77 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
@@ -115,6 +115,10 @@
     return OK;
 }
 
+const String8& Camera3DummyStream::getPhysicalCameraId() const {
+    return DUMMY_ID;
+}
+
 status_t Camera3DummyStream::setConsumers(const std::vector<sp<Surface>>& /*consumers*/) {
     ALOGE("%s: Stream %d: Dummy stream doesn't support set consumer surface!",
             __FUNCTION__, mId);
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h
index dcf9160..4627548 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.h
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h
@@ -63,6 +63,11 @@
     virtual status_t dropBuffers(bool /*dropping*/) override;
 
     /**
+     * Query the physical camera id for the output stream.
+     */
+    virtual const String8& getPhysicalCameraId() const override;
+
+    /**
      * Return if this output stream is for video encoding.
      */
     bool isVideoStream() const;
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index b73e256..b3c3717 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -806,6 +806,11 @@
     return OK;
 }
 
+const String8& Camera3OutputStream::getPhysicalCameraId() const {
+    Mutex::Autolock l(mLock);
+    return physicalCameraId();
+}
+
 status_t Camera3OutputStream::notifyBufferReleased(ANativeWindowBuffer* /*anwBuffer*/) {
     return OK;
 }
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 824aef7..6f36f92 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -174,6 +174,11 @@
     virtual status_t dropBuffers(bool dropping) override;
 
     /**
+     * Query the physical camera id for the output stream.
+     */
+    virtual const String8& getPhysicalCameraId() const override;
+
+    /**
      * Set the graphic buffer manager to get/return the stream buffers.
      *
      * It is only legal to call this method when stream is in STATE_CONSTRUCTED state.
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
index 08fcf38..a711a6d 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
@@ -78,6 +78,11 @@
      * Drop buffers if dropping is true. If dropping is false, do not drop buffers.
      */
     virtual status_t dropBuffers(bool /*dropping*/) = 0;
+
+    /**
+     * Query the physical camera id for the output stream.
+     */
+    virtual const String8& getPhysicalCameraId() const = 0;
 };
 
 } // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 0d91620..1105b75 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -143,6 +143,10 @@
     return mOriginalDataSpace;
 }
 
+const String8& Camera3Stream::physicalCameraId() const {
+    return mPhysicalCameraId;
+}
+
 status_t Camera3Stream::forceToIdle() {
     ATRACE_CALL();
     Mutex::Autolock l(mLock);
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index f85dff2..a60cb56 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -165,6 +165,7 @@
     void              setDataSpaceOverride(bool dataSpaceOverriden);
     bool              isDataSpaceOverridden() const;
     android_dataspace getOriginalDataSpace() const;
+    const String8&    physicalCameraId() const;
 
     camera3_stream*   asHalStream() override {
         return this;
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index 7f52802..6d84a42 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -100,9 +100,6 @@
           mDumpProtoDefault(MediaAnalyticsItem::PROTO_V1) {
 
     ALOGD("MediaAnalyticsService created");
-    // clear our queues
-    mOpen = new List<MediaAnalyticsItem *>();
-    mFinalized = new List<MediaAnalyticsItem *>();
 
     mItemsSubmitted = 0;
     mItemsFinalized = 0;
@@ -118,26 +115,13 @@
 MediaAnalyticsService::~MediaAnalyticsService() {
         ALOGD("MediaAnalyticsService destroyed");
 
-    // clean out mOpen and mFinalized
-    while (mOpen->size() > 0) {
-        MediaAnalyticsItem * oitem = *(mOpen->begin());
-        mOpen->erase(mOpen->begin());
+    while (mItems.size() > 0) {
+        MediaAnalyticsItem * oitem = *(mItems.begin());
+        mItems.erase(mItems.begin());
         delete oitem;
         mItemsDiscarded++;
         mItemsDiscardedCount++;
     }
-    delete mOpen;
-    mOpen = NULL;
-
-    while (mFinalized->size() > 0) {
-        MediaAnalyticsItem * oitem = *(mFinalized->begin());
-        mFinalized->erase(mFinalized->begin());
-        delete oitem;
-        mItemsDiscarded++;
-        mItemsDiscardedCount++;
-    }
-    delete mFinalized;
-    mFinalized = NULL;
 }
 
 
@@ -149,9 +133,14 @@
 }
 
 // caller surrenders ownership of 'item'
-MediaAnalyticsItem::SessionID_t MediaAnalyticsService::submit(MediaAnalyticsItem *item, bool forcenew) {
+MediaAnalyticsItem::SessionID_t MediaAnalyticsService::submit(MediaAnalyticsItem *item, bool forcenew)
+{
+    UNUSED(forcenew);
 
-    MediaAnalyticsItem::SessionID_t id = MediaAnalyticsItem::SessionIDInvalid;
+    // fill in a sessionID if we do not yet have one
+    if (item->getSessionID() <= MediaAnalyticsItem::SessionIDNone) {
+        item->setSessionID(generateUniqueSessionID());
+    }
 
     // we control these, generally not trusting user input
     nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
@@ -164,9 +153,7 @@
     int uid_given = item->getUid();
     int pid_given = item->getPid();
 
-    // although we do make exceptions for particular client uids
-    // that we know we trust.
-    //
+    // although we do make exceptions for some trusted client uids
     bool isTrusted = false;
 
     ALOGV("caller has uid=%d, embedded uid=%d", uid, uid_given);
@@ -192,7 +179,6 @@
             break;
     }
 
-
     // Overwrite package name and version if the caller was untrusted.
     if (!isTrusted) {
       setPkgInfo(item, item->getUid(), true, true);
@@ -217,75 +203,23 @@
         return MediaAnalyticsItem::SessionIDInvalid;
     }
 
-
-    // if we have a sesisonid in the new record, look to make
+    // XXX: if we have a sessionid in the new record, look to make
     // sure it doesn't appear in the finalized list.
     // XXX: this is for security / DOS prevention.
     // may also require that we persist the unique sessionIDs
     // across boots [instead of within a single boot]
 
-
-    // match this new record up against records in the open
-    // list...
-    // if there's a match, merge them together
-    // deal with moving the old / merged record into the finalized que
-
-    bool finalizing = item->getFinalized();
-
-    Mutex::Autolock _l(mLock);
-
-    // if finalizing, we'll remove it
-    MediaAnalyticsItem *oitem = findItem(mOpen, item, finalizing | forcenew);
-    if (oitem != NULL) {
-        if (forcenew) {
-            // old one gets finalized, then we insert the new one
-            // so we'll have 2 records at the end of this.
-            // but don't finalize an empty record
-            if (oitem->count() == 0) {
-                // we're responsible for disposing of the dead record
-                delete oitem;
-                oitem = NULL;
-            } else {
-                oitem->setFinalized(true);
-                saveItem(mFinalized, oitem, 0);
-            }
-            // new record could itself be marked finalized...
-            id = item->getSessionID();
-            if (finalizing) {
-                saveItem(mFinalized, item, 0);
-                mItemsFinalized++;
-            } else {
-                saveItem(mOpen, item, 1);
-            }
-        } else {
-            // combine the records, send it to finalized if appropriate
-            oitem->merge(item);
-            id = oitem->getSessionID();
-            if (finalizing) {
-                saveItem(mFinalized, oitem, 0);
-                mItemsFinalized++;
-            }
-
-            // we're responsible for disposing of the dead record
-            delete item;
-            item = NULL;
-        }
-    } else {
-        // nothing to merge, save the new record
-        id = item->getSessionID();
-        if (finalizing) {
-            if (item->count() == 0) {
-                // drop empty records
-                delete item;
-                item = NULL;
-            } else {
-                saveItem(mFinalized, item, 0);
-                mItemsFinalized++;
-            }
-        } else {
-            saveItem(mOpen, item, 1);
-        }
+    if (item->count() == 0) {
+        // drop empty records
+        delete item;
+        item = NULL;
+        return MediaAnalyticsItem::SessionIDInvalid;
     }
+
+    // save the new record
+    MediaAnalyticsItem::SessionID_t id = item->getSessionID();
+    saveItem(item);
+    mItemsFinalized++;
     return id;
 }
 
@@ -378,6 +312,7 @@
     }
 
     Mutex::Autolock _l(mLock);
+    // mutex between insertion and dumping the contents
 
     mDumpProto = chosenProto;
 
@@ -392,9 +327,9 @@
 
     if (clear) {
         // remove everything from the finalized queue
-        while (mFinalized->size() > 0) {
-            MediaAnalyticsItem * oitem = *(mFinalized->begin());
-            mFinalized->erase(mFinalized->begin());
+        while (mItems.size() > 0) {
+            MediaAnalyticsItem * oitem = *(mItems.begin());
+            mItems.erase(mItems.begin());
             delete oitem;
             mItemsDiscarded++;
         }
@@ -408,7 +343,8 @@
 }
 
 // dump headers
-void MediaAnalyticsService::dumpHeaders(String8 &result, nsecs_t ts_since) {
+void MediaAnalyticsService::dumpHeaders(String8 &result, nsecs_t ts_since)
+{
     const size_t SIZE = 512;
     char buffer[SIZE];
 
@@ -425,7 +361,7 @@
 
     snprintf(buffer, SIZE,
         "Since Boot: Submissions: %8" PRId64
-            " Finalizations: %8" PRId64 "\n",
+            " Accepted: %8" PRId64 "\n",
         mItemsSubmitted, mItemsFinalized);
     result.append(buffer);
     snprintf(buffer, SIZE,
@@ -433,19 +369,17 @@
             " (by Count: %" PRId64 " by Expiration: %" PRId64 ")\n",
          mItemsDiscarded, mItemsDiscardedCount, mItemsDiscardedExpire);
     result.append(buffer);
-    snprintf(buffer, SIZE,
-        "Summary Sets Discarded: %" PRId64 "\n", mSetsDiscarded);
-    result.append(buffer);
     if (ts_since != 0) {
         snprintf(buffer, SIZE,
-            "Dumping Queue entries more recent than: %" PRId64 "\n",
+            "Emitting Queue entries more recent than: %" PRId64 "\n",
             (int64_t) ts_since);
         result.append(buffer);
     }
 }
 
 // the recent, detailed queues
-void MediaAnalyticsService::dumpRecent(String8 &result, nsecs_t ts_since, const char * only) {
+void MediaAnalyticsService::dumpRecent(String8 &result, nsecs_t ts_since, const char * only)
+{
     const size_t SIZE = 512;
     char buffer[SIZE];
 
@@ -456,30 +390,27 @@
     // show the recently recorded records
     snprintf(buffer, sizeof(buffer), "\nFinalized Metrics (oldest first):\n");
     result.append(buffer);
-    result.append(this->dumpQueue(mFinalized, ts_since, only));
-
-    snprintf(buffer, sizeof(buffer), "\nIn-Progress Metrics (newest first):\n");
-    result.append(buffer);
-    result.append(this->dumpQueue(mOpen, ts_since, only));
+    result.append(this->dumpQueue(ts_since, only));
 
     // show who is connected and injecting records?
     // talk about # records fed to the 'readers'
     // talk about # records we discarded, perhaps "discarded w/o reading" too
 }
+
 // caller has locked mLock...
-String8 MediaAnalyticsService::dumpQueue(List<MediaAnalyticsItem *> *theList) {
-    return dumpQueue(theList, (nsecs_t) 0, NULL);
+String8 MediaAnalyticsService::dumpQueue() {
+    return dumpQueue((nsecs_t) 0, NULL);
 }
 
-String8 MediaAnalyticsService::dumpQueue(List<MediaAnalyticsItem *> *theList, nsecs_t ts_since, const char * only) {
+String8 MediaAnalyticsService::dumpQueue(nsecs_t ts_since, const char * only) {
     String8 result;
     int slot = 0;
 
-    if (theList->empty()) {
+    if (mItems.empty()) {
             result.append("empty\n");
     } else {
-        List<MediaAnalyticsItem *>::iterator it = theList->begin();
-        for (; it != theList->end(); it++) {
+        List<MediaAnalyticsItem *>::iterator it = mItems.begin();
+        for (; it != mItems.end(); it++) {
             nsecs_t when = (*it)->getTimestamp();
             if (when < ts_since) {
                 continue;
@@ -500,35 +431,25 @@
 
 //
 // Our Cheap in-core, non-persistent records management.
-// XXX: rewrite this to manage persistence, etc.
 
 // insert appropriately into queue
-// caller should hold mLock
-void MediaAnalyticsService::saveItem(List<MediaAnalyticsItem *> *l, MediaAnalyticsItem * item, int front) {
+void MediaAnalyticsService::saveItem(MediaAnalyticsItem * item)
+{
 
-    if (front)  {
-        // for non-finalized stuff, since we expect to reference it again soon,
-        // make it quicker to find (nearer the front of our list)
-        l->push_front(item);
-    } else {
-        // for finalized records, which we want to dump 'in sequence order'
-        l->push_back(item);
-    }
+    Mutex::Autolock _l(mLock);
+    // mutex between insertion and dumping the contents
 
-    // our reclaim process is for oldest-first queues
-    if (front) {
-        return;
-    }
-
+    // we want to dump 'in FIFO order', so insert at the end
+    mItems.push_back(item);
 
     // keep removing old records the front until we're in-bounds (count)
     if (mMaxRecords > 0) {
-        while (l->size() > (size_t) mMaxRecords) {
-            MediaAnalyticsItem * oitem = *(l->begin());
+        while (mItems.size() > (size_t) mMaxRecords) {
+            MediaAnalyticsItem * oitem = *(mItems.begin());
             if (oitem == item) {
                 break;
             }
-            l->erase(l->begin());
+            mItems.erase(mItems.begin());
             delete oitem;
             mItemsDiscarded++;
             mItemsDiscardedCount++;
@@ -536,10 +457,11 @@
     }
 
     // keep removing old records the front until we're in-bounds (count)
+    // NB: expired entries aren't removed until the next insertion, which could be a while
     if (mMaxRecordAgeNs > 0) {
         nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
-        while (l->size() > 0) {
-            MediaAnalyticsItem * oitem = *(l->begin());
+        while (mItems.size() > 0) {
+            MediaAnalyticsItem * oitem = *(mItems.begin());
             nsecs_t when = oitem->getTimestamp();
             if (oitem == item) {
                 break;
@@ -549,7 +471,7 @@
                 // this (and the rest) are recent enough to keep
                 break;
             }
-            l->erase(l->begin());
+            mItems.erase(mItems.begin());
             delete oitem;
             mItemsDiscarded++;
             mItemsDiscardedExpire++;
@@ -557,83 +479,13 @@
     }
 }
 
-// are they alike enough that nitem can be folded into oitem?
-static bool compatibleItems(MediaAnalyticsItem * oitem, MediaAnalyticsItem * nitem) {
-
-    // general safety
-    if (nitem->getUid() != oitem->getUid()) {
-        return false;
-    }
-    if (nitem->getPid() != oitem->getPid()) {
-        return false;
-    }
-
-    // key -- needs to match
-    if (nitem->getKey() == oitem->getKey()) {
-        // still in the game.
-    } else {
-        return false;
-    }
-
-    // session id -- empty field in new is allowed
-    MediaAnalyticsItem::SessionID_t osession = oitem->getSessionID();
-    MediaAnalyticsItem::SessionID_t nsession = nitem->getSessionID();
-    if (nsession != osession) {
-        // incoming '0' matches value in osession
-        if (nsession != 0) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-// find the incomplete record that this will overlay
-// caller should hold mLock
-MediaAnalyticsItem *MediaAnalyticsService::findItem(List<MediaAnalyticsItem*> *theList, MediaAnalyticsItem *nitem, bool removeit) {
-    if (nitem == NULL) {
-        return NULL;
-    }
-
-    MediaAnalyticsItem *item = NULL;
-
-    for (List<MediaAnalyticsItem *>::iterator it = theList->begin();
-        it != theList->end(); it++) {
-        MediaAnalyticsItem *tmp = (*it);
-
-        if (!compatibleItems(tmp, nitem)) {
-            continue;
-        }
-
-        // we match! this is the one I want.
-        if (removeit) {
-            theList->erase(it);
-        }
-        item = tmp;
-        break;
-    }
-    return item;
-}
-
-
-// delete the indicated record
-// caller should hold mLock
-void MediaAnalyticsService::deleteItem(List<MediaAnalyticsItem *> *l, MediaAnalyticsItem *item) {
-
-    for (List<MediaAnalyticsItem *>::iterator it = l->begin();
-        it != l->end(); it++) {
-        if ((*it)->getSessionID() != item->getSessionID())
-            continue;
-        delete *it;
-        l->erase(it);
-        break;
-    }
-}
-
 static std::string allowedKeys[] =
 {
+    "audiorecord",
+    "audiotrack",
     "codec",
-    "extractor"
+    "extractor",
+    "nuplayer",
 };
 
 static const int nAllowedKeys = sizeof(allowedKeys) / sizeof(allowedKeys[0]);
@@ -673,7 +525,9 @@
 #define PKG_EXPIRATION_NS (30*60*1000000000ll)   // 30 minutes, in nsecs
 
 // give me the package name, perhaps going to find it
-void MediaAnalyticsService::setPkgInfo(MediaAnalyticsItem *item, uid_t uid, bool setName, bool setVersion) {
+// manages its own mutex operations internally
+void MediaAnalyticsService::setPkgInfo(MediaAnalyticsItem *item, uid_t uid, bool setName, bool setVersion)
+{
     ALOGV("asking for packagename to go with uid=%d", uid);
 
     if (!setName && !setVersion) {
@@ -683,22 +537,26 @@
 
     nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
     struct UidToPkgMap mapping;
-    mapping.uid = (-1);
+    mapping.uid = (uid_t)(-1);
 
-    ssize_t i = mPkgMappings.indexOfKey(uid);
-    if (i >= 0) {
-        mapping = mPkgMappings.valueAt(i);
-        ALOGV("Expiration? uid %d expiration %" PRId64 " now %" PRId64,
-              uid, mapping.expiration, now);
-        if (mapping.expiration < now) {
-            // purge our current entry and re-query
-            ALOGV("entry for uid %d expired, now= %" PRId64 "", uid, now);
-            mPkgMappings.removeItemsAt(i, 1);
-            // could cheat and use a goto back to the top of the routine.
-            // a good compiler should recognize the local tail recursion...
-            return setPkgInfo(item, uid, setName, setVersion);
+    {
+        Mutex::Autolock _l(mLock_mappings);
+        int i = mPkgMappings.indexOfKey(uid);
+        if (i >= 0) {
+            mapping = mPkgMappings.valueAt(i);
+            ALOGV("Expiration? uid %d expiration %" PRId64 " now %" PRId64,
+                  uid, mapping.expiration, now);
+            if (mapping.expiration <= now) {
+                // purge the stale entry and fall into re-fetching
+                ALOGV("entry for uid %d expired, now= %" PRId64 "", uid, now);
+                mPkgMappings.removeItemsAt(i);
+                mapping.uid = (uid_t)(-1);
+            }
         }
-    } else {
+    }
+
+    // if we did not find it
+    if (mapping.uid == (uid_t)(-1)) {
         std::string pkg;
         std::string installer = "";
         int64_t versionCode = 0;
@@ -708,7 +566,7 @@
             pkg = pw->pw_name;
         }
 
-        // find the proper value -- should we cache this binder??
+        // find the proper value
 
         sp<IBinder> binder = NULL;
         sp<IServiceManager> sm = defaultServiceManager();
diff --git a/services/mediaanalytics/MediaAnalyticsService.h b/services/mediaanalytics/MediaAnalyticsService.h
index 484339c..b3c902a 100644
--- a/services/mediaanalytics/MediaAnalyticsService.h
+++ b/services/mediaanalytics/MediaAnalyticsService.h
@@ -53,7 +53,6 @@
     int64_t mItemsDiscarded;
     int64_t mItemsDiscardedExpire;
     int64_t mItemsDiscardedCount;
-    int64_t mSetsDiscarded;
     MediaAnalyticsItem::SessionID_t mLastSessionID;
 
     // partitioned a bit so we don't over serialize
@@ -76,25 +75,15 @@
     bool contentValid(MediaAnalyticsItem *item, bool isTrusted);
     bool rateLimited(MediaAnalyticsItem *);
 
-    // the ones that are still open
-    // (newest at front) since we keep looking for them
-    List<MediaAnalyticsItem *> *mOpen;
-    // the ones we've finalized
     // (oldest at front) so it prints nicely for dumpsys
-    List<MediaAnalyticsItem *> *mFinalized;
-    // searching within these queues: queue, key
-    MediaAnalyticsItem *findItem(List<MediaAnalyticsItem *> *,
-                                     MediaAnalyticsItem *, bool removeit);
-
-    void saveItem(MediaAnalyticsItem);
-    void saveItem(List<MediaAnalyticsItem *> *, MediaAnalyticsItem *, int);
-    void deleteItem(List<MediaAnalyticsItem *> *, MediaAnalyticsItem *);
+    List<MediaAnalyticsItem *> mItems;
+    void saveItem(MediaAnalyticsItem *);
 
     // support for generating output
     int mDumpProto;
     int mDumpProtoDefault;
-    String8 dumpQueue(List<MediaAnalyticsItem*> *);
-    String8 dumpQueue(List<MediaAnalyticsItem*> *, nsecs_t, const char *only);
+    String8 dumpQueue();
+    String8 dumpQueue(nsecs_t, const char *only);
 
     void dumpHeaders(String8 &result, nsecs_t ts_since);
     void dumpSummaries(String8 &result, nsecs_t ts_since, const char * only);
diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk
index 8e5b260..97eaf77 100644
--- a/services/mediacodec/Android.mk
+++ b/services/mediacodec/Android.mk
@@ -1,5 +1,28 @@
 LOCAL_PATH := $(call my-dir)
 
+_software_codecs := \
+    libstagefright_soft_aacdec \
+    libstagefright_soft_aacenc \
+    libstagefright_soft_amrdec \
+    libstagefright_soft_amrnbenc \
+    libstagefright_soft_amrwbenc \
+    libstagefright_soft_avcdec \
+    libstagefright_soft_avcenc \
+    libstagefright_soft_flacdec \
+    libstagefright_soft_flacenc \
+    libstagefright_soft_g711dec \
+    libstagefright_soft_gsmdec \
+    libstagefright_soft_hevcdec \
+    libstagefright_soft_mp3dec \
+    libstagefright_soft_mpeg2dec \
+    libstagefright_soft_mpeg4dec \
+    libstagefright_soft_mpeg4enc \
+    libstagefright_soft_opusdec \
+    libstagefright_soft_rawdec \
+    libstagefright_soft_vorbisdec \
+    libstagefright_soft_vpxdec \
+    libstagefright_soft_vpxenc \
+
 # service executable
 include $(CLEAR_VARS)
 # seccomp is not required for coverage build.
@@ -27,6 +50,15 @@
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_VENDOR_MODULE := true
 LOCAL_32_BIT_ONLY := true
+# Since this is 32-bit-only module, only 32-bit version of the codecs are installed.
+# TODO(b/72343507): eliminate the need for manually adding .vendor suffix. This should be done
+# by the build system.
+LOCAL_REQUIRED_MODULES := \
+$(foreach codec,$(_software_codecs),\
+  $(eval _vendor_suffix := $(if $(BOARD_VNDK_VERSION),.vendor))\
+  $(codec)$(_vendor_suffix)\
+)
+_software_codecs :=
 LOCAL_INIT_RC := android.hardware.media.omx@1.0-service.rc
 
 include $(BUILD_EXECUTABLE)