Merge "Camera: Allow streaming requests with individual physical settings"
diff --git a/camera/ndk/include/camera/NdkCameraDevice.h b/camera/ndk/include/camera/NdkCameraDevice.h
index c0eb5c1..f425830 100644
--- a/camera/ndk/include/camera/NdkCameraDevice.h
+++ b/camera/ndk/include/camera/NdkCameraDevice.h
@@ -252,35 +252,6 @@
      */
     TEMPLATE_MANUAL = 6,
 
-    /**
-     * A template for selecting camera parameters that match TEMPLATE_PREVIEW as closely as
-     * possible while improving the camera output for motion tracking use cases.
-     *
-     * <p>This template is best used by applications that are frequently switching between motion
-     * tracking use cases and regular still capture use cases, to minimize the IQ changes
-     * when swapping use cases.</p>
-     *
-     * <p>This template is guaranteed to be supported on camera devices that support the
-     * {@link ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING} capability.</p>
-     *
-     * @see ACameraDevice_createCaptureRequest
-     */
-    TEMPLATE_MOTION_TRACKING_PREVIEW = 7,
-
-    /**
-     * A template for selecting camera parameters that maximize the quality of camera output for
-     * motion tracking use cases.
-     *
-     * <p>This template is best used by applications dedicated to motion tracking applications,
-     * which aren't concerned about fast switches between motion tracking and other use cases.</p>
-     *
-     * <p>This template is guaranteed to be supported on camera devices that support the
-     * {@link ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING} capability.</p>
-     *
-     * @see ACameraDevice_createCaptureRequest
-     */
-    TEMPLATE_MOTION_TRACKING_BEST = 8,
-
 } ACameraDevice_request_template;
 
 /**
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 588e96a..d35a52b 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -2453,9 +2453,6 @@
      *
      * <p>Different calibration methods and use cases can produce better or worse results
      * depending on the selected coordinate origin.</p>
-     * <p>For devices designed to support the MOTION_TRACKING capability, the GYROSCOPE origin
-     * makes device calibration and later usage by applications combining camera and gyroscope
-     * information together simpler.</p>
      */
     ACAMERA_LENS_POSE_REFERENCE =                               // byte (acamera_metadata_enum_android_lens_pose_reference_t)
             ACAMERA_LENS_START + 12,
@@ -4565,7 +4562,7 @@
     ACAMERA_STATISTICS_LENS_SHADING_MAP_MODE =                  // byte (acamera_metadata_enum_android_statistics_lens_shading_map_mode_t)
             ACAMERA_STATISTICS_START + 16,
     /**
-     * <p>Whether the camera device outputs the OIS data in output
+     * <p>A control for selecting whether OIS position information is included in output
      * result metadata.</p>
      *
      * <p>Type: byte (acamera_metadata_enum_android_statistics_ois_data_mode_t)</p>
@@ -4578,7 +4575,7 @@
      *
      * <p>When set to ON,
      * ACAMERA_STATISTICS_OIS_TIMESTAMPS, android.statistics.oisShiftPixelX,
-     * android.statistics.oisShiftPixelY will provide OIS data in the output result metadata.</p>
+     * and android.statistics.oisShiftPixelY provide OIS data in the output result metadata.</p>
      *
      * @see ACAMERA_STATISTICS_OIS_TIMESTAMPS
      */
@@ -6601,7 +6598,7 @@
     /**
      * <p>The value of ACAMERA_LENS_POSE_TRANSLATION is relative to the optical center of
      * the largest camera device facing the same direction as this camera.</p>
-     * <p>This default value for API levels before Android P.</p>
+     * <p>This is the default value for API levels before Android P.</p>
      *
      * @see ACAMERA_LENS_POSE_TRANSLATION
      */
@@ -6610,7 +6607,6 @@
     /**
      * <p>The value of ACAMERA_LENS_POSE_TRANSLATION is relative to the position of the
      * primary gyroscope of this Android device.</p>
-     * <p>This is the value reported by all devices that support the MOTION_TRACKING capability.</p>
      *
      * @see ACAMERA_LENS_POSE_TRANSLATION
      */
@@ -6974,46 +6970,12 @@
     ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT              = 8,
 
     /**
-     * <p>The device supports controls and metadata required for accurate motion tracking for
-     * use cases such as augmented reality, electronic image stabilization, and so on.</p>
-     * <p>This means this camera device has accurate optical calibration and timestamps relative
-     * to the inertial sensors.</p>
-     * <p>This capability requires the camera device to support the following:</p>
-     * <ul>
-     * <li>Capture request templates <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice.html#TEMPLATE_MOTION_TRACKING_PREVIEW">CameraDevice#TEMPLATE_MOTION_TRACKING_PREVIEW</a> and <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice.html#TEMPLATE_MOTION_TRACKING_BEST">CameraDevice#TEMPLATE_MOTION_TRACKING_BEST</a> are defined.</li>
-     * <li>The stream configurations listed in <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice.html#createCaptureSession">CameraDevice#createCaptureSession</a> for MOTION_TRACKING are
-     *   supported, either at 30 or 60fps maximum frame rate.</li>
-     * <li>The following camera characteristics and capture result metadata are provided:<ul>
-     * <li>ACAMERA_LENS_INTRINSIC_CALIBRATION</li>
-     * <li>ACAMERA_LENS_RADIAL_DISTORTION</li>
-     * <li>ACAMERA_LENS_POSE_ROTATION</li>
-     * <li>ACAMERA_LENS_POSE_TRANSLATION</li>
-     * <li>ACAMERA_LENS_POSE_REFERENCE with value GYROSCOPE</li>
-     * </ul>
-     * </li>
-     * <li>The ACAMERA_SENSOR_INFO_TIMESTAMP_SOURCE field has value <code>REALTIME</code>. When compared to
-     *   timestamps from the device's gyroscopes, the clock difference for events occuring at
-     *   the same actual time instant will be less than 1 ms.</li>
-     * <li>The value of the ACAMERA_SENSOR_ROLLING_SHUTTER_SKEW field is accurate to within 1 ms.</li>
-     * <li>The value of ACAMERA_SENSOR_EXPOSURE_TIME is guaranteed to be available in the
-     *   capture result.</li>
-     * <li>The ACAMERA_CONTROL_CAPTURE_INTENT control supports MOTION_TRACKING to limit maximum
-     *   exposure to 20 milliseconds.</li>
-     * <li>The stream configurations required for MOTION_TRACKING (listed at <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice.html#createCaptureSession">CameraDevice#createCaptureSession</a>) can operate at least at
-     *   30fps; optionally, they can operate at 60fps, and '[60, 60]' is listed in
-     *   ACAMERA_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES.</li>
-     * </ul>
+     * <p>The camera device supports the MOTION_TRACKING value for
+     * ACAMERA_CONTROL_CAPTURE_INTENT, which limits maximum exposure time to 20 ms.</p>
+     * <p>This limits the motion blur of capture images, resulting in better image tracking
+     * results for use cases such as image stabilization or augmented reality.</p>
      *
-     * @see ACAMERA_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES
      * @see ACAMERA_CONTROL_CAPTURE_INTENT
-     * @see ACAMERA_LENS_INTRINSIC_CALIBRATION
-     * @see ACAMERA_LENS_POSE_REFERENCE
-     * @see ACAMERA_LENS_POSE_ROTATION
-     * @see ACAMERA_LENS_POSE_TRANSLATION
-     * @see ACAMERA_LENS_RADIAL_DISTORTION
-     * @see ACAMERA_SENSOR_EXPOSURE_TIME
-     * @see ACAMERA_SENSOR_INFO_TIMESTAMP_SOURCE
-     * @see ACAMERA_SENSOR_ROLLING_SHUTTER_SKEW
      */
     ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING           = 10,
 
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index 46bd8f0..9d2daab 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -50,6 +50,7 @@
 #include <media/stagefright/MediaCodec.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaMuxer.h>
+#include <media/stagefright/PersistentSurface.h>
 #include <media/ICrypto.h>
 #include <media/MediaCodecBuffer.h>
 
@@ -70,6 +71,7 @@
 static bool gVerbose = false;           // chatty on stdout
 static bool gRotate = false;            // rotate 90 degrees
 static bool gMonotonicTime = false;     // use system monotonic time for timestamps
+static bool gPersistentSurface = false; // use persistent surface
 static enum {
     FORMAT_MP4, FORMAT_H264, FORMAT_FRAMES, FORMAT_RAW_FRAMES
 } gOutputFormat = FORMAT_MP4;           // data format for output
@@ -199,10 +201,18 @@
 
     ALOGV("Creating encoder input surface");
     sp<IGraphicBufferProducer> bufferProducer;
-    err = codec->createInputSurface(&bufferProducer);
+    if (gPersistentSurface) {
+        sp<PersistentSurface> surface = MediaCodec::CreatePersistentInputSurface();
+        bufferProducer = surface->getBufferProducer();
+        err = codec->setInputSurface(surface);
+    } else {
+        err = codec->createInputSurface(&bufferProducer);
+    }
     if (err != NO_ERROR) {
         fprintf(stderr,
-            "ERROR: unable to create encoder input surface (err=%d)\n", err);
+            "ERROR: unable to %s encoder input surface (err=%d)\n",
+            gPersistentSurface ? "set" : "create",
+            err);
         codec->release();
         return err;
     }
@@ -920,6 +930,7 @@
         { "output-format",      required_argument,  NULL, 'o' },
         { "codec-name",         required_argument,  NULL, 'N' },
         { "monotonic-time",     no_argument,        NULL, 'm' },
+        { "persistent-surface", no_argument,        NULL, 'p' },
         { NULL,                 0,                  NULL, 0 }
     };
 
@@ -1005,6 +1016,9 @@
         case 'm':
             gMonotonicTime = true;
             break;
+        case 'p':
+            gPersistentSurface = true;
+            break;
         default:
             if (ic != '?') {
                 fprintf(stderr, "getopt_long returned unexpected value 0x%x\n", ic);
diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp
index ea239c5..5ea4614 100644
--- a/drm/libmediadrm/Android.bp
+++ b/drm/libmediadrm/Android.bp
@@ -15,25 +15,20 @@
         "IDrm.cpp",
         "IDrmClient.cpp",
         "IMediaDrmService.cpp",
-        "PluginMetricsReporting.cpp",
         "SharedLibrary.cpp",
         "DrmHal.cpp",
-	"DrmMetrics.cpp",
         "CryptoHal.cpp",
-        "protos/plugin_metrics.proto",
     ],
 
-    proto: {
-        type: "lite",
-    },
-
     shared_libs: [
         "libbinder",
         "libcutils",
         "libdl",
         "liblog",
+        "libmediadrmmetrics_lite",
         "libmediametrics",
         "libmediautils",
+        "libprotobuf-cpp-lite",
         "libstagefright_foundation",
         "libutils",
         "android.hardware.drm@1.0",
@@ -48,3 +43,66 @@
         "-Wall",
     ],
 }
+
+// This is the version of the drm metrics configured for protobuf lite.
+cc_library_shared {
+    name: "libmediadrmmetrics_lite",
+    srcs: [
+        "DrmMetrics.cpp",
+        "PluginMetricsReporting.cpp",
+        "protos/metrics.proto",
+    ],
+
+    proto: {
+        export_proto_headers: true,
+	type: "lite",
+    },
+    shared_libs: [
+        "android.hardware.drm@1.0",
+        "android.hardware.drm@1.1",
+        "libbase",
+        "libbinder",
+        "liblog",
+        "libmediametrics",
+        "libprotobuf-cpp-lite",
+        "libutils",
+    ],
+    cflags: [
+        // Suppress unused parameter and no error options. These cause problems
+        // with the when using the map type in a proto definition.
+        "-Wno-unused-parameter",
+        "-Wno-error",
+    ],
+}
+
+// This is the version of the drm metrics library configured for full protobuf.
+cc_library_shared {
+    name: "libmediadrmmetrics_full",
+    srcs: [
+        "DrmMetrics.cpp",
+        "PluginMetricsReporting.cpp",
+        "protos/metrics.proto",
+    ],
+
+    proto: {
+        export_proto_headers: true,
+	type: "full",
+    },
+    shared_libs: [
+        "android.hardware.drm@1.0",
+        "android.hardware.drm@1.1",
+        "libbase",
+        "libbinder",
+        "liblog",
+        "libmediametrics",
+        "libprotobuf-cpp-full",
+        "libutils",
+    ],
+    cflags: [
+        // Suppress unused parameter and no error options. These cause problems
+	// when using the map type in a proto definition.
+	"-Wno-unused-parameter",
+	"-Wno-error",
+    ],
+}
+
diff --git a/drm/libmediadrm/CryptoHal.cpp b/drm/libmediadrm/CryptoHal.cpp
index 2114d40..4377154 100644
--- a/drm/libmediadrm/CryptoHal.cpp
+++ b/drm/libmediadrm/CryptoHal.cpp
@@ -118,15 +118,24 @@
 
     auto manager = ::IServiceManager::getService();
     if (manager != NULL) {
-        manager->listByInterface(ICryptoFactory::descriptor,
+        manager->listByInterface(drm::V1_0::ICryptoFactory::descriptor,
                 [&factories](const hidl_vec<hidl_string> &registered) {
                     for (const auto &instance : registered) {
-                        auto factory = ICryptoFactory::getService(instance);
+                        auto factory = drm::V1_0::ICryptoFactory::getService(instance);
                         if (factory != NULL) {
+                            ALOGD("found drm@1.0 ICryptoFactory %s", instance.c_str());
                             factories.push_back(factory);
-                            ALOGI("makeCryptoFactories: factory instance %s is %s",
-                                    instance.c_str(),
-                                    factory->isRemote() ? "Remote" : "Not Remote");
+                        }
+                    }
+                }
+            );
+        manager->listByInterface(drm::V1_1::ICryptoFactory::descriptor,
+                [&factories](const hidl_vec<hidl_string> &registered) {
+                    for (const auto &instance : registered) {
+                        auto factory = drm::V1_1::ICryptoFactory::getService(instance);
+                        if (factory != NULL) {
+                            ALOGD("found drm@1.1 ICryptoFactory %s", instance.c_str());
+                            factories.push_back(factory);
                         }
                     }
                 }
@@ -137,7 +146,7 @@
         // must be in passthrough mode, load the default passthrough service
         auto passthrough = ICryptoFactory::getService();
         if (passthrough != NULL) {
-            ALOGI("makeCryptoFactories: using default crypto instance");
+            ALOGI("makeCryptoFactories: using default passthrough crypto instance");
             factories.push_back(passthrough);
         } else {
             ALOGE("Failed to find any crypto factories");
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index 07cec01..48f4479 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -16,13 +16,13 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "DrmHal"
+#include <iomanip>
+
 #include <utils/Log.h>
 
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 
-#include <android/hardware/drm/1.0/IDrmFactory.h>
-#include <android/hardware/drm/1.0/IDrmPlugin.h>
 #include <android/hardware/drm/1.0/types.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <hidl/ServiceManagement.h>
@@ -54,6 +54,7 @@
 using ::android::hardware::Return;
 using ::android::hardware::Void;
 using ::android::hidl::manager::V1_0::IServiceManager;
+using ::android::os::PersistableBundle;
 using ::android::sp;
 
 namespace {
@@ -99,6 +100,15 @@
     return hidl_string(string.string());
 }
 
+std::string ToHexString(const std::string& str) {
+  std::ostringstream out;
+  out << std::hex << std::setfill('0');
+  for (size_t i = 0; i < str.size(); i++) {
+    out << std::setw(2) << (int)(str[i]);
+  }
+  return out.str();
+}
+
 static DrmPlugin::SecurityLevel toSecurityLevel(SecurityLevel level) {
     switch(level) {
     case SecurityLevel::SW_SECURE_CRYPTO:
@@ -271,15 +281,24 @@
     auto manager = hardware::defaultServiceManager();
 
     if (manager != NULL) {
-        manager->listByInterface(IDrmFactory::descriptor,
+        manager->listByInterface(drm::V1_0::IDrmFactory::descriptor,
                 [&factories](const hidl_vec<hidl_string> &registered) {
                     for (const auto &instance : registered) {
-                        auto factory = IDrmFactory::getService(instance);
+                        auto factory = drm::V1_0::IDrmFactory::getService(instance);
                         if (factory != NULL) {
+                            ALOGD("found drm@1.0 IDrmFactory %s", instance.c_str());
                             factories.push_back(factory);
-                            ALOGI("makeDrmFactories: factory instance %s is %s",
-                                    instance.c_str(),
-                                    factory->isRemote() ? "Remote" : "Not Remote");
+                        }
+                    }
+                }
+            );
+        manager->listByInterface(drm::V1_1::IDrmFactory::descriptor,
+                [&factories](const hidl_vec<hidl_string> &registered) {
+                    for (const auto &instance : registered) {
+                        auto factory = drm::V1_1::IDrmFactory::getService(instance);
+                        if (factory != NULL) {
+                            ALOGD("found drm@1.1 IDrmFactory %s", instance.c_str());
+                            factories.push_back(factory);
                         }
                     }
                 }
@@ -290,7 +309,7 @@
         // must be in passthrough mode, load the default passthrough service
         auto passthrough = IDrmFactory::getService();
         if (passthrough != NULL) {
-            ALOGI("makeDrmFactories: using default drm instance");
+            ALOGI("makeDrmFactories: using default passthrough drm instance");
             factories.push_back(passthrough);
         } else {
             ALOGE("Failed to find any drm factories");
@@ -301,6 +320,7 @@
 
 sp<IDrmPlugin> DrmHal::makeDrmPlugin(const sp<IDrmFactory>& factory,
         const uint8_t uuid[16], const String8& appPackageName) {
+    mMetrics.SetAppPackageName(appPackageName);
 
     sp<IDrmPlugin> plugin;
     Return<void> hResult = factory->createPlugin(uuid, appPackageName.string(),
@@ -496,7 +516,8 @@
     INIT_CHECK();
 
     closeOpenSessions();
-    reportMetrics();
+    reportPluginMetrics();
+    reportFrameworkMetrics();
     setListener(NULL);
     mInitCheck = NO_INIT;
 
@@ -510,24 +531,62 @@
     return OK;
 }
 
-status_t DrmHal::openSession(Vector<uint8_t> &sessionId) {
+status_t DrmHal::openSession(DrmPlugin::SecurityLevel level,
+        Vector<uint8_t> &sessionId) {
     Mutex::Autolock autoLock(mLock);
     INIT_CHECK();
 
-    status_t  err = UNKNOWN_ERROR;
+    SecurityLevel hSecurityLevel;
+    bool setSecurityLevel = true;
 
+    switch(level) {
+    case DrmPlugin::kSecurityLevelSwSecureCrypto:
+        hSecurityLevel = SecurityLevel::SW_SECURE_CRYPTO;
+        break;
+    case DrmPlugin::kSecurityLevelSwSecureDecode:
+        hSecurityLevel = SecurityLevel::SW_SECURE_DECODE;
+        break;
+    case DrmPlugin::kSecurityLevelHwSecureCrypto:
+        hSecurityLevel = SecurityLevel::HW_SECURE_CRYPTO;
+        break;
+    case DrmPlugin::kSecurityLevelHwSecureDecode:
+        hSecurityLevel = SecurityLevel::HW_SECURE_DECODE;
+        break;
+    case DrmPlugin::kSecurityLevelHwSecureAll:
+        hSecurityLevel = SecurityLevel::HW_SECURE_ALL;
+        break;
+    case DrmPlugin::kSecurityLevelMax:
+        setSecurityLevel = false;
+        break;
+    default:
+        return ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    status_t  err = UNKNOWN_ERROR;
     bool retry = true;
     do {
         hidl_vec<uint8_t> hSessionId;
 
-        Return<void> hResult = mPlugin->openSession(
-                [&](Status status, const hidl_vec<uint8_t>& id) {
-                    if (status == Status::OK) {
-                        sessionId = toVector(id);
+        Return<void> hResult;
+        if (mPluginV1_1 == NULL || !setSecurityLevel) {
+            hResult = mPlugin->openSession(
+                    [&](Status status,const hidl_vec<uint8_t>& id) {
+                        if (status == Status::OK) {
+                            sessionId = toVector(id);
+                        }
+                        err = toStatusT(status);
                     }
-                    err = toStatusT(status);
-                }
-            );
+                );
+        } else {
+            hResult = mPluginV1_1->openSession_1_1(hSecurityLevel,
+                    [&](Status status, const hidl_vec<uint8_t>& id) {
+                        if (status == Status::OK) {
+                            sessionId = toVector(id);
+                        }
+                        err = toStatusT(status);
+                    }
+                );
+        }
 
         if (!hResult.isOk()) {
             err = DEAD_OBJECT;
@@ -549,6 +608,7 @@
         DrmSessionManager::Instance()->addSession(getCallingPid(),
                 mDrmSessionClient, sessionId);
         mOpenSessions.push(sessionId);
+        mMetrics.SetSessionStart(sessionId);
     }
 
     mMetrics.mOpenSessionCounter.Increment(err);
@@ -570,9 +630,10 @@
                 }
             }
         }
-        reportMetrics();
         status_t response = toStatusT(status);
+        mMetrics.SetSessionEnd(sessionId);
         mMetrics.mCloseSessionCounter.Increment(response);
+        reportPluginMetrics();
         return response;
     }
     mMetrics.mCloseSessionCounter.Increment(DEAD_OBJECT);
@@ -586,7 +647,7 @@
         String8 &defaultUrl, DrmPlugin::KeyRequestType *keyRequestType) {
     Mutex::Autolock autoLock(mLock);
     INIT_CHECK();
-    EventTimer<status_t> keyRequestTimer(&mMetrics.mGetKeyRequestTiming);
+    EventTimer<status_t> keyRequestTimer(&mMetrics.mGetKeyRequestTimeUs);
 
     DrmSessionManager::Instance()->useSession(sessionId);
 
@@ -681,7 +742,7 @@
 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);
+    EventTimer<status_t> keyResponseTimer(&mMetrics.mProvideKeyResponseTimeUs);
 
     INIT_CHECK();
 
@@ -979,42 +1040,6 @@
     return hResult.isOk() ? err : DEAD_OBJECT;
 }
 
-status_t DrmHal::setSecurityLevel(Vector<uint8_t> const &sessionId,
-        const DrmPlugin::SecurityLevel& level) {
-    Mutex::Autolock autoLock(mLock);
-    INIT_CHECK();
-
-    if (mPluginV1_1 == NULL) {
-        return ERROR_DRM_CANNOT_HANDLE;
-    }
-
-    SecurityLevel hSecurityLevel;
-
-    switch(level) {
-    case DrmPlugin::kSecurityLevelSwSecureCrypto:
-        hSecurityLevel = SecurityLevel::SW_SECURE_CRYPTO;
-        break;
-    case DrmPlugin::kSecurityLevelSwSecureDecode:
-        hSecurityLevel = SecurityLevel::SW_SECURE_DECODE;
-        break;
-    case DrmPlugin::kSecurityLevelHwSecureCrypto:
-        hSecurityLevel = SecurityLevel::HW_SECURE_CRYPTO;
-        break;
-    case DrmPlugin::kSecurityLevelHwSecureDecode:
-        hSecurityLevel = SecurityLevel::HW_SECURE_DECODE;
-        break;
-    case DrmPlugin::kSecurityLevelHwSecureAll:
-        hSecurityLevel = SecurityLevel::HW_SECURE_ALL;
-        break;
-    default:
-        return ERROR_DRM_CANNOT_HANDLE;
-    }
-
-    Status status = mPluginV1_1->setSecurityLevel(toHidlVec(sessionId),
-            hSecurityLevel);
-    return toStatusT(status);
-}
-
 status_t DrmHal::getPropertyString(String8 const &name, String8 &value ) const {
     Mutex::Autolock autoLock(mLock);
     return getPropertyStringInternal(name, value);
@@ -1086,7 +1111,7 @@
     return toStatusT(status);
 }
 
-status_t DrmHal::getMetrics(MediaAnalyticsItem* item) {
+status_t DrmHal::getMetrics(PersistableBundle* item) {
     if (item == nullptr) {
       return UNEXPECTED_NULL;
     }
@@ -1265,8 +1290,41 @@
     }
 }
 
+void DrmHal::reportFrameworkMetrics() const
+{
+    MediaAnalyticsItem item("mediadrm");
+    item.generateSessionID();
+    item.setPkgName(mMetrics.GetAppPackageName().c_str());
+    String8 vendor;
+    String8 description;
+    status_t result = getPropertyStringInternal(String8("vendor"), vendor);
+    if (result != OK) {
+        ALOGE("Failed to get vendor from drm plugin. %d", result);
+    } else {
+        item.setCString("vendor", vendor.c_str());
+    }
+    result = getPropertyStringInternal(String8("description"), description);
+    if (result != OK) {
+        ALOGE("Failed to get description from drm plugin. %d", result);
+    } else {
+        item.setCString("description", description.c_str());
+    }
 
-void DrmHal::reportMetrics() const
+    std::string serializedMetrics;
+    result = mMetrics.GetSerializedMetrics(&serializedMetrics);
+    if (result != OK) {
+        ALOGE("Failed to serialize Framework metrics: %d", result);
+    }
+    serializedMetrics = ToHexString(serializedMetrics);
+    if (!serializedMetrics.empty()) {
+        item.setCString("serialized_metrics", serializedMetrics.c_str());
+    }
+    if (!item.selfrecord()) {
+        ALOGE("Failed to self record framework metrics.");
+    }
+}
+
+void DrmHal::reportPluginMetrics() const
 {
     Vector<uint8_t> metrics;
     String8 vendor;
diff --git a/drm/libmediadrm/DrmMetrics.cpp b/drm/libmediadrm/DrmMetrics.cpp
index 258c4b0..03bd88a 100644
--- a/drm/libmediadrm/DrmMetrics.cpp
+++ b/drm/libmediadrm/DrmMetrics.cpp
@@ -13,140 +13,330 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define LOG_TAG "DrmMetrics"
+#include <iomanip>
+#include <utility>
 
 #include <android-base/macros.h>
 #include <media/DrmMetrics.h>
+#include <media/stagefright/foundation/base64.h>
+#include <sys/time.h>
+#include <utils/Log.h>
+#include <utils/Timers.h>
 
+#include "protos/metrics.pb.h"
+
+using ::android::String16;
+using ::android::String8;
+using ::android::drm_metrics::DrmFrameworkMetrics;
 using ::android::hardware::drm::V1_0::EventType;
 using ::android::hardware::drm::V1_0::KeyStatusType;
+using ::android::os::PersistableBundle;
 
 namespace {
 
-template<typename T>
-std::string GetAttributeName(T type);
+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<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 <> 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 ExportCounterMetric(const android::CounterMetric<T> &counter,
+                         PersistableBundle *metrics) {
+    if (!metrics) {
+        ALOGE("metrics was unexpectedly null.");
+        return;
+    }
+    std::string success_count_name = counter.metric_name() + ".ok.count";
+    std::string error_count_name = counter.metric_name() + ".error.count";
+    std::vector<int64_t> status_values;
+    counter.ExportValues(
+        [&](const android::status_t status, const int64_t value) {
+            if (status == android::OK) {
+                metrics->putLong(android::String16(success_count_name.c_str()),
+                                 value);
+            } else {
+                int64_t total_errors(0);
+                metrics->getLong(android::String16(error_count_name.c_str()),
+                                 &total_errors);
+                metrics->putLong(android::String16(error_count_name.c_str()),
+                                 total_errors + value);
+                status_values.push_back(status);
+            }
+        });
+    if (!status_values.empty()) {
+        std::string error_list_name = counter.metric_name() + ".error.list";
+        metrics->putLongVector(android::String16(error_list_name.c_str()),
+                               status_values);
+    }
 }
 
-template<typename T>
+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);
-      });
+    const android::CounterMetric<T> &counter, PersistableBundle *metrics) {
+    if (!metrics) {
+        ALOGE("metrics was unexpectedly null.");
+        return;
+    }
+    counter.ExportValues([&](const T &attribute, const int64_t value) {
+        std::string name = counter.metric_name() + "." +
+                           GetAttributeName(attribute) + ".count";
+        metrics->putLong(android::String16(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(),
+template <typename T>
+void ExportEventMetric(const android::EventMetric<T> &event,
+                       PersistableBundle *metrics) {
+    if (!metrics) {
+        ALOGE("metrics 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";
+    std::vector<int64_t> status_values;
+    event.ExportValues([&](const android::status_t &status,
+                           const android::EventStatistics &value) {
+        if (status == android::OK) {
+            metrics->putLong(android::String16(success_count_name.c_str()),
+                             value.count);
+            metrics->putLong(android::String16(timing_name.c_str()),
+                             value.mean);
+        } else {
+            int64_t total_errors(0);
+            metrics->getLong(android::String16(error_count_name.c_str()),
+                             &total_errors);
+            metrics->putLong(android::String16(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.
-          }
-      });
+            status_values.push_back(status);
+        }
+    });
+    if (!status_values.empty()) {
+        std::string error_list_name = event.metric_name() + ".error.list";
+        metrics->putLongVector(android::String16(error_list_name.c_str()),
+                               status_values);
+    }
 }
 
-}  // namespace anonymous
+void ExportSessionLifespans(
+    const std::map<std::string, std::pair<int64_t, int64_t>> &mSessionLifespans,
+    PersistableBundle *metrics) {
+    if (!metrics) {
+        ALOGE("metrics was unexpectedly null.");
+        return;
+    }
+
+    if (mSessionLifespans.empty()) {
+        return;
+    }
+
+    PersistableBundle startTimesBundle;
+    PersistableBundle endTimesBundle;
+    for (auto it = mSessionLifespans.begin(); it != mSessionLifespans.end();
+         it++) {
+        String16 key(it->first.c_str(), it->first.size());
+        startTimesBundle.putLong(key, it->second.first);
+        endTimesBundle.putLong(key, it->second.second);
+    }
+    metrics->putPersistableBundle(
+        android::String16("drm.mediadrm.session_start_times_ms"),
+        startTimesBundle);
+    metrics->putPersistableBundle(
+        android::String16("drm.mediadrm.session_end_times_ms"), endTimesBundle);
+}
+
+std::string ToHexString(const android::Vector<uint8_t> &sessionId) {
+    std::ostringstream out;
+    out << std::hex << std::setfill('0');
+    for (size_t i = 0; i < sessionId.size(); i++) {
+        out << std::setw(2) << (int)(sessionId[i]);
+    }
+    return out.str();
+}
+
+} // namespace
 
 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"),
+      mGetKeyRequestTimeUs("drm.mediadrm.get_key_request", "status"),
+      mProvideKeyResponseTimeUs("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"),
+      mKeyStatusChangeCounter("drm.mediadrm.key_status_change",
+                              "key_status_type"),
       mEventCounter("drm.mediadrm.event", "event_type"),
-      mGetDeviceUniqueIdCounter(
-          "drm.mediadrm.get_device_unique_id", "status") {
+      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);
+void MediaDrmMetrics::SetSessionStart(
+    const android::Vector<uint8_t> &sessionId) {
+    std::string sessionIdHex = ToHexString(sessionId);
+    mSessionLifespans[sessionIdHex] =
+        std::make_pair(GetCurrentTimeMs(), (int64_t)0);
 }
 
-}  // namespace android
+void MediaDrmMetrics::SetSessionEnd(const android::Vector<uint8_t> &sessionId) {
+    std::string sessionIdHex = ToHexString(sessionId);
+    int64_t endTimeMs = GetCurrentTimeMs();
+    if (mSessionLifespans.find(sessionIdHex) != mSessionLifespans.end()) {
+        mSessionLifespans[sessionIdHex] =
+            std::make_pair(mSessionLifespans[sessionIdHex].first, endTimeMs);
+    } else {
+        mSessionLifespans[sessionIdHex] = std::make_pair((int64_t)0, endTimeMs);
+    }
+}
+
+void MediaDrmMetrics::Export(PersistableBundle *metrics) {
+    if (!metrics) {
+        ALOGE("metrics was unexpectedly null.");
+        return;
+    }
+    ExportCounterMetric(mOpenSessionCounter, metrics);
+    ExportCounterMetric(mCloseSessionCounter, metrics);
+    ExportEventMetric(mGetKeyRequestTimeUs, metrics);
+    ExportEventMetric(mProvideKeyResponseTimeUs, metrics);
+    ExportCounterMetric(mGetProvisionRequestCounter, metrics);
+    ExportCounterMetric(mProvideProvisionResponseCounter, metrics);
+    ExportCounterMetricWithAttributeNames(mKeyStatusChangeCounter, metrics);
+    ExportCounterMetricWithAttributeNames(mEventCounter, metrics);
+    ExportCounterMetric(mGetDeviceUniqueIdCounter, metrics);
+    ExportSessionLifespans(mSessionLifespans, metrics);
+}
+
+status_t MediaDrmMetrics::GetSerializedMetrics(std::string *serializedMetrics) {
+
+    if (!serializedMetrics) {
+        ALOGE("serializedMetrics was unexpectedly null.");
+        return UNEXPECTED_NULL;
+    }
+
+    DrmFrameworkMetrics metrics;
+
+    mOpenSessionCounter.ExportValues(
+        [&](const android::status_t status, const int64_t value) {
+            DrmFrameworkMetrics::Counter *counter =
+                metrics.add_open_session_counter();
+            counter->set_count(value);
+            counter->mutable_attributes()->set_error_code(status);
+        });
+
+    mCloseSessionCounter.ExportValues(
+        [&](const android::status_t status, const int64_t value) {
+            DrmFrameworkMetrics::Counter *counter =
+                metrics.add_close_session_counter();
+            counter->set_count(value);
+            counter->mutable_attributes()->set_error_code(status);
+        });
+
+    mGetProvisionRequestCounter.ExportValues(
+        [&](const android::status_t status, const int64_t value) {
+            DrmFrameworkMetrics::Counter *counter =
+                metrics.add_get_provisioning_request_counter();
+            counter->set_count(value);
+            counter->mutable_attributes()->set_error_code(status);
+        });
+
+    mProvideProvisionResponseCounter.ExportValues(
+        [&](const android::status_t status, const int64_t value) {
+            DrmFrameworkMetrics::Counter *counter =
+                metrics.add_provide_provisioning_response_counter();
+            counter->set_count(value);
+            counter->mutable_attributes()->set_error_code(status);
+        });
+
+    mKeyStatusChangeCounter.ExportValues(
+        [&](const KeyStatusType key_status_type, const int64_t value) {
+            DrmFrameworkMetrics::Counter *counter =
+                metrics.add_key_status_change_counter();
+            counter->set_count(value);
+            counter->mutable_attributes()->set_key_status_type(
+                (uint32_t)key_status_type);
+        });
+
+    mEventCounter.ExportValues(
+        [&](const EventType event_type, const int64_t value) {
+            DrmFrameworkMetrics::Counter *counter =
+                metrics.add_event_callback_counter();
+            counter->set_count(value);
+            counter->mutable_attributes()->set_event_type((uint32_t)event_type);
+        });
+
+    mGetDeviceUniqueIdCounter.ExportValues(
+        [&](const status_t status, const int64_t value) {
+            DrmFrameworkMetrics::Counter *counter =
+                metrics.add_get_device_unique_id_counter();
+            counter->set_count(value);
+            counter->mutable_attributes()->set_error_code(status);
+        });
+
+    mGetKeyRequestTimeUs.ExportValues(
+        [&](const status_t status, const EventStatistics &stats) {
+            DrmFrameworkMetrics::DistributionMetric *metric =
+                metrics.add_get_key_request_time_us();
+            metric->set_min(stats.min);
+            metric->set_max(stats.max);
+            metric->set_mean(stats.mean);
+            metric->set_operation_count(stats.count);
+            metric->set_variance(stats.sum_squared_deviation / stats.count);
+            metric->mutable_attributes()->set_error_code(status);
+        });
+
+    mProvideKeyResponseTimeUs.ExportValues(
+        [&](const status_t status, const EventStatistics &stats) {
+            DrmFrameworkMetrics::DistributionMetric *metric =
+                metrics.add_provide_key_response_time_us();
+            metric->set_min(stats.min);
+            metric->set_max(stats.max);
+            metric->set_mean(stats.mean);
+            metric->set_operation_count(stats.count);
+            metric->set_variance(stats.sum_squared_deviation / stats.count);
+            metric->mutable_attributes()->set_error_code(status);
+        });
+
+    for (const auto &sessionLifespan : mSessionLifespans) {
+        auto *map = metrics.mutable_session_lifetimes();
+
+        (*map)[sessionLifespan.first].set_start_time_ms(
+            sessionLifespan.second.first);
+        (*map)[sessionLifespan.first].set_end_time_ms(
+            sessionLifespan.second.second);
+    }
+
+    if (!metrics.SerializeToString(serializedMetrics)) {
+        ALOGE("Failed to serialize metrics.");
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+int64_t MediaDrmMetrics::GetCurrentTimeMs() {
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    return ((int64_t)tv.tv_sec * 1000) + ((int64_t)tv.tv_usec / 1000);
+}
+
+} // namespace android
diff --git a/drm/libmediadrm/IDrm.cpp b/drm/libmediadrm/IDrm.cpp
index 63a9562..22e4e6c 100644
--- a/drm/libmediadrm/IDrm.cpp
+++ b/drm/libmediadrm/IDrm.cpp
@@ -60,7 +60,6 @@
     GET_HDCP_LEVELS,
     GET_NUMBER_OF_SESSIONS,
     GET_SECURITY_LEVEL,
-    SET_SECURITY_LEVEL,
     REMOVE_SECURE_STOP,
     GET_SECURE_STOP_IDS
 };
@@ -121,9 +120,11 @@
         return reply.readInt32();
     }
 
-    virtual status_t openSession(Vector<uint8_t> &sessionId) {
+    virtual status_t openSession(DrmPlugin::SecurityLevel securityLevel,
+            Vector<uint8_t> &sessionId) {
         Parcel data, reply;
         data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
+        data.writeInt32(securityLevel);
 
         status_t status = remote()->transact(OPEN_SESSION, data, &reply);
         if (status != OK) {
@@ -448,23 +449,6 @@
         return reply.readInt32();
     }
 
-    virtual status_t setSecurityLevel(Vector<uint8_t> const &sessionId,
-            const DrmPlugin::SecurityLevel& level) {
-        Parcel data, reply;
-
-        data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
-
-        writeVector(data, sessionId);
-        data.writeInt32(static_cast<uint32_t>(level));
-
-        status_t status = remote()->transact(SET_SECURITY_LEVEL, data, &reply);
-        if (status != OK) {
-            return status;
-        }
-
-        return reply.readInt32();
-    }
-
     virtual status_t getPropertyByteArray(String8 const &name, Vector<uint8_t> &value) const {
         Parcel data, reply;
         data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
@@ -508,7 +492,10 @@
         return reply.readInt32();
     }
 
-    virtual status_t getMetrics(MediaAnalyticsItem *item) {
+    virtual status_t getMetrics(os::PersistableBundle *metrics) {
+        if (metrics == NULL) {
+            return BAD_VALUE;
+        }
         Parcel data, reply;
         data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
 
@@ -516,9 +503,23 @@
         if (status != OK) {
             return status;
         }
+        // The reply data is ordered as
+        // 1) 32 bit integer reply followed by
+        // 2) Serialized PersistableBundle containing metrics.
+        status_t reply_status;
+        if (reply.readInt32(&reply_status) != OK
+           || reply_status != OK) {
+          ALOGE("Failed to read getMetrics response code from parcel. %d",
+                reply_status);
+          return reply_status;
+        }
 
-        item->readFromParcel(reply);
-        return reply.readInt32();
+        status = metrics->readFromParcel(&reply);
+        if (status != OK) {
+            ALOGE("Failed to read metrics from parcel. %d", status);
+            return status;
+        }
+        return reply_status;
     }
 
     virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
@@ -742,8 +743,10 @@
         case OPEN_SESSION:
         {
             CHECK_INTERFACE(IDrm, data, reply);
+            DrmPlugin::SecurityLevel level =
+                    static_cast<DrmPlugin::SecurityLevel>(data.readInt32());
             Vector<uint8_t> sessionId;
-            status_t result = openSession(sessionId);
+            status_t result = openSession(level, sessionId);
             writeVector(reply, sessionId);
             reply->writeInt32(result);
             return OK;
@@ -977,18 +980,6 @@
             return OK;
         }
 
-        case SET_SECURITY_LEVEL:
-        {
-            CHECK_INTERFACE(IDrm, data, reply);
-            Vector<uint8_t> sessionId;
-            readVector(data, sessionId);
-            DrmPlugin::SecurityLevel level =
-                    static_cast<DrmPlugin::SecurityLevel>(data.readInt32());
-            status_t result = setSecurityLevel(sessionId, level);
-            reply->writeInt32(result);
-            return OK;
-        }
-
         case GET_PROPERTY_STRING:
         {
             CHECK_INTERFACE(IDrm, data, reply);
@@ -1034,11 +1025,18 @@
         {
             CHECK_INTERFACE(IDrm, data, reply);
 
-            MediaAnalyticsItem item;
-            status_t result = getMetrics(&item);
-            item.writeToParcel(reply);
-            reply->writeInt32(result);
-            return OK;
+            os::PersistableBundle metrics;
+            status_t result = getMetrics(&metrics);
+            // The reply data is ordered as
+            // 1) 32 bit integer reply followed by
+            // 2) Serialized PersistableBundle containing metrics.
+            // Only write the metrics if the getMetrics result was
+            // OK and we successfully added the status to reply.
+            status_t parcel_result = reply->writeInt32(result);
+            if (result == OK && parcel_result == OK) {
+                parcel_result = metrics.writeToParcel(reply);
+            }
+            return parcel_result;
         }
 
         case SET_CIPHER_ALGORITHM:
diff --git a/drm/libmediadrm/PluginMetricsReporting.cpp b/drm/libmediadrm/PluginMetricsReporting.cpp
index 26c8427..6c97f2b 100644
--- a/drm/libmediadrm/PluginMetricsReporting.cpp
+++ b/drm/libmediadrm/PluginMetricsReporting.cpp
@@ -23,7 +23,7 @@
 
 #include <media/MediaAnalyticsItem.h>
 
-#include "protos/plugin_metrics.pb.h"
+#include "protos/metrics.pb.h"
 
 namespace android {
 
diff --git a/drm/libmediadrm/protos/metrics.proto b/drm/libmediadrm/protos/metrics.proto
new file mode 100644
index 0000000..aa26f5f
--- /dev/null
+++ b/drm/libmediadrm/protos/metrics.proto
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 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.
+ */
+
+syntax = "proto2";
+
+package android.drm_metrics;
+
+// The MetricsGroup is a collection of metric name/value pair instances
+// that can be serialized and provided to a caller.
+message MetricsGroup {
+  message Metric {
+    message MetricValue {
+      // Exactly one of the following values must be set.
+      optional int64 int_value = 1;
+      optional double double_value = 2;
+      optional string string_value = 3;
+    }
+
+    // The name of the metric. Must be valid UTF-8. Required.
+    optional string name = 1;
+
+    // The value of the metric. Required.
+    optional MetricValue value = 2;
+  }
+
+  // The list of name/value pairs of metrics.
+  repeated Metric metric = 1;
+
+  // Allow multiple sub groups of metrics.
+  repeated MetricsGroup metric_sub_group = 2;
+
+  // Name of the application package associated with the metrics.
+  optional string app_package_name = 3;
+}
+
+// This message contains the specific metrics captured by DrmMetrics. It is
+// used for serializing and logging metrics.
+// next id: 11.
+message DrmFrameworkMetrics {
+  // TODO: Consider using extensions.
+
+  // Attributes are associated with a recorded value. E.g. A counter may
+  // represent a count of an operation returning a specific error code. The
+  // error code will be an attribute.
+  message Attributes {
+    // Reserved for compatibility with logging proto.
+    reserved 2 to 13;
+
+    // A general purpose error code where 0 means OK.
+    optional int32 error_code = 1;
+
+    // Defined at ::android::hardware::drm::V1_0::KeyStatusType;
+    optional uint32 key_status_type = 14;
+
+    // Defined at ::android::hardware::drm::V1_0::EventType;
+    optional uint32 event_type = 15;
+  }
+
+  // The Counter message is used to store a count value with an associated
+  // Attribute.
+  message Counter {
+    optional int64 count = 1;
+    // Represents the attributes associated with this counter instance.
+    optional Attributes attributes = 2;
+  }
+
+  // The DistributionMetric is meant to capture the moments of a normally
+  // distributed (or approximately normal) value.
+  message DistributionMetric {
+    optional double min = 1;
+    optional double max = 2;
+    optional double mean = 3;
+    optional double variance = 4;
+    optional double operation_count = 5;
+
+    // Represents the attributes assocated with this distribution metric
+    // instance.
+    optional Attributes attributes = 6;
+  }
+
+  message SessionLifetime {
+    // Start time of the session in milliseconds since epoch.
+    optional int64 start_time_ms = 1;
+    // End time of the session in milliseconds since epoch.
+    optional int64 end_time_ms = 2;
+  }
+
+  // The count of open session operations. Each instance has a specific error
+  // code associated with it.
+  repeated Counter open_session_counter = 1;
+
+  // The count of close session operations. Each instance has a specific error
+  // code associated with it.
+  repeated Counter close_session_counter = 2;
+
+  // Count and execution time of getKeyRequest calls.
+  repeated DistributionMetric get_key_request_time_us = 3;
+
+  // Count and execution time of provideKeyResponse calls.
+  repeated DistributionMetric provide_key_response_time_us = 4;
+
+  // Count of getProvisionRequest calls.
+  repeated Counter get_provisioning_request_counter = 5;
+
+  // Count of provideProvisionResponse calls.
+  repeated Counter provide_provisioning_response_counter = 6;
+
+  // Count of key status events broken out by status type.
+  repeated Counter key_status_change_counter = 7;
+
+  // Count of events broken out by event type
+  repeated Counter event_callback_counter = 8;
+
+  // Count getPropertyByteArray calls to retrieve the device unique id.
+  repeated Counter get_device_unique_id_counter = 9;
+
+  // Session ids to lifetime (start and end time) map.
+  // Session ids are strings of hex-encoded byte arrays.
+  map<string, SessionLifetime> session_lifetimes = 10;
+}
+
diff --git a/drm/libmediadrm/protos/plugin_metrics.proto b/drm/libmediadrm/protos/plugin_metrics.proto
deleted file mode 100644
index 7e3bcf5..0000000
--- a/drm/libmediadrm/protos/plugin_metrics.proto
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-syntax = "proto2";
-
-package android.drm_metrics;
-
-// need this if we are using libprotobuf-cpp-2.3.0-lite
-option optimize_for = LITE_RUNTIME;
-
-// The MetricsGroup is a collection of metric name/value pair instances
-// that can be serialized and provided to a caller.
-message MetricsGroup {
-  message Metric {
-    message MetricValue {
-      // Exactly one of the following values must be set.
-      optional int64 int_value = 1;
-      optional double double_value = 2;
-      optional string string_value = 3;
-    }
-
-    // The name of the metric. Must be valid UTF-8. Required.
-    optional string name = 1;
-
-    // The value of the metric. Required.
-    optional MetricValue value = 2;
-  }
-
-  // The list of name/value pairs of metrics.
-  repeated Metric metric = 1;
-
-  // Allow multiple sub groups of metrics.
-  repeated MetricsGroup metric_sub_group = 2;
-
-  // Name of the application package associated with the metrics.
-  optional string app_package_name = 3;
-}
diff --git a/drm/libmediadrm/tests/Android.bp b/drm/libmediadrm/tests/Android.bp
index fdc982d..670d3b9 100644
--- a/drm/libmediadrm/tests/Android.bp
+++ b/drm/libmediadrm/tests/Android.bp
@@ -16,16 +16,23 @@
     srcs: ["DrmMetrics_test.cpp"],
     shared_libs: [
       "android.hardware.drm@1.0",
+      "libbinder",
       "liblog",
-      "libmediadrm",
+      "libmediadrmmetrics_full",
       "libmediametrics",
+      "libprotobuf-cpp-full",
       "libutils",
     ],
-    include_dirs: ["frameworks/av/include/media"],
-    cflags: [
-      "-Werror",
-      "-Wall",
+    static_libs: ["libgmock"],
+    include_dirs: [
+      "frameworks/av/include/media",
     ],
+    cflags: [
+        // Suppress unused parameter and no error options. These cause problems
+        // when using the map type in a proto definition.
+        "-Wno-unused-parameter",
+        "-Wno-error",
+    ]
 }
 
 cc_test {
diff --git a/drm/libmediadrm/tests/DrmMetrics_test.cpp b/drm/libmediadrm/tests/DrmMetrics_test.cpp
index d1948b4..fe762c9 100644
--- a/drm/libmediadrm/tests/DrmMetrics_test.cpp
+++ b/drm/libmediadrm/tests/DrmMetrics_test.cpp
@@ -14,27 +14,48 @@
  * limitations under the License.
  */
 
-#include <gtest/gtest.h>
-
+#define LOG_TAG "DrmMetricsTest"
 #include "DrmMetrics.h"
 
+#include <binder/PersistableBundle.h>
+#include <google/protobuf/text_format.h>
+#include <google/protobuf/util/message_differencer.h>
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+#include "protos/metrics.pb.h"
+
+using ::android::drm_metrics::DrmFrameworkMetrics;
 using ::android::hardware::drm::V1_0::EventType;
 using ::android::hardware::drm::V1_0::KeyStatusType;
+using ::android::os::PersistableBundle;
+using ::google::protobuf::util::MessageDifferencer;
+using ::google::protobuf::TextFormat;
 
 namespace android {
 
 /**
  * Unit tests for the MediaDrmMetrics class.
  */
-class MediaDrmMetricsTest : public ::testing::Test {
+class MediaDrmMetricsTest : public ::testing::Test {};
+
+/**
+ * This derived class mocks the clock for testing purposes.
+ */
+class FakeMediaDrmMetrics : public MediaDrmMetrics {
+ public:
+  FakeMediaDrmMetrics() : MediaDrmMetrics(), time_(0) {};
+
+  int64_t GetCurrentTimeMs() { return time_++; }
+  int64_t time_;
 };
 
 TEST_F(MediaDrmMetricsTest, EmptySuccess) {
   MediaDrmMetrics metrics;
-  MediaAnalyticsItem item;
+  PersistableBundle bundle;
 
-  metrics.Export(&item);
-  EXPECT_EQ(0, item.count());
+  metrics.Export(&bundle);
+  EXPECT_TRUE(bundle.empty());
 }
 
 TEST_F(MediaDrmMetricsTest, AllValuesSuccessCounts) {
@@ -44,9 +65,9 @@
   metrics.mCloseSessionCounter.Increment(OK);
 
   {
-    EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTiming);
+    EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTimeUs);
     EventTimer<status_t> provide_key_response_timer(
-        &metrics.mProvideKeyResponseTiming);
+        &metrics.mProvideKeyResponseTimeUs);
     get_key_request_timer.SetAttribute(OK);
     provide_key_response_timer.SetAttribute(OK);
   }
@@ -58,10 +79,10 @@
   metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::USABLE);
   metrics.mEventCounter.Increment(EventType::PROVISION_REQUIRED);
 
-  MediaAnalyticsItem item;
+  PersistableBundle bundle;
 
-  metrics.Export(&item);
-  EXPECT_EQ(11, item.count());
+  metrics.Export(&bundle);
+  EXPECT_EQ(11U, bundle.size());
 
   // Verify the list of pairs of int64 metrics.
   std::vector<std::pair<std::string, int64_t>> expected_values = {
@@ -75,24 +96,24 @@
       { "drm.mediadrm.event.PROVISION_REQUIRED.count", 1 },
       { "drm.mediadrm.get_device_unique_id.ok.count", 1 }};
   for (const auto& expected_pair : expected_values) {
+    String16 key(expected_pair.first.c_str());
     int64_t value = -1;
-    EXPECT_TRUE(item.getInt64(expected_pair.first.c_str(), &value))
-        << "Failed to get " << expected_pair.first;
+    EXPECT_TRUE(bundle.getLong(key, &value))
+        << "Unexpected error retrieviing key: " << key;
     EXPECT_EQ(expected_pair.second, value)
-        << "Unexpected value for " << expected_pair.first;
+        << "Unexpected value for " << expected_pair.first << ". " << value;
   }
 
   // Validate timing values exist.
+  String16 get_key_request_key(
+      "drm.mediadrm.get_key_request.ok.average_time_micros");
+  String16 provide_key_response_key(
+      "drm.mediadrm.provide_key_response.ok.average_time_micros");
   int64_t value = -1;
-  EXPECT_TRUE(
-      item.getInt64("drm.mediadrm.get_key_request.ok.average_time_micros",
-                    &value));
+  EXPECT_TRUE(bundle.getLong(get_key_request_key, &value));
   EXPECT_GE(value, 0);
-
   value = -1;
-  EXPECT_TRUE(
-      item.getInt64("drm.mediadrm.provide_key_response.ok.average_time_micros",
-                    &value));
+  EXPECT_TRUE(bundle.getLong(provide_key_response_key, &value));
   EXPECT_GE(value, 0);
 }
 
@@ -107,9 +128,9 @@
 
   for (status_t s : {OK, UNEXPECTED_NULL}) {
     {
-      EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTiming);
+      EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTimeUs);
       EventTimer<status_t> provide_key_response_timer(
-          &metrics.mProvideKeyResponseTiming);
+          &metrics.mProvideKeyResponseTimeUs);
       get_key_request_timer.SetAttribute(s);
       provide_key_response_timer.SetAttribute(s);
     }
@@ -133,10 +154,23 @@
   metrics.mEventCounter.Increment(EventType::VENDOR_DEFINED);
   metrics.mEventCounter.Increment(EventType::SESSION_RECLAIMED);
 
-  MediaAnalyticsItem item;
+  android::Vector<uint8_t> sessionId1;
+  sessionId1.push_back(1);
+  sessionId1.push_back(2);
+  android::Vector<uint8_t> sessionId2;
+  sessionId2.push_back(3);
+  sessionId2.push_back(4);
+  String16 hexSessionId1("0102");
+  String16 hexSessionId2("0304");
 
-  metrics.Export(&item);
-  EXPECT_EQ(26, item.count());
+  metrics.SetSessionStart(sessionId1);
+  metrics.SetSessionStart(sessionId2);
+  metrics.SetSessionEnd(sessionId2);
+  metrics.SetSessionEnd(sessionId1);
+
+  PersistableBundle bundle;
+  metrics.Export(&bundle);
+  EXPECT_EQ(35U, bundle.size());
 
   // Verify the list of pairs of int64 metrics.
   std::vector<std::pair<std::string, int64_t>> expected_values = {
@@ -165,25 +199,217 @@
       { "drm.mediadrm.event.VENDOR_DEFINED.count", 1 },
       { "drm.mediadrm.event.SESSION_RECLAIMED.count", 1 }};
   for (const auto& expected_pair : expected_values) {
+    String16 key(expected_pair.first.c_str());
     int64_t value = -1;
-    EXPECT_TRUE(item.getInt64(expected_pair.first.c_str(), &value))
-        << "Failed to get " << expected_pair.first;
+    EXPECT_TRUE(bundle.getLong(key, &value))
+        << "Unexpected error retrieviing key: " << key;
     EXPECT_EQ(expected_pair.second, value)
-        << "Unexpected value for " << expected_pair.first;
+        << "Unexpected value for " << expected_pair.first << ". " << value;
+  }
+
+  // Verify the error lists
+  std::vector<std::pair<std::string, std::vector<int64_t>>> expected_vector_values = {
+      { "drm.mediadrm.close_session.error.list", { UNEXPECTED_NULL } },
+      { "drm.mediadrm.get_device_unique_id.error.list", { UNEXPECTED_NULL } },
+      { "drm.mediadrm.get_key_request.error.list", { UNEXPECTED_NULL } },
+      { "drm.mediadrm.get_provision_request.error.list", { UNEXPECTED_NULL } },
+      { "drm.mediadrm.open_session.error.list", { UNEXPECTED_NULL } },
+      { "drm.mediadrm.provide_key_response.error.list", { UNEXPECTED_NULL } },
+      { "drm.mediadrm.provide_provision_response.error.list", { UNEXPECTED_NULL } }};
+  for (const auto& expected_pair : expected_vector_values) {
+    String16 key(expected_pair.first.c_str());
+    std::vector<int64_t> values;
+    EXPECT_TRUE(bundle.getLongVector(key, &values))
+        << "Unexpected error retrieviing key: " << key;
+    for (auto expected : expected_pair.second) {
+      EXPECT_TRUE(std::find(values.begin(), values.end(), expected) != values.end())
+          << "Could not find " << expected << " for key " << expected_pair.first;
+    }
+  }
+
+  // Verify the lifespans
+  PersistableBundle start_times;
+  PersistableBundle end_times;
+  String16 start_time_key("drm.mediadrm.session_start_times_ms");
+  String16 end_time_key("drm.mediadrm.session_end_times_ms");
+  ASSERT_TRUE(bundle.getPersistableBundle(start_time_key, &start_times));
+  ASSERT_TRUE(bundle.getPersistableBundle(end_time_key, &end_times));
+  EXPECT_EQ(2U, start_times.size());
+  EXPECT_EQ(2U, end_times.size());
+  int64_t start_time, end_time;
+  for (const auto& sid : { hexSessionId1, hexSessionId2 }) {
+    start_time = -1;
+    end_time = -1;
+    EXPECT_TRUE(start_times.getLong(sid, &start_time));
+    EXPECT_TRUE(end_times.getLong(sid, &end_time));
+    EXPECT_GT(start_time, 0);
+    EXPECT_GE(end_time, start_time);
   }
 
   // Validate timing values exist.
+  String16 get_key_request_key(
+      "drm.mediadrm.get_key_request.ok.average_time_micros");
+  String16 provide_key_response_key(
+      "drm.mediadrm.provide_key_response.ok.average_time_micros");
   int64_t value = -1;
-  EXPECT_TRUE(
-      item.getInt64("drm.mediadrm.get_key_request.ok.average_time_micros",
-                    &value));
+  EXPECT_TRUE(bundle.getLong(get_key_request_key, &value));
   EXPECT_GE(value, 0);
-
   value = -1;
-  EXPECT_TRUE(
-      item.getInt64("drm.mediadrm.provide_key_response.ok.average_time_micros",
-                    &value));
+  EXPECT_TRUE(bundle.getLong(provide_key_response_key, &value));
   EXPECT_GE(value, 0);
 }
 
+
+TEST_F(MediaDrmMetricsTest, CounterValuesProtoSerialization) {
+  MediaDrmMetrics metrics;
+
+  metrics.mOpenSessionCounter.Increment(OK);
+  metrics.mOpenSessionCounter.Increment(UNEXPECTED_NULL);
+  metrics.mCloseSessionCounter.Increment(OK);
+  metrics.mCloseSessionCounter.Increment(UNEXPECTED_NULL);
+
+  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);
+
+  std::string serializedMetrics;
+  ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics));
+
+  DrmFrameworkMetrics metricsProto;
+  ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics));
+
+  std::string expectedMetrics =
+      "open_session_counter { count: 1 attributes { error_code: -0x7FFFFFF8 } } "
+      "open_session_counter { count: 1 attributes { error_code: 0 } } "
+      "close_session_counter { count: 1 attributes { error_code: -0x7FFFFFF8 } } "
+      "close_session_counter { count: 1 attributes { error_code: 0 } } "
+      "get_provisioning_request_counter { count: 1 attributes { error_code: -0x7FFFFFF8 } } "
+      "get_provisioning_request_counter { count: 1 attributes { error_code: 0 } } "
+      "provide_provisioning_response_counter { count: 1 attributes { error_code: -0x7ffffff8 } } "
+      "provide_provisioning_response_counter { count: 1 attributes { error_code: 0 } } "
+      "get_device_unique_id_counter { count: 1 attributes { error_code: -0x7ffffff8 } } "
+      "get_device_unique_id_counter { count: 1 attributes { error_code: 0 } } "
+      "key_status_change_counter { count: 1 attributes { key_status_type: 0 } } "
+      "key_status_change_counter { count: 1 attributes { key_status_type: 1 } } "
+      "key_status_change_counter { count: 1 attributes { key_status_type: 2 } } "
+      "key_status_change_counter { count: 1 attributes { key_status_type: 3 } } "
+      "key_status_change_counter { count: 1 attributes { key_status_type: 4 } } "
+      "event_callback_counter { count: 1 attributes { event_type: 0 } } "
+      "event_callback_counter { count: 1 attributes { event_type: 1 } } "
+      "event_callback_counter { count: 1 attributes { event_type: 2 } } "
+      "event_callback_counter { count: 1 attributes { event_type: 3 } } "
+      "event_callback_counter { count: 1 attributes { event_type: 4 } } ";
+
+  DrmFrameworkMetrics expectedMetricsProto;
+  ASSERT_TRUE(TextFormat::MergeFromString(expectedMetrics, &expectedMetricsProto));
+
+  std::string diffString;
+  MessageDifferencer differ;
+  differ.ReportDifferencesToString(&diffString);
+  ASSERT_TRUE(differ.Compare(expectedMetricsProto, metricsProto))
+      << diffString;
+}
+
+TEST_F(MediaDrmMetricsTest, TimeMetricsProtoSerialization) {
+  MediaDrmMetrics metrics;
+
+  for (status_t s : {OK, UNEXPECTED_NULL}) {
+    double time = 0;
+    for (int i = 0; i < 5; i++) {
+      time += 1.0;
+      metrics.mGetKeyRequestTimeUs.Record(time, s);
+      metrics.mProvideKeyResponseTimeUs.Record(time, s);
+    }
+  }
+
+  std::string serializedMetrics;
+  ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics));
+
+  DrmFrameworkMetrics metricsProto;
+  ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics));
+
+  std::string expectedMetrics =
+      "get_key_request_timing { "
+      "  min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
+      "  attributes { error_code: -0x7FFFFFF8 } "
+      "} "
+      "get_key_request_timing { "
+      "  min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
+      "  attributes { error_code: 0 } "
+      "} "
+      "provide_key_response_timing { "
+      "  min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
+      "  attributes { error_code: -0x7FFFFFF8 } "
+      "} "
+      "provide_key_response_timing { "
+      "  min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
+      "  attributes { error_code: 0 } "
+      "} ";
+
+  DrmFrameworkMetrics expectedMetricsProto;
+  ASSERT_TRUE(TextFormat::MergeFromString(expectedMetrics, &expectedMetricsProto));
+
+  std::string diffString;
+  MessageDifferencer differ;
+  differ.ReportDifferencesToString(&diffString);
+  ASSERT_TRUE(differ.Compare(expectedMetricsProto, metricsProto))
+      << diffString;
+}
+
+TEST_F(MediaDrmMetricsTest, SessionLifetimeProtoSerialization) {
+  // Use the fake so the clock is predictable;
+  FakeMediaDrmMetrics metrics;
+
+  android::Vector<uint8_t> sessionId1;
+  sessionId1.push_back(1);
+  sessionId1.push_back(2);
+  android::Vector<uint8_t> sessionId2;
+  sessionId2.push_back(3);
+  sessionId2.push_back(4);
+
+  metrics.SetSessionStart(sessionId1);
+  metrics.SetSessionStart(sessionId2);
+  metrics.SetSessionEnd(sessionId2);
+  metrics.SetSessionEnd(sessionId1);
+
+  std::string serializedMetrics;
+  ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics));
+
+  DrmFrameworkMetrics metricsProto;
+  ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics));
+
+  std::string expectedMetrics =
+      "session_lifetimes: { "
+      "  key: '0102' "
+      "  value { start_time_ms: 0 end_time_ms: 3 } "
+      "} "
+      "session_lifetimes: { "
+      "  key: '0304' "
+      "  value { start_time_ms: 1 end_time_ms: 2 } "
+      "} ";
+
+  DrmFrameworkMetrics expectedMetricsProto;
+  ASSERT_TRUE(TextFormat::MergeFromString(expectedMetrics, &expectedMetricsProto));
+
+  std::string diffString;
+  MessageDifferencer differ;
+  differ.ReportDifferencesToString(&diffString);
+  ASSERT_TRUE(differ.Compare(expectedMetricsProto, metricsProto))
+      << diffString;
+}
+
 }  // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
index bf97db9..6b0201a 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
@@ -73,12 +73,24 @@
     mByteArrayProperties[kMetricsKey] = valueVector;
 }
 
+
 Return<void> DrmPlugin::openSession(openSession_cb _hidl_cb) {
     sp<Session> session = mSessionLibrary->createSession();
     std::vector<uint8_t> sessionId = session->sessionId();
 
-    setSecurityLevel(sessionId, SecurityLevel::SW_SECURE_CRYPTO);
-    _hidl_cb(Status::OK, toHidlVec(sessionId));
+    Status status = setSecurityLevel(sessionId, SecurityLevel::SW_SECURE_CRYPTO);
+    _hidl_cb(status, toHidlVec(sessionId));
+    mOpenSessionOkCount++;
+    return Void();
+}
+
+Return<void> DrmPlugin::openSession_1_1(SecurityLevel securityLevel,
+        openSession_1_1_cb _hidl_cb) {
+    sp<Session> session = mSessionLibrary->createSession();
+    std::vector<uint8_t> sessionId = session->sessionId();
+
+    Status status = setSecurityLevel(sessionId, securityLevel);
+    _hidl_cb(status, toHidlVec(sessionId));
     mOpenSessionOkCount++;
     return Void();
 }
@@ -98,6 +110,38 @@
     return Status::ERROR_DRM_SESSION_NOT_OPENED;
 }
 
+Status DrmPlugin::getKeyRequestCommon(const hidl_vec<uint8_t>& scope,
+        const hidl_vec<uint8_t>& initData,
+        const hidl_string& mimeType,
+        KeyType keyType,
+        const hidl_vec<KeyValue>& optionalParameters,
+        std::vector<uint8_t> *request,
+        KeyRequestType *keyRequestType,
+        std::string *defaultUrl) {
+        UNUSED(optionalParameters);
+
+    *defaultUrl = "";
+    *keyRequestType = KeyRequestType::UNKNOWN;
+    *request = std::vector<uint8_t>();
+
+    if (scope.size() == 0) {
+        return Status::BAD_VALUE;
+    }
+
+    if (keyType != KeyType::STREAMING) {
+        return Status::ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    sp<Session> session = mSessionLibrary->findSession(toVector(scope));
+    if (!session.get()) {
+        return Status::ERROR_DRM_SESSION_NOT_OPENED;
+    }
+
+    Status status = session->getKeyRequest(initData, mimeType, request);
+    *keyRequestType = KeyRequestType::INITIAL;
+    return status;
+}
+
 Return<void> DrmPlugin::getKeyRequest(
         const hidl_vec<uint8_t>& scope,
         const hidl_vec<uint8_t>& initData,
@@ -107,29 +151,16 @@
         getKeyRequest_cb _hidl_cb) {
     UNUSED(optionalParameters);
 
-    if (scope.size() == 0) {
-        // Returns empty keyRequest, unknown keyType and empty defaultUrl
-        _hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>(),
-                KeyRequestType::UNKNOWN, "");
-        return Void();
-    }
-
-    if (keyType != KeyType::STREAMING) {
-        _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<uint8_t>(),
-                KeyRequestType::UNKNOWN, "");
-        return Void();
-    }
-
-    sp<Session> session = mSessionLibrary->findSession(toVector(scope));
-    if (!session.get()) {
-        _hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, hidl_vec<uint8_t>(),
-                KeyRequestType::UNKNOWN, "");
-        return Void();
-    }
-
+    KeyRequestType keyRequestType = KeyRequestType::UNKNOWN;
+    std::string defaultUrl("");
     std::vector<uint8_t> request;
-    Status status = session->getKeyRequest(initData, mimeType, &request);
-    _hidl_cb(status, toHidlVec(request), KeyRequestType::INITIAL, "");
+    Status status = getKeyRequestCommon(
+            scope, initData, mimeType, keyType, optionalParameters,
+            &request, &keyRequestType, &defaultUrl);
+
+    _hidl_cb(status, toHidlVec(request),
+            static_cast<drm::V1_0::KeyRequestType>(keyRequestType),
+            hidl_string(defaultUrl));
     return Void();
 }
 
@@ -140,23 +171,16 @@
         KeyType keyType,
         const hidl_vec<KeyValue>& optionalParameters,
         getKeyRequest_1_1_cb _hidl_cb) {
-    hidl_string defaultUrl;
-    hidl_vec<uint8_t> request;
-    ::android::hardware::drm::V1_1::KeyRequestType requestType =
-            static_cast<::android::hardware::drm::V1_1::KeyRequestType>(KeyRequestType::UNKNOWN);
-    Status status = Status::OK;
+    UNUSED(optionalParameters);
 
-    defaultUrl.clear();
-    getKeyRequest(scope, initData, mimeType, keyType, optionalParameters,
-            [&](Status statusCode, const hidl_vec<uint8_t>& hResult,
-            KeyRequestType hKeyRequestType,
-            const hidl_string& hDefaultUrl) {
-        defaultUrl = hDefaultUrl;
-        request = hResult;
-        requestType = static_cast<::android::hardware::drm::V1_1::KeyRequestType>(hKeyRequestType);
-        status = statusCode;
-    });
-    _hidl_cb(status, request, requestType, defaultUrl);
+    KeyRequestType keyRequestType = KeyRequestType::UNKNOWN;
+    std::string defaultUrl("");
+    std::vector<uint8_t> request;
+    Status status = getKeyRequestCommon(
+            scope, initData, mimeType, keyType, optionalParameters,
+            &request, &keyRequestType, &defaultUrl);
+
+    _hidl_cb(status, toHidlVec(request), keyRequestType, hidl_string(defaultUrl));
     return Void();
 }
 
@@ -210,16 +234,16 @@
     std::string name(propertyName.c_str());
     std::string value;
 
-    if (name == "vendor") {
-        value = "Google";
-    } else if (name == "version") {
-        value = "1.1";
-    } else if (name == "description") {
-        value = "ClearKey CDM";
-    } else if (name == "algorithms") {
-        value = "";
-    } else if (name == "listenerTestSupport") {
-        value = mStringProperties[name];
+    if (name == kVendorKey) {
+        value = mStringProperties[kVendorKey];
+    } else if (name == kVersionKey) {
+        value = mStringProperties[kVersionKey];
+    } else if (name == kPluginDescriptionKey) {
+        value = mStringProperties[kPluginDescriptionKey];
+    } else if (name == kAlgorithmsKey) {
+        value = mStringProperties[kAlgorithmsKey];
+    } else if (name == kListenerTestSupportKey) {
+        value = mStringProperties[kListenerTestSupportKey];
     } else {
         ALOGE("App requested unknown string property %s", name.c_str());
         _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, "");
@@ -344,8 +368,8 @@
         return Status::BAD_VALUE;
     }
 
-    if (level > SecurityLevel::HW_SECURE_ALL) {
-        ALOGE("Cannot set invalid security level");
+    if (level > SecurityLevel::SW_SECURE_CRYPTO) {
+        ALOGE("Cannot set security level > max");
         return Status::BAD_VALUE;
     }
 
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h
index 7731b14..d65b25c 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h
@@ -28,7 +28,7 @@
 static const std::string kVendorKey("vendor");
 static const std::string kVendorValue("Google");
 static const std::string kVersionKey("version");
-static const std::string kVersionValue("1.0");
+static const std::string kVersionValue("1.1");
 static const std::string kPluginDescriptionKey("description");
 static const std::string kPluginDescriptionValue("ClearKey CDM");
 static const std::string kAlgorithmsKey("algorithms");
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
index 64c8262..19baf0b 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
@@ -31,7 +31,6 @@
 
 using ::android::hardware::drm::V1_0::EventType;
 using ::android::hardware::drm::V1_0::IDrmPluginListener;
-using ::android::hardware::drm::V1_0::KeyRequestType;
 using ::android::hardware::drm::V1_0::KeyStatus;
 using ::android::hardware::drm::V1_0::KeyType;
 using ::android::hardware::drm::V1_0::KeyValue;
@@ -39,6 +38,8 @@
 using ::android::hardware::drm::V1_0::SecureStopId;
 using ::android::hardware::drm::V1_0::Status;
 using ::android::hardware::drm::V1_1::DrmMetricGroup;
+using ::android::hardware::drm::V1_1::IDrmPlugin;
+using ::android::hardware::drm::V1_1::KeyRequestType;
 
 using ::android::hardware::hidl_string;
 using ::android::hardware::hidl_vec;
@@ -46,13 +47,14 @@
 using ::android::hardware::Void;
 using ::android::sp;
 
-
 struct DrmPlugin : public IDrmPlugin {
     explicit DrmPlugin(SessionLibrary* sessionLibrary);
 
     virtual ~DrmPlugin() {}
 
     Return<void> openSession(openSession_cb _hidl_cb) override;
+    Return<void> openSession_1_1(SecurityLevel securityLevel,
+            openSession_cb _hidl_cb) override;
 
     Return<Status> closeSession(const hidl_vec<uint8_t>& sessionId) override;
 
@@ -162,9 +164,6 @@
     Return<void> getSecurityLevel(const hidl_vec<uint8_t>& sessionId,
             getSecurityLevel_cb _hidl_cb) override;
 
-    Return<Status> setSecurityLevel(const hidl_vec<uint8_t>& sessionId,
-            SecurityLevel level) override;
-
     Return<void> getMetrics(getMetrics_cb _hidl_cb) override;
 
     Return<void> getPropertyString(
@@ -333,6 +332,18 @@
     void initProperties();
     void setPlayPolicy();
 
+    Return<Status> setSecurityLevel(const hidl_vec<uint8_t>& sessionId,
+            SecurityLevel level);
+
+    Status getKeyRequestCommon(const hidl_vec<uint8_t>& scope,
+            const hidl_vec<uint8_t>& initData,
+            const hidl_string& mimeType,
+            KeyType keyType,
+            const hidl_vec<KeyValue>& optionalParameters,
+            std::vector<uint8_t> *request,
+            KeyRequestType *getKeyRequestType,
+            std::string *defaultUrl);
+
     std::vector<KeyValue> mPlayPolicy;
     std::map<std::string, std::string> mStringProperties;
     std::map<std::string, std::vector<uint8_t> > mByteArrayProperties;
diff --git a/media/extractors/aac/AACExtractor.cpp b/media/extractors/aac/AACExtractor.cpp
index 1614ca4..9df0aaa 100644
--- a/media/extractors/aac/AACExtractor.cpp
+++ b/media/extractors/aac/AACExtractor.cpp
@@ -333,13 +333,20 @@
 
 static MediaExtractor* CreateExtractor(
         DataSourceBase *source,
-        const sp<AMessage>& meta) {
-    return new AACExtractor(source, meta);
+        void *meta) {
+    sp<AMessage> metaData = static_cast<AMessage *>(meta);
+    return new AACExtractor(source, metaData);
+}
+
+static void FreeMeta(void *meta) {
+    if (meta != nullptr) {
+        static_cast<AMessage *>(meta)->decStrong(nullptr);
+    }
 }
 
 static MediaExtractor::CreatorFunc Sniff(
-        DataSourceBase *source, String8 *mimeType, float *confidence,
-        sp<AMessage> *meta) {
+        DataSourceBase *source, float *confidence, void **meta,
+        MediaExtractor::FreeMetaFunc *freeMeta) {
     off64_t pos = 0;
 
     for (;;) {
@@ -377,11 +384,14 @@
 
     // ADTS syncword
     if ((header[0] == 0xff) && ((header[1] & 0xf6) == 0xf0)) {
-        *mimeType = MEDIA_MIMETYPE_AUDIO_AAC_ADTS;
         *confidence = 0.2;
 
-        *meta = new AMessage;
-        (*meta)->setInt64("offset", pos);
+        AMessage *msg = new AMessage;
+        msg->setInt64("offset", pos);
+        *meta = msg;
+        *freeMeta = &FreeMeta;
+        // ref count will be decreased in FreeMeta.
+        msg->incStrong(nullptr);
 
         return CreateExtractor;
     }
diff --git a/media/extractors/amr/AMRExtractor.cpp b/media/extractors/amr/AMRExtractor.cpp
index 547e3f5..eec18b5 100644
--- a/media/extractors/amr/AMRExtractor.cpp
+++ b/media/extractors/amr/AMRExtractor.cpp
@@ -122,7 +122,7 @@
       mOffsetTableLength(0) {
     String8 mimeType;
     float confidence;
-    if (!SniffAMR(mDataSource, &mimeType, &confidence, NULL)) {
+    if (!SniffAMR(mDataSource, &mimeType, &confidence)) {
         return;
     }
 
@@ -339,8 +339,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 bool SniffAMR(
-        DataSourceBase *source, String8 *mimeType, float *confidence,
-        sp<AMessage> *) {
+        DataSourceBase *source, String8 *mimeType, float *confidence) {
     char header[9];
 
     if (source->readAt(0, header, sizeof(header)) != sizeof(header)) {
@@ -348,12 +347,16 @@
     }
 
     if (!memcmp(header, "#!AMR\n", 6)) {
-        *mimeType = MEDIA_MIMETYPE_AUDIO_AMR_NB;
+        if (mimeType != nullptr) {
+            *mimeType = MEDIA_MIMETYPE_AUDIO_AMR_NB;
+        }
         *confidence = 0.5;
 
         return true;
     } else if (!memcmp(header, "#!AMR-WB\n", 9)) {
-        *mimeType = MEDIA_MIMETYPE_AUDIO_AMR_WB;
+        if (mimeType != nullptr) {
+            *mimeType = MEDIA_MIMETYPE_AUDIO_AMR_WB;
+        }
         *confidence = 0.5;
 
         return true;
@@ -373,13 +376,13 @@
         "AMR Extractor",
         [](
                 DataSourceBase *source,
-                String8 *mimeType,
                 float *confidence,
-                sp<AMessage> *meta __unused) -> MediaExtractor::CreatorFunc {
-            if (SniffAMR(source, mimeType, confidence, meta)) {
+                void **,
+                MediaExtractor::FreeMetaFunc *) -> MediaExtractor::CreatorFunc {
+            if (SniffAMR(source, nullptr, confidence)) {
                 return [](
                         DataSourceBase *source,
-                        const sp<AMessage>& meta __unused) -> MediaExtractor* {
+                        void *) -> MediaExtractor* {
                     return new AMRExtractor(source);};
             }
             return NULL;
diff --git a/media/extractors/amr/AMRExtractor.h b/media/extractors/amr/AMRExtractor.h
index d6d49f2..b8b44ea 100644
--- a/media/extractors/amr/AMRExtractor.h
+++ b/media/extractors/amr/AMRExtractor.h
@@ -55,8 +55,7 @@
 };
 
 bool SniffAMR(
-        DataSourceBase *source, String8 *mimeType, float *confidence,
-        sp<AMessage> *);
+        DataSourceBase *source, String8 *mimeType, float *confidence);
 
 }  // namespace android
 
diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp
index 8dbb5a1..2ce20db 100644
--- a/media/extractors/flac/FLACExtractor.cpp
+++ b/media/extractors/flac/FLACExtractor.cpp
@@ -968,9 +968,7 @@
 
 // Sniffer
 
-bool SniffFLAC(
-        DataSourceBase *source, String8 *mimeType, float *confidence,
-        sp<AMessage> *)
+bool SniffFLAC(DataSourceBase *source, float *confidence)
 {
     // first 4 is the signature word
     // second 4 is the sizeof STREAMINFO
@@ -983,7 +981,6 @@
         return false;
     }
 
-    *mimeType = MEDIA_MIMETYPE_AUDIO_FLAC;
     *confidence = 0.5;
 
     return true;
@@ -1001,13 +998,13 @@
             "FLAC Extractor",
             [](
                     DataSourceBase *source,
-                    String8 *mimeType,
                     float *confidence,
-                    sp<AMessage> *meta __unused) -> MediaExtractor::CreatorFunc {
-                if (SniffFLAC(source, mimeType, confidence, meta)) {
+                    void **,
+                    MediaExtractor::FreeMetaFunc *) -> MediaExtractor::CreatorFunc {
+                if (SniffFLAC(source, confidence)) {
                     return [](
                             DataSourceBase *source,
-                            const sp<AMessage>& meta __unused) -> MediaExtractor* {
+                            void *) -> MediaExtractor* {
                         return new FLACExtractor(source);};
                 }
                 return NULL;
diff --git a/media/extractors/flac/FLACExtractor.h b/media/extractors/flac/FLACExtractor.h
index ef07212..f41d878 100644
--- a/media/extractors/flac/FLACExtractor.h
+++ b/media/extractors/flac/FLACExtractor.h
@@ -56,8 +56,7 @@
 
 };
 
-bool SniffFLAC(DataSourceBase *source, String8 *mimeType,
-        float *confidence, sp<AMessage> *);
+bool SniffFLAC(DataSourceBase *source, float *confidence);
 
 }  // namespace android
 
diff --git a/media/extractors/midi/MidiExtractor.cpp b/media/extractors/midi/MidiExtractor.cpp
index 711c6a5..1e38194 100644
--- a/media/extractors/midi/MidiExtractor.cpp
+++ b/media/extractors/midi/MidiExtractor.cpp
@@ -307,13 +307,10 @@
 
 // Sniffer
 
-bool SniffMidi(
-        DataSourceBase *source, String8 *mimeType, float *confidence,
-        sp<AMessage> *)
+bool SniffMidi(DataSourceBase *source, float *confidence)
 {
     sp<MidiEngine> p = new MidiEngine(source, NULL, NULL);
     if (p->initCheck() == OK) {
-        *mimeType = MEDIA_MIMETYPE_AUDIO_MIDI;
         *confidence = 0.8;
         ALOGV("SniffMidi: yes");
         return true;
@@ -334,13 +331,13 @@
         "MIDI Extractor",
         [](
                 DataSourceBase *source,
-                String8 *mimeType,
                 float *confidence,
-                sp<AMessage> *meta __unused) -> MediaExtractor::CreatorFunc {
-            if (SniffMidi(source, mimeType, confidence, meta)) {
+                void **,
+                MediaExtractor::FreeMetaFunc *) -> MediaExtractor::CreatorFunc {
+            if (SniffMidi(source, confidence)) {
                 return [](
                         DataSourceBase *source,
-                        const sp<AMessage>& meta __unused) -> MediaExtractor* {
+                        void *) -> MediaExtractor* {
                     return new MidiExtractor(source);};
             }
             return NULL;
diff --git a/media/extractors/midi/MidiExtractor.h b/media/extractors/midi/MidiExtractor.h
index 91efd06..173a814 100644
--- a/media/extractors/midi/MidiExtractor.h
+++ b/media/extractors/midi/MidiExtractor.h
@@ -87,8 +87,7 @@
 
 };
 
-bool SniffMidi(DataSourceBase *source, String8 *mimeType,
-        float *confidence, sp<AMessage> *);
+bool SniffMidi(DataSourceBase *source, float *confidence);
 
 }  // namespace android
 
diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp
index f61f7c7..7396113 100644
--- a/media/extractors/mkv/MatroskaExtractor.cpp
+++ b/media/extractors/mkv/MatroskaExtractor.cpp
@@ -1548,8 +1548,7 @@
 }
 
 bool SniffMatroska(
-        DataSourceBase *source, String8 *mimeType, float *confidence,
-        sp<AMessage> *) {
+        DataSourceBase *source, float *confidence) {
     DataSourceBaseReader reader(source);
     mkvparser::EBMLHeader ebmlHeader;
     long long pos;
@@ -1557,7 +1556,6 @@
         return false;
     }
 
-    mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MATROSKA);
     *confidence = 0.6;
 
     return true;
@@ -1575,13 +1573,13 @@
         "Matroska Extractor",
         [](
                 DataSourceBase *source,
-                String8 *mimeType,
                 float *confidence,
-                sp<AMessage> *meta __unused) -> MediaExtractor::CreatorFunc {
-            if (SniffMatroska(source, mimeType, confidence, meta)) {
+                void **,
+                MediaExtractor::FreeMetaFunc *) -> MediaExtractor::CreatorFunc {
+            if (SniffMatroska(source, confidence)) {
                 return [](
                         DataSourceBase *source,
-                        const sp<AMessage>& meta __unused) -> MediaExtractor* {
+                        void *) -> MediaExtractor* {
                     return new MatroskaExtractor(source);};
             }
             return NULL;
diff --git a/media/extractors/mp3/MP3Extractor.cpp b/media/extractors/mp3/MP3Extractor.cpp
index 25d4deb..e55084e 100644
--- a/media/extractors/mp3/MP3Extractor.cpp
+++ b/media/extractors/mp3/MP3Extractor.cpp
@@ -668,13 +668,20 @@
 
 static MediaExtractor* CreateExtractor(
         DataSourceBase *source,
-        const sp<AMessage>& meta) {
-    return new MP3Extractor(source, meta);
+        void *meta) {
+    sp<AMessage> metaData = static_cast<AMessage *>(meta);
+    return new MP3Extractor(source, metaData);
+}
+
+static void FreeMeta(void *meta) {
+    if (meta != nullptr) {
+        static_cast<AMessage *>(meta)->decStrong(nullptr);
+    }
 }
 
 static MediaExtractor::CreatorFunc Sniff(
-        DataSourceBase *source, String8 *mimeType,
-        float *confidence, sp<AMessage> *meta) {
+        DataSourceBase *source, float *confidence, void **meta,
+        MediaExtractor::FreeMetaFunc *freeMeta) {
     off64_t pos = 0;
     off64_t post_id3_pos;
     uint32_t header;
@@ -691,12 +698,15 @@
         return NULL;
     }
 
-    *meta = new AMessage;
-    (*meta)->setInt64("offset", pos);
-    (*meta)->setInt32("header", header);
-    (*meta)->setInt64("post-id3-offset", post_id3_pos);
+    AMessage *msg = new AMessage;
+    msg->setInt64("offset", pos);
+    msg->setInt32("header", header);
+    msg->setInt64("post-id3-offset", post_id3_pos);
+    *meta = msg;
+    *freeMeta = &FreeMeta;
+    // ref count will be decreased in FreeMeta.
+    msg->incStrong(nullptr);
 
-    *mimeType = MEDIA_MIMETYPE_AUDIO_MPEG;
     *confidence = 0.2f;
 
     return CreateExtractor;
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 30dda13..3d7e45c 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -5382,8 +5382,7 @@
     return NULL;
 }
 
-static bool LegacySniffMPEG4(
-        DataSourceBase *source, String8 *mimeType, float *confidence) {
+static bool LegacySniffMPEG4(DataSourceBase *source, float *confidence) {
     uint8_t header[8];
 
     ssize_t n = source->readAt(4, header, sizeof(header));
@@ -5399,7 +5398,6 @@
         || !memcmp(header, "ftypkddi", 8) || !memcmp(header, "ftypM4VP", 8)
         || !memcmp(header, "ftypmif1", 8) || !memcmp(header, "ftypheic", 8)
         || !memcmp(header, "ftypmsf1", 8) || !memcmp(header, "ftyphevc", 8)) {
-        *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
         *confidence = 0.4;
 
         return true;
@@ -5449,9 +5447,7 @@
 // Also try to identify where this file's metadata ends
 // (end of the 'moov' atom) and report it to the caller as part of
 // the metadata.
-static bool BetterSniffMPEG4(
-        DataSourceBase *source, String8 *mimeType, float *confidence,
-        sp<AMessage> *meta) {
+static bool BetterSniffMPEG4(DataSourceBase *source, float *confidence) {
     // We scan up to 128 bytes to identify this file as an MP4.
     static const off64_t kMaxScanOffset = 128ll;
 
@@ -5553,35 +5549,23 @@
         return false;
     }
 
-    *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
     *confidence = 0.4f;
 
-    if (moovAtomEndOffset >= 0) {
-        *meta = new AMessage;
-        (*meta)->setInt64("meta-data-size", moovAtomEndOffset);
-
-        ALOGV("found metadata size: %lld", (long long)moovAtomEndOffset);
-    }
-
     return true;
 }
 
-static MediaExtractor* CreateExtractor(
-        DataSourceBase *source,
-        const sp<AMessage>& meta __unused) {
+static MediaExtractor* CreateExtractor(DataSourceBase *source, void *) {
     return new MPEG4Extractor(source);
 }
 
 static MediaExtractor::CreatorFunc Sniff(
-        DataSourceBase *source,
-        String8 *mimeType,
-        float *confidence,
-        sp<AMessage> *meta) {
-    if (BetterSniffMPEG4(source, mimeType, confidence, meta)) {
+        DataSourceBase *source, float *confidence, void **,
+        MediaExtractor::FreeMetaFunc *) {
+    if (BetterSniffMPEG4(source, confidence)) {
         return CreateExtractor;
     }
 
-    if (LegacySniffMPEG4(source, mimeType, confidence)) {
+    if (LegacySniffMPEG4(source, confidence)) {
         ALOGW("Identified supported mpeg4 through LegacySniffMPEG4.");
         return CreateExtractor;
     }
diff --git a/media/extractors/mpeg2/ExtractorBundle.cpp b/media/extractors/mpeg2/ExtractorBundle.cpp
index 443d685..8a0fa03 100644
--- a/media/extractors/mpeg2/ExtractorBundle.cpp
+++ b/media/extractors/mpeg2/ExtractorBundle.cpp
@@ -35,18 +35,18 @@
         "MPEG2-PS/TS Extractor",
         [](
                 DataSourceBase *source,
-                String8 *mimeType,
                 float *confidence,
-                sp<AMessage> *meta __unused) -> MediaExtractor::CreatorFunc {
-            if (SniffMPEG2TS(source, mimeType, confidence, meta)) {
+                void **,
+                MediaExtractor::FreeMetaFunc *) -> MediaExtractor::CreatorFunc {
+            if (SniffMPEG2TS(source, confidence)) {
                 return [](
                         DataSourceBase *source,
-                        const sp<AMessage>& meta __unused) -> MediaExtractor* {
+                        void *) -> MediaExtractor* {
                     return new MPEG2TSExtractor(source);};
-            } else if (SniffMPEG2PS(source, mimeType, confidence, meta)) {
+            } else if (SniffMPEG2PS(source, confidence)) {
                         return [](
                                 DataSourceBase *source,
-                                const sp<AMessage>& meta __unused) -> MediaExtractor* {
+                                void *) -> MediaExtractor* {
                             return new MPEG2PSExtractor(source);};
             }
             return NULL;
diff --git a/media/extractors/mpeg2/MPEG2PSExtractor.cpp b/media/extractors/mpeg2/MPEG2PSExtractor.cpp
index 697e44f..b4d0ee5 100644
--- a/media/extractors/mpeg2/MPEG2PSExtractor.cpp
+++ b/media/extractors/mpeg2/MPEG2PSExtractor.cpp
@@ -751,8 +751,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 bool SniffMPEG2PS(
-        DataSourceBase *source, String8 *mimeType, float *confidence,
-        sp<AMessage> *) {
+        DataSourceBase *source, float *confidence) {
     uint8_t header[5];
     if (source->readAt(0, header, sizeof(header)) < (ssize_t)sizeof(header)) {
         return false;
@@ -764,8 +763,6 @@
 
     *confidence = 0.25f;  // Slightly larger than .mp3 extractor's confidence
 
-    mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MPEG2PS);
-
     return true;
 }
 
diff --git a/media/extractors/mpeg2/MPEG2PSExtractor.h b/media/extractors/mpeg2/MPEG2PSExtractor.h
index adf719a..2541f4d 100644
--- a/media/extractors/mpeg2/MPEG2PSExtractor.h
+++ b/media/extractors/mpeg2/MPEG2PSExtractor.h
@@ -71,9 +71,7 @@
     DISALLOW_EVIL_CONSTRUCTORS(MPEG2PSExtractor);
 };
 
-bool SniffMPEG2PS(
-        DataSourceBase *source, String8 *mimeType, float *confidence,
-        sp<AMessage> *);
+bool SniffMPEG2PS(DataSourceBase *source, float *confidence);
 
 }  // namespace android
 
diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.cpp b/media/extractors/mpeg2/MPEG2TSExtractor.cpp
index a8a366b..3183064 100644
--- a/media/extractors/mpeg2/MPEG2TSExtractor.cpp
+++ b/media/extractors/mpeg2/MPEG2TSExtractor.cpp
@@ -645,9 +645,7 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-bool SniffMPEG2TS(
-        DataSourceBase *source, String8 *mimeType, float *confidence,
-        sp<AMessage> *) {
+bool SniffMPEG2TS(DataSourceBase *source, float *confidence) {
     for (int i = 0; i < 5; ++i) {
         char header;
         if (source->readAt(kTSPacketSize * i, &header, 1) != 1
@@ -657,7 +655,6 @@
     }
 
     *confidence = 0.1f;
-    mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
 
     return true;
 }
diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.h b/media/extractors/mpeg2/MPEG2TSExtractor.h
index fc15501..df07fac 100644
--- a/media/extractors/mpeg2/MPEG2TSExtractor.h
+++ b/media/extractors/mpeg2/MPEG2TSExtractor.h
@@ -99,9 +99,7 @@
     DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSExtractor);
 };
 
-bool SniffMPEG2TS(
-        DataSourceBase *source, String8 *mimeType, float *confidence,
-        sp<AMessage> *);
+bool SniffMPEG2TS(DataSourceBase *source, float *confidence);
 
 }  // namespace android
 
diff --git a/media/extractors/ogg/OggExtractor.cpp b/media/extractors/ogg/OggExtractor.cpp
index 1d04bed..ab51e5e 100644
--- a/media/extractors/ogg/OggExtractor.cpp
+++ b/media/extractors/ogg/OggExtractor.cpp
@@ -1371,21 +1371,20 @@
 
 static MediaExtractor* CreateExtractor(
         DataSourceBase *source,
-        const sp<AMessage>& meta __unused) {
+        void *) {
     return new OggExtractor(source);
 }
 
 static MediaExtractor::CreatorFunc Sniff(
         DataSourceBase *source,
-        String8 *mimeType,
         float *confidence,
-        sp<AMessage> *) {
+        void **,
+        MediaExtractor::FreeMetaFunc *) {
     char tmp[4];
     if (source->readAt(0, tmp, 4) < 4 || memcmp(tmp, "OggS", 4)) {
         return NULL;
     }
 
-    mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_OGG);
     *confidence = 0.2f;
 
     return CreateExtractor;
diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp
index 105a37f..2c991a7 100644
--- a/media/extractors/wav/WAVExtractor.cpp
+++ b/media/extractors/wav/WAVExtractor.cpp
@@ -546,15 +546,15 @@
 
 static MediaExtractor* CreateExtractor(
         DataSourceBase *source,
-        const sp<AMessage>& meta __unused) {
+        void *) {
     return new WAVExtractor(source);
 }
 
 static MediaExtractor::CreatorFunc Sniff(
         DataSourceBase *source,
-        String8 *mimeType,
         float *confidence,
-        sp<AMessage> *) {
+        void **,
+        MediaExtractor::FreeMetaFunc *) {
     char header[12];
     if (source->readAt(0, header, sizeof(header)) < (ssize_t)sizeof(header)) {
         return NULL;
@@ -571,7 +571,6 @@
         return NULL;
     }
 
-    *mimeType = MEDIA_MIMETYPE_CONTAINER_WAV;
     *confidence = 0.3f;
 
     return CreateExtractor;
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index f7ae7d7..28158e2 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -113,7 +113,7 @@
             .content_type = contentType,
             .usage = AUDIO_USAGE_UNKNOWN, // only used for output
             .source = source,
-            .flags = flags, // If attributes are set then the other flags parameter is ignored.
+            .flags = AUDIO_FLAG_NONE, // Different than the AUDIO_INPUT_FLAGS
             .tags = ""
     };
 
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index ee069ee..023e8af 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -22,6 +22,7 @@
 #include <media/AudioTrack.h>
 
 #include <aaudio/AAudio.h>
+#include <system/audio.h>
 #include "utility/AudioClock.h"
 #include "legacy/AudioStreamLegacy.h"
 #include "legacy/AudioStreamTrack.h"
@@ -130,7 +131,7 @@
             .content_type = contentType,
             .usage = usage,
             .source = AUDIO_SOURCE_DEFAULT, // only used for recording
-            .flags = flags, // If attributes are set then the other flags parameter is ignored.
+            .flags = AUDIO_FLAG_NONE, // Different than the AUDIO_OUTPUT_FLAGS
             .tags = ""
     };
 
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index 6b3a8f0..21d3fa6 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -91,11 +91,14 @@
 {
     // key for media statistics is defined in the header
     // attrs for media statistics
-    static constexpr char kAudioRecordChannelCount[] = "android.media.audiorecord.channels";
+    // NB: these are matched with public Java API constants defined
+    // in frameworks/base/media/java/android/media/AudioRecord.java
+    // These must be kept synchronized with the constants there.
     static constexpr char kAudioRecordEncoding[] = "android.media.audiorecord.encoding";
+    static constexpr char kAudioRecordSource[] = "android.media.audiorecord.source";
     static constexpr char kAudioRecordLatency[] = "android.media.audiorecord.latency";
     static constexpr char kAudioRecordSampleRate[] = "android.media.audiorecord.samplerate";
-    static constexpr char kAudioRecordSource[] = "android.media.audiotrack.source";
+    static constexpr char kAudioRecordChannelCount[] = "android.media.audiorecord.channels";
 
     // constructor guarantees mAnalyticsItem is valid
 
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 9dfb514..1c4a80e 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -904,6 +904,7 @@
                                 audio_session_t session,
                                 pid_t pid,
                                 uid_t uid,
+                                const String16& opPackageName,
                                 const audio_config_base_t *config,
                                 audio_input_flags_t flags,
                                 audio_port_handle_t *selectedDeviceId,
@@ -912,35 +913,29 @@
     const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
     if (aps == 0) return NO_INIT;
     return aps->getInputForAttr(
-            attr, input, session, pid, uid,
+            attr, input, session, pid, uid, opPackageName,
             config, flags, selectedDeviceId, portId);
 }
 
-status_t AudioSystem::startInput(audio_io_handle_t input,
-                                 audio_session_t session,
-                                 audio_devices_t device,
-                                 uid_t uid,
-                                 bool *silenced)
+status_t AudioSystem::startInput(audio_port_handle_t portId, bool *silenced)
 {
     const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
     if (aps == 0) return PERMISSION_DENIED;
-    return aps->startInput(input, session, device, uid, silenced);
+    return aps->startInput(portId, silenced);
 }
 
-status_t AudioSystem::stopInput(audio_io_handle_t input,
-                                audio_session_t session)
+status_t AudioSystem::stopInput(audio_port_handle_t portId)
 {
     const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
     if (aps == 0) return PERMISSION_DENIED;
-    return aps->stopInput(input, session);
+    return aps->stopInput(portId);
 }
 
-void AudioSystem::releaseInput(audio_io_handle_t input,
-                               audio_session_t session)
+void AudioSystem::releaseInput(audio_port_handle_t portId)
 {
     const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
     if (aps == 0) return;
-    aps->releaseInput(input, session);
+    aps->releaseInput(portId);
 }
 
 status_t AudioSystem::initStreamVolume(audio_stream_type_t stream,
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 55954f2..50c1295 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -56,8 +56,6 @@
     return x > y ? x : y;
 }
 
-static const int32_t NANOS_PER_SECOND = 1000000000;
-
 static inline nsecs_t framesToNanoseconds(ssize_t frames, uint32_t sampleRate, float speed)
 {
     return ((double)frames * 1000000000) / ((double)sampleRate * speed);
@@ -185,11 +183,16 @@
 
     // key for media statistics is defined in the header
     // attrs for media statistics
+    // NB: these are matched with public Java API constants defined
+    // in frameworks/base/media/java/android/media/AudioTrack.java
+    // These must be kept synchronized with the constants there.
     static constexpr char kAudioTrackStreamType[] = "android.media.audiotrack.streamtype";
     static constexpr char kAudioTrackContentType[] = "android.media.audiotrack.type";
     static constexpr char kAudioTrackUsage[] = "android.media.audiotrack.usage";
     static constexpr char kAudioTrackSampleRate[] = "android.media.audiotrack.samplerate";
     static constexpr char kAudioTrackChannelMask[] = "android.media.audiotrack.channelmask";
+
+    // NB: These are not yet exposed as public Java API constants.
     static constexpr char kAudioTrackUnderrunFrames[] = "android.media.audiotrack.underrunframes";
     static constexpr char kAudioTrackStartupGlitch[] = "android.media.audiotrack.glitch.startup";
 
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index a24a099..8f5ff30 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -285,6 +285,7 @@
                                      audio_session_t session,
                                      pid_t pid,
                                      uid_t uid,
+                                     const String16& opPackageName,
                                      const audio_config_base_t *config,
                                      audio_input_flags_t flags,
                                      audio_port_handle_t *selectedDeviceId,
@@ -313,6 +314,7 @@
         data.writeInt32(session);
         data.writeInt32(pid);
         data.writeInt32(uid);
+        data.writeString16(opPackageName);
         data.write(config, sizeof(audio_config_base_t));
         data.writeInt32(flags);
         data.writeInt32(*selectedDeviceId);
@@ -331,18 +333,12 @@
         return NO_ERROR;
     }
 
-    virtual status_t startInput(audio_io_handle_t input,
-                                audio_session_t session,
-                                audio_devices_t device,
-                                uid_t uid,
+    virtual status_t startInput(audio_port_handle_t portId,
                                 bool *silenced)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
-        data.writeInt32(input);
-        data.writeInt32(session);
-        data.writeInt32(device);
-        data.writeInt32(uid);
+        data.writeInt32(portId);
         data.writeInt32(*silenced ? 1 : 0);
         remote()->transact(START_INPUT, data, &reply);
         status_t status = static_cast <status_t> (reply.readInt32());
@@ -350,24 +346,20 @@
         return status;
     }
 
-    virtual status_t stopInput(audio_io_handle_t input,
-                               audio_session_t session)
+    virtual status_t stopInput(audio_port_handle_t portId)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
-        data.writeInt32(input);
-        data.writeInt32(session);
+        data.writeInt32(portId);
         remote()->transact(STOP_INPUT, data, &reply);
         return static_cast <status_t> (reply.readInt32());
     }
 
-    virtual void releaseInput(audio_io_handle_t input,
-                              audio_session_t session)
+    virtual void releaseInput(audio_port_handle_t portId)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
-        data.writeInt32(input);
-        data.writeInt32(session);
+        data.writeInt32(portId);
         remote()->transact(RELEASE_INPUT, data, &reply);
     }
 
@@ -1055,6 +1047,7 @@
             audio_session_t session = (audio_session_t)data.readInt32();
             pid_t pid = (pid_t)data.readInt32();
             uid_t uid = (uid_t)data.readInt32();
+            const String16 opPackageName = data.readString16();
             audio_config_base_t config;
             memset(&config, 0, sizeof(audio_config_base_t));
             data.read(&config, sizeof(audio_config_base_t));
@@ -1062,7 +1055,7 @@
             audio_port_handle_t selectedDeviceId = (audio_port_handle_t) data.readInt32();
             audio_port_handle_t portId = (audio_port_handle_t)data.readInt32();
             status_t status = getInputForAttr(&attr, &input, session, pid, uid,
-                                              &config,
+                                              opPackageName, &config,
                                               flags, &selectedDeviceId, &portId);
             reply->writeInt32(status);
             if (status == NO_ERROR) {
@@ -1075,12 +1068,9 @@
 
         case START_INPUT: {
             CHECK_INTERFACE(IAudioPolicyService, data, reply);
-            audio_io_handle_t input = static_cast <audio_io_handle_t>(data.readInt32());
-            audio_session_t session = static_cast <audio_session_t>(data.readInt32());
-            audio_devices_t device = static_cast <audio_devices_t>(data.readInt32());
-            uid_t uid = static_cast <uid_t>(data.readInt32());
+            audio_port_handle_t portId = static_cast <audio_port_handle_t>(data.readInt32());
             bool silenced = data.readInt32() == 1;
-            status_t status = startInput(input, session, device, uid, &silenced);
+            status_t status = startInput(portId, &silenced);
             reply->writeInt32(static_cast <uint32_t>(status));
             reply->writeInt32(silenced ? 1 : 0);
             return NO_ERROR;
@@ -1088,17 +1078,15 @@
 
         case STOP_INPUT: {
             CHECK_INTERFACE(IAudioPolicyService, data, reply);
-            audio_io_handle_t input = static_cast <audio_io_handle_t>(data.readInt32());
-            audio_session_t session = static_cast <audio_session_t>(data.readInt32());
-            reply->writeInt32(static_cast <uint32_t>(stopInput(input, session)));
+            audio_port_handle_t portId = static_cast <audio_port_handle_t>(data.readInt32());
+            reply->writeInt32(static_cast <uint32_t>(stopInput(portId)));
             return NO_ERROR;
         } break;
 
         case RELEASE_INPUT: {
             CHECK_INTERFACE(IAudioPolicyService, data, reply);
-            audio_io_handle_t input = static_cast <audio_io_handle_t>(data.readInt32());
-            audio_session_t session = static_cast <audio_session_t>(data.readInt32());
-            releaseInput(input, session);
+            audio_port_handle_t portId = static_cast <audio_port_handle_t>(data.readInt32());
+            releaseInput(portId);
             return NO_ERROR;
         } break;
 
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 3c8e7bc..22b700d 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -241,20 +241,16 @@
                                     audio_session_t session,
                                     pid_t pid,
                                     uid_t uid,
+                                    const String16& opPackageName,
                                     const audio_config_base_t *config,
                                     audio_input_flags_t flags,
                                     audio_port_handle_t *selectedDeviceId,
                                     audio_port_handle_t *portId);
 
-    static status_t startInput(audio_io_handle_t input,
-                               audio_session_t session,
-                               audio_devices_t device,
-                               uid_t uid,
+    static status_t startInput(audio_port_handle_t portId,
                                bool *silenced);
-    static status_t stopInput(audio_io_handle_t input,
-                              audio_session_t session);
-    static void releaseInput(audio_io_handle_t input,
-                             audio_session_t session);
+    static status_t stopInput(audio_port_handle_t portId);
+    static void releaseInput(audio_port_handle_t portId);
     static status_t initStreamVolume(audio_stream_type_t stream,
                                       int indexMin,
                                       int indexMax);
diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h
index 5338927..949d593 100644
--- a/media/libaudioclient/include/media/IAudioPolicyService.h
+++ b/media/libaudioclient/include/media/IAudioPolicyService.h
@@ -80,19 +80,15 @@
                               audio_session_t session,
                               pid_t pid,
                               uid_t uid,
+                              const String16& opPackageName,
                               const audio_config_base_t *config,
                               audio_input_flags_t flags,
                               audio_port_handle_t *selectedDeviceId,
                               audio_port_handle_t *portId) = 0;
-    virtual status_t startInput(audio_io_handle_t input,
-                                audio_session_t session,
-                                audio_devices_t device,
-                                uid_t uid,
+    virtual status_t startInput(audio_port_handle_t portId,
                                 bool *silenced) = 0;
-    virtual status_t stopInput(audio_io_handle_t input,
-                               audio_session_t session) = 0;
-    virtual void releaseInput(audio_io_handle_t input,
-                              audio_session_t session) = 0;
+    virtual status_t stopInput(audio_port_handle_t portId) = 0;
+    virtual void releaseInput(audio_port_handle_t portId) = 0;
     virtual status_t initStreamVolume(audio_stream_type_t stream,
                                       int indexMin,
                                       int indexMax) = 0;
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 3ac519b..34b15a8 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -259,7 +259,6 @@
         "IMediaExtractor.cpp",
         "IMediaExtractorService.cpp",
         "IMediaSource.cpp",
-        "IStreamSource.cpp",
         "MediaCodecBuffer.cpp",
         "MediaUtils.cpp",
         "Metadata.cpp",
@@ -313,77 +312,3 @@
         },
     },
 }
-
-cc_library {
-    name: "libmedia_player2",
-
-    srcs: [
-        "JAudioTrack.cpp",
-        "MediaPlayer2Manager.cpp",
-        "mediaplayer2.cpp",
-    ],
-
-    shared_libs: [
-        "libandroid_runtime",
-        "libaudioclient",
-        "libbinder",
-        "libcutils",
-        "libgui",
-        "liblog",
-        "libmedia_omx",
-        "libmedia_player2_util",
-        "libmediaextractor",
-        "libstagefright_foundation",
-        "libui",
-        "libutils",
-
-        "libcrypto",
-        "libmediadrm",
-        "libmediametrics",
-        "libmediandk",
-        "libmediautils",
-        "libmemunreachable",
-        "libnativewindow",
-        "libpowermanager",
-        "libstagefright_httplive",
-        "libstagefright_player2",
-    ],
-
-    export_shared_lib_headers: [
-        "libaudioclient",
-        "libbinder",
-        "libmedia_omx",
-    ],
-
-    header_libs: [
-        "media_plugin_headers",
-    ],
-
-    static_libs: [
-        "libmedia_helper",
-        "libstagefright_nuplayer2",
-        "libstagefright_rtsp",
-        "libstagefright_timedtext",
-    ],
-
-    export_include_dirs: [
-        "include",
-    ],
-
-    cflags: [
-        "-Werror",
-        "-Wno-error=deprecated-declarations",
-        "-Wall",
-    ],
-
-    sanitize: {
-        misc_undefined: [
-            "unsigned-integer-overflow",
-            "signed-integer-overflow",
-        ],
-        cfi: true,
-        diag: {
-            cfi: true,
-        },
-    },
-}
diff --git a/media/libmedia/MidiIoWrapper.cpp b/media/libmedia/MidiIoWrapper.cpp
index 0896e75..5ca3b48 100644
--- a/media/libmedia/MidiIoWrapper.cpp
+++ b/media/libmedia/MidiIoWrapper.cpp
@@ -38,6 +38,7 @@
     mFd = open(path, O_RDONLY | O_LARGEFILE);
     mBase = 0;
     mLength = lseek(mFd, 0, SEEK_END);
+    mDataSource = nullptr;
 }
 
 MidiIoWrapper::MidiIoWrapper(int fd, off64_t offset, int64_t size) {
@@ -45,6 +46,7 @@
     mFd = fd < 0 ? -1 : dup(fd);
     mBase = offset;
     mLength = size;
+    mDataSource = nullptr;
 }
 
 MidiIoWrapper::MidiIoWrapper(DataSourceBase *source) {
diff --git a/media/libmedia/NdkWrapper.cpp b/media/libmedia/NdkWrapper.cpp
index 942393d..936e92f 100644
--- a/media/libmedia/NdkWrapper.cpp
+++ b/media/libmedia/NdkWrapper.cpp
@@ -1057,6 +1057,13 @@
     return translateErrorCode(AMediaExtractor_setDataSource(mAMediaExtractor, location));
 }
 
+status_t AMediaExtractorWrapper::setDataSource(AMediaDataSource *source) {
+    if (mAMediaExtractor == NULL) {
+        return DEAD_OBJECT;
+    }
+    return translateErrorCode(AMediaExtractor_setDataSourceCustom(mAMediaExtractor, source));
+}
+
 size_t AMediaExtractorWrapper::getTrackCount() {
     if (mAMediaExtractor == NULL) {
         return 0;
@@ -1064,6 +1071,13 @@
     return AMediaExtractor_getTrackCount(mAMediaExtractor);
 }
 
+sp<AMediaFormatWrapper> AMediaExtractorWrapper::getFormat() {
+    if (mAMediaExtractor == NULL) {
+        return NULL;
+    }
+    return new AMediaFormatWrapper(AMediaExtractor_getFileFormat(mAMediaExtractor));
+}
+
 sp<AMediaFormatWrapper> AMediaExtractorWrapper::getTrackFormat(size_t idx) {
     if (mAMediaExtractor == NULL) {
         return NULL;
@@ -1085,6 +1099,26 @@
     return translateErrorCode(AMediaExtractor_unselectTrack(mAMediaExtractor, idx));
 }
 
+status_t AMediaExtractorWrapper::selectSingleTrack(size_t idx) {
+    if (mAMediaExtractor == NULL) {
+        return DEAD_OBJECT;
+    }
+    for (size_t i = 0; i < AMediaExtractor_getTrackCount(mAMediaExtractor); ++i) {
+        if (i == idx) {
+            media_status_t err = AMediaExtractor_selectTrack(mAMediaExtractor, i);
+            if (err != AMEDIA_OK) {
+                return translateErrorCode(err);
+            }
+        } else {
+            media_status_t err = AMediaExtractor_unselectTrack(mAMediaExtractor, i);
+            if (err != AMEDIA_OK) {
+                return translateErrorCode(err);
+            }
+        }
+    }
+    return OK;
+}
+
 ssize_t AMediaExtractorWrapper::readSampleData(const sp<ABuffer> &buffer) {
     if (mAMediaExtractor == NULL) {
         return -1;
@@ -1092,6 +1126,13 @@
     return AMediaExtractor_readSampleData(mAMediaExtractor, buffer->data(), buffer->capacity());
 }
 
+ssize_t AMediaExtractorWrapper::getSampleSize() {
+    if (mAMediaExtractor == NULL) {
+        return 0;
+    }
+    return AMediaExtractor_getSampleSize(mAMediaExtractor);
+}
+
 uint32_t AMediaExtractorWrapper::getSampleFlags() {
     if (mAMediaExtractor == NULL) {
         return 0;
@@ -1113,6 +1154,13 @@
     return AMediaExtractor_getSampleTime(mAMediaExtractor);
 }
 
+int64_t AMediaExtractorWrapper::getCachedDuration() {
+    if (mAMediaExtractor == NULL) {
+        return -1;
+    }
+    return AMediaExtractor_getCachedDuration(mAMediaExtractor);
+}
+
 bool AMediaExtractorWrapper::advance() {
     if (mAMediaExtractor == NULL) {
         return false;
@@ -1120,11 +1168,27 @@
     return AMediaExtractor_advance(mAMediaExtractor);
 }
 
-status_t AMediaExtractorWrapper::seekTo(int64_t seekPosUs, SeekMode mode) {
+status_t AMediaExtractorWrapper::seekTo(int64_t seekPosUs, MediaSource::ReadOptions::SeekMode mode) {
     if (mAMediaExtractor == NULL) {
         return DEAD_OBJECT;
     }
-    return AMediaExtractor_seekTo(mAMediaExtractor, seekPosUs, mode);
+
+    SeekMode aMode;
+    switch (mode) {
+        case MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC: {
+            aMode = AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC;
+            break;
+        }
+        case MediaSource::ReadOptions::SEEK_NEXT_SYNC: {
+            aMode = AMEDIAEXTRACTOR_SEEK_NEXT_SYNC;
+            break;
+        }
+        default: {
+            aMode = AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC;
+            break;
+        }
+    }
+    return AMediaExtractor_seekTo(mAMediaExtractor, seekPosUs, aMode);
 }
 
 PsshInfo* AMediaExtractorWrapper::getPsshInfo() {
@@ -1141,4 +1205,43 @@
     return new AMediaCodecCryptoInfoWrapper(AMediaExtractor_getSampleCryptoInfo(mAMediaExtractor));
 }
 
+ssize_t AMediaDataSourceWrapper::AMediaDataSourceWrapper_getSize(void *userdata) {
+    DataSource *source = static_cast<DataSource *>(userdata);
+    off64_t size = -1;
+    source->getSize(&size);
+    return size;
+}
+
+ssize_t AMediaDataSourceWrapper::AMediaDataSourceWrapper_readAt(void *userdata, off64_t offset, void * buf, size_t size) {
+    DataSource *source = static_cast<DataSource *>(userdata);
+    return source->readAt(offset, buf, size);
+}
+
+void AMediaDataSourceWrapper::AMediaDataSourceWrapper_close(void *userdata) {
+    DataSource *source = static_cast<DataSource *>(userdata);
+    source->close();
+}
+
+AMediaDataSourceWrapper::AMediaDataSourceWrapper(const sp<DataSource> &dataSource)
+    : mDataSource(dataSource),
+      mAMediaDataSource(AMediaDataSource_new()) {
+    ALOGV("setDataSource (source: %p)", dataSource.get());
+    AMediaDataSource_setUserdata(mAMediaDataSource, dataSource.get());
+    AMediaDataSource_setReadAt(mAMediaDataSource, AMediaDataSourceWrapper_readAt);
+    AMediaDataSource_setGetSize(mAMediaDataSource, AMediaDataSourceWrapper_getSize);
+    AMediaDataSource_setClose(mAMediaDataSource, AMediaDataSourceWrapper_close);
+}
+
+AMediaDataSourceWrapper::~AMediaDataSourceWrapper() {
+    if (mAMediaDataSource == NULL) {
+        return;
+    }
+    AMediaDataSource_delete(mAMediaDataSource);
+    mAMediaDataSource = NULL;
+}
+
+AMediaDataSource* AMediaDataSourceWrapper::getAMediaDataSource() {
+    return mAMediaDataSource;
+}
+
 }  // namespace android
diff --git a/media/libmedia/include/media/CryptoHal.h b/media/libmedia/include/media/CryptoHal.h
index a5d8b43..ed16f44 100644
--- a/media/libmedia/include/media/CryptoHal.h
+++ b/media/libmedia/include/media/CryptoHal.h
@@ -20,14 +20,16 @@
 
 #include <android/hardware/drm/1.0/ICryptoFactory.h>
 #include <android/hardware/drm/1.0/ICryptoPlugin.h>
+#include <android/hardware/drm/1.1/ICryptoFactory.h>
 
 #include <media/ICrypto.h>
 #include <utils/KeyedVector.h>
 #include <utils/threads.h>
 
-using ::android::hardware::drm::V1_0::ICryptoFactory;
-using ::android::hardware::drm::V1_0::ICryptoPlugin;
-using ::android::hardware::drm::V1_0::SharedBuffer;
+namespace drm = ::android::hardware::drm;
+using drm::V1_0::ICryptoFactory;
+using drm::V1_0::ICryptoPlugin;
+using drm::V1_0::SharedBuffer;
 
 class IMemoryHeap;
 
diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h
index ec3a9b3..bf91ea9 100644
--- a/media/libmedia/include/media/DrmHal.h
+++ b/media/libmedia/include/media/DrmHal.h
@@ -18,10 +18,11 @@
 
 #define DRM_HAL_H_
 
-#include <android/hardware/drm/1.0/IDrmPlugin.h>
-#include <android/hardware/drm/1.1/IDrmPlugin.h>
-#include <android/hardware/drm/1.0/IDrmPluginListener.h>
 #include <android/hardware/drm/1.0/IDrmFactory.h>
+#include <android/hardware/drm/1.0/IDrmPlugin.h>
+#include <android/hardware/drm/1.0/IDrmPluginListener.h>
+#include <android/hardware/drm/1.1/IDrmFactory.h>
+#include <android/hardware/drm/1.1/IDrmPlugin.h>
 
 #include <media/DrmMetrics.h>
 #include <media/IDrm.h>
@@ -63,7 +64,8 @@
 
     virtual status_t destroyPlugin();
 
-    virtual status_t openSession(Vector<uint8_t> &sessionId);
+    virtual status_t openSession(DrmPlugin::SecurityLevel level,
+            Vector<uint8_t> &sessionId);
 
     virtual status_t closeSession(Vector<uint8_t> const &sessionId);
 
@@ -110,8 +112,6 @@
             uint32_t *maxSessions) const;
     virtual status_t getSecurityLevel(Vector<uint8_t> const &sessionId,
             DrmPlugin::SecurityLevel *level) const;
-    virtual status_t setSecurityLevel(Vector<uint8_t> const &sessionId,
-            const DrmPlugin::SecurityLevel& level);
 
     virtual status_t getPropertyString(String8 const &name, String8 &value ) const;
     virtual status_t getPropertyByteArray(String8 const &name,
@@ -119,7 +119,7 @@
     virtual status_t setPropertyString(String8 const &name, String8 const &value ) const;
     virtual status_t setPropertyByteArray(String8 const &name,
                                           Vector<uint8_t> const &value ) const;
-    virtual status_t getMetrics(MediaAnalyticsItem *item);
+    virtual status_t getMetrics(os::PersistableBundle *metrics);
 
     virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
                                         String8 const &algorithm);
@@ -203,7 +203,8 @@
 
     void writeByteArray(Parcel &obj, const hidl_vec<uint8_t>& array);
 
-    void reportMetrics() const;
+    void reportPluginMetrics() const;
+    void reportFrameworkMetrics() const;
     status_t getPropertyStringInternal(String8 const &name, String8 &value) const;
     status_t getPropertyByteArrayInternal(String8 const &name,
                                           Vector<uint8_t> &value) const;
diff --git a/media/libmedia/include/media/DrmMetrics.h b/media/libmedia/include/media/DrmMetrics.h
index bb7509b..5c2fdf2 100644
--- a/media/libmedia/include/media/DrmMetrics.h
+++ b/media/libmedia/include/media/DrmMetrics.h
@@ -20,6 +20,7 @@
 #include <map>
 
 #include <android/hardware/drm/1.0/types.h>
+#include <binder/PersistableBundle.h>
 #include <media/CounterMetric.h>
 #include <media/EventMetric.h>
 
@@ -28,19 +29,20 @@
 /**
  * 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.
+ * PersistableBundle.
  */
 class MediaDrmMetrics {
  public:
   explicit MediaDrmMetrics();
+  virtual ~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;
+  EventMetric<status_t> mGetKeyRequestTimeUs;
   // Count and timing of provideKeyResponse calls.
-  EventMetric<status_t> mProvideKeyResponseTiming;
+  EventMetric<status_t> mProvideKeyResponseTimeUs;
   // Count of getProvisionRequest calls.
   CounterMetric<status_t> mGetProvisionRequestCounter;
   // Count of provideProvisionResponse calls.
@@ -55,10 +57,37 @@
   // 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.
+  // Adds a session start time record.
+  void SetSessionStart(const Vector<uint8_t>& sessionId);
 
-  // Export the metrics to a MediaAnalyticsItem.
-  void Export(MediaAnalyticsItem* item);
+  // Adds a session end time record.
+  void SetSessionEnd(const Vector<uint8_t>& sessionId);
+
+  // The app package name is the application package name that is using the
+  // instance. The app package name is held here for convenience. It is not
+  // serialized or exported with the metrics.
+  void SetAppPackageName(const String8& appPackageName) { mAppPackageName = appPackageName; }
+  const String8& GetAppPackageName() { return mAppPackageName; }
+
+  // Export the metrics to a PersistableBundle.
+  void Export(os::PersistableBundle* metricsBundle);
+
+  // Get the serialized metrics. Metrics are formatted as a serialized
+  // DrmFrameworkMetrics proto. If there is a failure serializing the metrics,
+  // this returns an error. The parameter |serlializedMetrics| is owned by the
+  // caller and must not be null.
+  status_t GetSerializedMetrics(std::string* serializedMetrics);
+
+ protected:
+  // This is visible for testing only.
+  virtual int64_t GetCurrentTimeMs();
+
+ private:
+  // Session lifetimes. A pair of values representing the milliseconds since
+  // epoch, UTC. The first value is the start time, the second is the end time.
+  std::map<std::string, std::pair<int64_t, int64_t>> mSessionLifespans;
+
+  String8 mAppPackageName;
 };
 
 }  // namespace android
diff --git a/media/libmedia/include/media/IDrm.h b/media/libmedia/include/media/IDrm.h
index 994cade..a19b06b 100644
--- a/media/libmedia/include/media/IDrm.h
+++ b/media/libmedia/include/media/IDrm.h
@@ -15,6 +15,7 @@
  */
 
 #include <binder/IInterface.h>
+#include <binder/PersistableBundle.h>
 #include <media/stagefright/foundation/ABase.h>
 #include <media/drm/DrmAPI.h>
 #include <media/IDrmClient.h>
@@ -40,7 +41,8 @@
 
     virtual status_t destroyPlugin() = 0;
 
-    virtual status_t openSession(Vector<uint8_t> &sessionId) = 0;
+    virtual status_t openSession(DrmPlugin::SecurityLevel securityLevel,
+            Vector<uint8_t> &sessionId) = 0;
 
     virtual status_t closeSession(Vector<uint8_t> const &sessionId) = 0;
 
@@ -88,8 +90,6 @@
             uint32_t *maxSessions) const = 0;
     virtual status_t getSecurityLevel(Vector<uint8_t> const &sessionId,
             DrmPlugin::SecurityLevel *level) const = 0;
-    virtual status_t setSecurityLevel(Vector<uint8_t> const &sessionId,
-            const DrmPlugin::SecurityLevel& level) = 0;
 
     virtual status_t getPropertyString(String8 const &name, String8 &value) const = 0;
     virtual status_t getPropertyByteArray(String8 const &name,
@@ -99,7 +99,7 @@
     virtual status_t setPropertyByteArray(String8 const &name,
                                           Vector<uint8_t> const &value) const = 0;
 
-    virtual status_t getMetrics(MediaAnalyticsItem *item) = 0;
+    virtual status_t getMetrics(os::PersistableBundle *metrics) = 0;
 
     virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
                                         String8 const &algorithm) = 0;
diff --git a/media/libmedia/include/media/NdkWrapper.h b/media/libmedia/include/media/NdkWrapper.h
index 00e0fd4..49d728d 100644
--- a/media/libmedia/include/media/NdkWrapper.h
+++ b/media/libmedia/include/media/NdkWrapper.h
@@ -18,6 +18,9 @@
 
 #define NDK_WRAPPER_H_
 
+#include <media/DataSource.h>
+#include <media/MediaSource.h>
+#include <media/NdkMediaDataSource.h>
 #include <media/NdkMediaError.h>
 #include <media/NdkMediaExtractor.h>
 #include <media/hardware/CryptoAPI.h>
@@ -286,25 +289,35 @@
 
     status_t setDataSource(const char *location);
 
+    status_t setDataSource(AMediaDataSource *);
+
     size_t getTrackCount();
 
+    sp<AMediaFormatWrapper> getFormat();
+
     sp<AMediaFormatWrapper> getTrackFormat(size_t idx);
 
     status_t selectTrack(size_t idx);
 
     status_t unselectTrack(size_t idx);
 
+    status_t selectSingleTrack(size_t idx);
+
     ssize_t readSampleData(const sp<ABuffer> &buffer);
 
+    ssize_t getSampleSize();
+
     uint32_t getSampleFlags();
 
     int getSampleTrackIndex();
 
     int64_t getSampleTime();
 
+    int64_t getCachedDuration();
+
     bool advance();
 
-    status_t seekTo(int64_t seekPosUs, SeekMode mode);
+    status_t seekTo(int64_t seekPosUs, MediaSource::ReadOptions::SeekMode mode);
 
     // the returned PsshInfo is still owned by this wrapper.
     PsshInfo* getPsshInfo();
@@ -320,6 +333,31 @@
     DISALLOW_EVIL_CONSTRUCTORS(AMediaExtractorWrapper);
 };
 
+struct AMediaDataSourceWrapper : public RefBase {
+
+    static status_t translate_error(media_status_t err);
+
+    static ssize_t AMediaDataSourceWrapper_getSize(void *userdata);
+
+    static ssize_t AMediaDataSourceWrapper_readAt(void *userdata, off64_t offset, void * buf, size_t size);
+
+    static void AMediaDataSourceWrapper_close(void *userdata);
+
+    AMediaDataSourceWrapper(const sp<DataSource> &dataSource);
+
+    AMediaDataSource *getAMediaDataSource();
+
+protected:
+    virtual ~AMediaDataSourceWrapper();
+
+private:
+    sp<DataSource> mDataSource;
+
+    AMediaDataSource *mAMediaDataSource;
+
+    DISALLOW_EVIL_CONSTRUCTORS(AMediaDataSourceWrapper);
+};
+
 }  // namespace android
 
 #endif  // NDK_WRAPPER_H_
diff --git a/media/libmedia/include/media/OMXBuffer.h b/media/libmedia/include/media/OMXBuffer.h
index 3e84858..9c9f5e7 100644
--- a/media/libmedia/include/media/OMXBuffer.h
+++ b/media/libmedia/include/media/OMXBuffer.h
@@ -91,6 +91,7 @@
 
 private:
     friend struct OMXNodeInstance;
+    friend struct C2OMXNode;
 
     // This is needed temporarily for OMX HIDL transition.
     friend inline bool (::android::hardware::media::omx::V1_0::implementation::
diff --git a/media/libmediaextractor/MediaBufferGroup.cpp b/media/libmediaextractor/MediaBufferGroup.cpp
index cb62d92..22f01a5 100644
--- a/media/libmediaextractor/MediaBufferGroup.cpp
+++ b/media/libmediaextractor/MediaBufferGroup.cpp
@@ -17,9 +17,13 @@
 #define LOG_TAG "MediaBufferGroup"
 #include <utils/Log.h>
 
+#include <list>
+
+#include <binder/MemoryDealer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaBufferGroup.h>
+#include <utils/threads.h>
 
 namespace android {
 
@@ -32,17 +36,26 @@
 static const size_t kSharedMemoryThreshold = MIN(
         (size_t)MediaBuffer::kSharedMemThreshold, (size_t)(4 * 1024));
 
-MediaBufferGroup::MediaBufferGroup(size_t growthLimit) :
-    mGrowthLimit(growthLimit) {
+struct MediaBufferGroup::InternalData {
+    Mutex mLock;
+    Condition mCondition;
+    size_t mGrowthLimit;  // Do not automatically grow group larger than this.
+    std::list<MediaBuffer *> mBuffers;
+};
+
+MediaBufferGroup::MediaBufferGroup(size_t growthLimit)
+    : mInternal(new InternalData()) {
+    mInternal->mGrowthLimit = growthLimit;
 }
 
 MediaBufferGroup::MediaBufferGroup(size_t buffers, size_t buffer_size, size_t growthLimit)
-    : mGrowthLimit(growthLimit) {
+    : mInternal(new InternalData()) {
+    mInternal->mGrowthLimit = growthLimit;
 
-    if (mGrowthLimit > 0 && buffers > mGrowthLimit) {
+    if (mInternal->mGrowthLimit > 0 && buffers > mInternal->mGrowthLimit) {
         ALOGW("Preallocated buffers %zu > growthLimit %zu, increasing growthLimit",
-                buffers, mGrowthLimit);
-        mGrowthLimit = buffers;
+                buffers, mInternal->mGrowthLimit);
+        mInternal->mGrowthLimit = buffers;
     }
 
     if (buffer_size >= kSharedMemoryThreshold) {
@@ -81,7 +94,7 @@
 }
 
 MediaBufferGroup::~MediaBufferGroup() {
-    for (MediaBuffer *buffer : mBuffers) {
+    for (MediaBuffer *buffer : mInternal->mBuffers) {
         if (buffer->refcount() != 0) {
             const int localRefcount = buffer->localRefcount();
             const int remoteRefcount = buffer->remoteRefcount();
@@ -103,34 +116,35 @@
         buffer->setObserver(nullptr);
         buffer->release();
     }
+    delete mInternal;
 }
 
 void MediaBufferGroup::add_buffer(MediaBuffer *buffer) {
-    Mutex::Autolock autoLock(mLock);
+    Mutex::Autolock autoLock(mInternal->mLock);
 
     // if we're above our growth limit, release buffers if we can
-    for (auto it = mBuffers.begin();
-            mGrowthLimit > 0
-            && mBuffers.size() >= mGrowthLimit
-            && it != mBuffers.end();) {
+    for (auto it = mInternal->mBuffers.begin();
+            mInternal->mGrowthLimit > 0
+            && mInternal->mBuffers.size() >= mInternal->mGrowthLimit
+            && it != mInternal->mBuffers.end();) {
         if ((*it)->refcount() == 0) {
             (*it)->setObserver(nullptr);
             (*it)->release();
-            it = mBuffers.erase(it);
+            it = mInternal->mBuffers.erase(it);
         } else {
             ++it;
         }
     }
 
     buffer->setObserver(this);
-    mBuffers.emplace_back(buffer);
+    mInternal->mBuffers.emplace_back(buffer);
 }
 
 bool MediaBufferGroup::has_buffers() {
-    if (mBuffers.size() < mGrowthLimit) {
+    if (mInternal->mBuffers.size() < mInternal->mGrowthLimit) {
         return true; // We can add more buffers internally.
     }
-    for (MediaBuffer *buffer : mBuffers) {
+    for (MediaBuffer *buffer : mInternal->mBuffers) {
         if (buffer->refcount() == 0) {
             return true;
         }
@@ -140,12 +154,12 @@
 
 status_t MediaBufferGroup::acquire_buffer(
         MediaBuffer **out, bool nonBlocking, size_t requestedSize) {
-    Mutex::Autolock autoLock(mLock);
+    Mutex::Autolock autoLock(mInternal->mLock);
     for (;;) {
         size_t smallest = requestedSize;
         MediaBuffer *buffer = nullptr;
-        auto free = mBuffers.end();
-        for (auto it = mBuffers.begin(); it != mBuffers.end(); ++it) {
+        auto free = mInternal->mBuffers.end();
+        for (auto it = mInternal->mBuffers.begin(); it != mInternal->mBuffers.end(); ++it) {
             if ((*it)->refcount() == 0) {
                 const size_t size = (*it)->size();
                 if (size >= requestedSize) {
@@ -159,7 +173,8 @@
             }
         }
         if (buffer == nullptr
-                && (free != mBuffers.end() || mBuffers.size() < mGrowthLimit)) {
+                && (free != mInternal->mBuffers.end()
+                    || mInternal->mBuffers.size() < mInternal->mGrowthLimit)) {
             // We alloc before we free so failure leaves group unchanged.
             const size_t allocateSize = requestedSize < SIZE_MAX / 3 * 2 /* NB: ordering */ ?
                     requestedSize * 3 / 2 : requestedSize;
@@ -170,7 +185,7 @@
                 buffer = nullptr;
             } else {
                 buffer->setObserver(this);
-                if (free != mBuffers.end()) {
+                if (free != mInternal->mBuffers.end()) {
                     ALOGV("reallocate buffer, requested size %zu vs available %zu",
                             requestedSize, (*free)->size());
                     (*free)->setObserver(nullptr);
@@ -178,7 +193,7 @@
                     *free = buffer; // in-place replace
                 } else {
                     ALOGV("allocate buffer, requested size %zu", requestedSize);
-                    mBuffers.emplace_back(buffer);
+                    mInternal->mBuffers.emplace_back(buffer);
                 }
             }
         }
@@ -193,14 +208,18 @@
             return WOULD_BLOCK;
         }
         // All buffers are in use, block until one of them is returned.
-        mCondition.wait(mLock);
+        mInternal->mCondition.wait(mInternal->mLock);
     }
     // Never gets here.
 }
 
+size_t MediaBufferGroup::buffers() const {
+    return mInternal->mBuffers.size();
+}
+
 void MediaBufferGroup::signalBufferReturned(MediaBuffer *) {
-    Mutex::Autolock autoLock(mLock);
-    mCondition.signal();
+    Mutex::Autolock autoLock(mInternal->mLock);
+    mInternal->mCondition.signal();
 }
 
 }  // namespace android
diff --git a/media/libmediaextractor/MetaData.cpp b/media/libmediaextractor/MetaData.cpp
index 98cddbe..69beea1 100644
--- a/media/libmediaextractor/MetaData.cpp
+++ b/media/libmediaextractor/MetaData.cpp
@@ -146,7 +146,7 @@
 /**
  * Note that the returned pointer becomes invalid when additional metadata is set.
  */
-bool MetaData::findCString(uint32_t key, const char **value) {
+bool MetaData::findCString(uint32_t key, const char **value) const {
     uint32_t type;
     const void *data;
     size_t size;
@@ -159,7 +159,7 @@
     return true;
 }
 
-bool MetaData::findInt32(uint32_t key, int32_t *value) {
+bool MetaData::findInt32(uint32_t key, int32_t *value) const {
     uint32_t type = 0;
     const void *data;
     size_t size;
@@ -174,7 +174,7 @@
     return true;
 }
 
-bool MetaData::findInt64(uint32_t key, int64_t *value) {
+bool MetaData::findInt64(uint32_t key, int64_t *value) const {
     uint32_t type = 0;
     const void *data;
     size_t size;
@@ -189,7 +189,7 @@
     return true;
 }
 
-bool MetaData::findFloat(uint32_t key, float *value) {
+bool MetaData::findFloat(uint32_t key, float *value) const {
     uint32_t type = 0;
     const void *data;
     size_t size;
@@ -204,7 +204,7 @@
     return true;
 }
 
-bool MetaData::findPointer(uint32_t key, void **value) {
+bool MetaData::findPointer(uint32_t key, void **value) const {
     uint32_t type = 0;
     const void *data;
     size_t size;
@@ -222,7 +222,7 @@
 bool MetaData::findRect(
         uint32_t key,
         int32_t *left, int32_t *top,
-        int32_t *right, int32_t *bottom) {
+        int32_t *right, int32_t *bottom) const {
     uint32_t type = 0;
     const void *data;
     size_t size;
diff --git a/media/libmediaextractor/include/media/MediaExtractor.h b/media/libmediaextractor/include/media/MediaExtractor.h
index 73c5f10..2e65620 100644
--- a/media/libmediaextractor/include/media/MediaExtractor.h
+++ b/media/libmediaextractor/include/media/MediaExtractor.h
@@ -29,8 +29,6 @@
 
 class DataSourceBase;
 class MetaData;
-class String8;
-struct AMessage;
 struct MediaSourceBase;
 
 
@@ -87,14 +85,16 @@
     virtual const char * name() { return "<unspecified>"; }
 
     typedef MediaExtractor* (*CreatorFunc)(
-            DataSourceBase *source, const sp<AMessage> &meta);
+            DataSourceBase *source, void *meta);
+    typedef void (*FreeMetaFunc)(void *meta);
 
-    // The sniffer can optionally fill in "meta" with an AMessage containing
-    // a dictionary of values that helps the corresponding extractor initialize
-    // its state without duplicating effort already exerted by the sniffer.
+    // The sniffer can optionally fill in an opaque object, "meta", that helps
+    // the corresponding extractor initialize its state without duplicating
+    // effort already exerted by the sniffer. If "freeMeta" is given, it will be
+    // called against the opaque object when it is no longer used.
     typedef CreatorFunc (*SnifferFunc)(
-            DataSourceBase *source, String8 *mimeType,
-            float *confidence, sp<AMessage> *meta);
+            DataSourceBase *source, float *confidence,
+            void **meta, FreeMetaFunc *freeMeta);
 
     typedef struct {
         const uint8_t b[16];
diff --git a/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h b/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h
index 3041181..63d0a18 100644
--- a/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h
+++ b/media/libmediaextractor/include/media/stagefright/MediaBufferGroup.h
@@ -19,8 +19,6 @@
 #define MEDIA_BUFFER_GROUP_H_
 
 #include <media/stagefright/MediaBuffer.h>
-#include <utils/Errors.h>
-#include <utils/threads.h>
 
 namespace android {
 
@@ -50,7 +48,7 @@
     status_t acquire_buffer(
             MediaBuffer **buffer, bool nonBlocking = false, size_t requestedSize = 0);
 
-    size_t buffers() const { return mBuffers.size(); }
+    size_t buffers() const;
 
     // If buffer is nullptr, have acquire_buffer() check for remote release.
     virtual void signalBufferReturned(MediaBuffer *buffer);
@@ -58,10 +56,8 @@
 private:
     friend class MediaBuffer;
 
-    Mutex mLock;
-    Condition mCondition;
-    size_t mGrowthLimit;  // Do not automatically grow group larger than this.
-    std::list<MediaBuffer *> mBuffers;
+    struct InternalData;
+    InternalData *mInternal;
 
     MediaBufferGroup(const MediaBufferGroup &);
     MediaBufferGroup &operator=(const MediaBufferGroup &);
diff --git a/media/libmediaextractor/include/media/stagefright/MetaData.h b/media/libmediaextractor/include/media/stagefright/MetaData.h
index e4a84b7..c2a2d1e 100644
--- a/media/libmediaextractor/include/media/stagefright/MetaData.h
+++ b/media/libmediaextractor/include/media/stagefright/MetaData.h
@@ -258,16 +258,16 @@
             int32_t left, int32_t top,
             int32_t right, int32_t bottom);
 
-    bool findCString(uint32_t key, const char **value);
-    bool findInt32(uint32_t key, int32_t *value);
-    bool findInt64(uint32_t key, int64_t *value);
-    bool findFloat(uint32_t key, float *value);
-    bool findPointer(uint32_t key, void **value);
+    bool findCString(uint32_t key, const char **value) const;
+    bool findInt32(uint32_t key, int32_t *value) const;
+    bool findInt64(uint32_t key, int64_t *value) const;
+    bool findFloat(uint32_t key, float *value) const;
+    bool findPointer(uint32_t key, void **value) const;
 
     bool findRect(
             uint32_t key,
             int32_t *left, int32_t *top,
-            int32_t *right, int32_t *bottom);
+            int32_t *right, int32_t *bottom) const;
 
     bool setData(uint32_t key, uint32_t type, const void *data, size_t size);
 
diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp
new file mode 100644
index 0000000..17fa01c
--- /dev/null
+++ b/media/libmediaplayer2/Android.bp
@@ -0,0 +1,79 @@
+cc_library_headers {
+    name: "libmediaplayer2_headers",
+    vendor_available: true,
+    export_include_dirs: ["include"],
+}
+
+cc_library {
+    name: "libmediaplayer2",
+
+    srcs: [
+        "JAudioTrack.cpp",
+        "MediaPlayer2Manager.cpp",
+        "mediaplayer2.cpp",
+    ],
+
+    shared_libs: [
+        "libandroid_runtime",
+        "libaudioclient",
+        "libbinder",
+        "libcutils",
+        "libgui",
+        "liblog",
+        "libmedia_omx",
+        "libmedia_player2_util",
+        "libmediaextractor",
+        "libstagefright_foundation",
+        "libui",
+        "libutils",
+
+        "libcrypto",
+        "libmediadrm",
+        "libmediametrics",
+        "libmediandk",
+        "libmediautils",
+        "libmemunreachable",
+        "libnativewindow",
+        "libpowermanager",
+        "libstagefright_httplive",
+        "libstagefright_player2",
+    ],
+
+    export_shared_lib_headers: [
+        "libaudioclient",
+        "libbinder",
+        "libmedia_omx",
+    ],
+
+    header_libs: [
+        "media_plugin_headers",
+    ],
+
+    static_libs: [
+        "libmedia_helper",
+        "libstagefright_nuplayer2",
+        "libstagefright_rtsp",
+        "libstagefright_timedtext",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wno-error=deprecated-declarations",
+        "-Wall",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "unsigned-integer-overflow",
+            "signed-integer-overflow",
+        ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
+    },
+}
diff --git a/media/libmedia/JAudioTrack.cpp b/media/libmediaplayer2/JAudioTrack.cpp
similarity index 82%
rename from media/libmedia/JAudioTrack.cpp
rename to media/libmediaplayer2/JAudioTrack.cpp
index 99da0f7..ac0cc57 100644
--- a/media/libmedia/JAudioTrack.cpp
+++ b/media/libmediaplayer2/JAudioTrack.cpp
@@ -18,7 +18,7 @@
 
 #include "media/JAudioAttributes.h"
 #include "media/JAudioFormat.h"
-#include "media/JAudioTrack.h"
+#include "mediaplayer2/JAudioTrack.h"
 
 #include <android_media_AudioErrors.h>
 #include <android_runtime/AndroidRuntime.h>
@@ -32,6 +32,8 @@
         uint32_t sampleRate,                          // AudioFormat && bufferSizeInBytes
         audio_format_t format,                        // AudioFormat && bufferSizeInBytes
         audio_channel_mask_t channelMask,             // AudioFormat && bufferSizeInBytes
+        callback_t cbf,                               // Offload
+        void* user,                                   // Offload
         size_t frameCount,                            // bufferSizeInBytes
         audio_session_t sessionId,                    // AudioTrack
         const audio_attributes_t* pAttributes,        // AudioAttributes
@@ -90,8 +92,27 @@
         jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetSessionId, sessionId);
     }
 
+    if (cbf != NULL) {
+        jmethodID jSetOffloadedPlayback = env->GetMethodID(jBuilderCls, "setOffloadedPlayback",
+                "(Z)Landroid/media/AudioTrack$Builder;");
+        jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetOffloadedPlayback, true);
+        mFlags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
+    }
+
     jmethodID jBuild = env->GetMethodID(jBuilderCls, "build", "()Landroid/media/AudioTrack;");
     mAudioTrackObj = env->CallObjectMethod(jBuilderObj, jBuild);
+
+    if (cbf != NULL) {
+        // Set offload mode callback
+        jobject jStreamEventCallbackObj = createStreamEventCallback(cbf, user);
+        jobject jExecutorObj = createCallbackExecutor();
+        jmethodID jSetStreamEventCallback = env->GetMethodID(
+                jAudioTrackCls,
+                "setStreamEventCallback",
+                "(Ljava/util/concurrent/Executor;Landroid/media/AudioTrack$StreamEventCallback;)V");
+        env->CallVoidMethod(
+                mAudioTrackObj, jSetStreamEventCallback, jExecutorObj, jStreamEventCallbackObj);
+    }
 }
 
 JAudioTrack::~JAudioTrack() {
@@ -160,6 +181,11 @@
     return true;
 }
 
+status_t JAudioTrack::getTimestamp(ExtendedTimestamp *timestamp __unused) {
+    // TODO: Implement this after appropriate Java AudioTrack method is available.
+    return NO_ERROR;
+}
+
 status_t JAudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate) {
     // TODO: existing native AudioTrack returns INVALID_OPERATION on offload/direct/fast tracks.
     // Should we do the same thing?
@@ -442,6 +468,80 @@
     return routedDeviceId;
 }
 
+audio_session_t JAudioTrack::getAudioSessionId() {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jmethodID jGetAudioSessionId = env->GetMethodID(mAudioTrackCls, "getAudioSessionId", "()I");
+    jint sessionId = env->CallIntMethod(mAudioTrackObj, jGetAudioSessionId);
+    return (audio_session_t) sessionId;
+}
+
+status_t JAudioTrack::setOutputDevice(audio_port_handle_t deviceId) {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jclass jMP2ImplCls = env->FindClass("android/media/MediaPlayer2Impl");
+    jmethodID jSetAudioOutputDeviceById = env->GetMethodID(
+            jMP2ImplCls, "setAudioOutputDeviceById", "(Landroid/media/AudioTrack;I)Z");
+    jboolean result = env->CallStaticBooleanMethod(
+            jMP2ImplCls, jSetAudioOutputDeviceById, mAudioTrackObj, deviceId);
+    return result == true ? NO_ERROR : BAD_VALUE;
+}
+
+status_t JAudioTrack::pendingDuration(int32_t *msec) {
+    if (msec == nullptr) {
+        return BAD_VALUE;
+    }
+
+    bool isPurePcmData = audio_is_linear_pcm(format()) && (getFlags() & AUDIO_FLAG_HW_AV_SYNC) == 0;
+    if (!isPurePcmData) {
+        return INVALID_OPERATION;
+    }
+
+    // TODO: Need to know the difference btw. client and server time.
+    // If getTimestamp(ExtendedTimestamp) is ready, and un-comment below and modify appropriately.
+    // (copied from AudioTrack.cpp)
+
+//    ExtendedTimestamp ets;
+//    ExtendedTimestamp::LOCATION location = ExtendedTimestamp::LOCATION_SERVER;
+//    if (getTimestamp_l(&ets) == OK && ets.mTimeNs[location] > 0) {
+//        int64_t diff = ets.mPosition[ExtendedTimestamp::LOCATION_CLIENT]
+//                - ets.mPosition[location];
+//        if (diff < 0) {
+//            *msec = 0;
+//        } else {
+//            // ms is the playback time by frames
+//            int64_t ms = (int64_t)((double)diff * 1000 /
+//                    ((double)mSampleRate * mPlaybackRate.mSpeed));
+//            // clockdiff is the timestamp age (negative)
+//            int64_t clockdiff = (mState != STATE_ACTIVE) ? 0 :
+//                    ets.mTimeNs[location]
+//                    + ets.mTimebaseOffset[ExtendedTimestamp::TIMEBASE_MONOTONIC]
+//                    - systemTime(SYSTEM_TIME_MONOTONIC);
+//
+//            //ALOGV("ms: %lld  clockdiff: %lld", (long long)ms, (long long)clockdiff);
+//            static const int NANOS_PER_MILLIS = 1000000;
+//            *msec = (int32_t)(ms + clockdiff / NANOS_PER_MILLIS);
+//        }
+//        return NO_ERROR;
+//    }
+
+    return NO_ERROR;
+}
+
+status_t JAudioTrack::addAudioDeviceCallback(
+        const sp<AudioSystem::AudioDeviceCallback>& callback __unused) {
+    // TODO: Implement this after appropriate Java AudioTrack method is available.
+    return NO_ERROR;
+}
+
+status_t JAudioTrack::removeAudioDeviceCallback(
+        const sp<AudioSystem::AudioDeviceCallback>& callback __unused) {
+    // TODO: Implement this after appropriate Java AudioTrack method is available.
+    return NO_ERROR;
+}
+
+/////////////////////////////////////////////////////////////
+///                Private method begins                  ///
+/////////////////////////////////////////////////////////////
+
 jobject JAudioTrack::createVolumeShaperConfigurationObj(
         const sp<media::VolumeShaper::Configuration>& config) {
 
@@ -546,6 +646,24 @@
     return env->CallObjectMethod(jBuilderObj, jBuild);
 }
 
+jobject JAudioTrack::createStreamEventCallback(callback_t cbf, void* user) {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jclass jCallbackCls = env->FindClass("android/media/MediaPlayer2Impl$StreamEventCallback");
+    jmethodID jCallbackCtor = env->GetMethodID(jCallbackCls, "<init>", "(JJJ)V");
+    jobject jCallbackObj = env->NewObject(jCallbackCls, jCallbackCtor, this, cbf, user);
+    return jCallbackObj;
+}
+
+jobject JAudioTrack::createCallbackExecutor() {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jclass jExecutorsCls = env->FindClass("java/util/concurrent/Executors");
+    jmethodID jNewSingleThreadExecutor = env->GetStaticMethodID(jExecutorsCls,
+            "newSingleThreadExecutor", "()Ljava/util/concurrent/ExecutorService;");
+    jobject jSingleThreadExecutorObj =
+            env->CallStaticObjectMethod(jExecutorsCls, jNewSingleThreadExecutor);
+    return jSingleThreadExecutorObj;
+}
+
 status_t JAudioTrack::javaToNativeStatus(int javaStatus) {
     switch (javaStatus) {
     case AUDIO_JAVA_SUCCESS:
diff --git a/media/libmedia/MediaPlayer2Manager.cpp b/media/libmediaplayer2/MediaPlayer2Manager.cpp
similarity index 98%
rename from media/libmedia/MediaPlayer2Manager.cpp
rename to media/libmediaplayer2/MediaPlayer2Manager.cpp
index 4c0a7ac..44df2ac 100644
--- a/media/libmedia/MediaPlayer2Manager.cpp
+++ b/media/libmediaplayer2/MediaPlayer2Manager.cpp
@@ -47,8 +47,6 @@
 #include <media/AudioPolicyHelper.h>
 #include <media/DataSourceDesc.h>
 #include <media/MediaHTTPService.h>
-#include <media/MediaPlayer2EngineClient.h>
-#include <media/MediaPlayer2Interface.h>
 #include <media/Metadata.h>
 #include <media/AudioTrack.h>
 #include <media/MemoryLeakTrackUtil.h>
@@ -63,6 +61,9 @@
 #include <media/stagefright/SurfaceUtils.h>
 #include <mediautils/BatteryNotifier.h>
 
+#include <mediaplayer2/MediaPlayer2EngineClient.h>
+#include <mediaplayer2/MediaPlayer2Interface.h>
+
 #include <memunreachable/memunreachable.h>
 #include <system/audio.h>
 #include <system/window.h>
@@ -511,7 +512,7 @@
 }
 
 bool MediaPlayer2Manager::Client::init() {
-    sp<MediaPlayer2Interface> p = new NuPlayer2Driver(mPid);
+    sp<MediaPlayer2Interface> p = new NuPlayer2Driver(mPid, mUid);
     status_t init_result = p->initCheck();
     if (init_result != NO_ERROR) {
         ALOGE("Failed to create player object, initCheck failed(%d)", init_result);
@@ -519,7 +520,6 @@
     }
 
     p->setNotifyCallback(this, notify);
-    p->setUID(mUid);
     mAudioDeviceUpdatedListener = new AudioDeviceUpdatedNotifier(p);
     mAudioOutput = new AudioOutput(mAudioSessionId, mUid,
             mPid, mAudioAttributes, mAudioDeviceUpdatedListener);
@@ -580,7 +580,7 @@
         audio_port_handle_t deviceId) {
     sp<MediaPlayer2Interface> listener = mListener.promote();
     if (listener != NULL) {
-        listener->sendEvent(MEDIA2_AUDIO_ROUTING_CHANGED, audioIo, deviceId);
+        listener->sendEvent(0, MEDIA2_AUDIO_ROUTING_CHANGED, audioIo, deviceId);
     } else {
         ALOGW("listener for process %d death is gone", MEDIA2_AUDIO_ROUTING_CHANGED);
     }
@@ -1031,7 +1031,8 @@
 }
 
 void MediaPlayer2Manager::Client::notify(
-        const wp<MediaPlayer2Engine> &listener, int msg, int ext1, int ext2, const Parcel *obj)
+        const wp<MediaPlayer2Engine> &listener, int64_t srcId,
+        int msg, int ext1, int ext2, const Parcel *obj)
 {
     sp<MediaPlayer2Engine> spListener = listener.promote();
     if (spListener == NULL) {
@@ -1063,9 +1064,9 @@
         }
         if (nc != NULL) {
             if (errStartNext == NO_ERROR) {
-                nc->notify(MEDIA2_INFO, MEDIA2_INFO_STARTED_AS_NEXT, 0, obj);
+                nc->notify(srcId, MEDIA2_INFO, MEDIA2_INFO_STARTED_AS_NEXT, 0, obj);
             } else {
-                nc->notify(MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN , 0, obj);
+                nc->notify(srcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN , 0, obj);
                 ALOGE("gapless:start playback for next track failed, err(%d)", errStartNext);
             }
         }
@@ -1085,8 +1086,9 @@
     }
 
     if (c != NULL) {
-        ALOGV("[%d] notify (%p, %d, %d, %d)", client->mConnId, spListener.get(), msg, ext1, ext2);
-        c->notify(msg, ext1, ext2, obj);
+        ALOGV("[%d] notify (%p, %lld, %d, %d, %d)", client->mConnId, spListener.get(),
+              (long long)srcId, msg, ext1, ext2);
+        c->notify(srcId, msg, ext1, ext2, obj);
     }
 }
 
diff --git a/media/libmedia/MediaPlayer2Manager.h b/media/libmediaplayer2/MediaPlayer2Manager.h
similarity index 98%
rename from media/libmedia/MediaPlayer2Manager.h
rename to media/libmediaplayer2/MediaPlayer2Manager.h
index 95b875b..7e6f0de 100644
--- a/media/libmedia/MediaPlayer2Manager.h
+++ b/media/libmediaplayer2/MediaPlayer2Manager.h
@@ -26,10 +26,10 @@
 #include <utils/String8.h>
 #include <utils/Vector.h>
 
-#include <media/MediaPlayer2Engine.h>
-#include <media/MediaPlayer2Interface.h>
 #include <media/Metadata.h>
 #include <media/stagefright/foundation/ABase.h>
+#include <mediaplayer2/MediaPlayer2Engine.h>
+#include <mediaplayer2/MediaPlayer2Interface.h>
 
 #include <system/audio.h>
 
@@ -276,8 +276,8 @@
 
         virtual status_t        setDataSource(const sp<DataSourceDesc> &dsd);
 
-        static  void            notify(const wp<MediaPlayer2Engine> &listener, int msg,
-                                       int ext1, int ext2, const Parcel *obj);
+        static  void            notify(const wp<MediaPlayer2Engine> &listener, int64_t srcId,
+                                       int msg, int ext1, int ext2, const Parcel *obj);
 
                 pid_t           pid() const { return mPid; }
         virtual status_t        dump(int fd, const Vector<String16>& args);
diff --git a/media/libmedia/include/media/JAudioTrack.h b/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h
similarity index 64%
rename from media/libmedia/include/media/JAudioTrack.h
rename to media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h
index 10fa5e8..301825b 100644
--- a/media/libmedia/include/media/JAudioTrack.h
+++ b/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h
@@ -19,6 +19,7 @@
 
 #include <jni.h>
 #include <media/AudioResamplerPublic.h>
+#include <media/AudioSystem.h>
 #include <media/VolumeShaper.h>
 #include <system/audio.h>
 #include <utils/Errors.h>
@@ -31,6 +32,42 @@
 class JAudioTrack {
 public:
 
+    /* Events used by AudioTrack callback function (callback_t).
+     * Keep in sync with frameworks/base/media/java/android/media/AudioTrack.java NATIVE_EVENT_*.
+     */
+    enum event_type {
+        EVENT_MORE_DATA = 0,        // Request to write more data to buffer.
+        EVENT_NEW_IAUDIOTRACK = 6,  // IAudioTrack was re-created, either due to re-routing and
+                                    // voluntary invalidation by mediaserver, or mediaserver crash.
+        EVENT_STREAM_END = 7,       // Sent after all the buffers queued in AF and HW are played
+                                    // back (after stop is called) for an offloaded track.
+    };
+
+    class Buffer
+    {
+    public:
+        size_t      mSize;        // input/output in bytes.
+        void*       mData;        // pointer to the audio data.
+    };
+
+    /* As a convenience, if a callback is supplied, a handler thread
+     * is automatically created with the appropriate priority. This thread
+     * invokes the callback when a new buffer becomes available or various conditions occur.
+     *
+     * Parameters:
+     *
+     * event:   type of event notified (see enum AudioTrack::event_type).
+     * user:    Pointer to context for use by the callback receiver.
+     * info:    Pointer to optional parameter according to event type:
+     *          - EVENT_MORE_DATA: pointer to JAudioTrack::Buffer struct. The callback must not
+     *            write more bytes than indicated by 'size' field and update 'size' if fewer bytes
+     *            are written.
+     *          - EVENT_NEW_IAUDIOTRACK: unused.
+     *          - EVENT_STREAM_END: unused.
+     */
+
+    typedef void (*callback_t)(int event, void* user, void *info);
+
     /* Creates an JAudioTrack object for non-offload mode.
      * Once created, the track needs to be started before it can be used.
      * Unspecified values are set to appropriate default values.
@@ -49,6 +86,9 @@
      *                     output sink.
      *                     (TODO: How can we check whether a format is supported?)
      * channelMask:        Channel mask, such that audio_is_output_channel(channelMask) is true.
+     * cbf:                Callback function. If not null, this function is called periodically
+     *                     to provide new data and inform of marker, position updates, etc.
+     * user:               Context for use by the callback receiver.
      * frameCount:         Minimum size of track PCM buffer in frames. This defines the
      *                     application's contribution to the latency of the track.
      *                     The actual size selected by the JAudioTrack could be larger if the
@@ -68,35 +108,20 @@
                 uint32_t sampleRate,
                 audio_format_t format,
                 audio_channel_mask_t channelMask,
+                callback_t cbf,
+                void* user,
                 size_t frameCount = 0,
                 audio_session_t sessionId  = AUDIO_SESSION_ALLOCATE,
                 const audio_attributes_t* pAttributes = NULL,
                 float maxRequiredSpeed = 1.0f);
 
     /*
-       Temporarily removed constructor arguments:
-
-       // Q. Values are in audio-base.h, but where can we find explanation for them?
-       audio_output_flags_t flags,
-
        // Q. May be used in AudioTrack.setPreferredDevice(AudioDeviceInfo)?
        audio_port_handle_t selectedDeviceId,
 
-       // Should be deleted, since we don't use Binder anymore.
-       bool doNotReconnect,
-
-       // Do we need UID and PID?
-       uid_t uid,
-       pid_t pid,
-
-       // TODO: Uses these values when Java AudioTrack supports the offload mode.
-       callback_t cbf,
-       void* user,
+       // TODO: No place to use these values.
        int32_t notificationFrames,
        const audio_offload_info_t *offloadInfo,
-
-       // Fixed to false, but what is this?
-       threadCanCallJava
     */
 
     virtual ~JAudioTrack();
@@ -138,6 +163,46 @@
      */
     bool getTimestamp(AudioTimestamp& timestamp);
 
+    // TODO: This doc is just copied from AudioTrack.h. Revise it after implemenation.
+    /* Return the extended timestamp, with additional timebase info and improved drain behavior.
+     *
+     * This is similar to the AudioTrack.java API:
+     * getTimestamp(@NonNull AudioTimestamp timestamp, @AudioTimestamp.Timebase int timebase)
+     *
+     * Some differences between this method and the getTimestamp(AudioTimestamp& timestamp) method
+     *
+     *   1. stop() by itself does not reset the frame position.
+     *      A following start() resets the frame position to 0.
+     *   2. flush() by itself does not reset the frame position.
+     *      The frame position advances by the number of frames flushed,
+     *      when the first frame after flush reaches the audio sink.
+     *   3. BOOTTIME clock offsets are provided to help synchronize with
+     *      non-audio streams, e.g. sensor data.
+     *   4. Position is returned with 64 bits of resolution.
+     *
+     * Parameters:
+     *  timestamp: A pointer to the caller allocated ExtendedTimestamp.
+     *
+     * Returns NO_ERROR    on success; timestamp is filled with valid data.
+     *         BAD_VALUE   if timestamp is NULL.
+     *         WOULD_BLOCK if called immediately after start() when the number
+     *                     of frames consumed is less than the
+     *                     overall hardware latency to physical output. In WOULD_BLOCK cases,
+     *                     one might poll again, or use getPosition(), or use 0 position and
+     *                     current time for the timestamp.
+     *                     If WOULD_BLOCK is returned, the timestamp is still
+     *                     modified with the LOCATION_CLIENT portion filled.
+     *         DEAD_OBJECT if AudioFlinger dies or the output device changes and
+     *                     the track cannot be automatically restored.
+     *                     The application needs to recreate the AudioTrack
+     *                     because the audio device changed or AudioFlinger died.
+     *                     This typically occurs for direct or offloaded tracks
+     *                     or if mDoNotReconnect is true.
+     *         INVALID_OPERATION  if called on a offloaded or direct track.
+     *                     Use getTimestamp(AudioTimestamp& timestamp) instead.
+     */
+    status_t getTimestamp(ExtendedTimestamp *timestamp);
+
     /* Set source playback rate for timestretch
      * 1.0 is normal speed: < 1.0 is slower, > 1.0 is faster
      * 1.0 is normal pitch: < 1.0 is lower pitch, > 1.0 is higher pitch
@@ -270,7 +335,65 @@
      */
     audio_port_handle_t getRoutedDeviceId();
 
+    /* Returns the ID of the audio session this AudioTrack belongs to. */
+    audio_session_t getAudioSessionId();
+
+    /* Selects the audio device to use for output of this AudioTrack. A value of
+     * AUDIO_PORT_HANDLE_NONE indicates default routing.
+     *
+     * Parameters:
+     *  The device ID of the selected device (as returned by the AudioDevicesManager API).
+     *
+     * Returned value:
+     *  - NO_ERROR: successful operation
+     *  - BAD_VALUE: failed to find the valid output device with given device Id.
+     */
+    status_t setOutputDevice(audio_port_handle_t deviceId);
+
+    // TODO: Add AUDIO_OUTPUT_FLAG_DIRECT when it is possible to check.
+    // TODO: Add AUDIO_FLAG_HW_AV_SYNC when it is possible to check.
+    /* Returns the flags */
+    audio_output_flags_t getFlags() const { return mFlags; }
+
+    /* Obtain the pending duration in milliseconds for playback of pure PCM data remaining in
+     * AudioTrack.
+     *
+     * Returns NO_ERROR if successful.
+     *         INVALID_OPERATION if the AudioTrack does not contain pure PCM data.
+     *         BAD_VALUE if msec is nullptr.
+     */
+    status_t pendingDuration(int32_t *msec);
+
+    /* Adds an AudioDeviceCallback. The caller will be notified when the audio device to which this
+     * AudioTrack is routed is updated.
+     * Replaces any previously installed callback.
+     *
+     * Parameters:
+     *
+     * callback: The callback interface
+     *
+     * Returns NO_ERROR if successful.
+     *         INVALID_OPERATION if the same callback is already installed.
+     *         NO_INIT or PREMISSION_DENIED if AudioFlinger service is not reachable
+     *         BAD_VALUE if the callback is NULL
+     */
+    status_t addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
+
+    /* Removes an AudioDeviceCallback.
+     *
+     * Parameters:
+     *
+     * callback: The callback interface
+     *
+     * Returns NO_ERROR if successful.
+     *         INVALID_OPERATION if the callback is not installed
+     *         BAD_VALUE if the callback is NULL
+     */
+    status_t removeAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
+
 private:
+    audio_output_flags_t mFlags;
+
     jclass mAudioTrackCls;
     jobject mAudioTrackObj;
 
@@ -282,6 +405,12 @@
     jobject createVolumeShaperOperationObj(
             const sp<media::VolumeShaper::Operation>& operation);
 
+    /* Creates a Java StreamEventCallback object */
+    jobject createStreamEventCallback(callback_t cbf, void* user);
+
+    /* Creates a Java Executor object for running a callback */
+    jobject createCallbackExecutor();
+
     status_t javaToNativeStatus(int javaStatus);
 };
 
diff --git a/media/libmedia/include/media/MediaPlayer2Engine.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Engine.h
similarity index 100%
rename from media/libmedia/include/media/MediaPlayer2Engine.h
rename to media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Engine.h
diff --git a/media/libmedia/include/media/MediaPlayer2EngineClient.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2EngineClient.h
similarity index 90%
rename from media/libmedia/include/media/MediaPlayer2EngineClient.h
rename to media/libmediaplayer2/include/mediaplayer2/MediaPlayer2EngineClient.h
index 22df095..0b066aa 100644
--- a/media/libmedia/include/media/MediaPlayer2EngineClient.h
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2EngineClient.h
@@ -25,7 +25,7 @@
 class MediaPlayer2EngineClient: public RefBase
 {
 public:
-    virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) = 0;
+    virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0;
 };
 
 }; // namespace android
diff --git a/media/libmedia/include/media/MediaPlayer2Interface.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
similarity index 96%
rename from media/libmedia/include/media/MediaPlayer2Interface.h
rename to media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
index be300bb..b1cdf96 100644
--- a/media/libmedia/include/media/MediaPlayer2Interface.h
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
@@ -31,8 +31,8 @@
 #include <media/AVSyncSettings.h>
 #include <media/BufferingSettings.h>
 #include <media/Metadata.h>
-#include <media/mediaplayer2.h>
 #include <media/stagefright/foundation/AHandler.h>
+#include <mediaplayer2/mediaplayer2.h>
 
 // Fwd decl to make sure everyone agrees that the scope of struct sockaddr_in is
 // global, and not in android::
@@ -64,7 +64,7 @@
 public:
     // callback mechanism for passing messages to MediaPlayer2 object
     typedef void (*NotifyCallback)(const wp<MediaPlayer2Engine> &listener,
-            int msg, int ext1, int ext2, const Parcel *obj);
+            int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj);
 
     // AudioSink: abstraction layer for audio output
     class AudioSink : public RefBase {
@@ -148,10 +148,6 @@
     virtual             ~MediaPlayer2Interface() { }
     virtual status_t    initCheck() = 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<DataSourceDesc>& /* dsd */) {
@@ -244,7 +240,7 @@
         mClient = client; mNotify = notifyFunc;
     }
 
-    void        sendEvent(int msg, int ext1=0, int ext2=0,
+    void        sendEvent(int64_t srcId, int msg, int ext1=0, int ext2=0,
                           const Parcel *obj=NULL) {
         NotifyCallback notifyCB;
         wp<MediaPlayer2Engine> client;
@@ -254,7 +250,7 @@
             client = mClient;
         }
 
-        if (notifyCB) notifyCB(client, msg, ext1, ext2, obj);
+        if (notifyCB) notifyCB(client, srcId, msg, ext1, ext2, obj);
     }
 
     virtual status_t dump(int /* fd */, const Vector<String16>& /* args */) const {
diff --git a/media/libmedia/include/media/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
similarity index 95%
rename from media/libmedia/include/media/mediaplayer2.h
rename to media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
index c8b124c..e9d6f84 100644
--- a/media/libmedia/include/media/mediaplayer2.h
+++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
@@ -23,8 +23,8 @@
 
 #include <media/AudioResamplerPublic.h>
 #include <media/BufferingSettings.h>
-#include <media/MediaPlayer2EngineClient.h>
-#include <media/MediaPlayer2Engine.h>
+#include <mediaplayer2/MediaPlayer2EngineClient.h>
+#include <mediaplayer2/MediaPlayer2Engine.h>
 
 #include <utils/Condition.h>
 #include <utils/KeyedVector.h>
@@ -83,10 +83,11 @@
     // 0xx
     MEDIA2_ERROR_UNKNOWN = 1,
     // 1xx
-    MEDIA2_ERROR_SERVER_DIED = 100,
+    // MEDIA2_ERROR_SERVER_DIED = 100,
     // 2xx
     MEDIA2_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200,
     // 3xx
+    MEDIA2_ERROR_FAILED_TO_SET_DATA_SOURCE = 300,
 };
 
 
@@ -193,7 +194,7 @@
 class MediaPlayer2Listener: virtual public RefBase
 {
 public:
-    virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) = 0;
+    virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0;
 };
 
 class MediaPlayer2 : public MediaPlayer2EngineClient
@@ -203,6 +204,7 @@
     ~MediaPlayer2();
             void            disconnect();
 
+            status_t        getSrcId(int64_t *srcId);
             status_t        setDataSource(const sp<DataSourceDesc> &dsd);
             status_t        setVideoSurfaceTexture(const sp<ANativeWindowWrapper>& nww);
             status_t        setListener(const sp<MediaPlayer2Listener>& listener);
@@ -234,7 +236,8 @@
             status_t        setLooping(int loop);
             bool            isLooping();
             status_t        setVolume(float leftVolume, float rightVolume);
-            void            notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
+            void            notify(int64_t srcId, int msg, int ext1, int ext2,
+                                   const Parcel *obj = NULL);
             status_t        invoke(const Parcel& request, Parcel *reply);
             status_t        setMetadataFilter(const Parcel& filter);
             status_t        getMetadata(bool update_only, bool apply_filter, Parcel *metadata);
@@ -259,11 +262,12 @@
             status_t        seekTo_l(int msec, MediaPlayer2SeekMode mode);
             status_t        prepareAsync_l();
             status_t        getDuration_l(int *msec);
-            status_t        attachNewPlayer(const sp<MediaPlayer2Engine>& player);
+            status_t        attachNewPlayer(const sp<MediaPlayer2Engine>& player, long srcId);
             status_t        reset_l();
             status_t        checkStateForKeySet_l(int key);
 
     sp<MediaPlayer2Engine>      mPlayer;
+    int64_t                     mSrcId;
     thread_id_t                 mLockThreadId;
     Mutex                       mLock;
     Mutex                       mNotifyLock;
diff --git a/media/libmedia/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp
similarity index 96%
rename from media/libmedia/mediaplayer2.cpp
rename to media/libmediaplayer2/mediaplayer2.cpp
index 1eb3795..ab30273 100644
--- a/media/libmedia/mediaplayer2.cpp
+++ b/media/libmediaplayer2/mediaplayer2.cpp
@@ -29,7 +29,6 @@
 #include <binder/IServiceManager.h>
 #include <binder/IPCThreadState.h>
 
-#include <media/mediaplayer2.h>
 #include <media/AudioResamplerPublic.h>
 #include <media/AudioSystem.h>
 #include <media/AVSyncSettings.h>
@@ -37,6 +36,7 @@
 #include <media/DataSourceDesc.h>
 #include <media/MediaAnalyticsItem.h>
 #include <media/NdkWrapper.h>
+#include <mediaplayer2/mediaplayer2.h>
 
 #include <binder/MemoryBase.h>
 
@@ -53,6 +53,7 @@
 MediaPlayer2::MediaPlayer2()
 {
     ALOGV("constructor");
+    mSrcId = 0;
     mListener = NULL;
     mCookie = NULL;
     mStreamType = AUDIO_STREAM_MUSIC;
@@ -118,8 +119,17 @@
     return NO_ERROR;
 }
 
+status_t MediaPlayer2::getSrcId(int64_t *srcId) {
+    if (srcId == NULL) {
+        return BAD_VALUE;
+    }
 
-status_t MediaPlayer2::attachNewPlayer(const sp<MediaPlayer2Engine>& player)
+    Mutex::Autolock _l(mLock);
+    *srcId = mSrcId;
+    return OK;
+}
+
+status_t MediaPlayer2::attachNewPlayer(const sp<MediaPlayer2Engine>& player, long srcId)
 {
     status_t err = UNKNOWN_ERROR;
     sp<MediaPlayer2Engine> p;
@@ -135,6 +145,7 @@
         clear_l();
         p = mPlayer;
         mPlayer = player;
+        mSrcId = srcId;
         if (player != 0) {
             mCurrentState = MEDIA_PLAYER2_INITIALIZED;
             err = NO_ERROR;
@@ -161,7 +172,7 @@
     if (NO_ERROR != player->setDataSource(dsd)) {
         player.clear();
     }
-    err = attachNewPlayer(player);
+    err = attachNewPlayer(player, dsd->mId);
     return err;
 }
 
@@ -763,9 +774,10 @@
     return INVALID_OPERATION;
 }
 
-void MediaPlayer2::notify(int msg, int ext1, int ext2, const Parcel *obj)
+void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj)
 {
-    ALOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
+    ALOGV("message received srcId=%lld, msg=%d, ext1=%d, ext2=%d",
+          (long long)srcId, msg, ext1, ext2);
     bool send = true;
     bool locked = false;
 
@@ -784,7 +796,8 @@
 
     // Allows calls from JNI in idle state to notify errors
     if (!(msg == MEDIA2_ERROR && mCurrentState == MEDIA_PLAYER2_IDLE) && mPlayer == 0) {
-        ALOGV("notify(%d, %d, %d) callback on disconnected mediaplayer", msg, ext1, ext2);
+        ALOGV("notify(%lld, %d, %d, %d) callback on disconnected mediaplayer",
+              (long long)srcId, msg, ext1, ext2);
         if (locked) mLock.unlock();   // release the lock when done.
         return;
     }
@@ -803,7 +816,8 @@
         }
         break;
     case MEDIA2_DRM_INFO:
-        ALOGV("MediaPlayer2::notify() MEDIA2_DRM_INFO(%d, %d, %d, %p)", msg, ext1, ext2, obj);
+        ALOGV("MediaPlayer2::notify() MEDIA2_DRM_INFO(%lld, %d, %d, %d, %p)",
+              (long long)srcId, msg, ext1, ext2, obj);
         break;
     case MEDIA2_PLAYBACK_COMPLETE:
         ALOGV("playback complete");
@@ -882,7 +896,7 @@
     if ((listener != 0) && send) {
         Mutex::Autolock _l(mNotifyLock);
         ALOGV("callback application");
-        listener->notify(msg, ext1, ext2, obj);
+        listener->notify(srcId, msg, ext1, ext2, obj);
         ALOGV("back from callback");
     }
 }
diff --git a/media/libmedia/nuplayer2/Android.bp b/media/libmediaplayer2/nuplayer2/Android.bp
similarity index 97%
rename from media/libmedia/nuplayer2/Android.bp
rename to media/libmediaplayer2/nuplayer2/Android.bp
index 1f4455f..c40b361 100644
--- a/media/libmedia/nuplayer2/Android.bp
+++ b/media/libmediaplayer2/nuplayer2/Android.bp
@@ -16,6 +16,7 @@
     ],
 
     header_libs: [
+        "libmediaplayer2_headers",
         "media_plugin_headers",
     ],
 
diff --git a/media/libmedia/nuplayer2/GenericSource2.cpp b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
similarity index 99%
rename from media/libmedia/nuplayer2/GenericSource2.cpp
rename to media/libmediaplayer2/nuplayer2/GenericSource2.cpp
index 0351a76..4700660 100644
--- a/media/libmedia/nuplayer2/GenericSource2.cpp
+++ b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
@@ -54,7 +54,6 @@
 
 NuPlayer2::GenericSource2::GenericSource2(
         const sp<AMessage> &notify,
-        bool uidValid,
         uid_t uid,
         const sp<MediaClock> &mediaClock)
     : Source(notify),
@@ -73,7 +72,6 @@
       mAudioIsVorbis(false),
       mIsSecure(false),
       mIsStreaming(false),
-      mUIDValid(uidValid),
       mUID(uid),
       mMediaClock(mediaClock),
       mFd(-1),
diff --git a/media/libmedia/nuplayer2/GenericSource2.h b/media/libmediaplayer2/nuplayer2/GenericSource2.h
similarity index 97%
rename from media/libmedia/nuplayer2/GenericSource2.h
rename to media/libmediaplayer2/nuplayer2/GenericSource2.h
index 2016304..1a5409a 100644
--- a/media/libmedia/nuplayer2/GenericSource2.h
+++ b/media/libmediaplayer2/nuplayer2/GenericSource2.h
@@ -23,8 +23,8 @@
 
 #include "ATSParser.h"
 
-#include <media/mediaplayer2.h>
 #include <media/stagefright/MediaBuffer.h>
+#include <mediaplayer2/mediaplayer2.h>
 
 namespace android {
 
@@ -43,7 +43,7 @@
 struct NuPlayer2::GenericSource2 : public NuPlayer2::Source,
                                    public MediaBufferObserver // Modular DRM
 {
-    GenericSource2(const sp<AMessage> &notify, bool uidValid, uid_t uid,
+    GenericSource2(const sp<AMessage> &notify, uid_t uid,
                    const sp<MediaClock> &mediaClock);
 
     status_t setDataSource(
@@ -150,7 +150,6 @@
     // Secure codec is required.
     bool mIsSecure;
     bool mIsStreaming;
-    bool mUIDValid;
     uid_t mUID;
     const sp<MediaClock> mMediaClock;
     sp<MediaHTTPService> mHTTPService;
diff --git a/media/libmedia/nuplayer2/HTTPLiveSource2.cpp b/media/libmediaplayer2/nuplayer2/HTTPLiveSource2.cpp
similarity index 100%
rename from media/libmedia/nuplayer2/HTTPLiveSource2.cpp
rename to media/libmediaplayer2/nuplayer2/HTTPLiveSource2.cpp
diff --git a/media/libmedia/nuplayer2/HTTPLiveSource2.h b/media/libmediaplayer2/nuplayer2/HTTPLiveSource2.h
similarity index 100%
rename from media/libmedia/nuplayer2/HTTPLiveSource2.h
rename to media/libmediaplayer2/nuplayer2/HTTPLiveSource2.h
diff --git a/media/libmedia/nuplayer2/JWakeLock.cpp b/media/libmediaplayer2/nuplayer2/JWakeLock.cpp
similarity index 100%
rename from media/libmedia/nuplayer2/JWakeLock.cpp
rename to media/libmediaplayer2/nuplayer2/JWakeLock.cpp
diff --git a/media/libmedia/nuplayer2/JWakeLock.h b/media/libmediaplayer2/nuplayer2/JWakeLock.h
similarity index 100%
rename from media/libmedia/nuplayer2/JWakeLock.h
rename to media/libmediaplayer2/nuplayer2/JWakeLock.h
diff --git a/media/libmedia/nuplayer2/MODULE_LICENSE_APACHE2 b/media/libmediaplayer2/nuplayer2/MODULE_LICENSE_APACHE2
similarity index 100%
rename from media/libmedia/nuplayer2/MODULE_LICENSE_APACHE2
rename to media/libmediaplayer2/nuplayer2/MODULE_LICENSE_APACHE2
diff --git a/media/libmedia/nuplayer2/NOTICE b/media/libmediaplayer2/nuplayer2/NOTICE
similarity index 100%
rename from media/libmedia/nuplayer2/NOTICE
rename to media/libmediaplayer2/nuplayer2/NOTICE
diff --git a/media/libmedia/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
similarity index 91%
rename from media/libmedia/nuplayer2/NuPlayer2.cpp
rename to media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
index 65e0957..462a904 100644
--- a/media/libmedia/nuplayer2/NuPlayer2.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
@@ -208,9 +208,9 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-NuPlayer2::NuPlayer2(pid_t pid, const sp<MediaClock> &mediaClock)
-    : mUIDValid(false),
-      mPID(pid),
+NuPlayer2::NuPlayer2(pid_t pid, uid_t uid, const sp<MediaClock> &mediaClock)
+    : mPID(pid),
+      mUID(uid),
       mMediaClock(mediaClock),
       mSourceFlags(0),
       mOffloadAudio(false),
@@ -249,11 +249,6 @@
 NuPlayer2::~NuPlayer2() {
 }
 
-void NuPlayer2::setUID(uid_t uid) {
-    mUIDValid = true;
-    mUID = uid;
-}
-
 void NuPlayer2::setDriver(const wp<NuPlayer2Driver> &driver) {
     mDriver = driver;
 }
@@ -275,10 +270,12 @@
     return false;
 }
 
-void NuPlayer2::setDataSourceAsync(const sp<DataSourceDesc> &dsd) {
-    sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
+status_t NuPlayer2::createNuPlayer2Source(const sp<DataSourceDesc> &dsd,
+                                          sp<Source> *source,
+                                          DATA_SOURCE_TYPE *dataSourceType) {
+    status_t err = NO_ERROR;
     sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);
-    sp<Source> source;
+    notify->setInt64("srcId", dsd->mId);
 
     switch (dsd->mType) {
         case DataSourceDesc::TYPE_URL:
@@ -290,38 +287,39 @@
             KeyedVector<String8, String8> *headers = &(dsd->mHeaders);
 
             if (IsHTTPLiveURL(url)) {
-                source = new HTTPLiveSource2(notify, httpService, url, headers);
-                ALOGV("setDataSourceAsync HTTPLiveSource2 %s", url);
-                mDataSourceType = DATA_SOURCE_TYPE_HTTP_LIVE;
+                *source = new HTTPLiveSource2(notify, httpService, url, headers);
+                ALOGV("createNuPlayer2Source HTTPLiveSource2 %s", url);
+                *dataSourceType = DATA_SOURCE_TYPE_HTTP_LIVE;
             } else if (!strncasecmp(url, "rtsp://", 7)) {
-                source = new RTSPSource2(
-                        notify, httpService, url, headers, mUIDValid, mUID);
-                ALOGV("setDataSourceAsync RTSPSource2 %s", url);
-                mDataSourceType = DATA_SOURCE_TYPE_RTSP;
+                *source = new RTSPSource2(
+                        notify, httpService, url, headers, mUID);
+                ALOGV("createNuPlayer2Source RTSPSource2 %s", url);
+                *dataSourceType = DATA_SOURCE_TYPE_RTSP;
             } else if ((!strncasecmp(url, "http://", 7)
                         || !strncasecmp(url, "https://", 8))
                             && ((len >= 4 && !strcasecmp(".sdp", &url[len - 4]))
                             || strstr(url, ".sdp?"))) {
-                source = new RTSPSource2(
-                        notify, httpService, url, headers, mUIDValid, mUID, true);
-                ALOGV("setDataSourceAsync RTSPSource2 http/https/.sdp %s", url);
-                mDataSourceType = DATA_SOURCE_TYPE_RTSP;
+                *source = new RTSPSource2(
+                        notify, httpService, url, headers, mUID, true);
+                ALOGV("createNuPlayer2Source RTSPSource2 http/https/.sdp %s", url);
+                *dataSourceType = DATA_SOURCE_TYPE_RTSP;
             } else {
-                ALOGV("setDataSourceAsync GenericSource2 %s", url);
+                ALOGV("createNuPlayer2Source GenericSource2 %s", url);
 
                 sp<GenericSource2> genericSource =
-                        new GenericSource2(notify, mUIDValid, mUID, mMediaClock);
+                        new GenericSource2(notify, mUID, mMediaClock);
 
-                status_t err = genericSource->setDataSource(httpService, url, headers);
+                err = genericSource->setDataSource(httpService, url, headers);
 
                 if (err == OK) {
-                    source = genericSource;
+                    *source = genericSource;
                 } else {
-                    ALOGE("Failed to set data source!");
+                    *source = NULL;
+                    ALOGE("Failed to create NuPlayer2Source!");
                 }
 
                 // regardless of success/failure
-                mDataSourceType = DATA_SOURCE_TYPE_GENERIC_URL;
+                *dataSourceType = DATA_SOURCE_TYPE_GENERIC_URL;
             }
             break;
         }
@@ -329,47 +327,99 @@
         case DataSourceDesc::TYPE_FD:
         {
             sp<GenericSource2> genericSource =
-                    new GenericSource2(notify, mUIDValid, mUID, mMediaClock);
+                    new GenericSource2(notify, mUID, mMediaClock);
 
-            ALOGV("setDataSourceAsync fd %d/%lld/%lld source: %p",
-                  dsd->mFD, (long long)dsd->mFDOffset, (long long)dsd->mFDLength, source.get());
+            ALOGV("createNuPlayer2Source fd %d/%lld/%lld source: %p",
+                  dsd->mFD, (long long)dsd->mFDOffset, (long long)dsd->mFDLength,
+                  genericSource.get());
 
-            status_t err = genericSource->setDataSource(dsd->mFD, dsd->mFDOffset, dsd->mFDLength);
+            err = genericSource->setDataSource(dsd->mFD, dsd->mFDOffset, dsd->mFDLength);
 
             if (err != OK) {
-                ALOGE("Failed to set data source!");
-                source = NULL;
+                ALOGE("Failed to create NuPlayer2Source!");
+                *source = NULL;
             } else {
-                source = genericSource;
+                *source = genericSource;
             }
 
-            mDataSourceType = DATA_SOURCE_TYPE_GENERIC_FD;
+            *dataSourceType = DATA_SOURCE_TYPE_GENERIC_FD;
             break;
         }
 
         case DataSourceDesc::TYPE_CALLBACK:
         {
             sp<GenericSource2> genericSource =
-                    new GenericSource2(notify, mUIDValid, mUID, mMediaClock);
-            status_t err = genericSource->setDataSource(dsd->mCallbackSource);
+                    new GenericSource2(notify, mUID, mMediaClock);
+            err = genericSource->setDataSource(dsd->mCallbackSource);
 
             if (err != OK) {
-                ALOGE("Failed to set data source!");
-                source = NULL;
+                ALOGE("Failed to create NuPlayer2Source!");
+                *source = NULL;
             } else {
-                source = genericSource;
+                *source = genericSource;
             }
 
-            mDataSourceType = DATA_SOURCE_TYPE_MEDIA;
+            *dataSourceType = DATA_SOURCE_TYPE_MEDIA;
             break;
         }
 
         default:
+            err = BAD_TYPE;
+            *source = NULL;
+            *dataSourceType = DATA_SOURCE_TYPE_NONE;
             ALOGE("invalid data source type!");
             break;
     }
 
+    return err;
+}
+
+void NuPlayer2::setDataSourceAsync(const sp<DataSourceDesc> &dsd) {
+    DATA_SOURCE_TYPE dataSourceType;
+    sp<Source> source;
+    createNuPlayer2Source(dsd, &source, &dataSourceType);
+
+    // TODO: currently NuPlayer2Driver makes blocking call to setDataSourceAsync
+    // and expects notifySetDataSourceCompleted regardless of success or failure.
+    // This will be changed since setDataSource should be asynchronous at JAVA level.
+    // When it succeeds, app will get onInfo notification. Otherwise, onError
+    // will be called.
+    /*
+    if (err != OK) {
+        notifyListener(dsd->mId, MEDIA2_ERROR, MEDIA2_ERROR_FAILED_TO_SET_DATA_SOURCE, err);
+        return;
+    }
+
+    // Now, source != NULL.
+    */
+
+    mDataSourceType = dataSourceType;
+
+    sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
     msg->setObject("source", source);
+    msg->setInt64("srcId", dsd->mId);
+    msg->post();
+}
+
+void NuPlayer2::prepareNextDataSourceAsync(const sp<DataSourceDesc> &dsd) {
+    DATA_SOURCE_TYPE dataSourceType;
+    sp<Source> source;
+    createNuPlayer2Source(dsd, &source, &dataSourceType);
+
+    /*
+    if (err != OK) {
+        notifyListener(dsd->mId, MEDIA2_ERROR, MEDIA2_ERROR_FAILED_TO_SET_DATA_SOURCE, err);
+        return;
+    }
+
+    // Now, source != NULL.
+    */
+
+    mNextDataSourceType = dataSourceType;
+
+    sp<AMessage> msg = new AMessage(kWhatPrepareNextDataSource, this);
+    msg->setObject("source", source);
+    msg->setInt64("srcId", dsd->mId);
     msg->post();
 }
 
@@ -587,19 +637,40 @@
             CHECK(msg->findObject("source", &obj));
             if (obj != NULL) {
                 Mutex::Autolock autoLock(mSourceLock);
+                CHECK(msg->findInt64("srcId", &mSrcId));
                 mSource = static_cast<Source *>(obj.get());
             } else {
                 err = UNKNOWN_ERROR;
+                ALOGE("kWhatSetDataSource, source should not be NULL");
             }
 
             CHECK(mDriver != NULL);
             sp<NuPlayer2Driver> driver = mDriver.promote();
             if (driver != NULL) {
-                driver->notifySetDataSourceCompleted(err);
+                driver->notifySetDataSourceCompleted(mSrcId, err);
             }
             break;
         }
 
+        case kWhatPrepareNextDataSource:
+        {
+            ALOGV("kWhatPrepareNextDataSource");
+
+            status_t err = OK;
+            sp<RefBase> obj;
+            CHECK(msg->findObject("source", &obj));
+            if (obj != NULL) {
+                Mutex::Autolock autoLock(mSourceLock);
+                CHECK(msg->findInt64("srcId", &mNextSrcId));
+                mNextSource = static_cast<Source *>(obj.get());
+                mNextSource->prepareAsync();
+            } else {
+                err = UNKNOWN_ERROR;
+            }
+
+            break;
+        }
+
         case kWhatGetBufferingSettings:
         {
             sp<AReplyToken> replyID;
@@ -775,7 +846,7 @@
             if (mDriver != NULL && mSource->getDuration(&durationUs) == OK) {
                 sp<NuPlayer2Driver> driver = mDriver.promote();
                 if (driver != NULL) {
-                    driver->notifyDuration(durationUs);
+                    driver->notifyDuration(mSrcId, durationUs);
                 }
             }
 
@@ -865,6 +936,7 @@
                 onStart();
             }
             mPausedByClient = false;
+            notifyListener(mSrcId, MEDIA2_STARTED, 0, 0);
             break;
         }
 
@@ -904,6 +976,7 @@
             if (err == OK) {
                 if (rate.mSpeed == 0.f) {
                     onPause();
+                    notifyListener(mSrcId, MEDIA2_PAUSED, 0, 0);
                     mPausedByClient = true;
                     // save all other settings (using non-paused speed)
                     // so we can restore them on start
@@ -1058,9 +1131,9 @@
                     // video tracks found) and we just ran out of input data.
 
                     if (err == ERROR_END_OF_STREAM) {
-                        notifyListener(MEDIA2_PLAYBACK_COMPLETE, 0, 0);
+                        notifyListener(mSrcId, MEDIA2_PLAYBACK_COMPLETE, 0, 0);
                     } else {
-                        notifyListener(MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
+                        notifyListener(mSrcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
                     }
                 }
                 break;
@@ -1145,7 +1218,7 @@
                         mSource->getFormat(false /* audio */);
 
                 setVideoScalingMode(mVideoScalingMode);
-                updateVideoSize(inputFormat, format);
+                updateVideoSize(mSrcId, inputFormat, format);
             } else if (what == DecoderBase::kWhatShutdownCompleted) {
                 ALOGV("%s shutdown completed", audio ? "audio" : "video");
                 if (audio) {
@@ -1219,10 +1292,10 @@
                                 || mVideoDecoder == NULL) {
                             // When both audio and video have error, or this stream has only audio
                             // which has error, notify client of error.
-                            notifyListener(MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
+                            notifyListener(mSrcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
                         } else {
                             // Only audio track has error. Video track could be still good to play.
-                            notifyListener(MEDIA2_INFO, MEDIA2_INFO_PLAY_AUDIO_ERROR, err);
+                            notifyListener(mSrcId, MEDIA2_INFO, MEDIA2_INFO_PLAY_AUDIO_ERROR, err);
                         }
                         mAudioDecoderError = true;
                     } else {
@@ -1230,10 +1303,10 @@
                                 || mAudioSink == NULL || mAudioDecoder == NULL) {
                             // When both audio and video have error, or this stream has only video
                             // which has error, notify client of error.
-                            notifyListener(MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
+                            notifyListener(mSrcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
                         } else {
                             // Only video track has error. Audio track could be still good to play.
-                            notifyListener(MEDIA2_INFO, MEDIA2_INFO_PLAY_VIDEO_ERROR, err);
+                            notifyListener(mSrcId, MEDIA2_INFO, MEDIA2_INFO_PLAY_VIDEO_ERROR, err);
                         }
                         mVideoDecoderError = true;
                     }
@@ -1283,12 +1356,12 @@
                          audio ? "audio" : "video", finalResult);
 
                     notifyListener(
-                            MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, finalResult);
+                            mSrcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, finalResult);
                 }
 
                 if ((mAudioEOS || mAudioDecoder == NULL)
                         && (mVideoEOS || mVideoDecoder == NULL)) {
-                    notifyListener(MEDIA2_PLAYBACK_COMPLETE, 0, 0);
+                    notifyListener(mSrcId, MEDIA2_PLAYBACK_COMPLETE, 0, 0);
                 }
             } else if (what == Renderer::kWhatFlushComplete) {
                 int32_t audio;
@@ -1309,10 +1382,10 @@
                 handleFlushComplete(audio, false /* isDecoder */);
                 finishFlushIfPossible();
             } else if (what == Renderer::kWhatVideoRenderingStart) {
-                notifyListener(MEDIA2_INFO, MEDIA2_INFO_RENDERING_START, 0);
+                notifyListener(mSrcId, MEDIA2_INFO, MEDIA2_INFO_RENDERING_START, 0);
             } else if (what == Renderer::kWhatMediaRenderingStart) {
                 ALOGV("media rendering started");
-                notifyListener(MEDIA2_STARTED, 0, 0);
+                notifyListener(mSrcId, MEDIA2_STARTED, 0, 0);
             } else if (what == Renderer::kWhatAudioTearDown) {
                 int32_t reason;
                 CHECK(msg->findInt32("reason", &reason));
@@ -1365,7 +1438,7 @@
             int64_t timerUs;
             CHECK(msg->findInt64("timerUs", &timerUs));
 
-            notifyListener(MEDIA2_NOTIFY_TIME, timerUs, 0);
+            notifyListener(mSrcId, MEDIA2_NOTIFY_TIME, timerUs, 0);
             break;
         }
 
@@ -1382,22 +1455,23 @@
                     (long long)seekTimeUs, mode, needNotify);
 
             if (!mStarted) {
-                // Seek before the player is started. In order to preview video,
-                // need to start the player and pause it. This branch is called
-                // only once if needed. After the player is started, any seek
-                // operation will go through normal path.
-                // Audio-only cases are handled separately.
-                onStart(seekTimeUs, (MediaPlayer2SeekMode)mode);
-                if (mStarted) {
-                    onPause();
-                    mPausedByClient = true;
+                if (!mSourceStarted) {
+                    mSourceStarted = true;
+                    mSource->start();
                 }
+                if (seekTimeUs > 0) {
+                    performSeek(seekTimeUs, (MediaPlayer2SeekMode)mode);
+                }
+
                 if (needNotify) {
-                    notifyDriverSeekComplete();
+                    notifyDriverSeekComplete(mSrcId);
                 }
                 break;
             }
 
+            // seeks can take a while, so we essentially paused
+            notifyListener(mSrcId, MEDIA2_PAUSED, 0, 0);
+
             mDeferredActions.push_back(
                     new FlushDecoderAction(FLUSH_CMD_FLUSH /* audio */,
                                            FLUSH_CMD_FLUSH /* video */));
@@ -1418,6 +1492,7 @@
         case kWhatPause:
         {
             onPause();
+            notifyListener(mSrcId, MEDIA2_PAUSED, 0, 0);
             mPausedByClient = true;
             break;
         }
@@ -1489,48 +1564,13 @@
     startPlaybackTimer("onresume");
 }
 
-status_t NuPlayer2::onInstantiateSecureDecoders() {
-    status_t err;
-    if (!(mSourceFlags & Source::FLAG_SECURE)) {
-        return BAD_TYPE;
-    }
-
-    if (mRenderer != NULL) {
-        ALOGE("renderer should not be set when instantiating secure decoders");
-        return UNKNOWN_ERROR;
-    }
-
-    // TRICKY: We rely on mRenderer being null, so that decoder does not start requesting
-    // data on instantiation.
-    if (mNativeWindow != NULL && mNativeWindow->getANativeWindow() != NULL) {
-        err = instantiateDecoder(false, &mVideoDecoder);
-        if (err != OK) {
-            return err;
-        }
-    }
-
-    if (mAudioSink != NULL) {
-        err = instantiateDecoder(true, &mAudioDecoder);
-        if (err != OK) {
-            return err;
-        }
-    }
-    return OK;
-}
-
-void NuPlayer2::onStart(int64_t startPositionUs, MediaPlayer2SeekMode mode) {
+void NuPlayer2::onStart() {
     ALOGV("onStart: mCrypto: %p", mCrypto.get());
 
     if (!mSourceStarted) {
         mSourceStarted = true;
         mSource->start();
     }
-    if (startPositionUs > 0) {
-        performSeek(startPositionUs, mode);
-        if (mSource->getFormat(false /* audio */) == NULL) {
-            return;
-        }
-    }
 
     mOffloadAudio = false;
     mAudioEOS = false;
@@ -1550,7 +1590,7 @@
         ALOGE("no metadata for either audio or video source");
         mSource->stop();
         mSourceStarted = false;
-        notifyListener(MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, ERROR_MALFORMED);
+        notifyListener(mSrcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, ERROR_MALFORMED);
         return;
     }
     ALOGV_IF(!hasAudio, "no metadata for audio source");  // video only stream
@@ -1589,7 +1629,7 @@
     if (err != OK) {
         mSource->stop();
         mSourceStarted = false;
-        notifyListener(MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
+        notifyListener(mSrcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
         return;
     }
 
@@ -1631,7 +1671,7 @@
             ALOGV("stopPlaybackTimer()  log  %20" PRId64 "", played);
 
             if (played > 0) {
-                driver->notifyMorePlayingTimeUs((played+500)/1000);
+                driver->notifyMorePlayingTimeUs(mSrcId, (played+500)/1000);
             }
         }
         mLastStartedPlayingTimeNs = 0;
@@ -1659,9 +1699,9 @@
             ALOGV("stopRebufferingTimer()  log  %20" PRId64 "", rebuffered);
 
             if (rebuffered > 0) {
-                driver->notifyMoreRebufferingTimeUs((rebuffered+500)/1000);
+                driver->notifyMoreRebufferingTimeUs(mSrcId, (rebuffered+500)/1000);
                 if (exitingPlayback) {
-                    driver->notifyRebufferingWhenExit(true);
+                    driver->notifyRebufferingWhenExit(mSrcId, true);
                 }
             }
         }
@@ -1978,11 +2018,12 @@
 }
 
 void NuPlayer2::updateVideoSize(
+        int64_t srcId,
         const sp<AMessage> &inputFormat,
         const sp<AMessage> &outputFormat) {
     if (inputFormat == NULL) {
         ALOGW("Unknown video size, reporting 0x0!");
-        notifyListener(MEDIA2_SET_VIDEO_SIZE, 0, 0);
+        notifyListener(srcId, MEDIA2_SET_VIDEO_SIZE, 0, 0);
         return;
     }
     int32_t err = OK;
@@ -2061,12 +2102,13 @@
     }
 
     notifyListener(
+            srcId,
             MEDIA2_SET_VIDEO_SIZE,
             displayWidth,
             displayHeight);
 }
 
-void NuPlayer2::notifyListener(int msg, int ext1, int ext2, const Parcel *in) {
+void NuPlayer2::notifyListener(int64_t srcId, int msg, int ext1, int ext2, const Parcel *in) {
     if (mDriver == NULL) {
         return;
     }
@@ -2077,7 +2119,7 @@
         return;
     }
 
-    driver->notifyListener(msg, ext1, ext2, in);
+    driver->notifyListener(srcId, msg, ext1, ext2, in);
 }
 
 void NuPlayer2::flushDecoder(bool audio, bool needShutdown) {
@@ -2342,7 +2384,7 @@
     if (mDriver != NULL) {
         sp<NuPlayer2Driver> driver = mDriver.promote();
         if (driver != NULL) {
-            driver->notifyResetComplete();
+            driver->notifyResetComplete(mSrcId);
         }
     }
 
@@ -2384,7 +2426,7 @@
     if (mDriver != NULL) {
         sp<NuPlayer2Driver> driver = mDriver.promote();
         if (driver != NULL) {
-            driver->notifySetSurfaceComplete();
+            driver->notifySetSurfaceComplete(mSrcId);
         }
     }
 }
@@ -2416,15 +2458,15 @@
 void NuPlayer2::finishResume() {
     if (mResumePending) {
         mResumePending = false;
-        notifyDriverSeekComplete();
+        notifyDriverSeekComplete(mSrcId);
     }
 }
 
-void NuPlayer2::notifyDriverSeekComplete() {
+void NuPlayer2::notifyDriverSeekComplete(int64_t srcId) {
     if (mDriver != NULL) {
         sp<NuPlayer2Driver> driver = mDriver.promote();
         if (driver != NULL) {
-            driver->notifySeekComplete();
+            driver->notifySeekComplete(srcId);
         }
     }
 }
@@ -2433,24 +2475,9 @@
     int32_t what;
     CHECK(msg->findInt32("what", &what));
 
+    int64_t srcId;
+    CHECK(msg->findInt64("srcId", &srcId));
     switch (what) {
-        case Source::kWhatInstantiateSecureDecoders:
-        {
-            if (mSource == NULL) {
-                // This is a stale notification from a source that was
-                // asynchronously preparing when the client called reset().
-                // We handled the reset, the source is gone.
-                break;
-            }
-
-            sp<AMessage> reply;
-            CHECK(msg->findMessage("reply", &reply));
-            status_t err = onInstantiateSecureDecoders();
-            reply->setInt32("err", err);
-            reply->post();
-            break;
-        }
-
         case Source::kWhatPrepared:
         {
             ALOGV("NuPlayer2::onSourceNotify Source::kWhatPrepared source: %p", mSource.get());
@@ -2480,9 +2507,9 @@
                 // the app received the "prepare complete" callback.
                 int64_t durationUs;
                 if (mSource->getDuration(&durationUs) == OK) {
-                    driver->notifyDuration(durationUs);
+                    driver->notifyDuration(srcId, durationUs);
                 }
-                driver->notifyPrepareCompleted(err);
+                driver->notifyPrepareCompleted(srcId, err);
             }
 
             break;
@@ -2499,7 +2526,7 @@
             ALOGV("onSourceNotify() kWhatDrmInfo MEDIA2_DRM_INFO drmInfo: %p  parcel size: %zu",
                     drmInfo.get(), parcel.dataSize());
 
-            notifyListener(MEDIA2_DRM_INFO, 0 /* ext1 */, 0 /* ext2 */, &parcel);
+            notifyListener(srcId, MEDIA2_DRM_INFO, 0 /* ext1 */, 0 /* ext2 */, &parcel);
 
             break;
         }
@@ -2526,9 +2553,9 @@
 
                 if ((flags & NuPlayer2::Source::FLAG_CAN_SEEK) == 0) {
                     driver->notifyListener(
-                            MEDIA2_INFO, MEDIA2_INFO_NOT_SEEKABLE, 0);
+                            srcId, MEDIA2_INFO, MEDIA2_INFO_NOT_SEEKABLE, 0);
                 }
-                driver->notifyFlagsChanged(flags);
+                driver->notifyFlagsChanged(srcId, flags);
             }
 
             if ((mSourceFlags & Source::FLAG_DYNAMIC_DURATION)
@@ -2549,7 +2576,7 @@
             sp<AMessage> format;
             CHECK(msg->findMessage("format", &format));
 
-            updateVideoSize(format);
+            updateVideoSize(srcId, format);
             break;
         }
 
@@ -2558,7 +2585,7 @@
             int32_t percentage;
             CHECK(msg->findInt32("percentage", &percentage));
 
-            notifyListener(MEDIA2_BUFFERING_UPDATE, percentage, 0);
+            notifyListener(srcId, MEDIA2_BUFFERING_UPDATE, percentage, 0);
             break;
         }
 
@@ -2572,7 +2599,7 @@
                 mPausedForBuffering = true;
                 onPause();
             }
-            notifyListener(MEDIA2_INFO, MEDIA2_INFO_BUFFERING_START, 0);
+            notifyListener(srcId, MEDIA2_INFO, MEDIA2_INFO_BUFFERING_START, 0);
             break;
         }
 
@@ -2590,7 +2617,7 @@
                     onResume();
                 }
             }
-            notifyListener(MEDIA2_INFO, MEDIA2_INFO_BUFFERING_END, 0);
+            notifyListener(srcId, MEDIA2_INFO, MEDIA2_INFO_BUFFERING_END, 0);
             break;
         }
 
@@ -2599,7 +2626,7 @@
             int32_t kbps;
             CHECK(msg->findInt32("bandwidth", &kbps));
 
-            notifyListener(MEDIA2_INFO, MEDIA2_INFO_NETWORK_BANDWIDTH, kbps);
+            notifyListener(srcId, MEDIA2_INFO, MEDIA2_INFO_NETWORK_BANDWIDTH, kbps);
             break;
         }
 
@@ -2616,7 +2643,7 @@
         {
             sp<ABuffer> buffer;
             if (!msg->findBuffer("buffer", &buffer)) {
-                notifyListener(MEDIA2_INFO, MEDIA2_INFO_METADATA_UPDATE, 0);
+                notifyListener(srcId, MEDIA2_INFO, MEDIA2_INFO_METADATA_UPDATE, 0);
             } else {
                 sendTimedMetaData(buffer);
             }
@@ -2671,7 +2698,7 @@
 
         case Source::kWhatDrmNoLicense:
         {
-            notifyListener(MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, ERROR_DRM_NO_LICENSE);
+            notifyListener(srcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, ERROR_DRM_NO_LICENSE);
             break;
         }
 
@@ -2701,7 +2728,7 @@
 
         case NuPlayer2::CCDecoder::kWhatTrackAdded:
         {
-            notifyListener(MEDIA2_INFO, MEDIA2_INFO_METADATA_UPDATE, 0);
+            notifyListener(mSrcId, MEDIA2_INFO, MEDIA2_INFO_METADATA_UPDATE, 0);
 
             break;
         }
@@ -2728,7 +2755,7 @@
     in.writeInt32(buffer->size());
     in.write(buffer->data(), buffer->size());
 
-    notifyListener(MEDIA2_SUBTITLE_DATA, 0, 0, &in);
+    notifyListener(mSrcId, MEDIA2_SUBTITLE_DATA, 0, 0, &in);
 }
 
 void NuPlayer2::sendTimedMetaData(const sp<ABuffer> &buffer) {
@@ -2741,7 +2768,7 @@
     in.writeInt32(buffer->size());
     in.write(buffer->data(), buffer->size());
 
-    notifyListener(MEDIA2_META_DATA, 0, 0, &in);
+    notifyListener(mSrcId, MEDIA2_META_DATA, 0, 0, &in);
 }
 
 void NuPlayer2::sendTimedTextData(const sp<ABuffer> &buffer) {
@@ -2771,9 +2798,9 @@
     }
 
     if ((parcel.dataSize() > 0)) {
-        notifyListener(MEDIA2_TIMED_TEXT, 0, 0, &parcel);
+        notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0, &parcel);
     } else {  // send an empty timed text
-        notifyListener(MEDIA2_TIMED_TEXT, 0, 0);
+        notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0);
     }
 }
 
@@ -2794,9 +2821,6 @@
         case DATA_SOURCE_TYPE_MEDIA:
             return "Media";
 
-        case DATA_SOURCE_TYPE_STREAM:
-            return "Stream";
-
         case DATA_SOURCE_TYPE_NONE:
         default:
             return "None";
@@ -2984,13 +3008,6 @@
     notify->post();
 }
 
-void NuPlayer2::Source::notifyInstantiateSecureDecoders(const sp<AMessage> &reply) {
-    sp<AMessage> notify = dupNotify();
-    notify->setInt32("what", kWhatInstantiateSecureDecoders);
-    notify->setMessage("reply", reply);
-    notify->post();
-}
-
 void NuPlayer2::Source::onMessageReceived(const sp<AMessage> & /* msg */) {
     TRESPASS();
 }
diff --git a/media/libmedia/nuplayer2/NuPlayer2.h b/media/libmediaplayer2/nuplayer2/NuPlayer2.h
similarity index 92%
rename from media/libmedia/nuplayer2/NuPlayer2.h
rename to media/libmediaplayer2/nuplayer2/NuPlayer2.h
index 1d74b55..e7b774c 100644
--- a/media/libmedia/nuplayer2/NuPlayer2.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.h
@@ -19,9 +19,10 @@
 #define NU_PLAYER2_H_
 
 #include <media/AudioResamplerPublic.h>
-#include <media/MediaPlayer2Interface.h>
 #include <media/stagefright/foundation/AHandler.h>
 
+#include <mediaplayer2/MediaPlayer2Interface.h>
+
 namespace android {
 
 struct ABuffer;
@@ -31,20 +32,18 @@
 struct AudioPlaybackRate;
 struct AVSyncSettings;
 struct DataSourceDesc;
-class IDataSource;
 struct MediaClock;
 struct MediaHTTPService;
 class MetaData;
 struct NuPlayer2Driver;
 
 struct NuPlayer2 : public AHandler {
-    explicit NuPlayer2(pid_t pid, const sp<MediaClock> &mediaClock);
-
-    void setUID(uid_t uid);
+    explicit NuPlayer2(pid_t pid, uid_t uid, const sp<MediaClock> &mediaClock);
 
     void setDriver(const wp<NuPlayer2Driver> &driver);
 
     void setDataSourceAsync(const sp<DataSourceDesc> &dsd);
+    void prepareNextDataSourceAsync(const sp<DataSourceDesc> &dsd);
 
     status_t getBufferingSettings(BufferingSettings* buffering /* nonnull */);
     status_t setBufferingSettings(const BufferingSettings& buffering);
@@ -121,6 +120,7 @@
     enum {
         kWhatSetDataSource              = '=DaS',
         kWhatPrepare                    = 'prep',
+        kWhatPrepareNextDataSource      = 'pNDS',
         kWhatSetVideoSurface            = '=VSu',
         kWhatSetAudioSink               = '=AuS',
         kWhatMoreDataQueued             = 'more',
@@ -151,13 +151,16 @@
     };
 
     wp<NuPlayer2Driver> mDriver;
-    bool mUIDValid;
-    uid_t mUID;
     pid_t mPID;
+    uid_t mUID;
     const sp<MediaClock> mMediaClock;
     Mutex mSourceLock;  // guard |mSource|.
     sp<Source> mSource;
+    int64_t mSrcId;
     uint32_t mSourceFlags;
+    sp<Source> mNextSource;
+    int64_t mNextSrcId;
+    uint32_t mNextSourceFlags;
     sp<ANativeWindowWrapper> mNativeWindow;
     sp<MediaPlayer2Interface::AudioSink> mAudioSink;
     sp<DecoderBase> mVideoDecoder;
@@ -250,10 +253,10 @@
         DATA_SOURCE_TYPE_GENERIC_URL,
         DATA_SOURCE_TYPE_GENERIC_FD,
         DATA_SOURCE_TYPE_MEDIA,
-        DATA_SOURCE_TYPE_STREAM,
     } DATA_SOURCE_TYPE;
 
     std::atomic<DATA_SOURCE_TYPE> mDataSourceType;
+    std::atomic<DATA_SOURCE_TYPE> mNextDataSourceType;
 
     inline const sp<DecoderBase> &getDecoder(bool audio) {
         return audio ? mAudioDecoder : mVideoDecoder;
@@ -266,6 +269,10 @@
         mFlushComplete[1][1] = false;
     }
 
+    status_t createNuPlayer2Source(const sp<DataSourceDesc> &dsd,
+                                   sp<Source> *source,
+                                   DATA_SOURCE_TYPE *dataSourceType);
+
     void tryOpenAudioSinkForOffload(
             const sp<AMessage> &format, const sp<MetaData> &audioMeta, bool hasVideo);
     void closeAudioSink();
@@ -276,20 +283,17 @@
     status_t instantiateDecoder(
             bool audio, sp<DecoderBase> *decoder, bool checkAudioModeChange = true);
 
-    status_t onInstantiateSecureDecoders();
-
     void updateVideoSize(
+            int64_t srcId,
             const sp<AMessage> &inputFormat,
             const sp<AMessage> &outputFormat = NULL);
 
-    void notifyListener(int msg, int ext1, int ext2, const Parcel *in = NULL);
+    void notifyListener(int64_t srcId, int msg, int ext1, int ext2, const Parcel *in = NULL);
 
     void handleFlushComplete(bool audio, bool isDecoder);
     void finishFlushIfPossible();
 
-    void onStart(
-            int64_t startPositionUs = -1,
-            MediaPlayer2SeekMode mode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC);
+    void onStart();
     void onResume();
     void onPause();
 
@@ -298,7 +302,7 @@
     void flushDecoder(bool audio, bool needShutdown);
 
     void finishResume();
-    void notifyDriverSeekComplete();
+    void notifyDriverSeekComplete(int64_t srcId);
 
     void postScanSources();
 
diff --git a/media/libmedia/nuplayer2/NuPlayer2CCDecoder.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp
similarity index 100%
rename from media/libmedia/nuplayer2/NuPlayer2CCDecoder.cpp
rename to media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp
diff --git a/media/libmedia/nuplayer2/NuPlayer2CCDecoder.h b/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.h
similarity index 100%
rename from media/libmedia/nuplayer2/NuPlayer2CCDecoder.h
rename to media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.h
diff --git a/media/libmedia/nuplayer2/NuPlayer2Decoder.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp
similarity index 100%
rename from media/libmedia/nuplayer2/NuPlayer2Decoder.cpp
rename to media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.cpp
diff --git a/media/libmedia/nuplayer2/NuPlayer2Decoder.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.h
similarity index 100%
rename from media/libmedia/nuplayer2/NuPlayer2Decoder.h
rename to media/libmediaplayer2/nuplayer2/NuPlayer2Decoder.h
diff --git a/media/libmedia/nuplayer2/NuPlayer2DecoderBase.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2DecoderBase.cpp
similarity index 100%
rename from media/libmedia/nuplayer2/NuPlayer2DecoderBase.cpp
rename to media/libmediaplayer2/nuplayer2/NuPlayer2DecoderBase.cpp
diff --git a/media/libmedia/nuplayer2/NuPlayer2DecoderBase.h b/media/libmediaplayer2/nuplayer2/NuPlayer2DecoderBase.h
similarity index 100%
rename from media/libmedia/nuplayer2/NuPlayer2DecoderBase.h
rename to media/libmediaplayer2/nuplayer2/NuPlayer2DecoderBase.h
diff --git a/media/libmedia/nuplayer2/NuPlayer2DecoderPassThrough.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2DecoderPassThrough.cpp
similarity index 100%
rename from media/libmedia/nuplayer2/NuPlayer2DecoderPassThrough.cpp
rename to media/libmediaplayer2/nuplayer2/NuPlayer2DecoderPassThrough.cpp
diff --git a/media/libmedia/nuplayer2/NuPlayer2DecoderPassThrough.h b/media/libmediaplayer2/nuplayer2/NuPlayer2DecoderPassThrough.h
similarity index 100%
rename from media/libmedia/nuplayer2/NuPlayer2DecoderPassThrough.h
rename to media/libmediaplayer2/nuplayer2/NuPlayer2DecoderPassThrough.h
diff --git a/media/libmedia/nuplayer2/NuPlayer2Driver.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
similarity index 92%
rename from media/libmedia/nuplayer2/NuPlayer2Driver.cpp
rename to media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
index 6137fd1..60a07a3 100644
--- a/media/libmedia/nuplayer2/NuPlayer2Driver.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
@@ -25,6 +25,7 @@
 #include "NuPlayer2.h"
 #include "NuPlayer2Source.h"
 
+#include <media/DataSourceDesc.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/ALooper.h>
 #include <media/stagefright/foundation/AUtils.h>
@@ -79,6 +80,9 @@
 // key for media statistics
 static const char *kKeyPlayer = "nuplayer";
 // attrs for media statistics
+    // NB: these are matched with public Java API constants defined
+    // in frameworks/base/media/java/android/media/MediaPlayer2.java
+    // These must be kept synchronized with the constants there.
 static const char *kPlayerVMime = "android.media.mediaplayer.video.mime";
 static const char *kPlayerVCodec = "android.media.mediaplayer.video.codec";
 static const char *kPlayerWidth = "android.media.mediaplayer.width";
@@ -91,6 +95,8 @@
 static const char *kPlayerPlaying = "android.media.mediaplayer.playingMs";
 static const char *kPlayerError = "android.media.mediaplayer.err";
 static const char *kPlayerErrorCode = "android.media.mediaplayer.errcode";
+
+// NB: These are not yet exposed as public Java API constants.
 static const char *kPlayerErrorState = "android.media.mediaplayer.errstate";
 static const char *kPlayerDataSourceType = "android.media.mediaplayer.dataSource";
 //
@@ -99,10 +105,11 @@
 static const char *kPlayerRebufferingAtExit = "android.media.mediaplayer.rebufferExit";
 
 
-NuPlayer2Driver::NuPlayer2Driver(pid_t pid)
+NuPlayer2Driver::NuPlayer2Driver(pid_t pid, uid_t uid)
     : mState(STATE_IDLE),
       mIsAsyncPrepare(false),
       mAsyncResult(UNKNOWN_ERROR),
+      mSrcId(0),
       mSetSurfaceInProgress(false),
       mDurationUs(-1),
       mPositionUs(-1),
@@ -114,10 +121,10 @@
       mLooper(new ALooper),
       mNuPlayer2Looper(new ALooper),
       mMediaClock(new MediaClock),
-      mPlayer(new NuPlayer2(pid, mMediaClock)),
+      mPlayer(new NuPlayer2(pid, uid, mMediaClock)),
       mPlayerFlags(0),
       mAnalyticsItem(NULL),
-      mClientUid(-1),
+      mClientUid(uid),
       mAtEOS(false),
       mLooping(false),
       mAutoLoop(false) {
@@ -129,6 +136,7 @@
 
     // set up an analytics record
     mAnalyticsItem = new MediaAnalyticsItem(kKeyPlayer);
+    mAnalyticsItem->setUid(mClientUid);
 
     mNuPlayer2Looper->start(
             false, /* runOnCallingThread */
@@ -165,16 +173,6 @@
     return OK;
 }
 
-status_t NuPlayer2Driver::setUID(uid_t uid) {
-    mPlayer->setUID(uid);
-    mClientUid = uid;
-    if (mAnalyticsItem) {
-        mAnalyticsItem->setUid(mClientUid);
-    }
-
-    return OK;
-}
-
 status_t NuPlayer2Driver::setDataSource(const sp<DataSourceDesc> &dsd) {
     ALOGV("setDataSource(%p) callback source", this);
     Mutex::Autolock autoLock(mLock);
@@ -183,6 +181,7 @@
         return INVALID_OPERATION;
     }
 
+    mSrcId = dsd->mId;
     mState = STATE_SET_DATASOURCE_PENDING;
 
     mPlayer->setDataSourceAsync(dsd);
@@ -365,7 +364,7 @@
 
         case STATE_PAUSED:
             mState = STATE_STOPPED;
-            notifyListener_l(MEDIA2_STOPPED);
+            //notifyListener_l(MEDIA2_STOPPED);
             break;
 
         case STATE_PREPARED:
@@ -400,7 +399,6 @@
 
         case STATE_RUNNING:
             mState = STATE_PAUSED;
-            notifyListener_l(MEDIA2_PAUSED);
             mPlayer->pause();
             break;
 
@@ -424,7 +422,6 @@
         Mutex::Autolock autoLock(mLock);
         if (rate.mSpeed == 0.f && mState == STATE_RUNNING) {
             mState = STATE_PAUSED;
-            notifyListener_l(MEDIA2_PAUSED);
         } else if (rate.mSpeed != 0.f
                 && (mState == STATE_PAUSED
                     || mState == STATE_STOPPED_AND_PREPARED
@@ -461,8 +458,6 @@
         {
             mAtEOS = false;
             mSeekInProgress = true;
-            // seeks can take a while, so we essentially paused
-            notifyListener_l(MEDIA2_PAUSED);
             mPlayer->seekToAsync(seekTimeUs, mode, true /* needNotify */);
             break;
         }
@@ -633,7 +628,7 @@
         {
             CHECK(mIsAsyncPrepare);
 
-            notifyListener_l(MEDIA2_PREPARED);
+            notifyListener_l(mSrcId, MEDIA2_PREPARED);
             break;
         }
 
@@ -642,7 +637,7 @@
     }
 
     if (mState != STATE_STOPPED) {
-        notifyListener_l(MEDIA2_STOPPED);
+        // notifyListener_l(MEDIA2_STOPPED);
     }
 
     mState = STATE_RESET_IN_PROGRESS;
@@ -776,7 +771,7 @@
     return OK;
 }
 
-void NuPlayer2Driver::notifyResetComplete() {
+void NuPlayer2Driver::notifyResetComplete(int64_t /* srcId */) {
     ALOGD("notifyResetComplete(%p)", this);
     Mutex::Autolock autoLock(mLock);
 
@@ -785,7 +780,7 @@
     mCondition.broadcast();
 }
 
-void NuPlayer2Driver::notifySetSurfaceComplete() {
+void NuPlayer2Driver::notifySetSurfaceComplete(int64_t /* srcId */) {
     ALOGV("notifySetSurfaceComplete(%p)", this);
     Mutex::Autolock autoLock(mLock);
 
@@ -795,35 +790,35 @@
     mCondition.broadcast();
 }
 
-void NuPlayer2Driver::notifyDuration(int64_t durationUs) {
+void NuPlayer2Driver::notifyDuration(int64_t /* srcId */, int64_t durationUs) {
     Mutex::Autolock autoLock(mLock);
     mDurationUs = durationUs;
 }
 
-void NuPlayer2Driver::notifyMorePlayingTimeUs(int64_t playingUs) {
+void NuPlayer2Driver::notifyMorePlayingTimeUs(int64_t /* srcId */, int64_t playingUs) {
     Mutex::Autolock autoLock(mLock);
     mPlayingTimeUs += playingUs;
 }
 
-void NuPlayer2Driver::notifyMoreRebufferingTimeUs(int64_t rebufferingUs) {
+void NuPlayer2Driver::notifyMoreRebufferingTimeUs(int64_t /* srcId */, int64_t rebufferingUs) {
     Mutex::Autolock autoLock(mLock);
     mRebufferingTimeUs += rebufferingUs;
     mRebufferingEvents++;
 }
 
-void NuPlayer2Driver::notifyRebufferingWhenExit(bool status) {
+void NuPlayer2Driver::notifyRebufferingWhenExit(int64_t /* srcId */, bool status) {
     Mutex::Autolock autoLock(mLock);
     mRebufferingAtExit = status;
 }
 
-void NuPlayer2Driver::notifySeekComplete() {
+void NuPlayer2Driver::notifySeekComplete(int64_t srcId) {
     ALOGV("notifySeekComplete(%p)", this);
     Mutex::Autolock autoLock(mLock);
     mSeekInProgress = false;
-    notifySeekComplete_l();
+    notifySeekComplete_l(srcId);
 }
 
-void NuPlayer2Driver::notifySeekComplete_l() {
+void NuPlayer2Driver::notifySeekComplete_l(int64_t srcId) {
     bool wasSeeking = true;
     if (mState == STATE_STOPPED_AND_PREPARING) {
         wasSeeking = false;
@@ -837,7 +832,7 @@
         // no need to notify listener
         return;
     }
-    notifyListener_l(wasSeeking ? MEDIA2_SEEK_COMPLETE : MEDIA2_PREPARED);
+    notifyListener_l(srcId, wasSeeking ? MEDIA2_SEEK_COMPLETE : MEDIA2_PREPARED);
 }
 
 status_t NuPlayer2Driver::dump(
@@ -920,9 +915,11 @@
 void NuPlayer2Driver::onMessageReceived(const sp<AMessage> &msg) {
     switch (msg->what()) {
         case kWhatNotifyListener: {
+            int64_t srcId;
             int32_t msgId;
             int32_t ext1 = 0;
             int32_t ext2 = 0;
+            CHECK(msg->findInt64("srcId", &srcId));
             CHECK(msg->findInt32("messageId", &msgId));
             msg->findInt32("ext1", &ext1);
             msg->findInt32("ext2", &ext2);
@@ -931,7 +928,7 @@
             if (msg->findObject("parcel", &obj) && obj != NULL) {
                 in = static_cast<ParcelWrapper *>(obj.get());
             }
-            sendEvent(msgId, ext1, ext2, (in == NULL ? NULL : in->getParcel()));
+            sendEvent(srcId, msgId, ext1, ext2, (in == NULL ? NULL : in->getParcel()));
             break;
         }
         default:
@@ -940,15 +937,16 @@
 }
 
 void NuPlayer2Driver::notifyListener(
-        int msg, int ext1, int ext2, const Parcel *in) {
+        int64_t srcId, int msg, int ext1, int ext2, const Parcel *in) {
     Mutex::Autolock autoLock(mLock);
-    notifyListener_l(msg, ext1, ext2, in);
+    notifyListener_l(srcId, msg, ext1, ext2, in);
 }
 
 void NuPlayer2Driver::notifyListener_l(
-        int msg, int ext1, int ext2, const Parcel *in) {
-    ALOGD("notifyListener_l(%p), (%d, %d, %d, %d), loop setting(%d, %d)",
-            this, msg, ext1, ext2, (in == NULL ? -1 : (int)in->dataSize()), mAutoLoop, mLooping);
+        int64_t srcId, int msg, int ext1, int ext2, const Parcel *in) {
+    ALOGD("notifyListener_l(%p), (%lld, %d, %d, %d, %d), loop setting(%d, %d)",
+            this, (long long)srcId, msg, ext1, ext2,
+            (in == NULL ? -1 : (int)in->dataSize()), mAutoLoop, mLooping);
     switch (msg) {
         case MEDIA2_PLAYBACK_COMPLETE:
         {
@@ -1004,6 +1002,7 @@
     }
 
     sp<AMessage> notify = new AMessage(kWhatNotifyListener, this);
+    notify->setInt64("srcId", srcId);
     notify->setInt32("messageId", msg);
     notify->setInt32("ext1", ext1);
     notify->setInt32("ext2", ext2);
@@ -1011,7 +1010,7 @@
     notify->post();
 }
 
-void NuPlayer2Driver::notifySetDataSourceCompleted(status_t err) {
+void NuPlayer2Driver::notifySetDataSourceCompleted(int64_t /* srcId */, status_t err) {
     Mutex::Autolock autoLock(mLock);
 
     CHECK_EQ(mState, STATE_SET_DATASOURCE_PENDING);
@@ -1021,7 +1020,7 @@
     mCondition.broadcast();
 }
 
-void NuPlayer2Driver::notifyPrepareCompleted(status_t err) {
+void NuPlayer2Driver::notifyPrepareCompleted(int64_t srcId, status_t err) {
     ALOGV("notifyPrepareCompleted %d", err);
 
     Mutex::Autolock autoLock(mLock);
@@ -1043,12 +1042,12 @@
         // in response, NuPlayer2Driver has the right state
         mState = STATE_PREPARED;
         if (mIsAsyncPrepare) {
-            notifyListener_l(MEDIA2_PREPARED);
+            notifyListener_l(srcId, MEDIA2_PREPARED);
         }
     } else {
         mState = STATE_UNPREPARED;
         if (mIsAsyncPrepare) {
-            notifyListener_l(MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
+            notifyListener_l(srcId, MEDIA2_ERROR, MEDIA2_ERROR_UNKNOWN, err);
         }
     }
 
@@ -1062,7 +1061,7 @@
     mCondition.broadcast();
 }
 
-void NuPlayer2Driver::notifyFlagsChanged(uint32_t flags) {
+void NuPlayer2Driver::notifyFlagsChanged(int64_t /* srcId */, uint32_t flags) {
     Mutex::Autolock autoLock(mLock);
 
     mPlayerFlags = flags;
diff --git a/media/libmedia/nuplayer2/NuPlayer2Driver.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
similarity index 80%
rename from media/libmedia/nuplayer2/NuPlayer2Driver.h
rename to media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
index 55fc9ef..4c57cfd 100644
--- a/media/libmedia/nuplayer2/NuPlayer2Driver.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <media/MediaPlayer2Interface.h>
+#include <mediaplayer2/MediaPlayer2Interface.h>
 
 #include <media/MediaAnalyticsItem.h>
 #include <media/stagefright/foundation/ABase.h>
@@ -26,12 +26,10 @@
 struct NuPlayer2;
 
 struct NuPlayer2Driver : public MediaPlayer2Interface {
-    explicit NuPlayer2Driver(pid_t pid);
+    explicit NuPlayer2Driver(pid_t pid, uid_t uid);
 
     virtual status_t initCheck();
 
-    virtual status_t setUID(uid_t uid);
-
     virtual status_t setDataSource(const sp<DataSourceDesc> &dsd) override;
 
     virtual status_t setVideoSurfaceTexture(const sp<ANativeWindowWrapper> &nww);
@@ -69,18 +67,19 @@
 
     virtual void onMessageReceived(const sp<AMessage> &msg) override;
 
-    void notifySetDataSourceCompleted(status_t err);
-    void notifyPrepareCompleted(status_t err);
-    void notifyResetComplete();
-    void notifySetSurfaceComplete();
-    void notifyDuration(int64_t durationUs);
-    void notifyMorePlayingTimeUs(int64_t timeUs);
-    void notifyMoreRebufferingTimeUs(int64_t timeUs);
-    void notifyRebufferingWhenExit(bool status);
-    void notifySeekComplete();
-    void notifySeekComplete_l();
-    void notifyListener(int msg, int ext1 = 0, int ext2 = 0, const Parcel *in = NULL);
-    void notifyFlagsChanged(uint32_t flags);
+    void notifySetDataSourceCompleted(int64_t srcId, status_t err);
+    void notifyPrepareCompleted(int64_t srcId, status_t err);
+    void notifyResetComplete(int64_t srcId);
+    void notifySetSurfaceComplete(int64_t srcId);
+    void notifyDuration(int64_t srcId, int64_t durationUs);
+    void notifyMorePlayingTimeUs(int64_t srcId, int64_t timeUs);
+    void notifyMoreRebufferingTimeUs(int64_t srcId, int64_t timeUs);
+    void notifyRebufferingWhenExit(int64_t srcId, bool status);
+    void notifySeekComplete(int64_t srcId);
+    void notifySeekComplete_l(int64_t srcId);
+    void notifyListener(int64_t srcId, int msg, int ext1 = 0, int ext2 = 0,
+                        const Parcel *in = NULL);
+    void notifyFlagsChanged(int64_t srcId, uint32_t flags);
 
     // Modular DRM
     virtual status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId);
@@ -120,6 +119,7 @@
 
     // The following are protected through "mLock"
     // >>>
+    int64_t mSrcId;
     bool mSetSurfaceInProgress;
     int64_t mDurationUs;
     int64_t mPositionUs;
@@ -149,7 +149,8 @@
 
     status_t prepare_l();
     status_t start_l();
-    void notifyListener_l(int msg, int ext1 = 0, int ext2 = 0, const Parcel *in = NULL);
+    void notifyListener_l(int64_t srcId, int msg, int ext1 = 0, int ext2 = 0,
+                          const Parcel *in = NULL);
 
     DISALLOW_EVIL_CONSTRUCTORS(NuPlayer2Driver);
 };
diff --git a/media/libmedia/nuplayer2/NuPlayer2Drm.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp
similarity index 100%
rename from media/libmedia/nuplayer2/NuPlayer2Drm.cpp
rename to media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp
diff --git a/media/libmedia/nuplayer2/NuPlayer2Drm.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h
similarity index 100%
rename from media/libmedia/nuplayer2/NuPlayer2Drm.h
rename to media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h
diff --git a/media/libmedia/nuplayer2/NuPlayer2Renderer.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp
similarity index 100%
rename from media/libmedia/nuplayer2/NuPlayer2Renderer.cpp
rename to media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp
diff --git a/media/libmedia/nuplayer2/NuPlayer2Renderer.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.h
similarity index 100%
rename from media/libmedia/nuplayer2/NuPlayer2Renderer.h
rename to media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.h
diff --git a/media/libmedia/nuplayer2/NuPlayer2Source.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Source.h
similarity index 96%
rename from media/libmedia/nuplayer2/NuPlayer2Source.h
rename to media/libmediaplayer2/nuplayer2/NuPlayer2Source.h
index 41ded9d..662235f 100644
--- a/media/libmedia/nuplayer2/NuPlayer2Source.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Source.h
@@ -20,9 +20,9 @@
 
 #include "NuPlayer2.h"
 
-#include <media/mediaplayer2.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/MetaData.h>
+#include <mediaplayer2/mediaplayer2.h>
 #include <utils/Vector.h>
 
 namespace android {
@@ -55,7 +55,6 @@
         kWhatTimedMetaData,
         kWhatQueueDecoderShutdown,
         kWhatDrmNoLicense,
-        kWhatInstantiateSecureDecoders,
         // Modular DRM
         kWhatDrmInfo,
     };
@@ -151,7 +150,6 @@
 
     void notifyFlagsChanged(uint32_t flags);
     void notifyVideoSizeChanged(const sp<AMessage> &format = NULL);
-    void notifyInstantiateSecureDecoders(const sp<AMessage> &reply);
     void notifyPrepared(status_t err = OK);
     // Modular DRM
     void notifyDrmInfo(const sp<ABuffer> &buffer);
diff --git a/media/libmedia/nuplayer2/RTSPSource2.cpp b/media/libmediaplayer2/nuplayer2/RTSPSource2.cpp
similarity index 98%
rename from media/libmedia/nuplayer2/RTSPSource2.cpp
rename to media/libmediaplayer2/nuplayer2/RTSPSource2.cpp
index 8b67ae5..1dfe383 100644
--- a/media/libmedia/nuplayer2/RTSPSource2.cpp
+++ b/media/libmediaplayer2/nuplayer2/RTSPSource2.cpp
@@ -43,13 +43,11 @@
         const sp<MediaHTTPService> &httpService,
         const char *url,
         const KeyedVector<String8, String8> *headers,
-        bool uidValid,
         uid_t uid,
         bool isSDP)
     : Source(notify),
       mHTTPService(httpService),
       mURL(url),
-      mUIDValid(uidValid),
       mUID(uid),
       mFlags(0),
       mIsSDP(isSDP),
@@ -128,7 +126,7 @@
         mSDPLoader->load(
                 mURL.c_str(), mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders);
     } else {
-        mHandler = new MyHandler(mURL.c_str(), notify, mUIDValid, mUID);
+        mHandler = new MyHandler(mURL.c_str(), notify, true /* uidValid */, mUID);
         mLooper->registerHandler(mHandler);
 
         mHandler->connect();
@@ -793,7 +791,7 @@
         } else {
             sp<AMessage> notify = new AMessage(kWhatNotify, this);
 
-            mHandler = new MyHandler(rtspUri.c_str(), notify, mUIDValid, mUID);
+            mHandler = new MyHandler(rtspUri.c_str(), notify, true /* uidValid */, mUID);
             mLooper->registerHandler(mHandler);
 
             mHandler->loadSDP(desc);
diff --git a/media/libmedia/nuplayer2/RTSPSource2.h b/media/libmediaplayer2/nuplayer2/RTSPSource2.h
similarity index 98%
rename from media/libmedia/nuplayer2/RTSPSource2.h
rename to media/libmediaplayer2/nuplayer2/RTSPSource2.h
index 739bd53..712c3e5 100644
--- a/media/libmedia/nuplayer2/RTSPSource2.h
+++ b/media/libmediaplayer2/nuplayer2/RTSPSource2.h
@@ -36,7 +36,6 @@
             const sp<MediaHTTPService> &httpService,
             const char *url,
             const KeyedVector<String8, String8> *headers,
-            bool uidValid = false,
             uid_t uid = 0,
             bool isSDP = false);
 
@@ -97,7 +96,6 @@
     sp<MediaHTTPService> mHTTPService;
     AString mURL;
     KeyedVector<String8, String8> mExtraHeaders;
-    bool mUIDValid;
     uid_t mUID;
     uint32_t mFlags;
     bool mIsSDP;
diff --git a/media/libmediaplayerservice/MediaPlayerFactory.cpp b/media/libmediaplayerservice/MediaPlayerFactory.cpp
index 3209c6d..1376ccc 100644
--- a/media/libmediaplayerservice/MediaPlayerFactory.cpp
+++ b/media/libmediaplayerservice/MediaPlayerFactory.cpp
@@ -126,8 +126,7 @@
 
 sp<MediaPlayerBase> MediaPlayerFactory::createPlayer(
         player_type playerType,
-        const wp<IMediaPlayer> &client,
-        notify_callback_f notifyFunc,
+        const sp<MediaPlayerBase::Listener> &listener,
         pid_t pid) {
     sp<MediaPlayerBase> p;
     IFactory* factory;
@@ -152,7 +151,7 @@
 
     init_result = p->initCheck();
     if (init_result == NO_ERROR) {
-        p->setNotifyCallback(client, notifyFunc);
+        p->setNotifyCallback(listener);
     } else {
         ALOGE("Failed to create player object of type %d, initCheck failed"
               " (res = %d)", playerType, init_result);
diff --git a/media/libmediaplayerservice/MediaPlayerFactory.h b/media/libmediaplayerservice/MediaPlayerFactory.h
index 6aedeb2..e88700c 100644
--- a/media/libmediaplayerservice/MediaPlayerFactory.h
+++ b/media/libmediaplayerservice/MediaPlayerFactory.h
@@ -65,8 +65,7 @@
                                      const sp<DataSource> &source);
 
     static sp<MediaPlayerBase> createPlayer(player_type playerType,
-                                            const wp<IMediaPlayer> &client,
-                                            notify_callback_f notifyFunc,
+                                            const sp<MediaPlayerBase::Listener> &listener,
                                             pid_t pid);
 
     static void registerBuiltinFactories();
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 88221ed..9bcfc83 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -572,10 +572,11 @@
     mUid = uid;
     mRetransmitEndpointValid = false;
     mAudioAttributes = NULL;
+    mListener = new Listener(this);
 
 #if CALLBACK_ANTAGONIZER
     ALOGD("create Antagonizer");
-    mAntagonizer = new Antagonizer(notify, this);
+    mAntagonizer = new Antagonizer(mListener);
 #endif
 }
 
@@ -610,7 +611,7 @@
     // and reset the player. We assume the player will serialize
     // access to itself if necessary.
     if (p != 0) {
-        p->setNotifyCallback(0, 0);
+        p->setNotifyCallback(0);
 #if CALLBACK_ANTAGONIZER
         ALOGD("kill Antagonizer");
         mAntagonizer->kill();
@@ -635,7 +636,7 @@
         p.clear();
     }
     if (p == NULL) {
-        p = MediaPlayerFactory::createPlayer(playerType, this, notify, mPid);
+        p = MediaPlayerFactory::createPlayer(playerType, mListener, mPid);
     }
 
     if (p != NULL) {
@@ -1421,25 +1422,19 @@
 }
 
 void MediaPlayerService::Client::notify(
-        const wp<IMediaPlayer> &listener, int msg, int ext1, int ext2, const Parcel *obj)
+        int msg, int ext1, int ext2, const Parcel *obj)
 {
-    sp<IMediaPlayer> spListener = listener.promote();
-    if (spListener == NULL) {
-        return;
-    }
-    Client* client = static_cast<Client*>(spListener.get());
-
     sp<IMediaPlayerClient> c;
     sp<Client> nextClient;
     status_t errStartNext = NO_ERROR;
     {
-        Mutex::Autolock l(client->mLock);
-        c = client->mClient;
-        if (msg == MEDIA_PLAYBACK_COMPLETE && client->mNextClient != NULL) {
-            nextClient = client->mNextClient;
+        Mutex::Autolock l(mLock);
+        c = mClient;
+        if (msg == MEDIA_PLAYBACK_COMPLETE && mNextClient != NULL) {
+            nextClient = mNextClient;
 
-            if (client->mAudioOutput != NULL)
-                client->mAudioOutput->switchToNextOutput();
+            if (mAudioOutput != NULL)
+                mAudioOutput->switchToNextOutput();
 
             errStartNext = nextClient->start();
         }
@@ -1465,17 +1460,17 @@
         MEDIA_INFO_METADATA_UPDATE == ext1) {
         const media::Metadata::Type metadata_type = ext2;
 
-        if(client->shouldDropMetadata(metadata_type)) {
+        if(shouldDropMetadata(metadata_type)) {
             return;
         }
 
         // Update the list of metadata that have changed. getMetadata
         // also access mMetadataUpdated and clears it.
-        client->addNewMetadataUpdate(metadata_type);
+        addNewMetadataUpdate(metadata_type);
     }
 
     if (c != NULL) {
-        ALOGV("[%d] notify (%p, %d, %d, %d)", client->mConnId, spListener.get(), msg, ext1, ext2);
+        ALOGV("[%d] notify (%d, %d, %d)", mConnId, msg, ext1, ext2);
         c->notify(msg, ext1, ext2, obj);
     }
 }
@@ -1569,8 +1564,8 @@
 #if CALLBACK_ANTAGONIZER
 const int Antagonizer::interval = 10000; // 10 msecs
 
-Antagonizer::Antagonizer(notify_callback_f cb, const wp<IMediaPlayer> &client) :
-    mExit(false), mActive(false), mClient(client), mCb(cb)
+Antagonizer::Antagonizer(const sp<MediaPlayerBase::Listener> &listener) :
+    mExit(false), mActive(false), mListener(listener)
 {
     createThread(callbackThread, this);
 }
@@ -1590,7 +1585,7 @@
     while (!p->mExit) {
         if (p->mActive) {
             ALOGV("send event");
-            p->mCb(p->mClient, 0, 0, 0);
+            p->mListener->notify(0, 0, 0, 0);
         }
         usleep(interval);
     }
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 58efa8c..bfb7cc2 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -51,7 +51,7 @@
 #if CALLBACK_ANTAGONIZER
 class Antagonizer {
 public:
-    Antagonizer(notify_callback_f cb, const wp<IMediaPlayer> &client);
+    Antagonizer(const sp<MediaPlayerBase::Listener> &listener);
     void start() { mActive = true; }
     void stop() { mActive = false; }
     void kill();
@@ -59,12 +59,11 @@
     static const int interval;
     Antagonizer();
     static int callbackThread(void* cookie);
-    Mutex               mLock;
-    Condition           mCondition;
-    bool                mExit;
-    bool                mActive;
-    wp<IMediaPlayer>    mClient;
-    notify_callback_f   mCb;
+    Mutex                         mLock;
+    Condition                     mCondition;
+    bool                          mExit;
+    bool                          mActive;
+    sp<MediaPlayerBase::Listener> mListener;
 };
 #endif
 
@@ -228,7 +227,6 @@
 
     }; // AudioOutput
 
-
 public:
     static  void                instantiate();
 
@@ -377,8 +375,7 @@
         status_t                setDataSource_post(const sp<MediaPlayerBase>& p,
                                                    status_t status);
 
-        static  void            notify(const wp<IMediaPlayer> &cookie, int msg,
-                                       int ext1, int ext2, const Parcel *obj);
+                void            notify(int msg, int ext1, int ext2, const Parcel *obj);
 
                 pid_t           pid() const { return mPid; }
         virtual status_t        dump(int fd, const Vector<String16>& args);
@@ -468,23 +465,38 @@
 
         status_t setAudioAttributes_l(const Parcel &request);
 
-        mutable     Mutex                       mLock;
-                    sp<MediaPlayerBase>         mPlayer;
-                    sp<MediaPlayerService>      mService;
-                    sp<IMediaPlayerClient>      mClient;
-                    sp<AudioOutput>             mAudioOutput;
-                    pid_t                       mPid;
-                    status_t                    mStatus;
-                    bool                        mLoop;
-                    int32_t                     mConnId;
-                    audio_session_t             mAudioSessionId;
-                    audio_attributes_t *        mAudioAttributes;
-                    uid_t                       mUid;
-                    sp<ANativeWindow>           mConnectedWindow;
-                    sp<IBinder>                 mConnectedWindowBinder;
-                    struct sockaddr_in          mRetransmitEndpoint;
-                    bool                        mRetransmitEndpointValid;
-                    sp<Client>                  mNextClient;
+        class Listener : public MediaPlayerBase::Listener {
+        public:
+            Listener(const wp<Client> &client) : mClient(client) {}
+            virtual ~Listener() {}
+            virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) {
+                sp<Client> client = mClient.promote();
+                if (client != NULL) {
+                    client->notify(msg, ext1, ext2, obj);
+                }
+            }
+        private:
+            wp<Client> mClient;
+        };
+
+        mutable     Mutex                         mLock;
+                    sp<MediaPlayerBase>           mPlayer;
+                    sp<MediaPlayerService>        mService;
+                    sp<IMediaPlayerClient>        mClient;
+                    sp<AudioOutput>               mAudioOutput;
+                    pid_t                         mPid;
+                    status_t                      mStatus;
+                    bool                          mLoop;
+                    int32_t                       mConnId;
+                    audio_session_t               mAudioSessionId;
+                    audio_attributes_t *          mAudioAttributes;
+                    uid_t                         mUid;
+                    sp<ANativeWindow>             mConnectedWindow;
+                    sp<IBinder>                   mConnectedWindowBinder;
+                    struct sockaddr_in            mRetransmitEndpoint;
+                    bool                          mRetransmitEndpointValid;
+                    sp<Client>                    mNextClient;
+                    sp<MediaPlayerBase::Listener> mListener;
 
         // Metadata filters.
         media::Metadata::Filter mMetadataAllow;  // protected by mLock
@@ -500,7 +512,7 @@
         sp<ServiceDeathNotifier> mCodecDeathListener;
         sp<AudioDeviceUpdatedNotifier> mAudioDeviceUpdatedListener;
 #if CALLBACK_ANTAGONIZER
-                    Antagonizer*                mAntagonizer;
+                    Antagonizer*                  mAntagonizer;
 #endif
     }; // Client
 
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index bc8d8c8..5cbf976 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -69,22 +69,26 @@
 // key for media statistics
 static const char *kKeyRecorder = "recorder";
 // attrs for media statistics
-static const char *kRecorderHeight = "android.media.mediarecorder.height";
-static const char *kRecorderWidth = "android.media.mediarecorder.width";
-static const char *kRecorderFrameRate = "android.media.mediarecorder.frame-rate";
-static const char *kRecorderVideoBitrate = "android.media.mediarecorder.video-bitrate";
-static const char *kRecorderAudioSampleRate = "android.media.mediarecorder.audio-samplerate";
-static const char *kRecorderAudioChannels = "android.media.mediarecorder.audio-channels";
+// NB: these are matched with public Java API constants defined
+// in frameworks/base/media/java/android/media/MediaRecorder.java
+// These must be kept synchronized with the constants there.
 static const char *kRecorderAudioBitrate = "android.media.mediarecorder.audio-bitrate";
-static const char *kRecorderVideoIframeInterval = "android.media.mediarecorder.video-iframe-interval";
-static const char *kRecorderMovieTimescale = "android.media.mediarecorder.movie-timescale";
+static const char *kRecorderAudioChannels = "android.media.mediarecorder.audio-channels";
+static const char *kRecorderAudioSampleRate = "android.media.mediarecorder.audio-samplerate";
 static const char *kRecorderAudioTimescale = "android.media.mediarecorder.audio-timescale";
-static const char *kRecorderVideoTimescale = "android.media.mediarecorder.video-timescale";
-static const char *kRecorderVideoProfile = "android.media.mediarecorder.video-encoder-profile";
-static const char *kRecorderVideoLevel = "android.media.mediarecorder.video-encoder-level";
-static const char *kRecorderCaptureFpsEnable = "android.media.mediarecorder.capture-fpsenable";
 static const char *kRecorderCaptureFps = "android.media.mediarecorder.capture-fps";
+static const char *kRecorderCaptureFpsEnable = "android.media.mediarecorder.capture-fpsenable";
+static const char *kRecorderFrameRate = "android.media.mediarecorder.frame-rate";
+static const char *kRecorderHeight = "android.media.mediarecorder.height";
+static const char *kRecorderMovieTimescale = "android.media.mediarecorder.movie-timescale";
 static const char *kRecorderRotation = "android.media.mediarecorder.rotation";
+static const char *kRecorderVideoBitrate = "android.media.mediarecorder.video-bitrate";
+static const char *kRecorderVideoIframeInterval = "android.media.mediarecorder.video-iframe-interval";
+static const char *kRecorderVideoLevel = "android.media.mediarecorder.video-encoder-level";
+static const char *kRecorderVideoProfile = "android.media.mediarecorder.video-encoder-profile";
+static const char *kRecorderVideoTimescale = "android.media.mediarecorder.video-timescale";
+static const char *kRecorderWidth = "android.media.mediarecorder.width";
+
 
 // To collect the encoder usage for the battery app
 static void addBatteryData(uint32_t params) {
diff --git a/media/libmediaplayerservice/include/MediaPlayerInterface.h b/media/libmediaplayerservice/include/MediaPlayerInterface.h
index 4b33e61..3119950 100644
--- a/media/libmediaplayerservice/include/MediaPlayerInterface.h
+++ b/media/libmediaplayerservice/include/MediaPlayerInterface.h
@@ -66,14 +66,17 @@
 // duration below which we do not allow deep audio buffering
 #define AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US 5000000
 
-// callback mechanism for passing messages to MediaPlayer object
-typedef void (*notify_callback_f)(const wp<IMediaPlayer> &listener,
-        int msg, int ext1, int ext2, const Parcel *obj);
-
 // abstract base class - use MediaPlayerInterface
 class MediaPlayerBase : public RefBase
 {
 public:
+    // callback mechanism for passing messages to MediaPlayer object
+    class Listener : public RefBase {
+    public:
+        virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) = 0;
+        virtual ~Listener() {}
+    };
+
     // AudioSink: abstraction layer for audio output
     class AudioSink : public RefBase {
     public:
@@ -157,7 +160,7 @@
         virtual status_t    enableAudioDeviceCallback(bool enabled);
     };
 
-                        MediaPlayerBase() : mClient(0), mNotify(0) {}
+                        MediaPlayerBase() {}
     virtual             ~MediaPlayerBase() {}
     virtual status_t    initCheck() = 0;
     virtual bool        hardwareOutput() = 0;
@@ -271,22 +274,22 @@
     };
 
     void        setNotifyCallback(
-            const wp<IMediaPlayer> &client, notify_callback_f notifyFunc) {
+            const sp<Listener> &listener) {
         Mutex::Autolock autoLock(mNotifyLock);
-        mClient = client; mNotify = notifyFunc;
+        mListener = listener;
     }
 
     void        sendEvent(int msg, int ext1=0, int ext2=0,
                           const Parcel *obj=NULL) {
-        notify_callback_f notifyCB;
-        wp<IMediaPlayer> client;
+        sp<Listener> listener;
         {
             Mutex::Autolock autoLock(mNotifyLock);
-            notifyCB = mNotify;
-            client = mClient;
+            listener = mListener;
         }
 
-        if (notifyCB) notifyCB(client, msg, ext1, ext2, obj);
+        if (listener != NULL) {
+            listener->notify(msg, ext1, ext2, obj);
+        }
     }
 
     virtual status_t dump(int /* fd */, const Vector<String16>& /* args */) const {
@@ -305,8 +308,7 @@
     friend class MediaPlayerService;
 
     Mutex               mNotifyLock;
-    wp<IMediaPlayer>    mClient;
-    notify_callback_f   mNotify;
+    sp<Listener>        mListener;
 };
 
 // Implement this class for media players that use the AudioFlinger software mixer
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index b3fd00a..c455951 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -43,6 +43,9 @@
 // key for media statistics
 static const char *kKeyPlayer = "nuplayer";
 // attrs for media statistics
+    // NB: these are matched with public Java API constants defined
+    // in frameworks/base/media/java/android/media/MediaPlayer.java
+    // These must be kept synchronized with the constants there.
 static const char *kPlayerVMime = "android.media.mediaplayer.video.mime";
 static const char *kPlayerVCodec = "android.media.mediaplayer.video.codec";
 static const char *kPlayerWidth = "android.media.mediaplayer.width";
@@ -55,6 +58,8 @@
 static const char *kPlayerPlaying = "android.media.mediaplayer.playingMs";
 static const char *kPlayerError = "android.media.mediaplayer.err";
 static const char *kPlayerErrorCode = "android.media.mediaplayer.errcode";
+
+    // NB: These are not yet exposed as public Java API constants.
 static const char *kPlayerErrorState = "android.media.mediaplayer.errstate";
 static const char *kPlayerDataSourceType = "android.media.mediaplayer.dataSource";
 //
diff --git a/media/libmediaplayerservice/tests/Android.bp b/media/libmediaplayerservice/tests/Android.bp
index d6c1d27..e936bdd 100644
--- a/media/libmediaplayerservice/tests/Android.bp
+++ b/media/libmediaplayerservice/tests/Android.bp
@@ -12,6 +12,7 @@
         "libmediadrm",
         "libutils",
         "android.hardware.drm@1.0",
+        "android.hardware.drm@1.1",
     ],
 
     compile_multilib: "32",
diff --git a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
index c5212fc..2c6a41b 100644
--- a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
+++ b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
@@ -20,7 +20,7 @@
 
 #include <gtest/gtest.h>
 
-#include <media/Drm.h>
+#include <media/DrmHal.h>
 #include <media/DrmSessionClientInterface.h>
 #include <media/DrmSessionManager.h>
 #include <media/stagefright/foundation/ADebug.h>
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 5240f7b..905817f 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -60,6 +60,7 @@
         "AudioPresentationInfo.cpp",
         "AudioSource.cpp",
         "BufferImpl.cpp",
+        "C2OMXNode.cpp",
         "CCodec.cpp",
         "CCodecBufferChannel.cpp",
         "CodecBase.cpp",
@@ -106,6 +107,10 @@
         "VideoFrameScheduler.cpp",
     ],
 
+    header_libs: [
+        "libstagefright_codec2_internal",
+    ],
+
     shared_libs: [
         "libaudioutils",
         "libbinder",
@@ -129,7 +134,6 @@
         "libstagefright_codec2",
         "libstagefright_codec2_vndk",
         "libstagefright_foundation",
-        "libstagefright_gbs",
         "libstagefright_omx",
         "libstagefright_omx_utils",
         "libstagefright_xmlparser",
@@ -145,6 +149,10 @@
         "android.hardware.media.omx@1.0",
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.mapper@2.0",
+
+        // TODO: do not link directly with impl
+        "android.hardware.media.c2@1.0-service-impl",
+        "libstagefright_bufferqueue_helper",
     ],
 
     static_libs: [
@@ -211,6 +219,7 @@
         "InterfaceUtils.cpp",
         "MediaClock.cpp",
         "MediaExtractorFactory.cpp",
+        "NdkUtils.cpp",
         "NuCachedSource2.cpp",
         "RemoteMediaExtractor.cpp",
         "RemoteMediaSource.cpp",
diff --git a/media/libstagefright/C2OMXNode.cpp b/media/libstagefright/C2OMXNode.cpp
new file mode 100644
index 0000000..e6f81af
--- /dev/null
+++ b/media/libstagefright/C2OMXNode.cpp
@@ -0,0 +1,281 @@
+/*
+ * 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.
+ */
+
+#ifdef __LP64__
+#define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
+#endif
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2OMXNode"
+#include <log/log.h>
+
+#include <C2AllocatorGralloc.h>
+#include <C2BlockInternal.h>
+#include <C2Component.h>
+#include <C2PlatformSupport.h>
+
+#include <OMX_Component.h>
+#include <OMX_Index.h>
+#include <OMX_IndexExt.h>
+
+#include <media/stagefright/omx/OMXUtils.h>
+#include <media/stagefright/MediaErrors.h>
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+
+#include "include/C2OMXNode.h"
+
+namespace android {
+
+namespace {
+
+class Buffer2D : public C2Buffer {
+public:
+    explicit Buffer2D(C2ConstGraphicBlock block) : C2Buffer({ block }) {}
+};
+
+}  // namespace
+
+C2OMXNode::C2OMXNode(const std::shared_ptr<C2Component> &comp) : mComp(comp) {}
+
+status_t C2OMXNode::freeNode() {
+    mComp.reset();
+    return OK;
+}
+
+status_t C2OMXNode::sendCommand(OMX_COMMANDTYPE cmd, OMX_S32 param) {
+    (void)cmd;
+    (void)param;
+    return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::getParameter(OMX_INDEXTYPE index, void *params, size_t size) {
+    status_t err = ERROR_UNSUPPORTED;
+    switch ((uint32_t)index) {
+        case OMX_IndexParamConsumerUsageBits: {
+            // TODO: read from intf()
+            OMX_U32 *usage = (OMX_U32 *)params;
+            *usage = GRALLOC_USAGE_SW_READ_OFTEN;
+            err = OK;
+            break;
+        }
+        case OMX_IndexParamPortDefinition: {
+            if (size < sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) {
+                return BAD_VALUE;
+            }
+            OMX_PARAM_PORTDEFINITIONTYPE *pDef = (OMX_PARAM_PORTDEFINITIONTYPE *)params;
+            // TODO: read these from intf()
+            pDef->nBufferCountActual = 16;
+            pDef->eDomain = OMX_PortDomainVideo;
+            pDef->format.video.nFrameWidth = 1080;
+            pDef->format.video.nFrameHeight = 1920;
+            err = OK;
+            break;
+        }
+        default:
+            break;
+    }
+    return err;
+}
+
+status_t C2OMXNode::setParameter(OMX_INDEXTYPE index, const void *params, size_t size) {
+    (void)index;
+    (void)params;
+    (void)size;
+    return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::getConfig(OMX_INDEXTYPE index, void *config, size_t size) {
+    (void)index;
+    (void)config;
+    (void)size;
+    return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::setConfig(OMX_INDEXTYPE index, const void *config, size_t size) {
+    (void)index;
+    (void)config;
+    (void)size;
+    return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::setPortMode(OMX_U32 portIndex, IOMX::PortMode mode) {
+    (void)portIndex;
+    (void)mode;
+    return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::prepareForAdaptivePlayback(
+        OMX_U32 portIndex, OMX_BOOL enable,
+        OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight) {
+    (void)portIndex;
+    (void)enable;
+    (void)maxFrameWidth;
+    (void)maxFrameHeight;
+    return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::configureVideoTunnelMode(
+        OMX_U32 portIndex, OMX_BOOL tunneled,
+        OMX_U32 audioHwSync, native_handle_t **sidebandHandle) {
+    (void)portIndex;
+    (void)tunneled;
+    (void)audioHwSync;
+    *sidebandHandle = nullptr;
+    return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::getGraphicBufferUsage(OMX_U32 portIndex, OMX_U32* usage) {
+    (void)portIndex;
+    *usage = 0;
+    return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::setInputSurface(const sp<IOMXBufferSource> &bufferSource) {
+    c2_status_t err = GetCodec2PlatformAllocatorStore()->fetchAllocator(
+            C2PlatformAllocatorStore::GRALLOC,
+            &mAllocator);
+    if (err != OK) {
+        return UNKNOWN_ERROR;
+    }
+    mBufferSource = bufferSource;
+    return OK;
+}
+
+status_t C2OMXNode::allocateSecureBuffer(
+        OMX_U32 portIndex, size_t size, buffer_id *buffer,
+        void **bufferData, sp<NativeHandle> *nativeHandle) {
+    (void)portIndex;
+    (void)size;
+    (void)nativeHandle;
+    *buffer = 0;
+    *bufferData = nullptr;
+    return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::useBuffer(
+        OMX_U32 portIndex, const OMXBuffer &omxBuf, buffer_id *buffer) {
+    (void)portIndex;
+    (void)omxBuf;
+    *buffer = 0;
+    return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::freeBuffer(OMX_U32 portIndex, buffer_id buffer) {
+    (void)portIndex;
+    (void)buffer;
+    return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::fillBuffer(
+        buffer_id buffer, const OMXBuffer &omxBuf, int fenceFd) {
+    (void)buffer;
+    (void)omxBuf;
+    (void)fenceFd;
+    return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::emptyBuffer(
+        buffer_id buffer, const OMXBuffer &omxBuf,
+        OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
+    // TODO: better fence handling
+    if (fenceFd >= 0) {
+        sp<Fence> fence = new Fence(fenceFd);
+        fence->waitForever(LOG_TAG);
+    }
+    std::shared_ptr<C2Component> comp = mComp.lock();
+    if (!comp) {
+        return NO_INIT;
+    }
+
+    uint32_t c2Flags = 0;
+    std::shared_ptr<C2GraphicBlock> block;
+
+    C2Handle *handle = nullptr;
+    if (omxBuf.mBufferType == OMXBuffer::kBufferTypeANWBuffer) {
+        std::shared_ptr<C2GraphicAllocation> alloc;
+        handle = WrapNativeCodec2GrallocHandle(
+                native_handle_clone(omxBuf.mGraphicBuffer->handle),
+                omxBuf.mGraphicBuffer->width,
+                omxBuf.mGraphicBuffer->height,
+                omxBuf.mGraphicBuffer->format,
+                omxBuf.mGraphicBuffer->usage,
+                omxBuf.mGraphicBuffer->stride);
+        c2_status_t err = mAllocator->priorGraphicAllocation(handle, &alloc);
+        if (err != OK) {
+            return UNKNOWN_ERROR;
+        }
+        block = _C2BlockFactory::CreateGraphicBlock(alloc);
+    } else if (flags & OMX_BUFFERFLAG_EOS) {
+        c2Flags = C2FrameData::FLAG_END_OF_STREAM;
+    } else {
+        return BAD_VALUE;
+    }
+
+    std::unique_ptr<C2Work> work(new C2Work);
+    work->input.flags = (C2FrameData::flags_t)c2Flags;
+    work->input.ordinal.timestamp = timestamp;
+    work->input.ordinal.frameIndex = mFrameIndex++;
+    work->input.buffers.clear();
+    if (block) {
+        std::shared_ptr<C2Buffer> c2Buffer(
+                // TODO: fence
+                new Buffer2D(block->share(
+                        C2Rect(block->width(), block->height()), ::android::C2Fence())),
+                [handle, buffer, source = getSource()](C2Buffer *ptr) {
+                    delete ptr;
+                    native_handle_delete(handle);
+                    // TODO: fence
+                    (void)source->onInputBufferEmptied(buffer, -1);
+                });
+        work->input.buffers.push_back(c2Buffer);
+    }
+    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 = comp->queue_nb(&items);
+    if (err != C2_OK) {
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+status_t C2OMXNode::getExtensionIndex(
+        const char *parameterName, OMX_INDEXTYPE *index) {
+    (void)parameterName;
+    *index = OMX_IndexMax;
+    return ERROR_UNSUPPORTED;
+}
+
+status_t C2OMXNode::dispatchMessage(const omx_message& msg) {
+    if (msg.type != omx_message::EVENT) {
+        return ERROR_UNSUPPORTED;
+    }
+    if (msg.u.event_data.event != OMX_EventDataSpaceChanged) {
+        return ERROR_UNSUPPORTED;
+    }
+    // TODO: fill intf() with info inside |msg|.
+    return OK;
+}
+
+sp<IOMXBufferSource> C2OMXNode::getSource() {
+    return mBufferSource;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/CCodec.cpp b/media/libstagefright/CCodec.cpp
index 84e98f8..bb70458 100644
--- a/media/libstagefright/CCodec.cpp
+++ b/media/libstagefright/CCodec.cpp
@@ -24,17 +24,23 @@
 #include <C2PlatformSupport.h>
 #include <C2V4l2Support.h>
 
+#include <android/IOMXBufferSource.h>
+#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
 #include <gui/Surface.h>
+#include <media/stagefright/codec2/1.0/InputSurface.h>
 #include <media/stagefright/BufferProducerWrapper.h>
 #include <media/stagefright/CCodec.h>
 #include <media/stagefright/PersistentSurface.h>
 
+#include "include/C2OMXNode.h"
 #include "include/CCodecBufferChannel.h"
-
-using namespace std::chrono_literals;
+#include "include/InputSurfaceWrapper.h"
 
 namespace android {
 
+using namespace std::chrono_literals;
+using ::android::hardware::graphics::bufferqueue::V1_0::utils::H2BGraphicBufferProducer;
+
 namespace {
 
 class CCodecWatchdog : public AHandler {
@@ -146,6 +152,76 @@
     wp<CCodec> mCodec;
 };
 
+class C2InputSurfaceWrapper : public InputSurfaceWrapper {
+public:
+    explicit C2InputSurfaceWrapper(const sp<InputSurface> &surface) : mSurface(surface) {}
+    ~C2InputSurfaceWrapper() override = default;
+
+    status_t connect(const std::shared_ptr<C2Component> &comp) override {
+        if (mConnection != nullptr) {
+            return ALREADY_EXISTS;
+        }
+        mConnection = mSurface->connectToComponent(comp);
+        return OK;
+    }
+
+    void disconnect() override {
+        if (mConnection != nullptr) {
+            mConnection->disconnect();
+            mConnection.clear();
+        }
+    }
+
+private:
+    sp<InputSurface> mSurface;
+    sp<InputSurfaceConnection> mConnection;
+};
+
+class GraphicBufferSourceWrapper : public InputSurfaceWrapper {
+public:
+    explicit GraphicBufferSourceWrapper(const sp<IGraphicBufferSource> &source) : mSource(source) {}
+    ~GraphicBufferSourceWrapper() override = default;
+
+    status_t connect(const std::shared_ptr<C2Component> &comp) override {
+        // TODO: proper color aspect & dataspace
+        android_dataspace dataSpace = HAL_DATASPACE_BT709;
+
+        mNode = new C2OMXNode(comp);
+        mSource->configure(mNode, dataSpace);
+
+        // TODO: configure according to intf().
+
+        sp<IOMXBufferSource> source = mNode->getSource();
+        if (source == nullptr) {
+            return NO_INIT;
+        }
+        constexpr size_t kNumSlots = 16;
+        for (size_t i = 0; i < kNumSlots; ++i) {
+            source->onInputBufferAdded(i);
+        }
+        source->onOmxExecuting();
+        return OK;
+    }
+
+    void disconnect() override {
+        if (mNode == nullptr) {
+            return;
+        }
+        sp<IOMXBufferSource> source = mNode->getSource();
+        if (source == nullptr) {
+            ALOGD("GBSWrapper::disconnect: node is not configured with OMXBufferSource.");
+            return;
+        }
+        source->onOmxIdle();
+        source->onOmxLoaded();
+        mNode.clear();
+    }
+
+private:
+    sp<IGraphicBufferSource> mSource;
+    sp<C2OMXNode> mNode;
+};
+
 }  // namespace
 
 CCodec::CCodec()
@@ -300,18 +376,18 @@
 }
 
 void CCodec::createInputSurface() {
-    sp<IGraphicBufferProducer> producer;
-    sp<GraphicBufferSource> source(new GraphicBufferSource);
+    // TODO: get this from codec process
+    sp<InputSurface> surface(InputSurface::Create());
 
-    status_t err = source->initCheck();
+    // TODO: get proper error code.
+    status_t err = (surface == nullptr) ? UNKNOWN_ERROR : OK;
     if (err != OK) {
-        ALOGE("Failed to initialize graphic buffer source: %d", err);
+        ALOGE("Failed to initialize input surface: %d", err);
         mCallback->onInputSurfaceCreationFailed(err);
         return;
     }
-    producer = source->getIGraphicBufferProducer();
 
-    err = setupInputSurface(source);
+    err = setupInputSurface(std::make_shared<C2InputSurfaceWrapper>(surface));
     if (err != OK) {
         ALOGE("Failed to set up input surface: %d", err);
         mCallback->onInputSurfaceCreationFailed(err);
@@ -328,16 +404,16 @@
     mCallback->onInputSurfaceCreated(
             inputFormat,
             outputFormat,
-            new BufferProducerWrapper(producer));
+            new BufferProducerWrapper(new H2BGraphicBufferProducer(surface)));
 }
 
-status_t CCodec::setupInputSurface(const sp<GraphicBufferSource> &source) {
-    status_t err = mChannel->setGraphicBufferSource(source);
+status_t CCodec::setupInputSurface(const std::shared_ptr<InputSurfaceWrapper> &surface) {
+    status_t err = mChannel->setInputSurface(surface);
     if (err != OK) {
         return err;
     }
 
-    // TODO: configure |source| with other settings.
+    // TODO: configure |surface| with other settings.
     return OK;
 }
 
@@ -348,10 +424,22 @@
 }
 
 void CCodec::setInputSurface(const sp<PersistentSurface> &surface) {
-    // TODO
-    (void)surface;
+    status_t err = setupInputSurface(std::make_shared<GraphicBufferSourceWrapper>(
+            surface->getBufferSource()));
+    if (err != OK) {
+        ALOGE("Failed to set up input surface: %d", err);
+        mCallback->onInputSurfaceDeclined(err);
+        return;
+    }
 
-    mCallback->onInputSurfaceDeclined(ERROR_UNSUPPORTED);
+    sp<AMessage> inputFormat;
+    sp<AMessage> outputFormat;
+    {
+        Mutexed<Formats>::Locked formats(mFormats);
+        inputFormat = formats->inputFormat;
+        outputFormat = formats->outputFormat;
+    }
+    mCallback->onInputSurfaceAccepted(inputFormat, outputFormat);
 }
 
 void CCodec::initiateStart() {
diff --git a/media/libstagefright/CCodecBufferChannel.cpp b/media/libstagefright/CCodecBufferChannel.cpp
index ca4ddc8..6fba890 100644
--- a/media/libstagefright/CCodecBufferChannel.cpp
+++ b/media/libstagefright/CCodecBufferChannel.cpp
@@ -23,6 +23,7 @@
 
 #include <C2AllocatorGralloc.h>
 #include <C2PlatformSupport.h>
+#include <C2BlockInternal.h>
 
 #include <android/hardware/cas/native/1.0/IDescrambler.h>
 #include <binder/MemoryDealer.h>
@@ -100,15 +101,6 @@
      * 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;
 
@@ -133,47 +125,6 @@
     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;
@@ -226,8 +177,6 @@
 
 namespace {
 
-constexpr int32_t kMaskI32 = ~0;
-
 // TODO: get this info from component
 const static size_t kMinBufferArraySize = 16;
 const static size_t kLinearBufferSize = 524288;
@@ -421,23 +370,8 @@
 public:
     GraphicInputBuffers() = default;
 
-    bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
-        *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> releaseBufferIndex(size_t index) override {
-        mAvailable[index] = true;
-        return nullptr;
+    bool requestNewBuffer(size_t *, sp<MediaCodecBuffer> *) override {
+        return false;
     }
 
     std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &) override {
@@ -451,8 +385,11 @@
         return nullptr;
     }
 
-private:
-    std::vector<bool> mAvailable;
+    bool isArrayMode() const final { return true; }
+
+    void getArray(Vector<sp<MediaCodecBuffer>> *array) const final {
+        array->clear();
+    }
 };
 
 class OutputBuffersArray : public CCodecBufferChannel::OutputBuffers {
@@ -752,163 +689,8 @@
     }
 };
 
-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);
@@ -965,13 +747,10 @@
     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);
@@ -1024,16 +803,11 @@
     }
 }
 
-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::setInputSurface(
+        const std::shared_ptr<InputSurfaceWrapper> &surface) {
+    ALOGV("setInputSurface");
+    mInputSurface = surface;
+    return OK;
 }
 
 status_t CCodecBufferChannel::queueInputBuffer(const sp<MediaCodecBuffer> &buffer) {
@@ -1098,12 +872,13 @@
     {
         Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
         if (!(*buffers)->requestNewBuffer(&index, &inBuffer)) {
-            ALOGW("no new buffer available");
+            ALOGV("no new buffer available");
             inBuffer = nullptr;
+            return;
         }
     }
     ALOGV("new input index = %zu", index);
-    mInputClient->onInputBufferAvailable(index, inBuffer);
+    mCallback->onInputBufferAvailable(index, inBuffer);
 }
 
 status_t CCodecBufferChannel::renderOutputBuffer(
@@ -1180,7 +955,9 @@
     {
         Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
         if((*buffers)->releaseBuffer(buffer)) {
+            buffers.unlock();
             feedInputBufferIfAvailable();
+            buffers.lock();
         }
     }
     return OK;
@@ -1219,29 +996,38 @@
     }
 
     mSync.start();
-    // TODO: use proper buffer depth instead of this random value
-    for (size_t i = 0; i < kMinBufferArraySize; ++i) {
-        size_t index;
-        sp<MediaCodecBuffer> buffer;
-        {
-            Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
-            if (!(*buffers)->requestNewBuffer(&index, &buffer)) {
-                buffers.unlock();
-                ALOGE("start: cannot allocate memory");
-                mOnError(NO_MEMORY, ACTION_CODE_FATAL);
-                buffers.lock();
-                return;
+    if (mInputSurface == nullptr) {
+        // TODO: use proper buffer depth instead of this random value
+        for (size_t i = 0; i < kMinBufferArraySize; ++i) {
+            size_t index;
+            sp<MediaCodecBuffer> buffer;
+            {
+                Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
+                if (!(*buffers)->requestNewBuffer(&index, &buffer)) {
+                    if (i == 0) {
+                        ALOGE("start: cannot allocate memory at all");
+                        buffers.unlock();
+                        mOnError(NO_MEMORY, ACTION_CODE_FATAL);
+                        buffers.lock();
+                    } else {
+                        ALOGV("start: cannot allocate memory, only %zu buffers allocated", i);
+                    }
+                    break;
+                }
             }
+            mCallback->onInputBufferAvailable(index, buffer);
         }
-        mInputClient->onInputBufferAdded(index, buffer);
+    } else {
+        (void)mInputSurface->connect(mComponent);
     }
-    mInputClient->onStart();
 }
 
 void CCodecBufferChannel::stop() {
     mSync.stop();
     mFirstValidFrameIndex = mFrameIndex.load();
-    mInputClient->onStop();
+    if (mInputSurface != nullptr) {
+        mInputSurface->disconnect();
+    }
 }
 
 void CCodecBufferChannel::flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) {
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 56ac3ed..2b33708 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -64,16 +64,22 @@
 // key for media statistics
 static const char *kCodecKeyName = "codec";
 // attrs for media statistics
+// NB: these are matched with public Java API constants defined
+// in frameworks/base/media/java/android/media/MediaCodec.java
+// These must be kept synchronized with the constants there.
 static const char *kCodecCodec = "android.media.mediacodec.codec";  /* e.g. OMX.google.aac.decoder */
 static const char *kCodecMime = "android.media.mediacodec.mime";    /* e.g. audio/mime */
 static const char *kCodecMode = "android.media.mediacodec.mode";    /* audio, video */
-static const char *kCodecSecure = "android.media.mediacodec.secure";   /* 0, 1 */
-static const char *kCodecHeight = "android.media.mediacodec.height";   /* 0..n */
-static const char *kCodecWidth = "android.media.mediacodec.width";     /* 0..n */
-static const char *kCodecRotation = "android.media.mediacodec.rotation-degrees";  /* 0/90/180/270 */
-static const char *kCodecCrypto = "android.media.mediacodec.crypto";   /* 0,1 */
+static const char *kCodecModeVideo = "video";            /* values returned for kCodecMode */
+static const char *kCodecModeAudio = "audio";
 static const char *kCodecEncoder = "android.media.mediacodec.encoder"; /* 0,1 */
+static const char *kCodecSecure = "android.media.mediacodec.secure";   /* 0, 1 */
+static const char *kCodecWidth = "android.media.mediacodec.width";     /* 0..n */
+static const char *kCodecHeight = "android.media.mediacodec.height";   /* 0..n */
+static const char *kCodecRotation = "android.media.mediacodec.rotation-degrees";  /* 0/90/180/270 */
 
+// NB: These are not yet exposed as public Java API constants.
+static const char *kCodecCrypto = "android.media.mediacodec.crypto";   /* 0,1 */
 static const char *kCodecBytesIn = "android.media.mediacodec.bytesin";  /* 0..n */
 static const char *kCodecProfile = "android.media.mediacodec.profile";  /* 0..n */
 static const char *kCodecLevel = "android.media.mediacodec.level";  /* 0..n */
@@ -645,7 +651,7 @@
         } else {
             mAnalyticsItem->setCString(kCodecCodec, name.c_str());
         }
-        mAnalyticsItem->setCString(kCodecMode, mIsVideo ? "video" : "audio");
+        mAnalyticsItem->setCString(kCodecMode, mIsVideo ? kCodecModeVideo : kCodecModeAudio);
         if (nameIsType)
             mAnalyticsItem->setInt32(kCodecEncoder, encoder);
     }
@@ -2949,8 +2955,8 @@
 
         if (mSoftRenderer != NULL) {
             std::list<FrameRenderTracker::Info> doneFrames = mSoftRenderer->render(
-                    buffer->data(), buffer->size(),
-                    mediaTimeUs, renderTimeNs, NULL, buffer->format());
+                    buffer->data(), buffer->size(), mediaTimeUs, renderTimeNs,
+                    mPortBuffers[kPortIndexOutput].size(), buffer->format());
 
             // if we are running, notify rendered frames
             if (!doneFrames.empty() && mState == STARTED && mOnFrameRenderedNotification != NULL) {
diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp
index bb72167..543c274 100644
--- a/media/libstagefright/MediaExtractorFactory.cpp
+++ b/media/libstagefright/MediaExtractorFactory.cpp
@@ -112,23 +112,25 @@
     // initialize source decryption if needed
     source->DrmInitialization(nullptr /* mime */);
 
-    sp<AMessage> meta;
-
+    void *meta = nullptr;
     MediaExtractor::CreatorFunc creator = NULL;
-    String8 tmp;
+    MediaExtractor::FreeMetaFunc freeMeta = nullptr;
     float confidence;
     sp<ExtractorPlugin> plugin;
-    creator = sniff(source.get(), &tmp, &confidence, &meta, plugin);
+    creator = sniff(source.get(), &confidence, &meta, &freeMeta, plugin);
     if (!creator) {
         ALOGV("FAILED to autodetect media content.");
         return NULL;
     }
 
-    mime = tmp.string();
-    ALOGV("Autodetected media content as '%s' with confidence %.2f",
-         mime, confidence);
-
     MediaExtractor *ret = creator(source.get(), meta);
+    if (meta != nullptr && freeMeta != nullptr) {
+        freeMeta(meta);
+    }
+
+    ALOGV("Created an extractor '%s' with confidence %.2f",
+         ret != nullptr ? ret->name() : "<null>", confidence);
+
     return CreateIMediaExtractorFromMediaExtractor(ret, source, plugin);
 }
 
@@ -165,11 +167,10 @@
 
 // static
 MediaExtractor::CreatorFunc MediaExtractorFactory::sniff(
-        DataSourceBase *source, String8 *mimeType, float *confidence, sp<AMessage> *meta,
-        sp<ExtractorPlugin> &plugin) {
-    *mimeType = "";
+        DataSourceBase *source, float *confidence, void **meta,
+        MediaExtractor::FreeMetaFunc *freeMeta, sp<ExtractorPlugin> &plugin) {
     *confidence = 0.0f;
-    meta->clear();
+    *meta = nullptr;
 
     std::shared_ptr<List<sp<ExtractorPlugin>>> plugins;
     {
@@ -183,16 +184,23 @@
     MediaExtractor::CreatorFunc curCreator = NULL;
     MediaExtractor::CreatorFunc bestCreator = NULL;
     for (auto it = plugins->begin(); it != plugins->end(); ++it) {
-        String8 newMimeType;
         float newConfidence;
-        sp<AMessage> newMeta;
-        if ((curCreator = (*it)->def.sniff(source, &newMimeType, &newConfidence, &newMeta))) {
+        void *newMeta = nullptr;
+        MediaExtractor::FreeMetaFunc newFreeMeta = nullptr;
+        if ((curCreator = (*it)->def.sniff(source, &newConfidence, &newMeta, &newFreeMeta))) {
             if (newConfidence > *confidence) {
-                *mimeType = newMimeType;
                 *confidence = newConfidence;
+                if (*meta != nullptr && *freeMeta != nullptr) {
+                    (*freeMeta)(*meta);
+                }
                 *meta = newMeta;
+                *freeMeta = newFreeMeta;
                 plugin = *it;
                 bestCreator = curCreator;
+            } else {
+                if (newMeta != nullptr && newFreeMeta != nullptr) {
+                    newFreeMeta(newMeta);
+                }
             }
         }
     }
diff --git a/media/libstagefright/NdkUtils.cpp b/media/libstagefright/NdkUtils.cpp
new file mode 100644
index 0000000..904fe72
--- /dev/null
+++ b/media/libstagefright/NdkUtils.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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
+
+#include <media/stagefright/NdkUtils.h>
+#include <media/stagefright/Utils.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+namespace android {
+
+sp<MetaData> convertMediaFormatWrapperToMetaData(const sp<AMediaFormatWrapper> &fmt) {
+    sp<AMessage> msg = fmt->toAMessage();
+    sp<MetaData> meta = new MetaData;
+    convertMessageToMetaData(msg, meta);
+    return meta;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/RemoteMediaExtractor.cpp b/media/libstagefright/RemoteMediaExtractor.cpp
index d190780..5bb0953 100644
--- a/media/libstagefright/RemoteMediaExtractor.cpp
+++ b/media/libstagefright/RemoteMediaExtractor.cpp
@@ -32,9 +32,12 @@
 static const char *kKeyExtractor = "extractor";
 
 // attrs for media statistics
+// NB: these are matched with public Java API constants defined
+// in frameworks/base/media/java/android/media/MediaExtractor.java
+// These must be kept synchronized with the constants there.
+static const char *kExtractorFormat = "android.media.mediaextractor.fmt";
 static const char *kExtractorMime = "android.media.mediaextractor.mime";
 static const char *kExtractorTracks = "android.media.mediaextractor.ntrk";
-static const char *kExtractorFormat = "android.media.mediaextractor.fmt";
 
 RemoteMediaExtractor::RemoteMediaExtractor(
         MediaExtractor *extractor,
diff --git a/media/libstagefright/gbs/Android.bp b/media/libstagefright/bqhelper/Android.bp
similarity index 81%
rename from media/libstagefright/gbs/Android.bp
rename to media/libstagefright/bqhelper/Android.bp
index a53b7b7..388ed6b 100644
--- a/media/libstagefright/gbs/Android.bp
+++ b/media/libstagefright/bqhelper/Android.bp
@@ -1,5 +1,5 @@
 cc_library_shared {
-    name: "libstagefright_gbs",
+    name: "libstagefright_bufferqueue_helper",
     vendor_available: true,
     vndk: {
         enabled: true,
@@ -8,6 +8,7 @@
     srcs: [
         "FrameDropper.cpp",
         "GraphicBufferSource.cpp",
+        "WProducerListener.cpp",
     ],
 
     export_include_dirs: [
@@ -25,17 +26,24 @@
     shared_libs: [
         "libbase",
         "libbinder",
-        "libutils",
-        "liblog",
-        "libui",
-        "libgui",
         "libcutils",
+        "libgui",
+        "libhidlbase",
+        "libhidlmemory",
+        "libhidltransport",
+        "liblog",
         "libstagefright_foundation",
+        "libui",
+        "libutils",
+
+        "android.hardware.graphics.bufferqueue@1.0",
+
         "libnativewindow", // TODO(b/62923479): use header library
     ],
 
     export_shared_lib_headers: [
         "libstagefright_foundation",
+        "libhidlmemory",
     ],
 
     cflags: [
diff --git a/media/libstagefright/gbs/FrameDropper.cpp b/media/libstagefright/bqhelper/FrameDropper.cpp
similarity index 97%
rename from media/libstagefright/gbs/FrameDropper.cpp
rename to media/libstagefright/bqhelper/FrameDropper.cpp
index 9f0b8cc..7afe837 100644
--- a/media/libstagefright/gbs/FrameDropper.cpp
+++ b/media/libstagefright/bqhelper/FrameDropper.cpp
@@ -18,7 +18,7 @@
 #define LOG_TAG "FrameDropper"
 #include <utils/Log.h>
 
-#include <media/stagefright/gbs/FrameDropper.h>
+#include <media/stagefright/bqhelper/FrameDropper.h>
 #include <media/stagefright/foundation/ADebug.h>
 
 namespace android {
diff --git a/media/libstagefright/gbs/GraphicBufferSource.cpp b/media/libstagefright/bqhelper/GraphicBufferSource.cpp
similarity index 99%
rename from media/libstagefright/gbs/GraphicBufferSource.cpp
rename to media/libstagefright/bqhelper/GraphicBufferSource.cpp
index 139c916..87d2555 100644
--- a/media/libstagefright/gbs/GraphicBufferSource.cpp
+++ b/media/libstagefright/bqhelper/GraphicBufferSource.cpp
@@ -22,8 +22,8 @@
 
 #define STRINGIFY_ENUMS // for asString in HardwareAPI.h/VideoAPI.h
 
-#include <media/stagefright/gbs/GraphicBufferSource.h>
-#include <media/stagefright/gbs/FrameDropper.h>
+#include <media/stagefright/bqhelper/GraphicBufferSource.h>
+#include <media/stagefright/bqhelper/FrameDropper.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/foundation/ColorUtils.h>
diff --git a/media/libstagefright/omx/1.0/WProducerListener.cpp b/media/libstagefright/bqhelper/WProducerListener.cpp
similarity index 82%
rename from media/libstagefright/omx/1.0/WProducerListener.cpp
rename to media/libstagefright/bqhelper/WProducerListener.cpp
index bdc3aa1..2ca13be 100644
--- a/media/libstagefright/omx/1.0/WProducerListener.cpp
+++ b/media/libstagefright/bqhelper/WProducerListener.cpp
@@ -14,14 +14,9 @@
  * limitations under the License.
  */
 
-#include <media/stagefright/omx/1.0/WProducerListener.h>
+#include <media/stagefright/bqhelper/WProducerListener.h>
 
 namespace android {
-namespace hardware {
-namespace media {
-namespace omx {
-namespace V1_0 {
-namespace implementation {
 
 // TWProducerListener
 TWProducerListener::TWProducerListener(
@@ -52,9 +47,4 @@
     return static_cast<bool>(mBase->needsReleaseNotify());
 }
 
-}  // namespace implementation
-}  // namespace V1_0
-}  // namespace omx
-}  // namespace media
-}  // namespace hardware
 }  // namespace android
diff --git a/media/libstagefright/gbs/include/media/stagefright/gbs/ComponentWrapper.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/ComponentWrapper.h
similarity index 100%
rename from media/libstagefright/gbs/include/media/stagefright/gbs/ComponentWrapper.h
rename to media/libstagefright/bqhelper/include/media/stagefright/bqhelper/ComponentWrapper.h
diff --git a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/Conversion.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/Conversion.h
new file mode 100644
index 0000000..c15fafa
--- /dev/null
+++ b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/Conversion.h
@@ -0,0 +1,1590 @@
+/*
+ * Copyright 2016, 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_STAGEFRIGHT_CONVERSION_H_
+#define MEDIA_STAGEFRIGHT_CONVERSION_H_
+
+#include <vector>
+#include <list>
+
+#include <unistd.h>
+
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <hidlmemory/mapping.h>
+
+#include <binder/Binder.h>
+#include <binder/Status.h>
+#include <ui/FenceTime.h>
+#include <cutils/native_handle.h>
+#include <gui/IGraphicBufferProducer.h>
+
+#include <media/hardware/VideoAPI.h>
+
+#include <android/hidl/memory/1.0/IMemory.h>
+#include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h>
+
+namespace android {
+namespace conversion {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+using ::android::status_t;
+
+using ::android::String8;
+
+using ::android::hardware::media::V1_0::Rect;
+using ::android::hardware::media::V1_0::Region;
+
+using ::android::hardware::graphics::common::V1_0::Dataspace;
+
+using ::android::hardware::graphics::common::V1_0::PixelFormat;
+
+using ::android::hardware::media::V1_0::AnwBuffer;
+using ::android::GraphicBuffer;
+
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer
+        HGraphicBufferProducer;
+typedef ::android::IGraphicBufferProducer
+        BGraphicBufferProducer;
+
+// native_handle_t helper functions.
+
+/**
+ * \brief Take an fd and create a native handle containing only the given fd.
+ * The created handle will need to be deleted manually with
+ * `native_handle_delete()`.
+ *
+ * \param[in] fd The source file descriptor (of type `int`).
+ * \return The create `native_handle_t*` that contains the given \p fd. If the
+ * supplied \p fd is negative, the created native handle will contain no file
+ * descriptors.
+ *
+ * If the native handle cannot be created, the return value will be
+ * `nullptr`.
+ *
+ * This function does not duplicate the file descriptor.
+ */
+inline native_handle_t* native_handle_create_from_fd(int fd) {
+    if (fd < 2) {
+        return native_handle_create(0, 0);
+    }
+    native_handle_t* nh = native_handle_create(1, 0);
+    if (nh == nullptr) {
+        return nullptr;
+    }
+    nh->data[0] = fd;
+    return nh;
+}
+
+/**
+ * \brief Extract a file descriptor from a native handle.
+ *
+ * \param[in] nh The source `native_handle_t*`.
+ * \param[in] index The index of the file descriptor in \p nh to read from. This
+ * input has the default value of `0`.
+ * \return The `index`-th file descriptor in \p nh. If \p nh does not have
+ * enough file descriptors, the returned value will be `-1`.
+ *
+ * This function does not duplicate the file descriptor.
+ */
+inline int native_handle_read_fd(native_handle_t const* nh, int index = 0) {
+    return ((nh == nullptr) || (nh->numFds == 0) ||
+            (nh->numFds <= index) || (index < 0)) ?
+            -1 : nh->data[index];
+}
+
+/**
+ * Conversion functions
+ * ====================
+ *
+ * There are two main directions of conversion:
+ * - `inTargetType(...)`: Create a wrapper whose lifetime depends on the
+ *   input. The wrapper has type `TargetType`.
+ * - `toTargetType(...)`: Create a standalone object of type `TargetType` that
+ *   corresponds to the input. The lifetime of the output does not depend on the
+ *   lifetime of the input.
+ * - `wrapIn(TargetType*, ...)`: Same as `inTargetType()`, but for `TargetType`
+ *   that cannot be copied and/or moved efficiently, or when there are multiple
+ *   output arguments.
+ * - `convertTo(TargetType*, ...)`: Same as `toTargetType()`, but for
+ *   `TargetType` that cannot be copied and/or moved efficiently, or when there
+ *   are multiple output arguments.
+ *
+ * `wrapIn()` and `convertTo()` functions will take output arguments before
+ * input arguments. Some of these functions might return a value to indicate
+ * success or error.
+ *
+ * In converting or wrapping something as a Treble type that contains a
+ * `hidl_handle`, `native_handle_t*` will need to be created and returned as
+ * an additional output argument, hence only `wrapIn()` or `convertTo()` would
+ * be available. The caller must call `native_handle_delete()` to deallocate the
+ * returned native handle when it is no longer needed.
+ *
+ * For types that contain file descriptors, `inTargetType()` and `wrapAs()` do
+ * not perform duplication of file descriptors, while `toTargetType()` and
+ * `convertTo()` do.
+ */
+
+/**
+ * \brief Convert `Return<void>` to `binder::Status`.
+ *
+ * \param[in] t The source `Return<void>`.
+ * \return The corresponding `binder::Status`.
+ */
+// convert: Return<void> -> ::android::binder::Status
+inline ::android::binder::Status toBinderStatus(
+        Return<void> const& t) {
+    return ::android::binder::Status::fromExceptionCode(
+            t.isOk() ? OK : UNKNOWN_ERROR,
+            t.description().c_str());
+}
+
+/**
+ * \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls.
+ *
+ * \param[in] t The source `Return<void>`.
+ * \return The corresponding `status_t`.
+ */
+// convert: Return<void> -> status_t
+inline status_t toStatusT(Return<void> const& t) {
+    return t.isOk() ? OK : UNKNOWN_ERROR;
+}
+
+/**
+ * \brief Wrap `native_handle_t*` in `hidl_handle`.
+ *
+ * \param[in] nh The source `native_handle_t*`.
+ * \return The `hidl_handle` that points to \p nh.
+ */
+// wrap: native_handle_t* -> hidl_handle
+inline hidl_handle inHidlHandle(native_handle_t const* nh) {
+    return hidl_handle(nh);
+}
+
+/**
+ * \brief Convert `int32_t` to `Dataspace`.
+ *
+ * \param[in] l The source `int32_t`.
+ * \result The corresponding `Dataspace`.
+ */
+// convert: int32_t -> Dataspace
+inline Dataspace toHardwareDataspace(int32_t l) {
+    return static_cast<Dataspace>(l);
+}
+
+/**
+ * \brief Convert `Dataspace` to `int32_t`.
+ *
+ * \param[in] t The source `Dataspace`.
+ * \result The corresponding `int32_t`.
+ */
+// convert: Dataspace -> int32_t
+inline int32_t toRawDataspace(Dataspace const& t) {
+    return static_cast<int32_t>(t);
+}
+
+/**
+ * \brief Wrap an opaque buffer inside a `hidl_vec<uint8_t>`.
+ *
+ * \param[in] l The pointer to the beginning of the opaque buffer.
+ * \param[in] size The size of the buffer.
+ * \return A `hidl_vec<uint8_t>` that points to the buffer.
+ */
+// wrap: void*, size_t -> hidl_vec<uint8_t>
+inline hidl_vec<uint8_t> inHidlBytes(void const* l, size_t size) {
+    hidl_vec<uint8_t> t;
+    t.setToExternal(static_cast<uint8_t*>(const_cast<void*>(l)), size, false);
+    return t;
+}
+
+/**
+ * \brief Create a `hidl_vec<uint8_t>` that is a copy of an opaque buffer.
+ *
+ * \param[in] l The pointer to the beginning of the opaque buffer.
+ * \param[in] size The size of the buffer.
+ * \return A `hidl_vec<uint8_t>` that is a copy of the input buffer.
+ */
+// convert: void*, size_t -> hidl_vec<uint8_t>
+inline hidl_vec<uint8_t> toHidlBytes(void const* l, size_t size) {
+    hidl_vec<uint8_t> t;
+    t.resize(size);
+    uint8_t const* src = static_cast<uint8_t const*>(l);
+    std::copy(src, src + size, t.data());
+    return t;
+}
+
+/**
+ * \brief Wrap `GraphicBuffer` in `AnwBuffer`.
+ *
+ * \param[out] t The wrapper of type `AnwBuffer`.
+ * \param[in] l The source `GraphicBuffer`.
+ */
+// wrap: GraphicBuffer -> AnwBuffer
+inline void wrapAs(AnwBuffer* t, GraphicBuffer const& l) {
+    t->attr.width = l.getWidth();
+    t->attr.height = l.getHeight();
+    t->attr.stride = l.getStride();
+    t->attr.format = static_cast<PixelFormat>(l.getPixelFormat());
+    t->attr.layerCount = l.getLayerCount();
+    t->attr.usage = l.getUsage();
+    t->attr.id = l.getId();
+    t->attr.generationNumber = l.getGenerationNumber();
+    t->nativeHandle = hidl_handle(l.handle);
+}
+
+/**
+ * \brief Convert `AnwBuffer` to `GraphicBuffer`.
+ *
+ * \param[out] l The destination `GraphicBuffer`.
+ * \param[in] t The source `AnwBuffer`.
+ *
+ * This function will duplicate all file descriptors in \p t.
+ */
+// convert: AnwBuffer -> GraphicBuffer
+// Ref: frameworks/native/libs/ui/GraphicBuffer.cpp: GraphicBuffer::flatten
+inline bool convertTo(GraphicBuffer* l, AnwBuffer const& t) {
+    native_handle_t* handle = t.nativeHandle == nullptr ?
+            nullptr : native_handle_clone(t.nativeHandle);
+
+    size_t const numInts = 12 + (handle ? handle->numInts : 0);
+    int32_t* ints = new int32_t[numInts];
+
+    size_t numFds = static_cast<size_t>(handle ? handle->numFds : 0);
+    int* fds = new int[numFds];
+
+    ints[0] = 'GBFR';
+    ints[1] = static_cast<int32_t>(t.attr.width);
+    ints[2] = static_cast<int32_t>(t.attr.height);
+    ints[3] = static_cast<int32_t>(t.attr.stride);
+    ints[4] = static_cast<int32_t>(t.attr.format);
+    ints[5] = static_cast<int32_t>(t.attr.layerCount);
+    ints[6] = static_cast<int32_t>(t.attr.usage);
+    ints[7] = static_cast<int32_t>(t.attr.id >> 32);
+    ints[8] = static_cast<int32_t>(t.attr.id & 0xFFFFFFFF);
+    ints[9] = static_cast<int32_t>(t.attr.generationNumber);
+    ints[10] = 0;
+    ints[11] = 0;
+    if (handle) {
+        ints[10] = static_cast<int32_t>(handle->numFds);
+        ints[11] = static_cast<int32_t>(handle->numInts);
+        int* intsStart = handle->data + handle->numFds;
+        std::copy(handle->data, intsStart, fds);
+        std::copy(intsStart, intsStart + handle->numInts, &ints[12]);
+    }
+
+    void const* constBuffer = static_cast<void const*>(ints);
+    size_t size = numInts * sizeof(int32_t);
+    int const* constFds = static_cast<int const*>(fds);
+    status_t status = l->unflatten(constBuffer, size, constFds, numFds);
+
+    delete [] fds;
+    delete [] ints;
+    native_handle_delete(handle);
+    return status == NO_ERROR;
+}
+
+/**
+ * Conversion functions for types outside media
+ * ============================================
+ *
+ * Some objects in libui and libgui that were made to go through binder calls do
+ * not expose ways to read or write their fields to the public. To pass an
+ * object of this kind through the HIDL boundary, translation functions need to
+ * work around the access restriction by using the publicly available
+ * `flatten()` and `unflatten()` functions.
+ *
+ * All `flatten()` and `unflatten()` overloads follow the same convention as
+ * follows:
+ *
+ *     status_t flatten(ObjectType const& object,
+ *                      [OtherType const& other, ...]
+ *                      void*& buffer, size_t& size,
+ *                      int*& fds, size_t& numFds)
+ *
+ *     status_t unflatten(ObjectType* object,
+ *                        [OtherType* other, ...,]
+ *                        void*& buffer, size_t& size,
+ *                        int*& fds, size_t& numFds)
+ *
+ * The number of `other` parameters varies depending on the `ObjectType`. For
+ * example, in the process of unflattening an object that contains
+ * `hidl_handle`, `other` is needed to hold `native_handle_t` objects that will
+ * be created.
+ *
+ * The last four parameters always work the same way in all overloads of
+ * `flatten()` and `unflatten()`:
+ * - For `flatten()`, `buffer` is the pointer to the non-fd buffer to be filled,
+ *   `size` is the size (in bytes) of the non-fd buffer pointed to by `buffer`,
+ *   `fds` is the pointer to the fd buffer to be filled, and `numFds` is the
+ *   size (in ints) of the fd buffer pointed to by `fds`.
+ * - For `unflatten()`, `buffer` is the pointer to the non-fd buffer to be read
+ *   from, `size` is the size (in bytes) of the non-fd buffer pointed to by
+ *   `buffer`, `fds` is the pointer to the fd buffer to be read from, and
+ *   `numFds` is the size (in ints) of the fd buffer pointed to by `fds`.
+ * - After a successful call to `flatten()` or `unflatten()`, `buffer` and `fds`
+ *   will be advanced, while `size` and `numFds` will be decreased to reflect
+ *   how much storage/data of the two buffers (fd and non-fd) have been used.
+ * - After an unsuccessful call, the values of `buffer`, `size`, `fds` and
+ *   `numFds` are invalid.
+ *
+ * The return value of a successful `flatten()` or `unflatten()` call will be
+ * `OK` (also aliased as `NO_ERROR`). Any other values indicate a failure.
+ *
+ * For each object type that supports flattening, there will be two accompanying
+ * functions: `getFlattenedSize()` and `getFdCount()`. `getFlattenedSize()` will
+ * return the size of the non-fd buffer that the object will need for
+ * flattening. `getFdCount()` will return the size of the fd buffer that the
+ * object will need for flattening.
+ *
+ * The set of these four functions, `getFlattenedSize()`, `getFdCount()`,
+ * `flatten()` and `unflatten()`, are similar to functions of the same name in
+ * the abstract class `Flattenable`. The only difference is that functions in
+ * this file are not member functions of the object type. For example, we write
+ *
+ *     flatten(x, buffer, size, fds, numFds)
+ *
+ * instead of
+ *
+ *     x.flatten(buffer, size, fds, numFds)
+ *
+ * because we cannot modify the type of `x`.
+ *
+ * There is one exception to the naming convention: `hidl_handle` that
+ * represents a fence. The four functions for this "Fence" type have the word
+ * "Fence" attched to their names because the object type, which is
+ * `hidl_handle`, does not carry the special meaning that the object itself can
+ * only contain zero or one file descriptor.
+ */
+
+// Ref: frameworks/native/libs/ui/Fence.cpp
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten a fence.
+ *
+ * \param[in] fence The input fence of type `hidl_handle`.
+ * \return The required size of the flat buffer.
+ *
+ * The current version of this function always returns 4, which is the number of
+ * bytes required to store the number of file descriptors contained in the fd
+ * part of the flat buffer.
+ */
+inline size_t getFenceFlattenedSize(hidl_handle const& /* fence */) {
+    return 4;
+};
+
+/**
+ * \brief Return the number of file descriptors contained in a fence.
+ *
+ * \param[in] fence The input fence of type `hidl_handle`.
+ * \return `0` if \p fence does not contain a valid file descriptor, or `1`
+ * otherwise.
+ */
+inline size_t getFenceFdCount(hidl_handle const& fence) {
+    return native_handle_read_fd(fence) == -1 ? 0 : 1;
+}
+
+/**
+ * \brief Unflatten `Fence` to `hidl_handle`.
+ *
+ * \param[out] fence The destination `hidl_handle`.
+ * \param[out] nh The underlying native handle.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR`, \p nh will point to a newly created
+ * native handle, which needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+inline status_t unflattenFence(hidl_handle* fence, native_handle_t** nh,
+        void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+    if (size < 4) {
+        return NO_MEMORY;
+    }
+
+    uint32_t numFdsInHandle;
+    FlattenableUtils::read(buffer, size, numFdsInHandle);
+
+    if (numFdsInHandle > 1) {
+        return BAD_VALUE;
+    }
+
+    if (numFds < numFdsInHandle) {
+        return NO_MEMORY;
+    }
+
+    if (numFdsInHandle) {
+        *nh = native_handle_create_from_fd(*fds);
+        if (*nh == nullptr) {
+            return NO_MEMORY;
+        }
+        *fence = *nh;
+        ++fds;
+        --numFds;
+    } else {
+        *nh = nullptr;
+        *fence = hidl_handle();
+    }
+
+    return NO_ERROR;
+}
+
+/**
+ * \brief Flatten `hidl_handle` as `Fence`.
+ *
+ * \param[in] t The source `hidl_handle`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+inline status_t flattenFence(hidl_handle const& fence,
+        void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+    if (size < getFenceFlattenedSize(fence) ||
+            numFds < getFenceFdCount(fence)) {
+        return NO_MEMORY;
+    }
+    // Cast to uint32_t since the size of a size_t can vary between 32- and
+    // 64-bit processes
+    FlattenableUtils::write(buffer, size,
+            static_cast<uint32_t>(getFenceFdCount(fence)));
+    int fd = native_handle_read_fd(fence);
+    if (fd != -1) {
+        *fds = fd;
+        ++fds;
+        --numFds;
+    }
+    return NO_ERROR;
+}
+
+/**
+ * \brief Wrap `Fence` in `hidl_handle`.
+ *
+ * \param[out] t The wrapper of type `hidl_handle`.
+ * \param[out] nh The native handle pointed to by \p t.
+ * \param[in] l The source `Fence`.
+ *
+ * On success, \p nh will hold a newly created native handle, which must be
+ * deleted manually with `native_handle_delete()` afterwards.
+ */
+// wrap: Fence -> hidl_handle
+inline bool wrapAs(hidl_handle* t, native_handle_t** nh, Fence const& l) {
+    size_t const baseSize = l.getFlattenedSize();
+    std::unique_ptr<uint8_t[]> baseBuffer(
+            new (std::nothrow) uint8_t[baseSize]);
+    if (!baseBuffer) {
+        return false;
+    }
+
+    size_t const baseNumFds = l.getFdCount();
+    std::unique_ptr<int[]> baseFds(
+            new (std::nothrow) int[baseNumFds]);
+    if (!baseFds) {
+        return false;
+    }
+
+    void* buffer = static_cast<void*>(baseBuffer.get());
+    size_t size = baseSize;
+    int* fds = static_cast<int*>(baseFds.get());
+    size_t numFds = baseNumFds;
+    if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+    size = baseSize;
+    int const* constFds = static_cast<int const*>(baseFds.get());
+    numFds = baseNumFds;
+    if (unflattenFence(t, nh, constBuffer, size, constFds, numFds)
+            != NO_ERROR) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * \brief Convert `hidl_handle` to `Fence`.
+ *
+ * \param[out] l The destination `Fence`. `l` must not have been used
+ * (`l->isValid()` must return `false`) before this function is called.
+ * \param[in] t The source `hidl_handle`.
+ *
+ * If \p t contains a valid file descriptor, it will be duplicated.
+ */
+// convert: hidl_handle -> Fence
+inline bool convertTo(Fence* l, hidl_handle const& t) {
+    int fd = native_handle_read_fd(t);
+    if (fd != -1) {
+        fd = dup(fd);
+        if (fd == -1) {
+            return false;
+        }
+    }
+    native_handle_t* nh = native_handle_create_from_fd(fd);
+    if (nh == nullptr) {
+        if (fd != -1) {
+            close(fd);
+        }
+        return false;
+    }
+
+    size_t const baseSize = getFenceFlattenedSize(t);
+    std::unique_ptr<uint8_t[]> baseBuffer(
+            new (std::nothrow) uint8_t[baseSize]);
+    if (!baseBuffer) {
+        native_handle_delete(nh);
+        return false;
+    }
+
+    size_t const baseNumFds = getFenceFdCount(t);
+    std::unique_ptr<int[]> baseFds(
+            new (std::nothrow) int[baseNumFds]);
+    if (!baseFds) {
+        native_handle_delete(nh);
+        return false;
+    }
+
+    void* buffer = static_cast<void*>(baseBuffer.get());
+    size_t size = baseSize;
+    int* fds = static_cast<int*>(baseFds.get());
+    size_t numFds = baseNumFds;
+    if (flattenFence(hidl_handle(nh), buffer, size, fds, numFds) != NO_ERROR) {
+        native_handle_delete(nh);
+        return false;
+    }
+    native_handle_delete(nh);
+
+    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+    size = baseSize;
+    int const* constFds = static_cast<int const*>(baseFds.get());
+    numFds = baseNumFds;
+    if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    return true;
+}
+
+// Ref: frameworks/native/libs/ui/FenceTime.cpp: FenceTime::Snapshot
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `FenceTimeSnapshot`.
+ *
+ * \param[in] t The input `FenceTimeSnapshot`.
+ * \return The required size of the flat buffer.
+ */
+inline size_t getFlattenedSize(
+        HGraphicBufferProducer::FenceTimeSnapshot const& t) {
+    constexpr size_t min = sizeof(t.state);
+    switch (t.state) {
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY:
+            return min;
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE:
+            return min + getFenceFlattenedSize(t.fence);
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME:
+            return min + sizeof(
+                    ::android::FenceTime::Snapshot::signalTime);
+    }
+    return 0;
+}
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `FenceTimeSnapshot`.
+ *
+ * \param[in] t The input `FenceTimeSnapshot`.
+ * \return The number of file descriptors contained in \p snapshot.
+ */
+inline size_t getFdCount(
+        HGraphicBufferProducer::FenceTimeSnapshot const& t) {
+    return t.state ==
+            HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE ?
+            getFenceFdCount(t.fence) : 0;
+}
+
+/**
+ * \brief Flatten `FenceTimeSnapshot`.
+ *
+ * \param[in] t The source `FenceTimeSnapshot`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate the file descriptor in `t.fence` if `t.state ==
+ * FENCE`.
+ */
+inline status_t flatten(HGraphicBufferProducer::FenceTimeSnapshot const& t,
+        void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+    if (size < getFlattenedSize(t)) {
+        return NO_MEMORY;
+    }
+
+    switch (t.state) {
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY:
+            FlattenableUtils::write(buffer, size,
+                    ::android::FenceTime::Snapshot::State::EMPTY);
+            return NO_ERROR;
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE:
+            FlattenableUtils::write(buffer, size,
+                    ::android::FenceTime::Snapshot::State::FENCE);
+            return flattenFence(t.fence, buffer, size, fds, numFds);
+        case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME:
+            FlattenableUtils::write(buffer, size,
+                    ::android::FenceTime::Snapshot::State::SIGNAL_TIME);
+            FlattenableUtils::write(buffer, size, t.signalTimeNs);
+            return NO_ERROR;
+    }
+    return NO_ERROR;
+}
+
+/**
+ * \brief Unflatten `FenceTimeSnapshot`.
+ *
+ * \param[out] t The destination `FenceTimeSnapshot`.
+ * \param[out] nh The underlying native handle.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR` and the constructed snapshot contains a
+ * file descriptor, \p nh will be created to hold that file descriptor. In this
+ * case, \p nh needs to be deleted with `native_handle_delete()` afterwards.
+ */
+inline status_t unflatten(
+        HGraphicBufferProducer::FenceTimeSnapshot* t, native_handle_t** nh,
+        void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+    if (size < sizeof(t->state)) {
+        return NO_MEMORY;
+    }
+
+    *nh = nullptr;
+    ::android::FenceTime::Snapshot::State state;
+    FlattenableUtils::read(buffer, size, state);
+    switch (state) {
+        case ::android::FenceTime::Snapshot::State::EMPTY:
+            t->state = HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY;
+            return NO_ERROR;
+        case ::android::FenceTime::Snapshot::State::FENCE:
+            t->state = HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE;
+            return unflattenFence(&t->fence, nh, buffer, size, fds, numFds);
+        case ::android::FenceTime::Snapshot::State::SIGNAL_TIME:
+            t->state = HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME;
+            if (size < sizeof(t->signalTimeNs)) {
+                return NO_MEMORY;
+            }
+            FlattenableUtils::read(buffer, size, t->signalTimeNs);
+            return NO_ERROR;
+    }
+    return NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventsDelta
+
+/**
+ * \brief Return a lower bound on the size of the non-fd buffer required to
+ * flatten `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return A lower bound on the size of the flat buffer.
+ */
+constexpr size_t minFlattenedSize(
+        HGraphicBufferProducer::FrameEventsDelta const& /* t */) {
+    return sizeof(uint64_t) + // mFrameNumber
+            sizeof(uint8_t) + // mIndex
+            sizeof(uint8_t) + // mAddPostCompositeCalled
+            sizeof(uint8_t) + // mAddRetireCalled
+            sizeof(uint8_t) + // mAddReleaseCalled
+            sizeof(nsecs_t) + // mPostedTime
+            sizeof(nsecs_t) + // mRequestedPresentTime
+            sizeof(nsecs_t) + // mLatchTime
+            sizeof(nsecs_t) + // mFirstRefreshStartTime
+            sizeof(nsecs_t); // mLastRefreshStartTime
+}
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return The required size of the flat buffer.
+ */
+inline size_t getFlattenedSize(
+        HGraphicBufferProducer::FrameEventsDelta const& t) {
+    return minFlattenedSize(t) +
+            getFlattenedSize(t.gpuCompositionDoneFence) +
+            getFlattenedSize(t.displayPresentFence) +
+            getFlattenedSize(t.displayRetireFence) +
+            getFlattenedSize(t.releaseFence);
+};
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `FrameEventsDelta`.
+ *
+ * \param[in] t The input `FrameEventsDelta`.
+ * \return The number of file descriptors contained in \p t.
+ */
+inline size_t getFdCount(
+        HGraphicBufferProducer::FrameEventsDelta const& t) {
+    return getFdCount(t.gpuCompositionDoneFence) +
+            getFdCount(t.displayPresentFence) +
+            getFdCount(t.displayRetireFence) +
+            getFdCount(t.releaseFence);
+};
+
+/**
+ * \brief Unflatten `FrameEventsDelta`.
+ *
+ * \param[out] t The destination `FrameEventsDelta`.
+ * \param[out] nh The underlying array of native handles.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR`, \p nh will have length 4, and it will be
+ * populated with `nullptr` or newly created handles. Each non-null slot in \p
+ * nh will need to be deleted manually with `native_handle_delete()`.
+ */
+inline status_t unflatten(HGraphicBufferProducer::FrameEventsDelta* t,
+        std::vector<native_handle_t*>* nh,
+        void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+    if (size < minFlattenedSize(*t)) {
+        return NO_MEMORY;
+    }
+    FlattenableUtils::read(buffer, size, t->frameNumber);
+
+    // These were written as uint8_t for alignment.
+    uint8_t temp = 0;
+    FlattenableUtils::read(buffer, size, temp);
+    size_t index = static_cast<size_t>(temp);
+    if (index >= ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+        return BAD_VALUE;
+    }
+    t->index = static_cast<uint32_t>(index);
+
+    FlattenableUtils::read(buffer, size, temp);
+    t->addPostCompositeCalled = static_cast<bool>(temp);
+    FlattenableUtils::read(buffer, size, temp);
+    t->addRetireCalled = static_cast<bool>(temp);
+    FlattenableUtils::read(buffer, size, temp);
+    t->addReleaseCalled = static_cast<bool>(temp);
+
+    FlattenableUtils::read(buffer, size, t->postedTimeNs);
+    FlattenableUtils::read(buffer, size, t->requestedPresentTimeNs);
+    FlattenableUtils::read(buffer, size, t->latchTimeNs);
+    FlattenableUtils::read(buffer, size, t->firstRefreshStartTimeNs);
+    FlattenableUtils::read(buffer, size, t->lastRefreshStartTimeNs);
+    FlattenableUtils::read(buffer, size, t->dequeueReadyTime);
+
+    // Fences
+    HGraphicBufferProducer::FenceTimeSnapshot* tSnapshot[4];
+    tSnapshot[0] = &t->gpuCompositionDoneFence;
+    tSnapshot[1] = &t->displayPresentFence;
+    tSnapshot[2] = &t->displayRetireFence;
+    tSnapshot[3] = &t->releaseFence;
+    nh->resize(4);
+    for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) {
+        status_t status = unflatten(
+                tSnapshot[snapshotIndex], &((*nh)[snapshotIndex]),
+                buffer, size, fds, numFds);
+        if (status != NO_ERROR) {
+            while (snapshotIndex > 0) {
+                --snapshotIndex;
+                if ((*nh)[snapshotIndex] != nullptr) {
+                    native_handle_delete((*nh)[snapshotIndex]);
+                }
+            }
+            return status;
+        }
+    }
+    return NO_ERROR;
+}
+
+/**
+ * \brief Flatten `FrameEventsDelta`.
+ *
+ * \param[in] t The source `FrameEventsDelta`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate file descriptors contained in \p t.
+ */
+// Ref: frameworks/native/libs/gui/FrameTimestamp.cpp:
+//      FrameEventsDelta::flatten
+inline status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t,
+        void*& buffer, size_t& size, int*& fds, size_t numFds) {
+    // Check that t.index is within a valid range.
+    if (t.index >= static_cast<uint32_t>(FrameEventHistory::MAX_FRAME_HISTORY)
+            || t.index > std::numeric_limits<uint8_t>::max()) {
+        return BAD_VALUE;
+    }
+
+    FlattenableUtils::write(buffer, size, t.frameNumber);
+
+    // These are static_cast to uint8_t for alignment.
+    FlattenableUtils::write(buffer, size, static_cast<uint8_t>(t.index));
+    FlattenableUtils::write(
+            buffer, size, static_cast<uint8_t>(t.addPostCompositeCalled));
+    FlattenableUtils::write(
+            buffer, size, static_cast<uint8_t>(t.addRetireCalled));
+    FlattenableUtils::write(
+            buffer, size, static_cast<uint8_t>(t.addReleaseCalled));
+
+    FlattenableUtils::write(buffer, size, t.postedTimeNs);
+    FlattenableUtils::write(buffer, size, t.requestedPresentTimeNs);
+    FlattenableUtils::write(buffer, size, t.latchTimeNs);
+    FlattenableUtils::write(buffer, size, t.firstRefreshStartTimeNs);
+    FlattenableUtils::write(buffer, size, t.lastRefreshStartTimeNs);
+    FlattenableUtils::write(buffer, size, t.dequeueReadyTime);
+
+    // Fences
+    HGraphicBufferProducer::FenceTimeSnapshot const* tSnapshot[4];
+    tSnapshot[0] = &t.gpuCompositionDoneFence;
+    tSnapshot[1] = &t.displayPresentFence;
+    tSnapshot[2] = &t.displayRetireFence;
+    tSnapshot[3] = &t.releaseFence;
+    for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) {
+        status_t status = flatten(
+                *(tSnapshot[snapshotIndex]), buffer, size, fds, numFds);
+        if (status != NO_ERROR) {
+            return status;
+        }
+    }
+    return NO_ERROR;
+}
+
+// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventHistoryDelta
+
+/**
+ * \brief Return the size of the non-fd buffer required to flatten
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \return The required size of the flat buffer.
+ */
+inline size_t getFlattenedSize(
+        HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+    size_t size = 4 + // mDeltas.size()
+            sizeof(t.compositorTiming);
+    for (size_t i = 0; i < t.deltas.size(); ++i) {
+        size += getFlattenedSize(t.deltas[i]);
+    }
+    return size;
+}
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \return The number of file descriptors contained in \p t.
+ */
+inline size_t getFdCount(
+        HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+    size_t numFds = 0;
+    for (size_t i = 0; i < t.deltas.size(); ++i) {
+        numFds += getFdCount(t.deltas[i]);
+    }
+    return numFds;
+}
+
+/**
+ * \brief Unflatten `FrameEventHistoryDelta`.
+ *
+ * \param[out] t The destination `FrameEventHistoryDelta`.
+ * \param[out] nh The underlying array of arrays of native handles.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR`, \p nh will be populated with `nullptr` or
+ * newly created handles. The second dimension of \p nh will be 4. Each non-null
+ * slot in \p nh will need to be deleted manually with `native_handle_delete()`.
+ */
+inline status_t unflatten(
+        HGraphicBufferProducer::FrameEventHistoryDelta* t,
+        std::vector<std::vector<native_handle_t*> >* nh,
+        void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+    if (size < 4) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, t->compositorTiming);
+
+    uint32_t deltaCount = 0;
+    FlattenableUtils::read(buffer, size, deltaCount);
+    if (static_cast<size_t>(deltaCount) >
+            ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+        return BAD_VALUE;
+    }
+    t->deltas.resize(deltaCount);
+    nh->resize(deltaCount);
+    for (size_t deltaIndex = 0; deltaIndex < deltaCount; ++deltaIndex) {
+        status_t status = unflatten(
+                &(t->deltas[deltaIndex]), &((*nh)[deltaIndex]),
+                buffer, size, fds, numFds);
+        if (status != NO_ERROR) {
+            return status;
+        }
+    }
+    return NO_ERROR;
+}
+
+/**
+ * \brief Flatten `FrameEventHistoryDelta`.
+ *
+ * \param[in] t The source `FrameEventHistoryDelta`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate file descriptors contained in \p t.
+ */
+inline status_t flatten(
+        HGraphicBufferProducer::FrameEventHistoryDelta const& t,
+        void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+    if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
+        return BAD_VALUE;
+    }
+    if (size < getFlattenedSize(t)) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(buffer, size, t.compositorTiming);
+
+    FlattenableUtils::write(buffer, size, static_cast<uint32_t>(t.deltas.size()));
+    for (size_t deltaIndex = 0; deltaIndex < t.deltas.size(); ++deltaIndex) {
+        status_t status = flatten(t.deltas[deltaIndex], buffer, size, fds, numFds);
+        if (status != NO_ERROR) {
+            return status;
+        }
+    }
+    return NO_ERROR;
+}
+
+/**
+ * \brief Wrap `::android::FrameEventHistoryData` in
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * \param[out] t The wrapper of type
+ * `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ * \param[out] nh The array of array of native handles that are referred to by
+ * members of \p t.
+ * \param[in] l The source `::android::FrameEventHistoryDelta`.
+ *
+ * On success, each member of \p nh will be either `nullptr` or a newly created
+ * native handle. All the non-`nullptr` elements must be deleted individually
+ * with `native_handle_delete()`.
+ */
+inline bool wrapAs(HGraphicBufferProducer::FrameEventHistoryDelta* t,
+        std::vector<std::vector<native_handle_t*> >* nh,
+        ::android::FrameEventHistoryDelta const& l) {
+
+    size_t const baseSize = l.getFlattenedSize();
+    std::unique_ptr<uint8_t[]> baseBuffer(
+            new (std::nothrow) uint8_t[baseSize]);
+    if (!baseBuffer) {
+        return false;
+    }
+
+    size_t const baseNumFds = l.getFdCount();
+    std::unique_ptr<int[]> baseFds(
+            new (std::nothrow) int[baseNumFds]);
+    if (!baseFds) {
+        return false;
+    }
+
+    void* buffer = static_cast<void*>(baseBuffer.get());
+    size_t size = baseSize;
+    int* fds = baseFds.get();
+    size_t numFds = baseNumFds;
+    if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+    size = baseSize;
+    int const* constFds = static_cast<int const*>(baseFds.get());
+    numFds = baseNumFds;
+    if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * \brief Convert `HGraphicBufferProducer::FrameEventHistoryDelta` to
+ * `::android::FrameEventHistoryDelta`.
+ *
+ * \param[out] l The destination `::android::FrameEventHistoryDelta`.
+ * \param[in] t The source `HGraphicBufferProducer::FrameEventHistoryDelta`.
+ *
+ * This function will duplicate all file descriptors contained in \p t.
+ */
+inline bool convertTo(
+        ::android::FrameEventHistoryDelta* l,
+        HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
+
+    size_t const baseSize = getFlattenedSize(t);
+    std::unique_ptr<uint8_t[]> baseBuffer(
+            new (std::nothrow) uint8_t[baseSize]);
+    if (!baseBuffer) {
+        return false;
+    }
+
+    size_t const baseNumFds = getFdCount(t);
+    std::unique_ptr<int[]> baseFds(
+            new (std::nothrow) int[baseNumFds]);
+    if (!baseFds) {
+        return false;
+    }
+
+    void* buffer = static_cast<void*>(baseBuffer.get());
+    size_t size = baseSize;
+    int* fds = static_cast<int*>(baseFds.get());
+    size_t numFds = baseNumFds;
+    if (flatten(t, buffer, size, fds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+    size = baseSize;
+    int const* constFds = static_cast<int const*>(baseFds.get());
+    numFds = baseNumFds;
+    if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    return true;
+}
+
+// Ref: frameworks/native/libs/ui/Region.cpp
+
+/**
+ * \brief Return the size of the buffer required to flatten `Region`.
+ *
+ * \param[in] t The input `Region`.
+ * \return The required size of the flat buffer.
+ */
+inline size_t getFlattenedSize(Region const& t) {
+    return sizeof(uint32_t) + t.size() * sizeof(::android::Rect);
+}
+
+/**
+ * \brief Unflatten `Region`.
+ *
+ * \param[out] t The destination `Region`.
+ * \param[in,out] buffer The pointer to the flat buffer.
+ * \param[in,out] size The size of the flat buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+inline status_t unflatten(Region* t, void const*& buffer, size_t& size) {
+    if (size < sizeof(uint32_t)) {
+        return NO_MEMORY;
+    }
+
+    uint32_t numRects = 0;
+    FlattenableUtils::read(buffer, size, numRects);
+    if (size < numRects * sizeof(Rect)) {
+        return NO_MEMORY;
+    }
+    if (numRects > (UINT32_MAX / sizeof(Rect))) {
+        return NO_MEMORY;
+    }
+
+    t->resize(numRects);
+    for (size_t r = 0; r < numRects; ++r) {
+        ::android::Rect rect(::android::Rect::EMPTY_RECT);
+        status_t status = rect.unflatten(buffer, size);
+        if (status != NO_ERROR) {
+            return status;
+        }
+        FlattenableUtils::advance(buffer, size, sizeof(rect));
+        (*t)[r] = Rect{
+                static_cast<int32_t>(rect.left),
+                static_cast<int32_t>(rect.top),
+                static_cast<int32_t>(rect.right),
+                static_cast<int32_t>(rect.bottom)};
+    }
+    return NO_ERROR;
+}
+
+/**
+ * \brief Flatten `Region`.
+ *
+ * \param[in] t The source `Region`.
+ * \param[in,out] buffer The pointer to the flat buffer.
+ * \param[in,out] size The size of the flat buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ */
+inline status_t flatten(Region const& t, void*& buffer, size_t& size) {
+    if (size < getFlattenedSize(t)) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(buffer, size, static_cast<uint32_t>(t.size()));
+    for (size_t r = 0; r < t.size(); ++r) {
+        ::android::Rect rect(
+                static_cast<int32_t>(t[r].left),
+                static_cast<int32_t>(t[r].top),
+                static_cast<int32_t>(t[r].right),
+                static_cast<int32_t>(t[r].bottom));
+        status_t status = rect.flatten(buffer, size);
+        if (status != NO_ERROR) {
+            return status;
+        }
+        FlattenableUtils::advance(buffer, size, sizeof(rect));
+    }
+    return NO_ERROR;
+}
+
+/**
+ * \brief Convert `::android::Region` to `Region`.
+ *
+ * \param[out] t The destination `Region`.
+ * \param[in] l The source `::android::Region`.
+ */
+// convert: ::android::Region -> Region
+inline bool convertTo(Region* t, ::android::Region const& l) {
+    size_t const baseSize = l.getFlattenedSize();
+    std::unique_ptr<uint8_t[]> baseBuffer(
+            new (std::nothrow) uint8_t[baseSize]);
+    if (!baseBuffer) {
+        return false;
+    }
+
+    void* buffer = static_cast<void*>(baseBuffer.get());
+    size_t size = baseSize;
+    if (l.flatten(buffer, size) != NO_ERROR) {
+        return false;
+    }
+
+    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+    size = baseSize;
+    if (unflatten(t, constBuffer, size) != NO_ERROR) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * \brief Convert `Region` to `::android::Region`.
+ *
+ * \param[out] l The destination `::android::Region`.
+ * \param[in] t The source `Region`.
+ */
+// convert: Region -> ::android::Region
+inline bool convertTo(::android::Region* l, Region const& t) {
+    size_t const baseSize = getFlattenedSize(t);
+    std::unique_ptr<uint8_t[]> baseBuffer(
+            new (std::nothrow) uint8_t[baseSize]);
+    if (!baseBuffer) {
+        return false;
+    }
+
+    void* buffer = static_cast<void*>(baseBuffer.get());
+    size_t size = baseSize;
+    if (flatten(t, buffer, size) != NO_ERROR) {
+        return false;
+    }
+
+    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+    size = baseSize;
+    if (l->unflatten(constBuffer, size) != NO_ERROR) {
+        return false;
+    }
+
+    return true;
+}
+
+// Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp:
+//      BGraphicBufferProducer::QueueBufferInput
+
+/**
+ * \brief Return a lower bound on the size of the buffer required to flatten
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`.
+ * \return A lower bound on the size of the flat buffer.
+ */
+constexpr size_t minFlattenedSize(
+        HGraphicBufferProducer::QueueBufferInput const& /* t */) {
+    return sizeof(int64_t) + // timestamp
+            sizeof(int) + // isAutoTimestamp
+            sizeof(android_dataspace) + // dataSpace
+            sizeof(::android::Rect) + // crop
+            sizeof(int) + // scalingMode
+            sizeof(uint32_t) + // transform
+            sizeof(uint32_t) + // stickyTransform
+            sizeof(bool); // getFrameTimestamps
+}
+
+/**
+ * \brief Return the size of the buffer required to flatten
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`.
+ * \return The required size of the flat buffer.
+ */
+inline size_t getFlattenedSize(HGraphicBufferProducer::QueueBufferInput const& t) {
+    return minFlattenedSize(t) +
+            getFenceFlattenedSize(t.fence) +
+            getFlattenedSize(t.surfaceDamage) +
+            sizeof(HdrMetadata::validTypes);
+}
+
+/**
+ * \brief Return the number of file descriptors contained in
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`.
+ * \return The number of file descriptors contained in \p t.
+ */
+inline size_t getFdCount(
+        HGraphicBufferProducer::QueueBufferInput const& t) {
+    return getFenceFdCount(t.fence);
+}
+
+/**
+ * \brief Flatten `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The native handle cloned from `t.fence`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * This function will duplicate the file descriptor in `t.fence`. */
+inline status_t flatten(HGraphicBufferProducer::QueueBufferInput const& t,
+        native_handle_t** nh,
+        void*& buffer, size_t& size, int*& fds, size_t& numFds) {
+    if (size < getFlattenedSize(t)) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(buffer, size, t.timestamp);
+    FlattenableUtils::write(buffer, size, static_cast<int>(t.isAutoTimestamp));
+    FlattenableUtils::write(buffer, size,
+            static_cast<android_dataspace_t>(t.dataSpace));
+    FlattenableUtils::write(buffer, size, ::android::Rect(
+            static_cast<int32_t>(t.crop.left),
+            static_cast<int32_t>(t.crop.top),
+            static_cast<int32_t>(t.crop.right),
+            static_cast<int32_t>(t.crop.bottom)));
+    FlattenableUtils::write(buffer, size, static_cast<int>(t.scalingMode));
+    FlattenableUtils::write(buffer, size, t.transform);
+    FlattenableUtils::write(buffer, size, t.stickyTransform);
+    FlattenableUtils::write(buffer, size, t.getFrameTimestamps);
+
+    *nh = t.fence.getNativeHandle() == nullptr ?
+            nullptr : native_handle_clone(t.fence);
+    status_t status = flattenFence(hidl_handle(*nh), buffer, size, fds, numFds);
+    if (status != NO_ERROR) {
+        return status;
+    }
+    status = flatten(t.surfaceDamage, buffer, size);
+    if (status != NO_ERROR) {
+        return status;
+    }
+    FlattenableUtils::write(buffer, size, decltype(HdrMetadata::validTypes)(0));
+    return NO_ERROR;
+}
+
+/**
+ * \brief Unflatten `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] t The destination `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The underlying native handle for `t->fence`.
+ * \param[in,out] buffer The pointer to the flat non-fd buffer.
+ * \param[in,out] size The size of the flat non-fd buffer.
+ * \param[in,out] fds The pointer to the flat fd buffer.
+ * \param[in,out] numFds The size of the flat fd buffer.
+ * \return `NO_ERROR` on success; other value on failure.
+ *
+ * If the return value is `NO_ERROR` and `t->fence` contains a valid file
+ * descriptor, \p nh will be a newly created native handle holding that file
+ * descriptor. \p nh needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+inline status_t unflatten(
+        HGraphicBufferProducer::QueueBufferInput* t, native_handle_t** nh,
+        void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
+    if (size < minFlattenedSize(*t)) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, t->timestamp);
+    int lIsAutoTimestamp;
+    FlattenableUtils::read(buffer, size, lIsAutoTimestamp);
+    t->isAutoTimestamp = static_cast<int32_t>(lIsAutoTimestamp);
+    android_dataspace_t lDataSpace;
+    FlattenableUtils::read(buffer, size, lDataSpace);
+    t->dataSpace = static_cast<Dataspace>(lDataSpace);
+    Rect lCrop;
+    FlattenableUtils::read(buffer, size, lCrop);
+    t->crop = Rect{
+            static_cast<int32_t>(lCrop.left),
+            static_cast<int32_t>(lCrop.top),
+            static_cast<int32_t>(lCrop.right),
+            static_cast<int32_t>(lCrop.bottom)};
+    int lScalingMode;
+    FlattenableUtils::read(buffer, size, lScalingMode);
+    t->scalingMode = static_cast<int32_t>(lScalingMode);
+    FlattenableUtils::read(buffer, size, t->transform);
+    FlattenableUtils::read(buffer, size, t->stickyTransform);
+    FlattenableUtils::read(buffer, size, t->getFrameTimestamps);
+
+    status_t status = unflattenFence(&(t->fence), nh,
+            buffer, size, fds, numFds);
+    if (status != NO_ERROR) {
+        return status;
+    }
+    // HdrMetadata ignored
+    return unflatten(&(t->surfaceDamage), buffer, size);
+}
+
+/**
+ * \brief Wrap `BGraphicBufferProducer::QueueBufferInput` in
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] t The wrapper of type
+ * `HGraphicBufferProducer::QueueBufferInput`.
+ * \param[out] nh The underlying native handle for `t->fence`.
+ * \param[in] l The source `BGraphicBufferProducer::QueueBufferInput`.
+ *
+ * If the return value is `true` and `t->fence` contains a valid file
+ * descriptor, \p nh will be a newly created native handle holding that file
+ * descriptor. \p nh needs to be deleted with `native_handle_delete()`
+ * afterwards.
+ */
+inline bool wrapAs(
+        HGraphicBufferProducer::QueueBufferInput* t,
+        native_handle_t** nh,
+        BGraphicBufferProducer::QueueBufferInput const& l) {
+
+    size_t const baseSize = l.getFlattenedSize();
+    std::unique_ptr<uint8_t[]> baseBuffer(
+            new (std::nothrow) uint8_t[baseSize]);
+    if (!baseBuffer) {
+        return false;
+    }
+
+    size_t const baseNumFds = l.getFdCount();
+    std::unique_ptr<int[]> baseFds(
+            new (std::nothrow) int[baseNumFds]);
+    if (!baseFds) {
+        return false;
+    }
+
+    void* buffer = static_cast<void*>(baseBuffer.get());
+    size_t size = baseSize;
+    int* fds = baseFds.get();
+    size_t numFds = baseNumFds;
+    if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+    size = baseSize;
+    int const* constFds = static_cast<int const*>(baseFds.get());
+    numFds = baseNumFds;
+    if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * \brief Convert `HGraphicBufferProducer::QueueBufferInput` to
+ * `BGraphicBufferProducer::QueueBufferInput`.
+ *
+ * \param[out] l The destination `BGraphicBufferProducer::QueueBufferInput`.
+ * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`.
+ *
+ * If `t.fence` has a valid file descriptor, it will be duplicated.
+ */
+inline bool convertTo(
+        BGraphicBufferProducer::QueueBufferInput* l,
+        HGraphicBufferProducer::QueueBufferInput const& t) {
+
+    size_t const baseSize = getFlattenedSize(t);
+    std::unique_ptr<uint8_t[]> baseBuffer(
+            new (std::nothrow) uint8_t[baseSize]);
+    if (!baseBuffer) {
+        return false;
+    }
+
+    size_t const baseNumFds = getFdCount(t);
+    std::unique_ptr<int[]> baseFds(
+            new (std::nothrow) int[baseNumFds]);
+    if (!baseFds) {
+        return false;
+    }
+
+    void* buffer = static_cast<void*>(baseBuffer.get());
+    size_t size = baseSize;
+    int* fds = baseFds.get();
+    size_t numFds = baseNumFds;
+    native_handle_t* nh;
+    if (flatten(t, &nh, buffer, size, fds, numFds) != NO_ERROR) {
+        return false;
+    }
+
+    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
+    size = baseSize;
+    int const* constFds = static_cast<int const*>(baseFds.get());
+    numFds = baseNumFds;
+    if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
+        if (nh != nullptr) {
+            native_handle_close(nh);
+            native_handle_delete(nh);
+        }
+        return false;
+    }
+
+    native_handle_delete(nh);
+    return true;
+}
+
+// Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp:
+//      BGraphicBufferProducer::QueueBufferOutput
+
+/**
+ * \brief Wrap `BGraphicBufferProducer::QueueBufferOutput` in
+ * `HGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * \param[out] t The wrapper of type
+ * `HGraphicBufferProducer::QueueBufferOutput`.
+ * \param[out] nh The array of array of native handles that are referred to by
+ * members of \p t.
+ * \param[in] l The source `BGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * On success, each member of \p nh will be either `nullptr` or a newly created
+ * native handle. All the non-`nullptr` elements must be deleted individually
+ * with `native_handle_delete()`.
+ */
+// wrap: BGraphicBufferProducer::QueueBufferOutput ->
+// HGraphicBufferProducer::QueueBufferOutput
+inline bool wrapAs(HGraphicBufferProducer::QueueBufferOutput* t,
+        std::vector<std::vector<native_handle_t*> >* nh,
+        BGraphicBufferProducer::QueueBufferOutput const& l) {
+    if (!wrapAs(&(t->frameTimestamps), nh, l.frameTimestamps)) {
+        return false;
+    }
+    t->width = l.width;
+    t->height = l.height;
+    t->transformHint = l.transformHint;
+    t->numPendingBuffers = l.numPendingBuffers;
+    t->nextFrameNumber = l.nextFrameNumber;
+    t->bufferReplaced = l.bufferReplaced;
+    return true;
+}
+
+/**
+ * \brief Convert `HGraphicBufferProducer::QueueBufferOutput` to
+ * `BGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * \param[out] l The destination `BGraphicBufferProducer::QueueBufferOutput`.
+ * \param[in] t The source `HGraphicBufferProducer::QueueBufferOutput`.
+ *
+ * This function will duplicate all file descriptors contained in \p t.
+ */
+// convert: HGraphicBufferProducer::QueueBufferOutput ->
+// BGraphicBufferProducer::QueueBufferOutput
+inline bool convertTo(
+        BGraphicBufferProducer::QueueBufferOutput* l,
+        HGraphicBufferProducer::QueueBufferOutput const& t) {
+    if (!convertTo(&(l->frameTimestamps), t.frameTimestamps)) {
+        return false;
+    }
+    l->width = t.width;
+    l->height = t.height;
+    l->transformHint = t.transformHint;
+    l->numPendingBuffers = t.numPendingBuffers;
+    l->nextFrameNumber = t.nextFrameNumber;
+    l->bufferReplaced = t.bufferReplaced;
+    return true;
+}
+
+/**
+ * \brief Convert `BGraphicBufferProducer::DisconnectMode` to
+ * `HGraphicBufferProducer::DisconnectMode`.
+ *
+ * \param[in] l The source `BGraphicBufferProducer::DisconnectMode`.
+ * \return The corresponding `HGraphicBufferProducer::DisconnectMode`.
+ */
+inline HGraphicBufferProducer::DisconnectMode toHidlDisconnectMode(
+        BGraphicBufferProducer::DisconnectMode l) {
+    switch (l) {
+        case BGraphicBufferProducer::DisconnectMode::Api:
+            return HGraphicBufferProducer::DisconnectMode::API;
+        case BGraphicBufferProducer::DisconnectMode::AllLocal:
+            return HGraphicBufferProducer::DisconnectMode::ALL_LOCAL;
+    }
+    return HGraphicBufferProducer::DisconnectMode::API;
+}
+
+/**
+ * \brief Convert `HGraphicBufferProducer::DisconnectMode` to
+ * `BGraphicBufferProducer::DisconnectMode`.
+ *
+ * \param[in] l The source `HGraphicBufferProducer::DisconnectMode`.
+ * \return The corresponding `BGraphicBufferProducer::DisconnectMode`.
+ */
+inline BGraphicBufferProducer::DisconnectMode toGuiDisconnectMode(
+        HGraphicBufferProducer::DisconnectMode t) {
+    switch (t) {
+        case HGraphicBufferProducer::DisconnectMode::API:
+            return BGraphicBufferProducer::DisconnectMode::Api;
+        case HGraphicBufferProducer::DisconnectMode::ALL_LOCAL:
+            return BGraphicBufferProducer::DisconnectMode::AllLocal;
+    }
+    return BGraphicBufferProducer::DisconnectMode::Api;
+}
+
+}  // namespace conversion
+}  // namespace android
+
+#endif  // MEDIA_STAGEFRIGHT_CONVERSION_H_
diff --git a/media/libstagefright/gbs/include/media/stagefright/gbs/FrameDropper.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/FrameDropper.h
similarity index 100%
rename from media/libstagefright/gbs/include/media/stagefright/gbs/FrameDropper.h
rename to media/libstagefright/bqhelper/include/media/stagefright/bqhelper/FrameDropper.h
diff --git a/media/libstagefright/gbs/include/media/stagefright/gbs/GraphicBufferSource.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h
similarity index 99%
rename from media/libstagefright/gbs/include/media/stagefright/gbs/GraphicBufferSource.h
rename to media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h
index 89f6cf8..5af9556 100644
--- a/media/libstagefright/gbs/include/media/stagefright/gbs/GraphicBufferSource.h
+++ b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h
@@ -27,7 +27,7 @@
 #include <media/stagefright/foundation/ABase.h>
 #include <media/stagefright/foundation/AHandlerReflector.h>
 #include <media/stagefright/foundation/ALooper.h>
-#include <media/stagefright/gbs/ComponentWrapper.h>
+#include <media/stagefright/bqhelper/ComponentWrapper.h>
 
 namespace android {
 
diff --git a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WGraphicBufferProducer.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WGraphicBufferProducer.h
new file mode 100644
index 0000000..8ddf20f
--- /dev/null
+++ b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WGraphicBufferProducer.h
@@ -0,0 +1,380 @@
+/*
+ * Copyright 2016, 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_STAGEFRIGHT_WGRAPHICBUFFERPRODUCER_H_
+#define MEDIA_STAGEFRIGHT_WGRAPHICBUFFERPRODUCER_H_
+
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <binder/Binder.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/IProducerListener.h>
+#include <media/stagefright/bqhelper/Conversion.h>
+#include <media/stagefright/bqhelper/WProducerListener.h>
+#include <system/window.h>
+
+#include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
+
+namespace android {
+
+using ::android::hardware::media::V1_0::AnwBuffer;
+using ::android::hidl::base::V1_0::IBase;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+typedef ::android::hardware::graphics::bufferqueue::V1_0::
+        IGraphicBufferProducer HGraphicBufferProducer;
+typedef ::android::hardware::graphics::bufferqueue::V1_0::
+        IProducerListener HProducerListener;
+
+typedef ::android::IGraphicBufferProducer BGraphicBufferProducer;
+typedef ::android::IProducerListener BProducerListener;
+using ::android::BnGraphicBufferProducer;
+
+#ifndef LOG
+struct LOG_dummy {
+    template <typename T>
+    LOG_dummy& operator<< (const T&) { return *this; }
+};
+
+#define LOG(x)  LOG_dummy()
+#endif
+
+// Instantiate only if HGraphicBufferProducer is base of BASE.
+template <typename BASE,
+          typename = typename std::enable_if<std::is_base_of<HGraphicBufferProducer, BASE>::value>::type>
+struct TWGraphicBufferProducer : public BASE {
+    TWGraphicBufferProducer(sp<BGraphicBufferProducer> const& base) : mBase(base) {}
+    Return<void> requestBuffer(int32_t slot, HGraphicBufferProducer::requestBuffer_cb _hidl_cb) override {
+        sp<GraphicBuffer> buf;
+        status_t status = mBase->requestBuffer(slot, &buf);
+        AnwBuffer anwBuffer;
+        if (buf != nullptr) {
+            ::android::conversion::wrapAs(&anwBuffer, *buf);
+        }
+        _hidl_cb(static_cast<int32_t>(status), anwBuffer);
+        return Void();
+    }
+
+    Return<int32_t> setMaxDequeuedBufferCount(int32_t maxDequeuedBuffers) override {
+        return static_cast<int32_t>(mBase->setMaxDequeuedBufferCount(
+                static_cast<int>(maxDequeuedBuffers)));
+    }
+
+    Return<int32_t> setAsyncMode(bool async) override {
+        return static_cast<int32_t>(mBase->setAsyncMode(async));
+    }
+
+    Return<void> dequeueBuffer(
+            uint32_t width, uint32_t height,
+            ::android::hardware::graphics::common::V1_0::PixelFormat format, uint32_t usage,
+            bool getFrameTimestamps, HGraphicBufferProducer::dequeueBuffer_cb _hidl_cb) override {
+        int slot;
+        sp<Fence> fence;
+        ::android::FrameEventHistoryDelta outTimestamps;
+        status_t status = mBase->dequeueBuffer(
+            &slot, &fence, width, height,
+            static_cast<::android::PixelFormat>(format), usage, nullptr,
+            getFrameTimestamps ? &outTimestamps : nullptr);
+        hidl_handle tFence;
+        HGraphicBufferProducer::FrameEventHistoryDelta tOutTimestamps;
+
+        native_handle_t* nh = nullptr;
+        if ((fence == nullptr) || !::android::conversion::wrapAs(&tFence, &nh, *fence)) {
+            LOG(ERROR) << "TWGraphicBufferProducer::dequeueBuffer - "
+                    "Invalid output fence";
+            _hidl_cb(static_cast<int32_t>(status),
+                     static_cast<int32_t>(slot),
+                     tFence,
+                     tOutTimestamps);
+            return Void();
+        }
+        std::vector<std::vector<native_handle_t*> > nhAA;
+        if (getFrameTimestamps && !::android::conversion::wrapAs(&tOutTimestamps, &nhAA, outTimestamps)) {
+            LOG(ERROR) << "TWGraphicBufferProducer::dequeueBuffer - "
+                    "Invalid output timestamps";
+            _hidl_cb(static_cast<int32_t>(status),
+                     static_cast<int32_t>(slot),
+                     tFence,
+                     tOutTimestamps);
+            native_handle_delete(nh);
+            return Void();
+        }
+
+        _hidl_cb(static_cast<int32_t>(status),
+                static_cast<int32_t>(slot),
+                tFence,
+                tOutTimestamps);
+        native_handle_delete(nh);
+        if (getFrameTimestamps) {
+            for (auto& nhA : nhAA) {
+                for (auto& handle : nhA) {
+                    native_handle_delete(handle);
+                }
+            }
+        }
+        return Void();
+    }
+
+    Return<int32_t> detachBuffer(int32_t slot) override {
+        return static_cast<int32_t>(mBase->detachBuffer(slot));
+    }
+
+    Return<void> detachNextBuffer(HGraphicBufferProducer::detachNextBuffer_cb _hidl_cb) override {
+        sp<GraphicBuffer> outBuffer;
+        sp<Fence> outFence;
+        status_t status = mBase->detachNextBuffer(&outBuffer, &outFence);
+        AnwBuffer tBuffer;
+        hidl_handle tFence;
+
+        if (outBuffer == nullptr) {
+            LOG(ERROR) << "TWGraphicBufferProducer::detachNextBuffer - "
+                    "Invalid output buffer";
+            _hidl_cb(static_cast<int32_t>(status), tBuffer, tFence);
+            return Void();
+        }
+        ::android::conversion::wrapAs(&tBuffer, *outBuffer);
+        native_handle_t* nh = nullptr;
+        if ((outFence != nullptr) && !::android::conversion::wrapAs(&tFence, &nh, *outFence)) {
+            LOG(ERROR) << "TWGraphicBufferProducer::detachNextBuffer - "
+                    "Invalid output fence";
+            _hidl_cb(static_cast<int32_t>(status), tBuffer, tFence);
+            return Void();
+        }
+
+        _hidl_cb(static_cast<int32_t>(status), tBuffer, tFence);
+        native_handle_delete(nh);
+        return Void();
+    }
+
+    Return<void> attachBuffer(const AnwBuffer& buffer, HGraphicBufferProducer::attachBuffer_cb _hidl_cb) override {
+        int outSlot;
+        sp<GraphicBuffer> lBuffer = new GraphicBuffer();
+        if (!::android::conversion::convertTo(lBuffer.get(), buffer)) {
+            LOG(ERROR) << "TWGraphicBufferProducer::attachBuffer - "
+                    "Invalid input native window buffer";
+            _hidl_cb(static_cast<int32_t>(BAD_VALUE), -1);
+            return Void();
+        }
+        status_t status = mBase->attachBuffer(&outSlot, lBuffer);
+
+        _hidl_cb(static_cast<int32_t>(status), static_cast<int32_t>(outSlot));
+        return Void();
+    }
+
+    Return<void> queueBuffer(
+            int32_t slot, const HGraphicBufferProducer::QueueBufferInput& input,
+            HGraphicBufferProducer::queueBuffer_cb _hidl_cb) override {
+        HGraphicBufferProducer::QueueBufferOutput tOutput;
+        BGraphicBufferProducer::QueueBufferInput lInput(
+                0, false, HAL_DATASPACE_UNKNOWN,
+                ::android::Rect(0, 0, 1, 1),
+                NATIVE_WINDOW_SCALING_MODE_FREEZE,
+                0, ::android::Fence::NO_FENCE);
+        if (!::android::conversion::convertTo(&lInput, input)) {
+            LOG(ERROR) << "TWGraphicBufferProducer::queueBuffer - "
+                    "Invalid input";
+            _hidl_cb(static_cast<int32_t>(BAD_VALUE), tOutput);
+            return Void();
+        }
+        BGraphicBufferProducer::QueueBufferOutput lOutput;
+        status_t status = mBase->queueBuffer(
+                static_cast<int>(slot), lInput, &lOutput);
+
+        std::vector<std::vector<native_handle_t*> > nhAA;
+        if (!::android::conversion::wrapAs(&tOutput, &nhAA, lOutput)) {
+            LOG(ERROR) << "TWGraphicBufferProducer::queueBuffer - "
+                    "Invalid output";
+            _hidl_cb(static_cast<int32_t>(BAD_VALUE), tOutput);
+            return Void();
+        }
+
+        _hidl_cb(static_cast<int32_t>(status), tOutput);
+        for (auto& nhA : nhAA) {
+            for (auto& nh : nhA) {
+                native_handle_delete(nh);
+            }
+        }
+        return Void();
+    }
+
+    Return<int32_t> cancelBuffer(int32_t slot, const hidl_handle& fence) override {
+        sp<Fence> lFence = new Fence();
+        if (!::android::conversion::convertTo(lFence.get(), fence)) {
+            LOG(ERROR) << "TWGraphicBufferProducer::cancelBuffer - "
+                    "Invalid input fence";
+            return static_cast<int32_t>(BAD_VALUE);
+        }
+        return static_cast<int32_t>(mBase->cancelBuffer(static_cast<int>(slot), lFence));
+    }
+
+    Return<void> query(int32_t what, HGraphicBufferProducer::query_cb _hidl_cb) override {
+        int lValue;
+        int lReturn = mBase->query(static_cast<int>(what), &lValue);
+        _hidl_cb(static_cast<int32_t>(lReturn), static_cast<int32_t>(lValue));
+        return Void();
+    }
+
+    Return<void> connect(const sp<HProducerListener>& listener,
+            int32_t api, bool producerControlledByApp,
+            HGraphicBufferProducer::connect_cb _hidl_cb) override {
+        sp<BProducerListener> lListener = listener == nullptr ?
+                nullptr : new LWProducerListener(listener);
+        BGraphicBufferProducer::QueueBufferOutput lOutput;
+        status_t status = mBase->connect(lListener,
+                static_cast<int>(api),
+                producerControlledByApp,
+                &lOutput);
+
+        HGraphicBufferProducer::QueueBufferOutput tOutput;
+        std::vector<std::vector<native_handle_t*> > nhAA;
+        if (!::android::conversion::wrapAs(&tOutput, &nhAA, lOutput)) {
+            LOG(ERROR) << "TWGraphicBufferProducer::connect - "
+                    "Invalid output";
+            _hidl_cb(static_cast<int32_t>(status), tOutput);
+            return Void();
+        }
+
+        _hidl_cb(static_cast<int32_t>(status), tOutput);
+        for (auto& nhA : nhAA) {
+            for (auto& nh : nhA) {
+                native_handle_delete(nh);
+            }
+        }
+        return Void();
+    }
+
+    Return<int32_t> disconnect(
+            int32_t api,
+            HGraphicBufferProducer::DisconnectMode mode) override {
+        return static_cast<int32_t>(mBase->disconnect(
+                static_cast<int>(api),
+                ::android::conversion::toGuiDisconnectMode(mode)));
+    }
+
+    Return<int32_t> setSidebandStream(const hidl_handle& stream) override {
+        return static_cast<int32_t>(mBase->setSidebandStream(NativeHandle::create(
+                stream ? native_handle_clone(stream) : NULL, true)));
+    }
+
+    Return<void> allocateBuffers(
+            uint32_t width, uint32_t height,
+            ::android::hardware::graphics::common::V1_0::PixelFormat format,
+            uint32_t usage) override {
+        mBase->allocateBuffers(
+                width, height,
+                static_cast<::android::PixelFormat>(format),
+                usage);
+        return Void();
+    }
+
+    Return<int32_t> allowAllocation(bool allow) override {
+        return static_cast<int32_t>(mBase->allowAllocation(allow));
+    }
+
+    Return<int32_t> setGenerationNumber(uint32_t generationNumber) override {
+        return static_cast<int32_t>(mBase->setGenerationNumber(generationNumber));
+    }
+
+    Return<void> getConsumerName(HGraphicBufferProducer::getConsumerName_cb _hidl_cb) override {
+        _hidl_cb(mBase->getConsumerName().string());
+        return Void();
+    }
+
+    Return<int32_t> setSharedBufferMode(bool sharedBufferMode) override {
+        return static_cast<int32_t>(mBase->setSharedBufferMode(sharedBufferMode));
+    }
+
+    Return<int32_t> setAutoRefresh(bool autoRefresh) override {
+        return static_cast<int32_t>(mBase->setAutoRefresh(autoRefresh));
+    }
+
+    Return<int32_t> setDequeueTimeout(int64_t timeoutNs) override {
+        return static_cast<int32_t>(mBase->setDequeueTimeout(timeoutNs));
+    }
+
+    Return<void> getLastQueuedBuffer(HGraphicBufferProducer::getLastQueuedBuffer_cb _hidl_cb) override {
+        sp<GraphicBuffer> lOutBuffer = new GraphicBuffer();
+        sp<Fence> lOutFence = new Fence();
+        float lOutTransformMatrix[16];
+        status_t status = mBase->getLastQueuedBuffer(
+                &lOutBuffer, &lOutFence, lOutTransformMatrix);
+
+        AnwBuffer tOutBuffer;
+        if (lOutBuffer != nullptr) {
+            ::android::conversion::wrapAs(&tOutBuffer, *lOutBuffer);
+        }
+        hidl_handle tOutFence;
+        native_handle_t* nh = nullptr;
+        if ((lOutFence == nullptr) || !::android::conversion::wrapAs(&tOutFence, &nh, *lOutFence)) {
+            LOG(ERROR) << "TWGraphicBufferProducer::getLastQueuedBuffer - "
+                    "Invalid output fence";
+            _hidl_cb(static_cast<int32_t>(status),
+                    tOutBuffer,
+                    tOutFence,
+                    hidl_array<float, 16>());
+            return Void();
+        }
+        hidl_array<float, 16> tOutTransformMatrix(lOutTransformMatrix);
+
+        _hidl_cb(static_cast<int32_t>(status), tOutBuffer, tOutFence, tOutTransformMatrix);
+        native_handle_delete(nh);
+        return Void();
+    }
+
+    Return<void> getFrameTimestamps(HGraphicBufferProducer::getFrameTimestamps_cb _hidl_cb) override {
+        ::android::FrameEventHistoryDelta lDelta;
+        mBase->getFrameTimestamps(&lDelta);
+
+        HGraphicBufferProducer::FrameEventHistoryDelta tDelta;
+        std::vector<std::vector<native_handle_t*> > nhAA;
+        if (!::android::conversion::wrapAs(&tDelta, &nhAA, lDelta)) {
+            LOG(ERROR) << "TWGraphicBufferProducer::getFrameTimestamps - "
+                    "Invalid output frame timestamps";
+            _hidl_cb(tDelta);
+            return Void();
+        }
+
+        _hidl_cb(tDelta);
+        for (auto& nhA : nhAA) {
+            for (auto& nh : nhA) {
+                native_handle_delete(nh);
+            }
+        }
+        return Void();
+    }
+
+    Return<void> getUniqueId(HGraphicBufferProducer::getUniqueId_cb _hidl_cb) override {
+        uint64_t outId;
+        status_t status = mBase->getUniqueId(&outId);
+        _hidl_cb(static_cast<int32_t>(status), outId);
+        return Void();
+    }
+
+private:
+    sp<BGraphicBufferProducer> mBase;
+};
+
+}  // namespace android
+
+#endif  // MEDIA_STAGEFRIGHT_WGRAPHICBUFFERPRODUCER_H_
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/WProducerListener.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WProducerListener.h
similarity index 81%
rename from media/libstagefright/omx/include/media/stagefright/omx/1.0/WProducerListener.h
rename to media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WProducerListener.h
index a75e48a..febba87 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/WProducerListener.h
+++ b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/WProducerListener.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_HARDWARE_MEDIA_OMX_V1_0_WOMXPRODUCERLISTENER_H
-#define ANDROID_HARDWARE_MEDIA_OMX_V1_0_WOMXPRODUCERLISTENER_H
+#ifndef MEDIA_STAGEFRIGHT_WPRODUCERLISTENER_H_
+#define MEDIA_STAGEFRIGHT_WPRODUCERLISTENER_H_
 
 #include <hidl/MQDescriptor.h>
 #include <hidl/Status.h>
@@ -26,11 +26,6 @@
 #include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h>
 
 namespace android {
-namespace hardware {
-namespace media {
-namespace omx {
-namespace V1_0 {
-namespace implementation {
 
 using ::android::hidl::base::V1_0::IBase;
 using ::android::hardware::hidl_array;
@@ -62,11 +57,6 @@
     bool needsReleaseNotify() override;
 };
 
-}  // namespace implementation
-}  // namespace V1_0
-}  // namespace omx
-}  // namespace media
-}  // namespace hardware
 }  // namespace android
 
-#endif  // ANDROID_HARDWARE_MEDIA_OMX_V1_0_WOMXPRODUCERLISTENER_H
+#endif  // MEDIA_STAGEFRIGHT_WPRODUCERLISTENER_H_
diff --git a/media/libstagefright/gbs/tests/Android.bp b/media/libstagefright/bqhelper/tests/Android.bp
similarity index 80%
rename from media/libstagefright/gbs/tests/Android.bp
rename to media/libstagefright/bqhelper/tests/Android.bp
index 1463e73..2fbc3d6 100644
--- a/media/libstagefright/gbs/tests/Android.bp
+++ b/media/libstagefright/bqhelper/tests/Android.bp
@@ -4,7 +4,7 @@
     srcs: ["FrameDropper_test.cpp"],
 
     shared_libs: [
-        "libstagefright_gbs",
+        "libstagefright_bufferqueue_helper",
         "libutils",
     ],
 
diff --git a/media/libstagefright/gbs/tests/FrameDropper_test.cpp b/media/libstagefright/bqhelper/tests/FrameDropper_test.cpp
similarity index 98%
rename from media/libstagefright/gbs/tests/FrameDropper_test.cpp
rename to media/libstagefright/bqhelper/tests/FrameDropper_test.cpp
index 54a84d3..55ae77c 100644
--- a/media/libstagefright/gbs/tests/FrameDropper_test.cpp
+++ b/media/libstagefright/bqhelper/tests/FrameDropper_test.cpp
@@ -20,7 +20,7 @@
 
 #include <gtest/gtest.h>
 
-#include <media/stagefright/gbs/FrameDropper.h>
+#include <media/stagefright/bqhelper/FrameDropper.h>
 #include <media/stagefright/foundation/ADebug.h>
 
 namespace android {
diff --git a/media/libstagefright/codec2/1.0/Android.bp b/media/libstagefright/codec2/1.0/Android.bp
new file mode 100644
index 0000000..84d301a
--- /dev/null
+++ b/media/libstagefright/codec2/1.0/Android.bp
@@ -0,0 +1,43 @@
+cc_library_shared {
+    name: "android.hardware.media.c2@1.0-service-impl",
+    // relative_install_path: "hw",
+    // TODO: vendor: true,
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
+
+    srcs: [
+        //"ComponentAuth.cpp",
+        //"Component.cpp",
+        //"ComponentListener.cpp",
+        //"ComponentStore.cpp",
+        //"Configurable.cpp",
+        "InputSurface.cpp",
+        "InputSurfaceConnection.cpp",
+        //"types.cpp",
+    ],
+
+    include_dirs: [
+        "frameworks/av/media/libstagefright/codec2/include",
+        "frameworks/av/media/libstagefright/codec2/vndk/internal",
+    ],
+
+    shared_libs: [
+        "libcutils",
+	"libgui",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libnativewindow",
+        "libstagefright_bufferqueue_helper",
+        "libstagefright_codec2_vndk",
+        "libui",
+        "libutils",
+
+        //"android.hardware.media.c2@1.0",
+        "android.hardware.graphics.bufferqueue@1.0",
+        "android.hidl.token@1.0-utils",
+    ],
+}
+
diff --git a/media/libstagefright/codec2/1.0/InputSurface.cpp b/media/libstagefright/codec2/1.0/InputSurface.cpp
new file mode 100644
index 0000000..977d410
--- /dev/null
+++ b/media/libstagefright/codec2/1.0/InputSurface.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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 "InputSurface"
+#include <utils/Log.h>
+
+#include <C2AllocatorGralloc.h>
+#include <C2PlatformSupport.h>
+
+#include <media/stagefright/bqhelper/GraphicBufferSource.h>
+#include <media/stagefright/codec2/1.0/InputSurface.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::GraphicBufferSource;
+
+sp<InputSurface> InputSurface::Create() {
+    sp<GraphicBufferSource> source = new GraphicBufferSource;
+    if (source->initCheck() != OK) {
+        return nullptr;
+    }
+    return new InputSurface(source->getIGraphicBufferProducer(), source);
+}
+
+InputSurface::InputSurface(
+        const sp<BGraphicBufferProducer> &base, const sp<GraphicBufferSource> &source)
+    : InputSurfaceBase(base),
+      mSource(source) {
+}
+
+sp<InputSurfaceConnection> InputSurface::connectToComponent(
+        const std::shared_ptr<C2Component> &comp) {
+    sp<InputSurfaceConnection> conn = new InputSurfaceConnection(mSource, comp);
+    if (!conn->init()) {
+        return nullptr;
+    }
+    return conn;
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/libstagefright/codec2/1.0/InputSurfaceConnection.cpp b/media/libstagefright/codec2/1.0/InputSurfaceConnection.cpp
new file mode 100644
index 0000000..32d6404
--- /dev/null
+++ b/media/libstagefright/codec2/1.0/InputSurfaceConnection.cpp
@@ -0,0 +1,225 @@
+/*
+ * 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 "InputSurfaceConnection"
+#include <utils/Log.h>
+
+#include <C2AllocatorGralloc.h>
+#include <C2BlockInternal.h>
+#include <C2PlatformSupport.h>
+
+#include <gui/Surface.h>
+#include <media/stagefright/codec2/1.0/InputSurfaceConnection.h>
+#include <system/window.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::status_t;
+
+namespace {
+
+class Buffer2D : public C2Buffer {
+public:
+    explicit Buffer2D(C2ConstGraphicBlock block) : C2Buffer({ block }) {}
+};
+
+}  // namespace
+
+constexpr int32_t kBufferCount = 16;
+
+class InputSurfaceConnection::Impl : public ComponentWrapper {
+public:
+    Impl(const sp<GraphicBufferSource> &source, const std::shared_ptr<C2Component> &comp)
+        : mSource(source), mComp(comp) {
+    }
+
+    virtual ~Impl() = default;
+
+    bool init() {
+        sp<GraphicBufferSource> source = mSource.promote();
+        if (source == nullptr) {
+            return false;
+        }
+        status_t err = source->initCheck();
+        if (err != OK) {
+            ALOGE("Impl::init: GBS init failed: %d", err);
+            return false;
+        }
+        // TODO: proper color aspect & dataspace
+        android_dataspace dataSpace = HAL_DATASPACE_BT709;
+        // TODO: read settings properly from the interface
+        err = source->configure(
+                this, dataSpace, kBufferCount, 1080, 1920, GRALLOC_USAGE_SW_READ_OFTEN);
+        if (err != OK) {
+            ALOGE("Impl::init: GBS configure failed: %d", err);
+            return false;
+        }
+        for (int32_t i = 0; i < kBufferCount; ++i) {
+            if (!source->onInputBufferAdded(i).isOk()) {
+                ALOGE("Impl::init: population GBS slots failed");
+                return false;
+            }
+        }
+        if (!source->start().isOk()) {
+            ALOGE("Impl::init: GBS start failed");
+            return false;
+        }
+        c2_status_t c2err = GetCodec2PlatformAllocatorStore()->fetchAllocator(
+                C2AllocatorStore::PLATFORM_START + 1,  // GRALLOC
+                &mAllocator);
+        if (c2err != OK) {
+            ALOGE("Impl::init: failed to fetch gralloc allocator: %d", c2err);
+            return false;
+        }
+        return true;
+    }
+
+    // From ComponentWrapper
+    status_t submitBuffer(
+            int32_t bufferId, const sp<GraphicBuffer> &buffer,
+            int64_t timestamp, int fenceFd) override {
+        ALOGV("Impl::submitBuffer bufferId = %d", bufferId);
+        // TODO: Use fd to construct fence
+        (void)fenceFd;
+
+        std::shared_ptr<C2Component> comp = mComp.lock();
+        if (!comp) {
+            return NO_INIT;
+        }
+
+        std::shared_ptr<C2GraphicAllocation> alloc;
+        C2Handle *handle = WrapNativeCodec2GrallocHandle(
+                buffer->handle, buffer->width, buffer->height,
+                buffer->format, buffer->usage, buffer->stride);
+        c2_status_t err = mAllocator->priorGraphicAllocation(handle, &alloc);
+        if (err != OK) {
+            return UNKNOWN_ERROR;
+        }
+        std::shared_ptr<C2GraphicBlock> block = _C2BlockFactory::CreateGraphicBlock(alloc);
+
+        std::unique_ptr<C2Work> work(new C2Work);
+        work->input.flags = (C2FrameData::flags_t)0;
+        work->input.ordinal.timestamp = timestamp;
+        work->input.ordinal.frameIndex = mFrameIndex++;
+        work->input.buffers.clear();
+        std::shared_ptr<C2Buffer> c2Buffer(
+                // TODO: fence
+                new Buffer2D(block->share(
+                        C2Rect(block->width(), block->height()), ::android::C2Fence())),
+                [handle, bufferId, src = mSource](C2Buffer *ptr) {
+                    delete ptr;
+                    native_handle_delete(handle);
+                    sp<GraphicBufferSource> source = src.promote();
+                    if (source != nullptr) {
+                        // TODO: fence
+                        (void)source->onInputBufferEmptied(bufferId, -1);
+                    }
+                });
+        work->input.buffers.push_back(c2Buffer);
+        work->worklets.clear();
+        work->worklets.emplace_back(new C2Worklet);
+        std::list<std::unique_ptr<C2Work>> items;
+        items.push_back(std::move(work));
+
+        err = comp->queue_nb(&items);
+        if (err != C2_OK) {
+            return UNKNOWN_ERROR;
+        }
+
+        mLastTimestamp = timestamp;
+
+        return OK;
+    }
+
+    status_t submitEos(int32_t) override {
+        std::shared_ptr<C2Component> comp = mComp.lock();
+        if (!comp) {
+            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 = mFrameIndex++;
+        work->input.buffers.clear();
+        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 = comp->queue_nb(&items);
+        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:
+    wp<GraphicBufferSource> mSource;
+    std::weak_ptr<C2Component> mComp;
+
+    // Needed for ComponentWrapper implementation
+    int64_t mLastTimestamp;
+    std::shared_ptr<C2Allocator> mAllocator;
+    std::atomic_uint64_t mFrameIndex;
+};
+
+InputSurfaceConnection::InputSurfaceConnection(
+        const sp<GraphicBufferSource> &source,
+        const std::shared_ptr<C2Component> &comp)
+    : mSource(source),
+      mImpl(new Impl(source, comp)) {
+}
+
+InputSurfaceConnection::~InputSurfaceConnection() {
+    disconnect();
+}
+
+bool InputSurfaceConnection::init() {
+    if (mImpl == nullptr) {
+        return false;
+    }
+    return mImpl->init();
+}
+
+void InputSurfaceConnection::disconnect() {
+    ALOGV("disconnect");
+    if (mSource != nullptr) {
+        (void)mSource->stop();
+        (void)mSource->release();
+    }
+    mImpl.clear();
+    mSource.clear();
+    ALOGV("disconnected");
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/libstagefright/codec2/Android.bp b/media/libstagefright/codec2/Android.bp
index ee5c3eb..e1ac44e 100644
--- a/media/libstagefright/codec2/Android.bp
+++ b/media/libstagefright/codec2/Android.bp
@@ -1,5 +1,6 @@
 cc_library_shared {
     name: "libstagefright_codec2",
+    vendor_available: true,
 
     tags: [
         "optional",
@@ -21,6 +22,16 @@
         "include",
     ],
 
+    header_libs: [
+        "libhardware_headers",
+        "libutils_headers",
+    ],
+
+    export_header_lib_headers: [
+        "libhardware_headers",
+        "libutils_headers",
+    ],
+
     sanitize: {
         misc_undefined: [
             "unsigned-integer-overflow",
@@ -37,6 +48,7 @@
 
 cc_library_shared {
     name: "libstagefright_simple_c2component",
+    vendor_available: true,
 
     tags: [
         "optional",
diff --git a/media/libstagefright/codec2/include/C2.h b/media/libstagefright/codec2/include/C2.h
index ab81cdc..00e3924 100644
--- a/media/libstagefright/codec2/include/C2.h
+++ b/media/libstagefright/codec2/include/C2.h
@@ -534,6 +534,18 @@
     });
 }
 
+/**
+ *  \ingroup utils_internal
+ */
+template<typename T, typename U, typename V>
+inline constexpr typename c2_types<T, V>::wide_type c2_clamp(const T a, const U b, const V c) {
+    typedef typename c2_types<T, U, V>::wide_type wide_type;
+    return ({
+        wide_type a_(a), b_(b), c_(c);
+        static_cast<typename c2_types<T, V>::wide_type>(b_ < a_ ? a_ : b_ > c_ ? c_ : b_);
+    });
+}
+
 /// @}
 
 #ifdef __ANDROID__
diff --git a/media/libstagefright/codec2/include/C2Buffer.h b/media/libstagefright/codec2/include/C2Buffer.h
index 45ad17f..034075f 100644
--- a/media/libstagefright/codec2/include/C2Buffer.h
+++ b/media/libstagefright/codec2/include/C2Buffer.h
@@ -134,7 +134,7 @@
 private:
     class Impl;
     std::shared_ptr<Impl> mImpl;
-    C2Fence(const std::shared_ptr<Impl> &impl);
+    C2Fence(std::shared_ptr<Impl> impl);
     friend struct _C2FenceFactory;
 };
 
@@ -324,15 +324,20 @@
 
     C2_ALLOW_OVERFLOW
     inline constexpr C2Segment intersect(const C2Segment &other) const {
-        if (!isValid()) {
-            return *this;
-        } else if (!other.isValid()) {
-            return other;
-        } else {
-            return C2Segment(c2_max(offset, other.offset),
-                             c2_min(end(), other.end()) - c2_max(offset, other.offset));
-        }
+        return C2Segment(c2_max(offset, other.offset),
+                         c2_min(end(), other.end()) - c2_max(offset, other.offset));
     }
+
+    /** clamps end to offset if it overflows */
+    inline constexpr C2Segment normalize() const {
+        return C2Segment(offset, c2_max(offset, end()) - offset);
+    }
+
+    /** clamps end to max if it overflows */
+    inline constexpr C2Segment saturate() const {
+        return C2Segment(offset, c2_min(size, ~offset));
+    }
+
 };
 
 /**
@@ -370,7 +375,7 @@
 };
 
 /**
- * Aspect for objects that have a linear range.
+ * Aspect for objects that have a linear range inside a linear capacity.
  *
  * This class is copiable.
  */
@@ -386,33 +391,52 @@
         return C2Segment(mOffset, mSize);
     }
 
-protected:
-    // capacity range [0, capacity]
-    inline constexpr explicit _C2LinearRangeAspect(const _C2LinearCapacityAspect *parent)
-        : _C2LinearCapacityAspect(parent),
-          mOffset(0),
-          mSize(capacity()) { }
+private:
+    // subrange of capacity [0, capacity] & [size, size + offset]
+    inline constexpr _C2LinearRangeAspect(uint32_t capacity_, size_t offset, size_t size)
+        : _C2LinearCapacityAspect(capacity_),
+          mOffset(c2_min(offset, capacity())),
+          mSize(c2_min(size, capacity() - mOffset)) {
+    }
 
+protected:
     // copy constructor (no error check)
     inline constexpr _C2LinearRangeAspect(const _C2LinearRangeAspect &other)
         : _C2LinearCapacityAspect(other.capacity()),
           mOffset(other.offset()),
-          mSize(other.size()) { }
+          mSize(other.size()) {
+    }
 
-    // subrange of capacity [0, capacity] & [size, size + offset]
+    // parent capacity range [0, capacity]
+    inline constexpr explicit _C2LinearRangeAspect(const _C2LinearCapacityAspect *parent)
+        : _C2LinearCapacityAspect(parent),
+          mOffset(0),
+          mSize(capacity()) {
+    }
+
+    // subrange of parent capacity [0, capacity] & [size, size + offset]
     inline constexpr _C2LinearRangeAspect(const _C2LinearCapacityAspect *parent, size_t offset, size_t size)
         : _C2LinearCapacityAspect(parent),
           mOffset(c2_min(offset, capacity())),
-          mSize(c2_min(size, capacity() - mOffset)) { }
+          mSize(c2_min(size, capacity() - mOffset)) {
+    }
 
-    // subsection of the two [offset, offset + size] ranges
+    // subsection of the parent's and [offset, offset + size] ranges
     inline constexpr _C2LinearRangeAspect(const _C2LinearRangeAspect *parent, size_t offset, size_t size)
-        : _C2LinearCapacityAspect(parent == nullptr ? 0 : parent->capacity()),
+        : _C2LinearCapacityAspect(parent),
           mOffset(c2_min(c2_max(offset, parent == nullptr ? 0 : parent->offset()), capacity())),
-          mSize(c2_min(c2_min(size, parent == nullptr ? 0 : parent->size()), capacity() - mOffset)) { }
+          mSize(std::min(c2_min(size, parent == nullptr ? 0 : parent->size()), capacity() - mOffset)) {
+    }
 
-private:
-    friend class _C2EditableLinearRange;
+public:
+    inline constexpr _C2LinearRangeAspect childRange(size_t offset, size_t size) const {
+        return _C2LinearRangeAspect(
+            mSize,
+            c2_min(c2_max(offset, mOffset), capacity()) - mOffset,
+            c2_min(c2_min(size, mSize), capacity() - c2_min(c2_max(offset, mOffset), capacity())));
+    }
+
+    friend class _C2EditableLinearRangeAspect;
     // invariants 0 <= mOffset <= mOffset + mSize <= capacity()
     uint32_t mOffset;
     uint32_t mSize;
@@ -420,7 +444,7 @@
 };
 
 /**
- * Utility class for safe range calculations.
+ * Utility class for safe range calculations using size_t-s.
  */
 class C2LinearRange : public _C2LinearRangeAspect {
 public:
@@ -436,7 +460,7 @@
 };
 
 /**
- * Utility class for simple capacity and range construction.
+ * Utility class for simple and safe capacity and range construction.
  */
 class C2LinearCapacity : public _C2LinearCapacityAspect {
 public:
@@ -453,21 +477,10 @@
  *
  * This class is copiable.
  */
-class _C2EditableLinearRange : public _C2LinearRangeAspect {
-protected:
-    inline explicit _C2EditableLinearRange(const _C2LinearCapacityAspect *parent)
-        : _C2LinearRangeAspect(parent) { }
+class _C2EditableLinearRangeAspect : public _C2LinearRangeAspect {
+    using _C2LinearRangeAspect::_C2LinearRangeAspect;
 
-    inline _C2EditableLinearRange(const _C2LinearRangeAspect &other)
-        : _C2LinearRangeAspect(other) { }
-
-    inline _C2EditableLinearRange(const _C2LinearCapacityAspect *parent, size_t offset, size_t size)
-        : _C2LinearRangeAspect(parent, offset, size) { }
-
-    // subsection of the two [offset, offset + size] ranges
-    inline _C2EditableLinearRange(const _C2LinearRangeAspect *parent, size_t offset, size_t size)
-        : _C2LinearRangeAspect(parent, offset, size) { }
-
+public:
 /// \name Editable linear range interface
 /// @{
 
@@ -1086,7 +1099,7 @@
  *
  * This class is copiable. \todo movable only?
  */
-class C2WriteView : public _C2EditableLinearRange {
+class C2WriteView : public _C2EditableLinearRangeAspect {
 public:
     /**
      * Start of the block.
@@ -1354,30 +1367,6 @@
 /// @{
 
 /**
- * Interface for objects that have a width and height (planar capacity).
- */
-class _C2PlanarCapacityAspect {
-/// \name Planar capacity interface
-/// @{
-public:
-    inline uint32_t width() const { return _mWidth; }
-    inline uint32_t height() const { return _mHeight; }
-
-protected:
-    inline _C2PlanarCapacityAspect(uint32_t width, uint32_t height)
-      : _mWidth(width), _mHeight(height) { }
-
-    inline _C2PlanarCapacityAspect(const _C2PlanarCapacityAspect *parent)
-        : _mWidth(parent == nullptr ? 0 : parent->width()),
-          _mHeight(parent == nullptr ? 0 : parent->height()) { }
-
-private:
-    const uint32_t _mWidth;
-    const uint32_t _mHeight;
-/// @}
-};
-
-/**
  * C2Rect: rectangle type with non-negative coordinates.
  *
  * \note This struct has public fields without getters/setters. All methods are inline.
@@ -1465,17 +1454,44 @@
 
     C2_ALLOW_OVERFLOW
     inline constexpr C2Rect intersect(const C2Rect &other) const {
-        if (!isValid()) {
-            return *this;
-        } else if (!other.isValid()) {
-            return other;
-        } else {
-            return C2Rect(c2_min(right(), other.right()) - c2_max(left, other.left),
-                          c2_min(bottom(), other.bottom()) - c2_max(top, other.top),
-                          c2_max(left, other.left),
-                          c2_max(top, other.top));
-        }
+        return C2Rect(c2_min(right(), other.right()) - c2_max(left, other.left),
+                      c2_min(bottom(), other.bottom()) - c2_max(top, other.top),
+                      c2_max(left, other.left),
+                      c2_max(top, other.top));
     }
+
+    /** clamps right and bottom to top, left if they overflow */
+    inline constexpr C2Rect normalize() const {
+        return C2Rect(c2_max(left, right()) - left, c2_max(top, bottom()) - top, left, top);
+    }
+};
+
+/**
+ * Interface for objects that have a width and height (planar capacity).
+ */
+class _C2PlanarCapacityAspect {
+/// \name Planar capacity interface
+/// @{
+public:
+    inline constexpr uint32_t width() const { return _mWidth; }
+    inline constexpr uint32_t height() const { return _mHeight; }
+
+    inline constexpr operator C2Rect() const {
+        return C2Rect(_mWidth, _mHeight);
+    }
+
+protected:
+    inline constexpr _C2PlanarCapacityAspect(uint32_t width, uint32_t height)
+      : _mWidth(width), _mHeight(height) { }
+
+    inline explicit constexpr _C2PlanarCapacityAspect(const _C2PlanarCapacityAspect *parent)
+        : _mWidth(parent == nullptr ? 0 : parent->width()),
+          _mHeight(parent == nullptr ? 0 : parent->height()) { }
+
+private:
+    uint32_t _mWidth;
+    uint32_t _mHeight;
+/// @}
 };
 
 /**
@@ -1497,6 +1513,7 @@
 
     int32_t colInc;       ///< column increment in bytes. may be negative
     int32_t rowInc;       ///< row increment in bytes. may be negative
+
     uint32_t colSampling; ///< subsampling compared to width (must be a power of 2)
     uint32_t rowSampling; ///< subsampling compared to height (must be a power of 2)
 
@@ -1529,7 +1546,7 @@
         BIG_END,    // BIG_ENDIAN is a reserved macro
     } endianness; ///< endianness of the samples
 
-    inline ssize_t minOffset(uint32_t width, uint32_t height) {
+    inline constexpr ssize_t minOffset(uint32_t width, uint32_t height) const {
         ssize_t offs = 0;
         if (width > 0 && colInc < 0) {
             offs += colInc * (ssize_t)(width - 1);
@@ -1540,7 +1557,7 @@
         return offs;
     }
 
-    inline ssize_t maxOffset(uint32_t width, uint32_t height, uint32_t allocatedDepth) {
+    inline constexpr ssize_t maxOffset(uint32_t width, uint32_t height) const {
         ssize_t offs = (allocatedDepth + 7) >> 3;
         if (width > 0 && colInc > 0) {
             offs += colInc * (ssize_t)(width - 1);
@@ -1550,20 +1567,20 @@
         }
         return offs;
     }
-};
+} C2_PACK;
 
 struct C2PlanarLayout {
 //public:
     enum type_t : uint32_t {
         TYPE_UNKNOWN = 0,
-        TYPE_YUV = 0x100,
-        TYPE_YUVA,
-        TYPE_RGB,
-        TYPE_RGBA,
+        TYPE_YUV = 0x100,   ///< YUV image with 3 planes
+        TYPE_YUVA,          ///< YUVA image with 4 planes
+        TYPE_RGB,           ///< RGB image with 3 planes
+        TYPE_RGBA,          ///< RBGA image with 4 planes
     };
 
-    type_t type;
-    uint32_t numPlanes;               // number of planes
+    type_t type;                    // image type
+    uint32_t numPlanes;             // number of planes
 
     enum plane_index_t : uint32_t {
         PLANE_Y = 0,
@@ -1584,13 +1601,68 @@
  *
  * This class is copiable.
  */
-class _C2PlanarSection : public _C2PlanarCapacityAspect {
+class _C2PlanarSectionAspect : public _C2PlanarCapacityAspect {
 /// \name Planar section interface
 /// @{
+private:
+    inline constexpr _C2PlanarSectionAspect(uint32_t width, uint32_t height, const C2Rect &crop)
+        : _C2PlanarCapacityAspect(width, height),
+          mCrop(std::min(width - std::min(crop.left, width), crop.width),
+                std::min(height - std::min(crop.top, height), crop.height),
+                std::min(crop.left, width),
+                std::min(crop.height, height)) {
+    }
+
 public:
     // crop can be an empty rect, does not have to line up with subsampling
     // NOTE: we do not support floating-point crop
-    inline const C2Rect crop() const { return mCrop; }
+    inline constexpr C2Rect crop() const { return mCrop; }
+
+    /**
+     * Returns a child planar section for |crop|, where the capacity represents this section.
+     */
+    inline constexpr _C2PlanarSectionAspect childSection(const C2Rect &crop) const {
+        return _C2PlanarSectionAspect(
+                mCrop.width, mCrop.height,
+                // crop and translate |crop| rect
+                C2Rect(c2_min(mCrop.right() - c2_clamp(mCrop.left, crop.left, mCrop.right()), crop.width),
+                       c2_min(mCrop.bottom() - c2_clamp(mCrop.top, crop.top, mCrop.bottom()), crop.height),
+                       c2_clamp(mCrop.left, crop.left, mCrop.right()) - mCrop.left,
+                       c2_clamp(mCrop.top, crop.top, mCrop.bottom()) - mCrop.top));
+    }
+
+protected:
+    inline constexpr _C2PlanarSectionAspect(const _C2PlanarCapacityAspect *parent)
+        : _C2PlanarCapacityAspect(parent), mCrop(width(), height()) {}
+
+    inline constexpr _C2PlanarSectionAspect(const _C2PlanarCapacityAspect *parent, const C2Rect &crop)
+        : _C2PlanarCapacityAspect(parent),
+          mCrop(parent == nullptr ? C2Rect(0, 0) : ((C2Rect)*parent).intersect(crop).normalize()) { }
+
+    inline constexpr _C2PlanarSectionAspect(const _C2PlanarSectionAspect *parent, const C2Rect &crop)
+        : _C2PlanarCapacityAspect(parent),
+          mCrop(parent == nullptr ? C2Rect(0, 0) : parent->crop().intersect(crop).normalize()) { }
+
+private:
+    friend class _C2EditablePlanarSectionAspect;
+    C2Rect mCrop;
+/// @}
+};
+
+/**
+ * Aspect for objects that have an editable planar section (crop rectangle).
+ *
+ * This class is copiable.
+ */
+class _C2EditablePlanarSectionAspect : public _C2PlanarSectionAspect {
+/// \name Planar section interface
+/// @{
+    using _C2PlanarSectionAspect::_C2PlanarSectionAspect;
+
+public:
+    // crop can be an empty rect, does not have to line up with subsampling
+    // NOTE: we do not support floating-point crop
+    inline constexpr C2Rect crop() const { return mCrop; }
 
     /**
      *  Sets crop to crop intersected with [(0,0) .. (width, height)]
@@ -1616,17 +1688,41 @@
         mCrop = crop;
         return true;
     }
-
-protected:
-    inline _C2PlanarSection(const _C2PlanarCapacityAspect *parent)
-        : _C2PlanarCapacityAspect(parent), mCrop(width(), height()) {}
-
-private:
-    C2Rect mCrop;
 /// @}
 };
 
 /**
+ * Utility class for safe range calculations using size_t-s.
+ */
+class C2PlanarSection : public _C2PlanarSectionAspect {
+public:
+    inline constexpr C2PlanarSection(const _C2PlanarCapacityAspect &parent, const C2Rect &crop)
+        : _C2PlanarSectionAspect(&parent, crop) { }
+
+    inline constexpr C2PlanarSection(const _C2PlanarSectionAspect &parent, const C2Rect &crop)
+        : _C2PlanarSectionAspect(&parent, crop) { }
+
+    inline constexpr C2PlanarSection intersect(const C2Rect &crop) const {
+        return C2PlanarSection(*this, crop);
+    }
+};
+
+/**
+ * Utility class for simple and safe planar capacity and section construction.
+ */
+class C2PlanarCapacity : public _C2PlanarCapacityAspect {
+public:
+    inline constexpr explicit C2PlanarCapacity(size_t width, size_t height)
+        : _C2PlanarCapacityAspect(c2_min(width, std::numeric_limits<uint32_t>::max()),
+                                  c2_min(height, std::numeric_limits<uint32_t>::max())) { }
+
+    inline constexpr C2PlanarSection section(const C2Rect &crop) const {
+        return C2PlanarSection(*this, crop);
+    }
+};
+
+
+/**
  * \ingroup graphic allocator
  * 2D allocation interface.
  */
@@ -1640,19 +1736,22 @@
      * descriptor referring to an acquire sync fence object. If it is already safe to access the
      * buffer contents, then -1.
      *
+     * Safe regions for the pointer addresses returned can be gotten via C2LayoutInfo.minOffse()/
+     * maxOffset().
+     *
      * \note Only one portion of the graphic allocation can be mapped at the same time. (This is
      * from gralloc1 limitation.)
      *
-     * \param rect            section to be mapped (this does not have to be aligned)
-     * \param usage           the desired usage. \todo this must be kSoftwareRead and/or
+     * \param rect          section to be mapped (this does not have to be aligned)
+     * \param usage         the desired usage. \todo this must be kSoftwareRead and/or
      *                      kSoftwareWrite.
-     * \param fenceFd         a pointer to a file descriptor if an async mapping is requested. If
+     * \param fenceFd       a pointer to a file descriptor if an async mapping is requested. If
      *                      not-null, and acquire fence FD will be stored here on success, or -1
      *                      on failure. If null, the mapping will be synchronous.
-     * \param layout          a pointer to where the mapped planes' descriptors will be
+     * \param layout        a pointer to where the mapped planes' descriptors will be
      *                      stored. On failure, nullptr will be stored here.
-     *
-     * \todo Do we need to support sync operation as we could just wait for the fence?
+     * \param addr          pointer to an array with at least C2PlanarLayout::MAX_NUM_PLANES
+     *                      elements. Only layout.numPlanes elements will be modified on success.
      *
      * \retval C2_OK        the operation was successful
      * \retval C2_REFUSED   no permission to map the section
@@ -1666,7 +1765,6 @@
      */
     virtual c2_status_t map(
             C2Rect rect, C2MemoryUsage usage, int *fenceFd,
-            // TODO: return <addr, size> buffers with plane sizes
             C2PlanarLayout *layout /* nonnull */, uint8_t **addr /* nonnull */) = 0;
 
     /**
@@ -1687,11 +1785,9 @@
     virtual c2_status_t unmap(C2Fence *fenceFd /* nullable */) = 0;
 
     /**
-     * Returns true if this is a valid allocation.
-     *
-     * \todo remove?
+     * Returns the allocator ID for this allocation. This is useful to put the handle into context.
      */
-    virtual bool isValid() const = 0;
+    virtual C2Allocator::id_t getAllocatorId() const = 0;
 
     /**
      * Returns a pointer to the allocation handle.
@@ -1710,15 +1806,36 @@
 
 class C2GraphicAllocation;
 
-class C2Block2D : public _C2PlanarSection {
+/**
+ * A 2D block.
+ *
+ * \note width()/height() is not meaningful for users of blocks; instead, crop().width() and
+ * crop().height() is the capacity of the usable portion. Use and crop() if accessing the block
+ * directly through its handle to represent the allotted region of the underlying allocation to this
+ * block.
+ */
+class C2Block2D : public _C2PlanarSectionAspect {
 public:
+    /**
+     * Returns the underlying handle for this allocation.
+     *
+     * \note that the block and its block pool has shared ownership of the handle
+     *       and if all references to the block are released, the underlying block
+     *       allocation may get reused even if a client keeps a clone of this handle.
+     */
     const C2Handle *handle() const;
 
-protected:
-    C2Block2D(const std::shared_ptr<C2GraphicAllocation> &alloc);
+    /**
+     * Returns the allocator's ID that created the underlying allocation for this block. This
+     * provides the context for understanding the handle.
+     */
+    C2Allocator::id_t getAllocatorId() const;
 
-private:
+protected:
     class Impl;
+    C2Block2D(std::shared_ptr<Impl> impl, const _C2PlanarSectionAspect &section);
+
+    friend struct _C2BlockFactory;
     std::shared_ptr<Impl> mImpl;
 };
 
@@ -1731,21 +1848,19 @@
  * to ensure subsampling is followed. This results in nearly identical interface between read and
  * write views, so C2GraphicView can encompass both of them.
  */
-class C2GraphicView : public _C2PlanarSection {
+class C2GraphicView : public _C2EditablePlanarSectionAspect {
 public:
     /**
-     * \return array of pointers to the start of the planes or nullptr on error.
-     * Regardless of crop rect, they always point to the top-left corner of
-     * each plane.  Access outside of the crop rect results in an undefined
-     * behavior.
+     * \return array of pointers (of layout().numPlanes elements) to the start of the planes or
+     * nullptr on error. Regardless of crop rect, they always point to the top-left corner of each
+     * plane. Access outside of the crop rect results in an undefined behavior.
      */
     const uint8_t *const *data() const;
 
     /**
-     * \return array of pointers to the start of the planes or nullptr on error.
-     * Regardless of crop rect, they always point to the top-left corner of
-     * each plane.  Access outside of the crop rect results in an undefined
-     * behavior.
+     * \return array of pointers (of layout().numPlanes elements) to the start of the planes or
+     * nullptr on error. Regardless of crop rect, they always point to the top-left corner of each
+     * plane. Access outside of the crop rect results in an undefined behavior.
      */
     uint8_t *const *data();
 
@@ -1770,14 +1885,12 @@
     c2_status_t error() const;
 
 protected:
-    C2GraphicView(
-            const _C2PlanarCapacityAspect *parent,
-            uint8_t *const *data,
-            const C2PlanarLayout& layout);
+    class Impl;
+    C2GraphicView(std::shared_ptr<Impl> impl, const _C2PlanarSectionAspect &section);
     explicit C2GraphicView(c2_status_t error);
 
 private:
-    class Impl;
+    friend struct _C2BlockFactory;
     std::shared_ptr<Impl> mImpl;
 };
 
@@ -1814,11 +1927,11 @@
     C2Fence fence() const { return mFence; }
 
 protected:
-    C2ConstGraphicBlock(const std::shared_ptr<C2GraphicAllocation> &alloc, C2Fence fence);
+    C2ConstGraphicBlock(
+            std::shared_ptr<Impl> impl, const _C2PlanarSectionAspect &section, C2Fence fence);
 
 private:
-    class Impl;
-    std::shared_ptr<Impl> mImpl;
+    friend struct _C2BlockFactory;
     C2Fence mFence;
 };
 
@@ -1848,11 +1961,9 @@
     C2ConstGraphicBlock share(const C2Rect &crop, C2Fence fence);
 
 protected:
-    explicit C2GraphicBlock(const std::shared_ptr<C2GraphicAllocation> &alloc);
+    C2GraphicBlock(std::shared_ptr<Impl> impl, const _C2PlanarSectionAspect &section);
 
-private:
-    class Impl;
-    std::shared_ptr<Impl> mImpl;
+    friend struct _C2BlockFactory;
 };
 
 /// @}
diff --git a/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurface.h b/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurface.h
new file mode 100644
index 0000000..e46d03c
--- /dev/null
+++ b/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurface.h
@@ -0,0 +1,71 @@
+/*
+ * 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 ANDROID_HARDWARE_MEDIA_C2_V1_0_INPUT_SURFACE_H
+#define ANDROID_HARDWARE_MEDIA_C2_V1_0_INPUT_SURFACE_H
+
+#include <memory>
+
+#include <C2Component.h>
+#include <media/stagefright/bqhelper/WGraphicBufferProducer.h>
+#include <media/stagefright/codec2/1.0/InputSurfaceConnection.h>
+
+namespace android {
+
+class GraphicBufferSource;
+
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::sp;
+
+typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer
+        HGraphicBufferProducer;
+typedef ::android::IGraphicBufferProducer BGraphicBufferProducer;
+
+// TODO: ::android::TWGraphicBufferProducer<IInputSurface>
+typedef ::android::TWGraphicBufferProducer<HGraphicBufferProducer> InputSurfaceBase;
+
+class InputSurface : public InputSurfaceBase {
+public:
+    virtual ~InputSurface() = default;
+
+    // Methods from IInputSurface
+    sp<InputSurfaceConnection> connectToComponent(
+            const std::shared_ptr<::android::C2Component> &comp);
+    // TODO: intf()
+
+    static sp<InputSurface> Create();
+
+private:
+    InputSurface(
+            const sp<BGraphicBufferProducer> &base,
+            const sp<::android::GraphicBufferSource> &source);
+
+    sp<::android::GraphicBufferSource> mSource;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_C2_V1_0_INPUT_SURFACE_H
diff --git a/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h b/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h
new file mode 100644
index 0000000..fc19acd
--- /dev/null
+++ b/media/libstagefright/codec2/include/media/stagefright/codec2/1.0/InputSurfaceConnection.h
@@ -0,0 +1,70 @@
+/*
+ * 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 ANDROID_HARDWARE_MEDIA_C2_V1_0_INPUT_SURFACE_CONNECTION_H
+#define ANDROID_HARDWARE_MEDIA_C2_V1_0_INPUT_SURFACE_CONNECTION_H
+
+#include <memory>
+
+#include <C2Component.h>
+#include <media/stagefright/bqhelper/GraphicBufferSource.h>
+#include <media/stagefright/bqhelper/WGraphicBufferProducer.h>
+#include <media/stagefright/codec2/1.0/InputSurfaceConnection.h>
+
+namespace android {
+
+class C2Allocator;
+
+namespace hardware {
+namespace media {
+namespace c2 {
+namespace V1_0 {
+namespace implementation {
+
+// TODO: inherit from IInputSurfaceConnection
+class InputSurfaceConnection : public RefBase {
+public:
+    virtual ~InputSurfaceConnection();
+
+    // From IInputSurfaceConnection
+    void disconnect();
+
+private:
+    friend class InputSurface;
+
+    // For InputSurface
+    InputSurfaceConnection(
+            const sp<GraphicBufferSource> &source, const std::shared_ptr<C2Component> &comp);
+    bool init();
+
+    InputSurfaceConnection() = delete;
+
+    class Impl;
+
+    sp<GraphicBufferSource> mSource;
+    sp<Impl> mImpl;
+
+    DISALLOW_EVIL_CONSTRUCTORS(InputSurfaceConnection);
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace c2
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_C2_V1_0_INPUT_SURFACE_CONNECTION_H
diff --git a/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp b/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp
index 2de43c8..f0e57e2 100644
--- a/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp
+++ b/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp
@@ -26,6 +26,209 @@
 
 namespace android {
 
+class C2BufferUtilsTest : public ::testing::Test {
+    static void StaticSegmentTest() {
+        // constructor
+        static_assert(C2Segment(123u, 456u).offset == 123, "");
+        static_assert(C2Segment(123u, 456u).size == 456, "");
+
+        // empty
+        static_assert(!C2Segment(123u, 456u).isEmpty(), "");
+        static_assert(C2Segment(123u, 0u).isEmpty(), "");
+
+        // valid
+        static_assert(C2Segment(123u, 456u).isValid(), "");
+        static_assert(C2Segment(123u, ~123u).isValid(), "");
+        static_assert(!C2Segment(123u, 1 + ~123u).isValid(), "");
+
+        // bool()
+        static_assert(C2Segment(123u, 456u), "");
+        static_assert((bool)C2Segment(123u, ~123u), "");
+        static_assert(!bool(C2Segment(123u, 1 + ~123u)), "");
+        static_assert(!bool(C2Segment(123u, 0)), "");
+
+        // !
+        static_assert(!!C2Segment(123u, 456u), "");
+        static_assert(!!C2Segment(123u, ~123u), "");
+        static_assert(!C2Segment(123u, 1 + ~123u), "");
+        static_assert(!C2Segment(123u, 0), "");
+
+        // contains
+        static_assert(C2Segment(123u, ~123u).contains(C2Segment(123u, 0)), "");
+        static_assert(!C2Segment(123u, 1 + ~123u).contains(C2Segment(123u, 0)), "");
+        static_assert(C2Segment(123u, ~123u).contains(C2Segment(123u, ~123u)), "");
+        static_assert(!C2Segment(123u, ~123u).contains(C2Segment(123u, 1 + ~123u)), "");
+        static_assert(!C2Segment(123u, 1 + ~123u).contains(C2Segment(123u, 1 + ~123u)), "");
+        static_assert(!C2Segment(123u, ~123u).contains(C2Segment(122u, 2u)), "");
+        static_assert(C2Segment(123u, ~123u).contains(C2Segment(123u, 2u)), "");
+        static_assert(C2Segment(123u, 3u).contains(C2Segment(124u, 2u)), "");
+        static_assert(!C2Segment(123u, 3u).contains(C2Segment(125u, 2u)), "");
+
+        // ==
+        static_assert(C2Segment(123u, 456u) == C2Segment(123u, 456u), "");
+        static_assert(!(C2Segment(123u, 456u) == C2Segment(123u, 457u)), "");
+        static_assert(!(C2Segment(123u, 456u) == C2Segment(123u, 455u)), "");
+        static_assert(!(C2Segment(123u, 456u) == C2Segment(122u, 456u)), "");
+        static_assert(!(C2Segment(123u, 456u) == C2Segment(124u, 456u)), "");
+        static_assert(!(C2Segment(123u, 0u) == C2Segment(124u, 0u)), "");
+        static_assert(C2Segment(123u, 0u) == C2Segment(123u, 0u), "");
+        static_assert(C2Segment(123u, 1 + ~123u) == C2Segment(124u, 1 + ~124u), "");
+
+        // !=
+        static_assert(!(C2Segment(123u, 456u) != C2Segment(123u, 456u)), "");
+        static_assert(C2Segment(123u, 456u) != C2Segment(123u, 457u), "");
+        static_assert(C2Segment(123u, 456u) != C2Segment(123u, 455u), "");
+        static_assert(C2Segment(123u, 456u) != C2Segment(122u, 456u), "");
+        static_assert(C2Segment(123u, 456u) != C2Segment(124u, 456u), "");
+        static_assert(C2Segment(123u, 0u) != C2Segment(124u, 0u), "");
+        static_assert(!(C2Segment(123u, 0u) != C2Segment(123u, 0u)), "");
+        static_assert(!(C2Segment(123u, 1 + ~123u) != C2Segment(124u, 1 + ~124u)), "");
+
+        // <=
+        static_assert(C2Segment(123u, 456u) <= C2Segment(123u, 456u), "");
+        static_assert(C2Segment(123u, 456u) <= C2Segment(123u, 457u), "");
+        static_assert(C2Segment(123u, 456u) <= C2Segment(122u, 457u), "");
+        static_assert(!(C2Segment(123u, 457u) <= C2Segment(123u, 456u)), "");
+        static_assert(!(C2Segment(122u, 457u) <= C2Segment(123u, 456u)), "");
+        static_assert(!(C2Segment(123u, 457u) <= C2Segment(124u, 457u)), "");
+        static_assert(!(C2Segment(122u, 457u) <= C2Segment(123u, 457u)), "");
+        static_assert(!(C2Segment(122u, 0u) <= C2Segment(123u, 0u)), "");
+        static_assert(C2Segment(123u, 0u) <= C2Segment(122u, 1u), "");
+        static_assert(C2Segment(122u, 0u) <= C2Segment(122u, 1u), "");
+        static_assert(!(C2Segment(122u, ~122u) <= C2Segment(122u, 1 + ~122u)), "");
+        static_assert(!(C2Segment(122u, 1 + ~122u) <= C2Segment(122u, ~122u)), "");
+        static_assert(!(C2Segment(122u, 1 + ~122u) <= C2Segment(122u, 1 + ~122u)), "");
+
+        // <
+        static_assert(!(C2Segment(123u, 456u) < C2Segment(123u, 456u)), "");
+        static_assert(C2Segment(123u, 456u) < C2Segment(123u, 457u), "");
+        static_assert(C2Segment(123u, 456u) < C2Segment(122u, 457u), "");
+        static_assert(!(C2Segment(123u, 457u) < C2Segment(123u, 456u)), "");
+        static_assert(!(C2Segment(122u, 457u) < C2Segment(123u, 456u)), "");
+        static_assert(!(C2Segment(123u, 457u) < C2Segment(124u, 457u)), "");
+        static_assert(!(C2Segment(122u, 457u) < C2Segment(123u, 457u)), "");
+        static_assert(!(C2Segment(122u, 0u) < C2Segment(123u, 0u)), "");
+        static_assert(C2Segment(123u, 0u) < C2Segment(122u, 1u), "");
+        static_assert(C2Segment(122u, 0u) < C2Segment(122u, 1u), "");
+        static_assert(!(C2Segment(122u, ~122u) < C2Segment(122u, 1 + ~122u)), "");
+        static_assert(!(C2Segment(122u, 1 + ~122u) < C2Segment(122u, ~122u)), "");
+        static_assert(!(C2Segment(122u, 1 + ~122u) < C2Segment(122u, 1 + ~122u)), "");
+
+        // <=
+        static_assert(C2Segment(123u, 456u) >= C2Segment(123u, 456u), "");
+        static_assert(C2Segment(123u, 457u) >= C2Segment(123u, 456u), "");
+        static_assert(C2Segment(122u, 457u) >= C2Segment(123u, 456u), "");
+        static_assert(!(C2Segment(123u, 456u) >= C2Segment(123u, 457u)), "");
+        static_assert(!(C2Segment(123u, 456u) >= C2Segment(122u, 457u)), "");
+        static_assert(!(C2Segment(124u, 457u) >= C2Segment(123u, 457u)), "");
+        static_assert(!(C2Segment(123u, 457u) >= C2Segment(122u, 457u)), "");
+        static_assert(!(C2Segment(123u, 0u) >= C2Segment(122u, 0u)), "");
+        static_assert(C2Segment(122u, 1u) >= C2Segment(123u, 0u), "");
+        static_assert(C2Segment(122u, 1u) >= C2Segment(122u, 0u), "");
+        static_assert(!(C2Segment(122u, 1 + ~122u) >= C2Segment(122u, ~122u)), "");
+        static_assert(!(C2Segment(122u, ~122u) >= C2Segment(122u, 1 + ~122u)), "");
+        static_assert(!(C2Segment(122u, 1 + ~122u) >= C2Segment(122u, 1 + ~122u)), "");
+
+        // <
+        static_assert(!(C2Segment(123u, 456u) > C2Segment(123u, 456u)), "");
+        static_assert(C2Segment(123u, 457u) > C2Segment(123u, 456u), "");
+        static_assert(C2Segment(122u, 457u) > C2Segment(123u, 456u), "");
+        static_assert(!(C2Segment(123u, 456u) > C2Segment(123u, 457u)), "");
+        static_assert(!(C2Segment(123u, 456u) > C2Segment(122u, 457u)), "");
+        static_assert(!(C2Segment(124u, 457u) > C2Segment(123u, 457u)), "");
+        static_assert(!(C2Segment(123u, 457u) > C2Segment(122u, 457u)), "");
+        static_assert(!(C2Segment(123u, 0u) > C2Segment(122u, 0u)), "");
+        static_assert(C2Segment(122u, 1u) > C2Segment(123u, 0u), "");
+        static_assert(C2Segment(122u, 1u) > C2Segment(122u, 0u), "");
+        static_assert(!(C2Segment(122u, 1 + ~122u) > C2Segment(122u, ~122u)), "");
+        static_assert(!(C2Segment(122u, ~122u) > C2Segment(122u, 1 + ~122u)), "");
+        static_assert(!(C2Segment(122u, 1 + ~122u) > C2Segment(122u, 1 + ~122u)), "");
+
+        // end
+        static_assert(C2Segment(123u, 456u).end() == 579u, "");
+        static_assert(C2Segment(123u, 0u).end() == 123u, "");
+        static_assert(C2Segment(123u, ~123u).end() == 0xffffffffu, "");
+        static_assert(C2Segment(123u, 1 + ~123u).end() == 0u, "");
+
+        // intersect
+        static_assert(C2Segment(123u, 456u).intersect(C2Segment(123u, 456u)) == C2Segment(123u, 456u), "");
+        static_assert(C2Segment(123u, 456u).intersect(C2Segment(123u, 460u)) == C2Segment(123u, 456u), "");
+        static_assert(C2Segment(123u, 456u).intersect(C2Segment(124u, 460u)) == C2Segment(124u, 455u), "");
+        static_assert(C2Segment(123u, 456u).intersect(C2Segment(579u, 460u)) == C2Segment(579u, 0u), "");
+        static_assert(C2Segment(123u, 456u).intersect(C2Segment(589u, 460u)) == C2Segment(589u, ~9u /* -10 */), "");
+        static_assert(C2Segment(123u, 456u).intersect(C2Segment(123u, 455u)) == C2Segment(123u, 455u), "");
+        static_assert(C2Segment(123u, 456u).intersect(C2Segment(122u, 456u)) == C2Segment(123u, 455u), "");
+        static_assert(C2Segment(123u, 456u).intersect(C2Segment(0u, 123u)) == C2Segment(123u, 0u), "");
+        static_assert(C2Segment(123u, 456u).intersect(C2Segment(0u, 0u)) == C2Segment(123u, ~122u /* -123 */), "");
+
+        // normalize (change invalid segments to empty segments)
+        static_assert(C2Segment(123u, 456u).normalize() == C2Segment(123u, 456u), "");
+        static_assert(C2Segment(123u, ~123u).normalize() == C2Segment(123u, ~123u), "");
+        static_assert(C2Segment(123u, 1 + ~123u).normalize() == C2Segment(123u, 0u), "");
+
+        // note: normalize cannot be used to make this work
+        static_assert(C2Segment(123u, 456u).intersect(C2Segment(150u, ~150u)).normalize() == C2Segment(150u, 429u), "");
+        static_assert(C2Segment(123u, 456u).intersect(C2Segment(150u, 1 + ~150u)).normalize() != C2Segment(150u, 429u), "");
+        static_assert(C2Segment(123u, 456u).intersect(C2Segment(150u, 1 + ~150u)).normalize() == C2Segment(150u, 0u), "");
+
+        // saturate (change invalid segments to full segments up to max)
+        static_assert(C2Segment(123u, 456u).saturate() == C2Segment(123u, 456u), "");
+        static_assert(C2Segment(123u, ~123u).saturate() == C2Segment(123u, ~123u), "");
+        static_assert(C2Segment(123u, 1 + ~123u).saturate() == C2Segment(123u, ~123u), "");
+
+        // note: saturate can be used to make this work but only partially
+        static_assert(C2Segment(123u, 456u).intersect(C2Segment(150u, 1 + ~150u).saturate()).normalize() == C2Segment(150u, 429u), "");
+        static_assert(C2Segment(123u, 456u).intersect(C2Segment(0u, 100u).saturate()).normalize() == C2Segment(123u, 0u), "");
+        static_assert(C2Segment(123u, 456u).intersect(C2Segment(1000u, 100u).saturate()).normalize() != C2Segment(579u, 0u), "");
+        static_assert(C2Segment(123u, 456u).intersect(C2Segment(1000u, 100u).saturate()).normalize() == C2Segment(1000u, 0u), "");
+
+    }
+
+    static void StaticLinearRangeAndCapacityTest() {
+        class TestCapacity : public _C2LinearCapacityAspect {
+            using _C2LinearCapacityAspect::_C2LinearCapacityAspect;
+            friend class C2BufferUtilsTest;
+        };
+
+        class TestRange : public _C2LinearRangeAspect {
+            using _C2LinearRangeAspect::_C2LinearRangeAspect;
+            friend class C2BufferUtilsTest;
+        };
+
+        // _C2LinearCapacityAspect
+        static_assert(TestCapacity(0u).capacity() == 0u, "");
+        constexpr TestCapacity cap(123u);
+        static_assert(TestCapacity(&cap).capacity() == 123u, "");
+        static_assert(TestCapacity(nullptr).capacity() == 0u, "");
+
+        // _C2LinearCapacityRange
+        static_assert(TestRange(&cap).capacity() == 123u, "");
+        static_assert(TestRange(&cap).offset() == 0u, "");
+        static_assert(TestRange(&cap).size() == 123u, "");
+        static_assert(TestRange(&cap).endOffset() == 123u, "");
+
+        constexpr TestRange range(&cap, 50u, 100u);
+
+        static_assert(range.capacity() == 123u, "");
+        static_assert(range.offset() == 50u, "");
+        static_assert(range.size() == 73u, "");
+        static_assert(range.endOffset() == 123u, "");
+
+        static_assert(TestRange(&cap, 20u, 30u).capacity() == 123u, "");
+        static_assert(TestRange(&cap, 20u, 30u).offset() == 20u, "");
+        static_assert(TestRange(&cap, 20u, 30u).size() == 30u, "");
+        static_assert(TestRange(&cap, 20u, 30u).endOffset() == 50u, "");
+
+        static_assert(TestRange(&cap, 200u, 30u).capacity() == 123u, "");
+        static_assert(TestRange(&cap, 200u, 30u).offset() == 123u, "");
+        static_assert(TestRange(&cap, 200u, 30u).size() == 0u, "");
+        static_assert(TestRange(&cap, 200u, 30u).endOffset() == 123u, "");
+
+    }
+
+};
+
+
 class C2BufferTest : public ::testing::Test {
 public:
     C2BufferTest()
@@ -180,6 +383,14 @@
         data[i] = i % 100u;
     }
 
+    writeView.setOffset(kCapacity / 3);
+    data = writeView.data();
+    ASSERT_NE(nullptr, data);
+    for (size_t i = 0; i < writeView.size(); ++i) {
+        ASSERT_EQ((i + kCapacity / 3) % 100u, data[i]) << " at i = " << i
+            << "; data = " << static_cast<void *>(data);
+    }
+
     C2Fence fence;
     C2ConstLinearBlock constBlock = block->share(
             kCapacity / 3, kCapacity / 3, fence);
diff --git a/media/libstagefright/codec2/vndk/Android.bp b/media/libstagefright/codec2/vndk/Android.bp
index cdd0488..d6cbe96 100644
--- a/media/libstagefright/codec2/vndk/Android.bp
+++ b/media/libstagefright/codec2/vndk/Android.bp
@@ -10,6 +10,10 @@
 
 cc_library_shared {
     name: "libstagefright_codec2_vndk",
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
 
     srcs: [
         "C2AllocatorIon.cpp",
@@ -23,12 +27,9 @@
         "include",
     ],
 
-    header_libs:[
-        "libstagefright_codec2_internal",
-    ],
-
     include_dirs: [
         "frameworks/av/media/libstagefright/codec2/include",
+        "frameworks/av/media/libstagefright/codec2/vndk/internal",
         "frameworks/native/include/media/hardware",
     ],
 
@@ -42,7 +43,6 @@
         "libhidlbase",
         "libion",
         "liblog",
-        "libmedia",
         "libstagefright_foundation",
         "libui",
         "libutils",
diff --git a/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp b/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp
index 155583d..b287ca8 100644
--- a/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/libstagefright/codec2/vndk/C2AllocatorGralloc.cpp
@@ -25,6 +25,7 @@
 
 #include <C2AllocatorGralloc.h>
 #include <C2Buffer.h>
+#include <C2PlatformSupport.h>
 
 namespace android {
 
@@ -185,7 +186,7 @@
             C2Rect rect, C2MemoryUsage usage, int *fenceFd,
             C2PlanarLayout *layout /* nonnull */, uint8_t **addr /* nonnull */) override;
     virtual c2_status_t unmap(C2Fence *fenceFd /* nullable */) override;
-    virtual bool isValid() const override { return true; }
+    virtual C2Allocator::id_t getAllocatorId() const override { return mAllocatorId; }
     virtual const C2Handle *handle() const override { return mLockedHandle ? : mHandle; }
     virtual bool equals(const std::shared_ptr<const C2GraphicAllocation> &other) const override;
 
@@ -195,7 +196,8 @@
               const BufferDescriptorInfo &info,
               const sp<IMapper> &mapper,
               hidl_handle &hidlHandle,
-              const C2HandleGralloc *const handle);
+              const C2HandleGralloc *const handle,
+              C2Allocator::id_t allocatorId);
     int dup() const;
     c2_status_t status() const;
 
@@ -207,20 +209,24 @@
     buffer_handle_t mBuffer;
     const C2HandleGralloc *mLockedHandle;
     bool mLocked;
+    C2Allocator::id_t mAllocatorId;
 };
 
 C2AllocationGralloc::C2AllocationGralloc(
           const BufferDescriptorInfo &info,
           const sp<IMapper> &mapper,
           hidl_handle &hidlHandle,
-          const C2HandleGralloc *const handle)
+          const C2HandleGralloc *const handle,
+          C2Allocator::id_t allocatorId)
     : C2GraphicAllocation(info.mapperInfo.width, info.mapperInfo.height),
       mInfo(info),
       mMapper(mapper),
       mHidlHandle(std::move(hidlHandle)),
       mHandle(handle),
       mBuffer(nullptr),
-      mLocked(false) {}
+      mLocked(false),
+      mAllocatorId(allocatorId) {
+}
 
 C2AllocationGralloc::~C2AllocationGralloc() {
     if (!mBuffer) {
@@ -422,11 +428,19 @@
 /* ===================================== GRALLOC ALLOCATOR ==================================== */
 class C2AllocatorGralloc::Impl {
 public:
-    Impl();
+    Impl(id_t id);
 
-    id_t getId() const;
+    id_t getId() const {
+        return mTraits->id;
+    }
 
-    C2String getName() const;
+    C2String getName() const {
+        return mTraits->name;
+    }
+
+    std::shared_ptr<const C2Allocator::Traits> getTraits() const {
+        return mTraits;
+    }
 
     c2_status_t newGraphicAllocation(
             uint32_t width, uint32_t height, uint32_t format, const C2MemoryUsage &usage,
@@ -439,13 +453,19 @@
     c2_status_t status() const { return mInit; }
 
 private:
+    std::shared_ptr<C2Allocator::Traits> mTraits;
     c2_status_t mInit;
     sp<IAllocator> mAllocator;
     sp<IMapper> mMapper;
 };
 
-C2AllocatorGralloc::Impl::Impl() : mInit(C2_OK) {
-    // TODO: share a global service
+C2AllocatorGralloc::Impl::Impl(id_t id) : mInit(C2_OK) {
+    // TODO: get this from allocator
+    C2MemoryUsage minUsage = { 0, 0 }, maxUsage = { ~(uint64_t)0, ~(uint64_t)0 };
+    Traits traits = { "android.allocator.gralloc", id, C2Allocator::GRAPHIC, minUsage, maxUsage };
+    mTraits = std::make_shared<C2Allocator::Traits>(traits);
+
+    // gralloc allocator is a singleton, so all objects share a global service
     mAllocator = IAllocator::getService();
     mMapper = IMapper::getService();
     if (mAllocator == nullptr || mMapper == nullptr) {
@@ -453,14 +473,6 @@
     }
 }
 
-C2Allocator::id_t C2AllocatorGralloc::Impl::getId() const {
-    return 1; /// \todo implement ID
-}
-
-C2String C2AllocatorGralloc::Impl::getName() const {
-    return "android.allocator.gralloc";
-}
-
 c2_status_t C2AllocatorGralloc::Impl::newGraphicAllocation(
         uint32_t width, uint32_t height, uint32_t format, const C2MemoryUsage &usage,
         std::shared_ptr<C2GraphicAllocation> *allocation) {
@@ -517,7 +529,8 @@
             C2HandleGralloc::WrapNativeHandle(
                     buffer.getNativeHandle(),
                     info.mapperInfo.width, info.mapperInfo.height,
-                    (uint32_t)info.mapperInfo.format, info.mapperInfo.usage, info.stride)));
+                    (uint32_t)info.mapperInfo.format, info.mapperInfo.usage, info.stride),
+            mTraits->id));
     return C2_OK;
 }
 
@@ -536,11 +549,11 @@
 
     hidl_handle hidlHandle = C2HandleGralloc::UnwrapNativeHandle(grallocHandle);
 
-    allocation->reset(new C2AllocationGralloc(info, mMapper, hidlHandle, grallocHandle));
+    allocation->reset(new C2AllocationGralloc(info, mMapper, hidlHandle, grallocHandle, mTraits->id));
     return C2_OK;
 }
 
-C2AllocatorGralloc::C2AllocatorGralloc(id_t) : mImpl(new Impl) {}
+C2AllocatorGralloc::C2AllocatorGralloc(id_t id) : mImpl(new Impl(id)) {}
 
 C2AllocatorGralloc::~C2AllocatorGralloc() { delete mImpl; }
 
@@ -552,6 +565,10 @@
     return mImpl->getName();
 }
 
+std::shared_ptr<const C2Allocator::Traits> C2AllocatorGralloc::getTraits() const {
+    return mImpl->getTraits();
+}
+
 c2_status_t C2AllocatorGralloc::newGraphicAllocation(
         uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage,
         std::shared_ptr<C2GraphicAllocation> *allocation) {
diff --git a/media/libstagefright/codec2/vndk/C2Buffer.cpp b/media/libstagefright/codec2/vndk/C2Buffer.cpp
index ff92679..511ffe0 100644
--- a/media/libstagefright/codec2/vndk/C2Buffer.cpp
+++ b/media/libstagefright/codec2/vndk/C2Buffer.cpp
@@ -19,6 +19,7 @@
 #include <utils/Log.h>
 
 #include <map>
+#include <mutex>
 
 #include <C2BufferPriv.h>
 #include <C2BlockInternal.h>
@@ -61,33 +62,33 @@
     friend class ::android::C2LinearBlock;
 };
 
-class C2DefaultGraphicView : public C2GraphicView {
+class GraphicViewBuddy : public C2GraphicView {
     using C2GraphicView::C2GraphicView;
     friend class ::android::C2ConstGraphicBlock;
     friend class ::android::C2GraphicBlock;
 };
 
-class C2AcquirableConstGraphicView : public C2Acquirable<const C2GraphicView> {
+class AcquirableConstGraphicViewBuddy : public C2Acquirable<const C2GraphicView> {
     using C2Acquirable::C2Acquirable;
     friend class ::android::C2ConstGraphicBlock;
 };
 
-class C2AcquirableGraphicView : public C2Acquirable<C2GraphicView> {
+class AcquirableGraphicViewBuddy : public C2Acquirable<C2GraphicView> {
     using C2Acquirable::C2Acquirable;
     friend class ::android::C2GraphicBlock;
 };
 
-class C2DefaultConstGraphicBlock : public C2ConstGraphicBlock {
+class ConstGraphicBlockBuddy : public C2ConstGraphicBlock {
     using C2ConstGraphicBlock::C2ConstGraphicBlock;
     friend class ::android::C2GraphicBlock;
 };
 
-class C2DefaultGraphicBlock : public C2GraphicBlock {
+class GraphicBlockBuddy : public C2GraphicBlock {
     using C2GraphicBlock::C2GraphicBlock;
     friend class ::android::C2BasicGraphicBlockPool;
 };
 
-class C2DefaultBufferData : public C2BufferData {
+class BufferDataBuddy : public C2BufferData {
     using C2BufferData::C2BufferData;
     friend class ::android::C2Buffer;
 };
@@ -241,10 +242,11 @@
 C2WriteView::C2WriteView(std::shared_ptr<Impl> impl)
 // UGLY: _C2LinearRangeAspect requires a bona-fide object for capacity to prevent spoofing, so
 // this is what we have to do.
-    : _C2EditableLinearRange(std::make_unique<C2LinearCapacity>(impl->size()).get()), mImpl(impl) { }
+// TODO: use childRange
+    : _C2EditableLinearRangeAspect(std::make_unique<C2LinearCapacity>(impl->size()).get()), mImpl(impl) { }
 
 C2WriteView::C2WriteView(c2_status_t error)
-    : _C2EditableLinearRange(nullptr), mImpl(std::make_shared<Impl>(error)) {}
+    : _C2EditableLinearRangeAspect(nullptr), mImpl(std::make_shared<Impl>(error)) {}
 
 uint8_t *C2WriteView::base() { return mImpl->data(); }
 
@@ -269,6 +271,7 @@
                 new ReadViewBuddy::Impl(*mImpl, (uint8_t *)base, offset(), len),
                 [base, len](ReadViewBuddy::Impl *i) {
                     (void)i->getAllocation()->unmap(base, len, nullptr);
+                    delete i;
         });
         return AcquirableReadViewBuddy(error, C2Fence(), ReadViewBuddy(rvi, 0, len));
     } else {
@@ -298,6 +301,7 @@
                 new WriteViewBuddy::Impl(*mImpl, (uint8_t *)base, 0, len),
                 [base, len](WriteViewBuddy::Impl *i) {
                     (void)i->getAllocation()->unmap(base, len, nullptr);
+                    delete i;
         });
         return AcquirableWriteViewBuddy(error, C2Fence(), WriteViewBuddy(rvi));
     } else {
@@ -309,14 +313,6 @@
     return ConstLinearBlockBuddy(mImpl, C2LinearRange(*this, offset_, size_), fence);
 }
 
-std::shared_ptr<C2LinearBlock> _C2BlockFactory::CreateLinearBlock(
-        const std::shared_ptr<C2LinearAllocation> &alloc,
-        const std::shared_ptr<_C2BlockPoolData> &data, size_t offset, size_t size) {
-    std::shared_ptr<C2Block1D::Impl> impl =
-        std::make_shared<C2Block1D::Impl>(alloc, data, offset, size);
-    return std::shared_ptr<C2LinearBlock>(new C2LinearBlock(impl, *impl));
-}
-
 C2BasicLinearBlockPool::C2BasicLinearBlockPool(
         const std::shared_ptr<C2Allocator> &allocator)
   : mAllocator(allocator) { }
@@ -338,215 +334,288 @@
     return C2_OK;
 }
 
+std::shared_ptr<C2LinearBlock> _C2BlockFactory::CreateLinearBlock(
+        const std::shared_ptr<C2LinearAllocation> &alloc,
+        const std::shared_ptr<_C2BlockPoolData> &data, size_t offset, size_t size) {
+    std::shared_ptr<C2Block1D::Impl> impl =
+        std::make_shared<C2Block1D::Impl>(alloc, data, offset, size);
+    return std::shared_ptr<C2LinearBlock>(new C2LinearBlock(impl, *impl));
+}
+
 /* ========================================== 2D BLOCK ========================================= */
 
-class C2Block2D::Impl {
+/**
+ * Implementation that is shared between all 2D blocks and views.
+ *
+ * For blocks' Impl's crop is always the allotted crop, even if it is a sub block.
+ *
+ * For views' Impl's crop is the mapped portion - which for now is always the
+ * allotted crop.
+ */
+class C2_HIDE _C2Block2DImpl : public _C2PlanarSectionAspect {
 public:
-    const C2Handle *handle() const {
-        return mAllocation->handle();
+    /**
+     * Impl's crop is always the or part of the allotted crop of the allocation.
+     */
+    _C2Block2DImpl(const std::shared_ptr<C2GraphicAllocation> &alloc,
+            const std::shared_ptr<_C2BlockPoolData> &poolData = nullptr,
+            const C2Rect &allottedCrop = C2Rect(~0u, ~0u))
+        : _C2PlanarSectionAspect(alloc.get(), allottedCrop),
+          mAllocation(alloc),
+          mPoolData(poolData) { }
+
+    /** returns const pool data  */
+    std::shared_ptr<const _C2BlockPoolData> poolData() const {
+        return mPoolData;
     }
 
-    Impl(const std::shared_ptr<C2GraphicAllocation> &alloc)
-        : mAllocation(alloc) {}
+    /** returns native handle */
+    const C2Handle *handle() const {
+        return mAllocation ? mAllocation->handle() : nullptr;
+    }
+
+    /** returns the allocator's ID */
+    C2Allocator::id_t getAllocatorId() const {
+        // BAD_ID can only happen if this Impl class is initialized for a view - never for a block.
+        return mAllocation ? mAllocation->getAllocatorId() : C2Allocator::BAD_ID;
+    }
+
+    std::shared_ptr<C2GraphicAllocation> getAllocation() const {
+        return mAllocation;
+    }
 
 private:
     std::shared_ptr<C2GraphicAllocation> mAllocation;
+    std::shared_ptr<_C2BlockPoolData> mPoolData;
 };
 
-C2Block2D::C2Block2D(const std::shared_ptr<C2GraphicAllocation> &alloc)
-    : _C2PlanarSection(alloc.get()), mImpl(new Impl(alloc)) {}
+class C2_HIDE _C2MappingBlock2DImpl : public _C2Block2DImpl {
+public:
+    using _C2Block2DImpl::_C2Block2DImpl;
+
+    /**
+     * This class contains the mapped data pointer, and the potential error.
+     */
+    struct Mapped {
+    private:
+        friend class _C2MappingBlock2DImpl;
+
+        Mapped(const _C2Block2DImpl *impl, bool writable, C2Fence *fence __unused)
+            : mImpl(impl), mWritable(writable) {
+            memset(mData, 0, sizeof(mData));
+            const C2Rect crop = mImpl->crop();
+            // gralloc requires mapping the whole region of interest as we cannot
+            // map multiple regions
+            mError = mImpl->getAllocation()->map(
+                    crop,
+                    { C2MemoryUsage::CPU_READ, writable ? C2MemoryUsage::CPU_WRITE : 0 },
+                    nullptr,
+                    &mLayout,
+                    mData);
+            if (mError != C2_OK) {
+                memset(&mLayout, 0, sizeof(mLayout));
+                memset(mData, 0, sizeof(mData));
+            } else {
+                // TODO: validate plane layout and
+                // adjust data pointers to the crop region's top left corner.
+                // fail if it is not on a subsampling boundary
+                for (size_t planeIx = 0; planeIx < mLayout.numPlanes; ++planeIx) {
+                    const uint32_t colSampling = mLayout.planes[planeIx].colSampling;
+                    const uint32_t rowSampling = mLayout.planes[planeIx].rowSampling;
+                    if (crop.left % colSampling || crop.right() % colSampling
+                            || crop.top % rowSampling || crop.bottom() % rowSampling) {
+                        // cannot calculate data pointer
+                        mImpl->getAllocation()->unmap(nullptr);
+                        memset(&mLayout, 0, sizeof(mLayout));
+                        memset(mData, 0, sizeof(mData));
+                        mError = C2_BAD_VALUE;
+                        return;
+                    }
+                    mData[planeIx] += (ssize_t)crop.left * mLayout.planes[planeIx].colInc
+                            + (ssize_t)crop.top * mLayout.planes[planeIx].rowInc;
+                }
+            }
+        }
+
+        explicit Mapped(c2_status_t error)
+            : mImpl(nullptr), mWritable(false), mError(error) {
+            // CHECK(error != C2_OK);
+            memset(&mLayout, 0, sizeof(mLayout));
+            memset(mData, 0, sizeof(mData));
+        }
+
+    public:
+        ~Mapped() {
+            if (mData[0] != nullptr) {
+                mImpl->getAllocation()->unmap(nullptr);
+            }
+        }
+
+        /** returns mapping status */
+        c2_status_t error() const { return mError; }
+
+        /** returns data pointer */
+        uint8_t *const *data() const { return mData; }
+
+        /** returns the plane layout */
+        C2PlanarLayout layout() const { return mLayout; }
+
+        /** returns whether the mapping is writable */
+        bool writable() const { return mWritable; }
+
+    private:
+        const _C2Block2DImpl *mImpl;
+        bool mWritable;
+        c2_status_t mError;
+        uint8_t *mData[C2PlanarLayout::MAX_NUM_PLANES];
+        C2PlanarLayout mLayout;
+    };
+
+    /**
+     * Maps the allotted region.
+     *
+     * If already mapped and it is currently in use, returns the existing mapping.
+     * If fence is provided, an acquire fence is stored there.
+     */
+    std::shared_ptr<Mapped> map(bool writable, C2Fence *fence) {
+        std::lock_guard<std::mutex> lock(mMappedLock);
+        std::shared_ptr<Mapped> existing = mMapped.lock();
+        if (!existing) {
+            existing = std::shared_ptr<Mapped>(new Mapped(this, writable, fence));
+            mMapped = existing;
+        } else {
+            // if we mapped the region read-only, we cannot remap it read-write
+            if (writable && !existing->writable()) {
+                existing = std::shared_ptr<Mapped>(new Mapped(C2_CANNOT_DO));
+            }
+            if (fence != nullptr) {
+                *fence = C2Fence();
+            }
+        }
+        return existing;
+    }
+
+private:
+    std::weak_ptr<Mapped> mMapped;
+    std::mutex mMappedLock;
+};
+
+class C2_HIDE _C2MappedBlock2DImpl : public _C2Block2DImpl {
+public:
+    _C2MappedBlock2DImpl(const _C2Block2DImpl &impl,
+                         std::shared_ptr<_C2MappingBlock2DImpl::Mapped> mapping)
+        : _C2Block2DImpl(impl), mMapping(mapping) {
+    }
+
+    std::shared_ptr<_C2MappingBlock2DImpl::Mapped> mapping() const { return mMapping; }
+
+private:
+    std::shared_ptr<_C2MappingBlock2DImpl::Mapped> mMapping;
+};
+
+/**
+ * Block implementation.
+ */
+class C2Block2D::Impl : public _C2MappingBlock2DImpl {
+    using _C2MappingBlock2DImpl::_C2MappingBlock2DImpl;
+};
 
 const C2Handle *C2Block2D::handle() const {
     return mImpl->handle();
 }
 
-class C2GraphicView::Impl {
-public:
-    Impl(uint8_t *const *data, const C2PlanarLayout &layout)
-        : mData(data), mLayout(layout), mError(C2_OK) {}
-    explicit Impl(c2_status_t error) : mData(nullptr), mError(error) {}
+C2Allocator::id_t C2Block2D::getAllocatorId() const {
+    return mImpl->getAllocatorId();
+}
 
-    uint8_t *const *data() const { return mData; }
-    const C2PlanarLayout &layout() const { return mLayout; }
-    c2_status_t error() const { return mError; }
+C2Block2D::C2Block2D(std::shared_ptr<Impl> impl, const _C2PlanarSectionAspect &section)
+    // always clamp subsection to parent (impl) crop for safety
+    : _C2PlanarSectionAspect(impl.get(), section.crop()), mImpl(impl) {
+}
 
-private:
-    uint8_t *const *mData;
-    C2PlanarLayout mLayout;
-    c2_status_t mError;
+/**
+ * Graphic view implementation.
+ *
+ * range of Impl is the mapped range of the underlying allocation. range of View is the current
+ * crop.
+ */
+class C2GraphicView::Impl : public _C2MappedBlock2DImpl {
+    using _C2MappedBlock2DImpl::_C2MappedBlock2DImpl;
 };
 
-C2GraphicView::C2GraphicView(
-        const _C2PlanarCapacityAspect *parent,
-        uint8_t *const *data,
-        const C2PlanarLayout& layout)
-    : _C2PlanarSection(parent), mImpl(new Impl(data, layout)) {}
-
-C2GraphicView::C2GraphicView(c2_status_t error)
-    : _C2PlanarSection(nullptr), mImpl(new Impl(error)) {}
+C2GraphicView::C2GraphicView(std::shared_ptr<Impl> impl, const _C2PlanarSectionAspect &section)
+    : _C2EditablePlanarSectionAspect(impl.get(), section.crop()), mImpl(impl) {
+}
 
 const uint8_t *const *C2GraphicView::data() const {
-    return mImpl->data();
+    return mImpl->mapping()->data();
 }
 
 uint8_t *const *C2GraphicView::data() {
-    return mImpl->data();
+    return mImpl->mapping()->data();
 }
 
 const C2PlanarLayout C2GraphicView::layout() const {
-    return mImpl->layout();
+    return mImpl->mapping()->layout();
 }
 
 const C2GraphicView C2GraphicView::subView(const C2Rect &rect) const {
-    C2GraphicView view(this, mImpl->data(), mImpl->layout());
-    view.setCrop_be(rect);
-    return view;
+    return C2GraphicView(mImpl, C2PlanarSection(*mImpl, rect));
 }
 
 C2GraphicView C2GraphicView::subView(const C2Rect &rect) {
-    C2GraphicView view(this, mImpl->data(), mImpl->layout());
-    view.setCrop_be(rect);
-    return view;
+    return C2GraphicView(mImpl, C2PlanarSection(*mImpl, rect));
 }
 
 c2_status_t C2GraphicView::error() const {
-    return mImpl->error();
+    return mImpl->mapping()->error();
 }
 
-class C2ConstGraphicBlock::Impl {
-public:
-    explicit Impl(const std::shared_ptr<C2GraphicAllocation> &alloc)
-        : mAllocation(alloc), mData{ nullptr } {}
-
-    ~Impl() {
-        if (mData[0] != nullptr) {
-            // TODO: fence
-            mAllocation->unmap(nullptr);
-        }
-    }
-
-    c2_status_t map(C2Rect rect) {
-        if (mData[0] != nullptr) {
-            // Already mapped.
-            return C2_OK;
-        }
-        c2_status_t err = mAllocation->map(
-                rect,
-                { C2MemoryUsage::CPU_READ, 0 },
-                nullptr,
-                &mLayout,
-                mData);
-        if (err != C2_OK) {
-            memset(mData, 0, sizeof(mData));
-        }
-        return err;
-    }
-
-    C2ConstGraphicBlock subBlock(const C2Rect &rect, C2Fence fence) const {
-        C2ConstGraphicBlock block(mAllocation, fence);
-        block.setCrop_be(rect);
-        return block;
-    }
-
-    uint8_t *const *data() const {
-        return mData[0] == nullptr ? nullptr : &mData[0];
-    }
-
-    const C2PlanarLayout &layout() const { return mLayout; }
-
-private:
-    std::shared_ptr<C2GraphicAllocation> mAllocation;
-    C2PlanarLayout mLayout;
-    uint8_t *mData[C2PlanarLayout::MAX_NUM_PLANES];
-};
-
+/**
+ * Const graphic block implementation.
+ */
 C2ConstGraphicBlock::C2ConstGraphicBlock(
-        const std::shared_ptr<C2GraphicAllocation> &alloc, C2Fence fence)
-    : C2Block2D(alloc), mImpl(new Impl(alloc)), mFence(fence) {}
+        std::shared_ptr<Impl> impl, const _C2PlanarSectionAspect &section, C2Fence fence)
+    : C2Block2D(impl, section), mFence(fence) { }
 
 C2Acquirable<const C2GraphicView> C2ConstGraphicBlock::map() const {
-    c2_status_t err = mImpl->map(crop());
-    if (err != C2_OK) {
-        C2DefaultGraphicView view(err);
-        return C2AcquirableConstGraphicView(err, mFence, view);
-    }
-    C2DefaultGraphicView view(this, mImpl->data(), mImpl->layout());
-    return C2AcquirableConstGraphicView(err, mFence, view);
+    C2Fence fence;
+    std::shared_ptr<_C2MappingBlock2DImpl::Mapped> mapping =
+        mImpl->map(false /* writable */, &fence);
+    std::shared_ptr<GraphicViewBuddy::Impl> gvi =
+        std::shared_ptr<GraphicViewBuddy::Impl>(new GraphicViewBuddy::Impl(*mImpl, mapping));
+    return AcquirableConstGraphicViewBuddy(
+            mapping->error(), fence, GraphicViewBuddy(gvi, C2PlanarSection(*mImpl, crop())));
 }
 
 C2ConstGraphicBlock C2ConstGraphicBlock::subBlock(const C2Rect &rect) const {
-    return mImpl->subBlock(rect, mFence);
+    return C2ConstGraphicBlock(mImpl, C2PlanarSection(*mImpl, crop().intersect(rect)), mFence);
 }
 
-class C2GraphicBlock::Impl {
-public:
-    explicit Impl(const std::shared_ptr<C2GraphicAllocation> &alloc)
-        : mAllocation(alloc), mData{ nullptr } {}
-
-    ~Impl() {
-        if (mData[0] != nullptr) {
-            // TODO: fence
-            mAllocation->unmap(nullptr);
-        }
-    }
-
-    c2_status_t map(C2Rect rect) {
-        if (mData[0] != nullptr) {
-            // Already mapped.
-            return C2_OK;
-        }
-        uint8_t *data[C2PlanarLayout::MAX_NUM_PLANES];
-        c2_status_t err = mAllocation->map(
-                rect,
-                { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE },
-                nullptr,
-                &mLayout,
-                data);
-        if (err == C2_OK) {
-            memcpy(mData, data, sizeof(mData));
-        } else {
-            memset(mData, 0, sizeof(mData));
-        }
-        return err;
-    }
-
-    C2ConstGraphicBlock share(const C2Rect &crop, C2Fence fence) const {
-        C2DefaultConstGraphicBlock block(mAllocation, fence);
-        block.setCrop_be(crop);
-        return block;
-    }
-
-    uint8_t *const *data() const {
-        return mData[0] == nullptr ? nullptr : mData;
-    }
-
-    const C2PlanarLayout &layout() const { return mLayout; }
-
-private:
-    std::shared_ptr<C2GraphicAllocation> mAllocation;
-    C2PlanarLayout mLayout;
-    uint8_t *mData[C2PlanarLayout::MAX_NUM_PLANES];
-};
-
-C2GraphicBlock::C2GraphicBlock(const std::shared_ptr<C2GraphicAllocation> &alloc)
-    : C2Block2D(alloc), mImpl(new Impl(alloc)) {}
+/**
+ * Graphic block implementation.
+ */
+C2GraphicBlock::C2GraphicBlock(
+    std::shared_ptr<Impl> impl, const _C2PlanarSectionAspect &section)
+    : C2Block2D(impl, section) { }
 
 C2Acquirable<C2GraphicView> C2GraphicBlock::map() {
-    c2_status_t err = mImpl->map(crop());
-    if (err != C2_OK) {
-        C2DefaultGraphicView view(err);
-        // TODO: fence
-        return C2AcquirableGraphicView(err, C2Fence(), view);
-    }
-    C2DefaultGraphicView view(this, mImpl->data(), mImpl->layout());
-    // TODO: fence
-    return C2AcquirableGraphicView(err, C2Fence(), view);
+    C2Fence fence;
+    std::shared_ptr<_C2MappingBlock2DImpl::Mapped> mapping =
+        mImpl->map(true /* writable */, &fence);
+    std::shared_ptr<GraphicViewBuddy::Impl> gvi =
+        std::shared_ptr<GraphicViewBuddy::Impl>(new GraphicViewBuddy::Impl(*mImpl, mapping));
+    return AcquirableGraphicViewBuddy(
+            mapping->error(), fence, GraphicViewBuddy(gvi, C2PlanarSection(*mImpl, crop())));
 }
 
 C2ConstGraphicBlock C2GraphicBlock::share(const C2Rect &crop, C2Fence fence) {
-    return mImpl->share(crop, fence);
+    return ConstGraphicBlockBuddy(mImpl, C2PlanarSection(*mImpl, crop), fence);
 }
 
+/**
+ * Basic block pool implementations.
+ */
 C2BasicGraphicBlockPool::C2BasicGraphicBlockPool(
         const std::shared_ptr<C2Allocator> &allocator)
   : mAllocator(allocator) {}
@@ -565,11 +634,19 @@
         return err;
     }
 
-    block->reset(new C2DefaultGraphicBlock(alloc));
+    *block = _C2BlockFactory::CreateGraphicBlock(alloc);
 
     return C2_OK;
 }
 
+std::shared_ptr<C2GraphicBlock> _C2BlockFactory::CreateGraphicBlock(
+        const std::shared_ptr<C2GraphicAllocation> &alloc,
+        const std::shared_ptr<_C2BlockPoolData> &data, const C2Rect &allottedCrop) {
+    std::shared_ptr<C2Block2D::Impl> impl =
+        std::make_shared<C2Block2D::Impl>(alloc, data, allottedCrop);
+    return std::shared_ptr<C2GraphicBlock>(new C2GraphicBlock(impl, *impl));
+}
+
 /* ========================================== BUFFER ========================================= */
 
 class C2BufferData::Impl {
@@ -679,7 +756,7 @@
 
 private:
     C2Buffer * const mThis;
-    C2DefaultBufferData mData;
+    BufferDataBuddy mData;
     std::map<C2Param::Type, std::shared_ptr<C2Info>> mInfos;
     std::list<std::pair<OnDestroyNotify, void *>> mNotify;
 };
@@ -716,4 +793,14 @@
     return mImpl->removeInfo(index);
 }
 
+// static
+std::shared_ptr<C2Buffer> C2Buffer::CreateLinearBuffer(const C2ConstLinearBlock &block) {
+    return std::shared_ptr<C2Buffer>(new C2Buffer({ block }));
+}
+
+// static
+std::shared_ptr<C2Buffer> C2Buffer::CreateGraphicBuffer(const C2ConstGraphicBlock &block) {
+    return std::shared_ptr<C2Buffer>(new C2Buffer({ block }));
+}
+
 } // namespace android
diff --git a/media/libstagefright/codec2/vndk/include/C2AllocatorGralloc.h b/media/libstagefright/codec2/vndk/include/C2AllocatorGralloc.h
index 0e1a3ac..9dc7152 100644
--- a/media/libstagefright/codec2/vndk/include/C2AllocatorGralloc.h
+++ b/media/libstagefright/codec2/vndk/include/C2AllocatorGralloc.h
@@ -49,9 +49,7 @@
 
     virtual C2String getName() const override;
 
-    virtual std::shared_ptr<const Traits> getTraits() const override {
-        return nullptr; // \todo
-    }
+    virtual std::shared_ptr<const Traits> getTraits() const override;
 
     virtual c2_status_t newGraphicAllocation(
             uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage,
@@ -61,7 +59,7 @@
             const C2Handle *handle,
             std::shared_ptr<C2GraphicAllocation> *allocation) override;
 
-    C2AllocatorGralloc(id_t id = 0);
+    C2AllocatorGralloc(id_t id);
 
     c2_status_t status() const;
 
diff --git a/media/libstagefright/codec2/vndk/internal/C2BlockInternal.h b/media/libstagefright/codec2/vndk/internal/C2BlockInternal.h
index c84d1fb..9c68369 100644
--- a/media/libstagefright/codec2/vndk/internal/C2BlockInternal.h
+++ b/media/libstagefright/codec2/vndk/internal/C2BlockInternal.h
@@ -24,15 +24,44 @@
 struct _C2BlockPoolData;
 
 /**
- * Interface for creating blocks by block pool/buffer passing implementations.
+ * Internal only interface for creating blocks by block pool/buffer passing implementations.
+ *
+ * \todo this must be hidden
  */
-struct C2_HIDE _C2BlockFactory {
+struct _C2BlockFactory {
+    /**
+     * Create a linear block from an allocation for an allotted range.
+     *
+     * @param alloc parent allocation
+     * @param data  blockpool data
+     * @param offset allotted range offset
+     * @param size  allotted size
+     *
+     * @return shared pointer to the linear block. nullptr if there was not enough memory to
+     *         create this block.
+     */
     static
     std::shared_ptr<C2LinearBlock> C2_HIDE CreateLinearBlock(
             const std::shared_ptr<C2LinearAllocation> &alloc,
             const std::shared_ptr<_C2BlockPoolData> &data = nullptr,
             size_t offset = 0,
             size_t size = ~(size_t)0);
+
+    /**
+     * Create a graphic block from an allocation for an allotted section.
+     *
+     * @param alloc parent allocation
+     * @param data  blockpool data
+     * @param crop  allotted crop region
+     *
+     * @return shared pointer to the graphic block. nullptr if there was not enough memory to
+     *         create this block.
+     */
+    static
+    std::shared_ptr<C2GraphicBlock> CreateGraphicBlock(
+            const std::shared_ptr<C2GraphicAllocation> &alloc,
+            const std::shared_ptr<_C2BlockPoolData> &data = nullptr,
+            const C2Rect &allottedCrop = C2Rect(~0u, ~0u));
 };
 
 }
diff --git a/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp b/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp
index 911f0f8..9ea3589 100644
--- a/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp
+++ b/media/libstagefright/codecs/avcenc/C2SoftAvcEnc.cpp
@@ -15,7 +15,7 @@
  */
 
 #define LOG_NDEBUG 0
-#define LOG_TAG "C2SoftAvcEncEnc"
+#define LOG_TAG "C2SoftAvcEnc"
 #include <utils/Log.h>
 #include <utils/misc.h>
 
diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.h b/media/libstagefright/codecs/on2/dec/SoftVPX.h
index a01a4f3..9ac2791 100644
--- a/media/libstagefright/codecs/on2/dec/SoftVPX.h
+++ b/media/libstagefright/codecs/on2/dec/SoftVPX.h
@@ -44,7 +44,7 @@
 
 private:
     enum {
-        kNumBuffers = 4
+        kNumBuffers = 16
     };
 
     enum {
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index c9ebbdc..838bc5f 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -60,7 +60,8 @@
     mConverter = NULL;
 }
 
-void SoftwareRenderer::resetFormatIfChanged(const sp<AMessage> &format) {
+void SoftwareRenderer::resetFormatIfChanged(
+        const sp<AMessage> &format, size_t numOutputBuffers) {
     CHECK(format != NULL);
 
     int32_t colorFormatNew;
@@ -168,7 +169,7 @@
     CHECK_EQ(0,
             native_window_set_usage(
             mNativeWindow.get(),
-            GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_OFTEN
+            GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_RARELY
             | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP));
 
     CHECK_EQ(0,
@@ -184,6 +185,11 @@
     CHECK_EQ(0, native_window_set_buffers_format(
                 mNativeWindow.get(),
                 halFormat));
+    if (OK != native_window_set_buffer_count(
+                mNativeWindow.get(), numOutputBuffers + 4)) {
+        ALOGE("Failed to set native window buffer count to (%zu + 4)",
+                numOutputBuffers);
+    }
 
     // NOTE: native window uses extended right-bottom coordinate
     android_native_rect_t crop;
@@ -219,8 +225,8 @@
 
 std::list<FrameRenderTracker::Info> SoftwareRenderer::render(
         const void *data, size_t , int64_t mediaTimeUs, nsecs_t renderTimeNs,
-        void* /*platformPrivate*/, const sp<AMessage>& format) {
-    resetFormatIfChanged(format);
+        size_t numOutputBuffers, const sp<AMessage>& format) {
+    resetFormatIfChanged(format, numOutputBuffers);
     FrameRenderTracker::Info *info = NULL;
 
     ANativeWindowBuffer *buf;
@@ -243,8 +249,9 @@
     Rect bounds(mCropWidth, mCropHeight);
 
     void *dst;
-    CHECK_EQ(0, mapper.lock(
-                buf->handle, GRALLOC_USAGE_SW_WRITE_OFTEN, bounds, &dst));
+    CHECK_EQ(0, mapper.lock(buf->handle,
+            GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_RARELY,
+            bounds, &dst));
 
     // TODO move the other conversions also into ColorConverter, and
     // fix cropping issues (when mCropLeft/Top != 0 or mWidth != mCropWidth)
diff --git a/media/libstagefright/foundation/avc_utils.cpp b/media/libstagefright/foundation/avc_utils.cpp
index b58474c..8414af3 100644
--- a/media/libstagefright/foundation/avc_utils.cpp
+++ b/media/libstagefright/foundation/avc_utils.cpp
@@ -351,7 +351,7 @@
     const uint8_t *nalStart;
     size_t nalSize;
     while (getNextNALUnit(&data, &size, &nalStart, &nalSize, true) == OK) {
-        if ((nalStart[0] & 0x1f) == nalType) {
+        if (nalSize > 0 && (nalStart[0] & 0x1f) == nalType) {
             sp<ABuffer> buffer = new ABuffer(nalSize);
             memcpy(buffer->data(), nalStart, nalSize);
             return buffer;
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index bc3e57c..8f349fc 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -659,10 +659,13 @@
         }
 
         if (!line.startsWith("#")) {
+            if (itemMeta == NULL) {
+                ALOGV("itemMeta == NULL");
+                return ERROR_MALFORMED;
+            }
             if (!mIsVariantPlaylist) {
                 int64_t durationUs;
-                if (itemMeta == NULL
-                        || !itemMeta->findInt64("durationUs", &durationUs)) {
+                if (!itemMeta->findInt64("durationUs", &durationUs)) {
                     return ERROR_MALFORMED;
                 }
                 itemMeta->setInt32("discontinuity-sequence",
diff --git a/media/libstagefright/include/C2OMXNode.h b/media/libstagefright/include/C2OMXNode.h
new file mode 100644
index 0000000..3c007c4
--- /dev/null
+++ b/media/libstagefright/include/C2OMXNode.h
@@ -0,0 +1,89 @@
+/*
+ * 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_OMX_NODE_H_
+#define C2_OMX_NODE_H_
+
+#include <atomic>
+
+#include <android/IOMXBufferSource.h>
+#include <media/IOMX.h>
+#include <media/OMXBuffer.h>
+
+namespace android {
+
+/**
+ * IOmxNode implementation around codec 2.0 component, only to be used in
+ * IGraphicBufferSource::configure. Only subset of IOmxNode API is implemented
+ * and others are left as stub. As a result, one cannot expect this IOmxNode
+ * to work in any other usage than IGraphicBufferSource.
+ */
+struct C2OMXNode : public BnOMXNode {
+    // TODO: this should take android::hardware::media::c2::V1_0::IComponent
+    explicit C2OMXNode(const std::shared_ptr<C2Component> &comp);
+    ~C2OMXNode() override = default;
+
+    // IOMXNode
+    status_t freeNode() override;
+    status_t sendCommand(OMX_COMMANDTYPE cmd, OMX_S32 param) override;
+    status_t getParameter(
+            OMX_INDEXTYPE index, void *params, size_t size) override;
+    status_t setParameter(
+            OMX_INDEXTYPE index, const void *params, size_t size) override;
+    status_t getConfig(
+            OMX_INDEXTYPE index, void *params, size_t size) override;
+    status_t setConfig(
+            OMX_INDEXTYPE index, const void *params, size_t size) override;
+    status_t setPortMode(OMX_U32 port_index, IOMX::PortMode mode) override;
+    status_t prepareForAdaptivePlayback(
+            OMX_U32 portIndex, OMX_BOOL enable,
+            OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight) override;
+    status_t configureVideoTunnelMode(
+            OMX_U32 portIndex, OMX_BOOL tunneled,
+            OMX_U32 audioHwSync, native_handle_t **sidebandHandle) override;
+    status_t getGraphicBufferUsage(
+            OMX_U32 port_index, OMX_U32* usage) override;
+    status_t setInputSurface(
+            const sp<IOMXBufferSource> &bufferSource) override;
+    status_t allocateSecureBuffer(
+            OMX_U32 port_index, size_t size, buffer_id *buffer,
+            void **buffer_data, sp<NativeHandle> *native_handle) override;
+    status_t useBuffer(
+            OMX_U32 port_index, const OMXBuffer &omxBuf, buffer_id *buffer) override;
+    status_t freeBuffer(
+            OMX_U32 port_index, buffer_id buffer) override;
+    status_t fillBuffer(
+            buffer_id buffer, const OMXBuffer &omxBuf, int fenceFd) override;
+    status_t emptyBuffer(
+            buffer_id buffer, const OMXBuffer &omxBuf,
+            OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) override;
+    status_t getExtensionIndex(
+            const char *parameter_name,
+            OMX_INDEXTYPE *index) override;
+    status_t dispatchMessage(const omx_message &msg) override;
+
+    sp<IOMXBufferSource> getSource();
+
+private:
+    std::weak_ptr<C2Component> mComp;
+    sp<IOMXBufferSource> mBufferSource;
+    std::shared_ptr<C2Allocator> mAllocator;
+    std::atomic_uint64_t mFrameIndex;
+};
+
+}  // namespace android
+
+#endif  // C2_OMX_NODE_H_
diff --git a/media/libstagefright/include/CCodecBufferChannel.h b/media/libstagefright/include/CCodecBufferChannel.h
index 57a1374..eb3255f 100644
--- a/media/libstagefright/include/CCodecBufferChannel.h
+++ b/media/libstagefright/include/CCodecBufferChannel.h
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-#ifndef A_BUFFER_CHANNEL_H_
+#ifndef CCODEC_BUFFER_CHANNEL_H_
 
-#define A_BUFFER_CHANNEL_H_
+#define CCODEC_BUFFER_CHANNEL_H_
 
 #include <map>
 #include <memory>
@@ -26,13 +26,19 @@
 #include <C2Buffer.h>
 #include <C2Component.h>
 
+#include <media/stagefright/bqhelper/GraphicBufferSource.h>
+#include <media/stagefright/codec2/1.0/InputSurface.h>
 #include <media/stagefright/foundation/Mutexed.h>
-#include <media/stagefright/gbs/GraphicBufferSource.h>
 #include <media/stagefright/CodecBase.h>
 #include <media/ICrypto.h>
 
+#include "InputSurfaceWrapper.h"
+
 namespace android {
 
+using ::android::hardware::media::c2::V1_0::implementation::InputSurface;
+using ::android::hardware::media::c2::V1_0::implementation::InputSurfaceConnection;
+
 /**
  * BufferChannelBase implementation for CCodec.
  */
@@ -76,7 +82,7 @@
      * Set GraphicBufferSource object from which the component extracts input
      * buffers.
      */
-    status_t setGraphicBufferSource(const sp<GraphicBufferSource> &source);
+    status_t setInputSurface(const std::shared_ptr<InputSurfaceWrapper> &surface);
 
     /**
      * Start queueing buffers to the component. This object should never queue
@@ -103,7 +109,6 @@
     class Buffers;
     class InputBuffers;
     class OutputBuffers;
-    class InputBufferClient;
 
 private:
     class QueueGuard;
@@ -156,8 +161,6 @@
         bool mRunning;
     };
 
-    class C2ComponentWrapper;
-
     void feedInputBufferIfAvailable();
 
     QueueSync mSync;
@@ -166,7 +169,6 @@
     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;
@@ -180,6 +182,8 @@
     sp<MemoryDealer> makeMemoryDealer(size_t heapSize);
     Mutexed<sp<Surface>> mSurface;
 
+    std::shared_ptr<InputSurfaceWrapper> mInputSurface;
+
     inline bool hasCryptoOrDescrambler() {
         return mCrypto != NULL || mDescrambler != NULL;
     }
@@ -187,4 +191,4 @@
 
 }  // namespace android
 
-#endif  // A_BUFFER_CHANNEL_H_
+#endif  // CCODEC_BUFFER_CHANNEL_H_
diff --git a/media/libstagefright/include/InputSurfaceWrapper.h b/media/libstagefright/include/InputSurfaceWrapper.h
new file mode 100644
index 0000000..a4d8f29
--- /dev/null
+++ b/media/libstagefright/include/InputSurfaceWrapper.h
@@ -0,0 +1,49 @@
+/*
+ * 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 INPUT_SURFACE_WRAPPER_H_
+
+#define INPUT_SURFACE_WRAPPER_H_
+
+namespace android {
+
+/**
+ * Wrapper interface around InputSurface.
+ */
+class InputSurfaceWrapper {
+public:
+    virtual ~InputSurfaceWrapper() = default;
+
+    /**
+     * Connect the surface with |comp| and start pushing buffers. A surface can
+     * connect to at most one component at a time.
+     *
+     * \return OK               successfully connected to |comp|
+     * \return ALREADY_EXISTS   already connected to another component.
+     */
+    virtual status_t connect(const std::shared_ptr<C2Component> &comp) = 0;
+
+    /**
+     * Disconnect the surface from the component if any.
+     */
+    virtual void disconnect() = 0;
+
+    // TODO: intf()
+};
+
+}  // namespace android
+
+#endif  // INPUT_SURFACE_WRAPPER_H_
diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h
index e04b59f..c286516 100644
--- a/media/libstagefright/include/SoftwareRenderer.h
+++ b/media/libstagefright/include/SoftwareRenderer.h
@@ -39,7 +39,7 @@
 
     std::list<FrameRenderTracker::Info> render(
             const void *data, size_t size, int64_t mediaTimeUs, nsecs_t renderTimeNs,
-            void *platformPrivate, const sp<AMessage> &format);
+            size_t numOutputBuffers, const sp<AMessage> &format);
     void clearTracker();
 
 private:
@@ -59,10 +59,11 @@
     HDRStaticInfo mHDRStaticInfo;
     FrameRenderTracker mRenderTracker;
 
+    void resetFormatIfChanged(
+            const sp<AMessage> &format, size_t numOutputBuffers);
+
     SoftwareRenderer(const SoftwareRenderer &);
     SoftwareRenderer &operator=(const SoftwareRenderer &);
-
-    void resetFormatIfChanged(const sp<AMessage> &format);
 };
 
 }  // namespace android
diff --git a/media/libstagefright/include/media/stagefright/CCodec.h b/media/libstagefright/include/media/stagefright/CCodec.h
index fa19c6e..24ee0a3 100644
--- a/media/libstagefright/include/media/stagefright/CCodec.h
+++ b/media/libstagefright/include/media/stagefright/CCodec.h
@@ -24,7 +24,6 @@
 #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>
@@ -36,6 +35,7 @@
 namespace android {
 
 class CCodecBufferChannel;
+class InputSurfaceWrapper;
 
 class CCodec : public CodecBase {
 public:
@@ -81,7 +81,7 @@
 
     void createInputSurface();
     void setInputSurface(const sp<PersistentSurface> &surface);
-    status_t setupInputSurface(const sp<GraphicBufferSource> &source);
+    status_t setupInputSurface(const std::shared_ptr<InputSurfaceWrapper> &surface);
 
     void setDeadline(const TimePoint &deadline);
 
diff --git a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
index 4d2f4f0..90c66eb 100644
--- a/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
+++ b/media/libstagefright/include/media/stagefright/MediaExtractorFactory.h
@@ -57,7 +57,7 @@
             const sp<ExtractorPlugin> &plugin, List<sp<ExtractorPlugin>> &pluginList);
 
     static MediaExtractor::CreatorFunc sniff(DataSourceBase *source,
-            String8 *mimeType, float *confidence, sp<AMessage> *meta,
+            float *confidence, void **meta, MediaExtractor::FreeMetaFunc *freeMeta,
             sp<ExtractorPlugin> &plugin);
 
     static void UpdateExtractors(const char *newUpdateApkPath);
diff --git a/media/libstagefright/include/media/stagefright/NdkUtils.h b/media/libstagefright/include/media/stagefright/NdkUtils.h
new file mode 100644
index 0000000..a68884a
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/NdkUtils.h
@@ -0,0 +1,31 @@
+/*
+ * 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 NDK_UTILS_H_
+
+#define NDK_UTILS_H_
+
+#include <media/stagefright/MetaData.h>
+#include <media/NdkWrapper.h>
+
+namespace android {
+
+sp<MetaData> convertMediaFormatWrapperToMetaData(
+        const sp<AMediaFormatWrapper> &fmt);
+
+}  // namespace android
+
+#endif  // NDK_UTILS_H_
diff --git a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
deleted file mode 100644
index c4499dc..0000000
--- a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
- * Copyright 2016, 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_TAG "WGraphicBufferProducer-impl"
-
-#include <android-base/logging.h>
-
-#include <media/stagefright/omx/1.0/WGraphicBufferProducer.h>
-#include <media/stagefright/omx/1.0/WProducerListener.h>
-#include <media/stagefright/omx/1.0/Conversion.h>
-#include <system/window.h>
-
-namespace android {
-namespace hardware {
-namespace media {
-namespace omx {
-namespace V1_0 {
-namespace implementation {
-
-// TWGraphicBufferProducer
-TWGraphicBufferProducer::TWGraphicBufferProducer(
-        sp<BGraphicBufferProducer> const& base):
-    mBase(base) {
-}
-
-Return<void> TWGraphicBufferProducer::requestBuffer(
-        int32_t slot, requestBuffer_cb _hidl_cb) {
-    sp<GraphicBuffer> buf;
-    status_t status = mBase->requestBuffer(slot, &buf);
-    AnwBuffer anwBuffer;
-    if (buf != nullptr) {
-        wrapAs(&anwBuffer, *buf);
-    }
-    _hidl_cb(static_cast<int32_t>(status), anwBuffer);
-    return Void();
-}
-
-Return<int32_t> TWGraphicBufferProducer::setMaxDequeuedBufferCount(
-        int32_t maxDequeuedBuffers) {
-    return static_cast<int32_t>(mBase->setMaxDequeuedBufferCount(
-            static_cast<int>(maxDequeuedBuffers)));
-}
-
-Return<int32_t> TWGraphicBufferProducer::setAsyncMode(bool async) {
-    return static_cast<int32_t>(mBase->setAsyncMode(async));
-}
-
-Return<void> TWGraphicBufferProducer::dequeueBuffer(
-        uint32_t width, uint32_t height,
-        PixelFormat format, uint32_t usage,
-        bool getFrameTimestamps, dequeueBuffer_cb _hidl_cb) {
-    int slot;
-    sp<Fence> fence;
-    ::android::FrameEventHistoryDelta outTimestamps;
-    status_t status = mBase->dequeueBuffer(
-        &slot, &fence, width, height,
-        static_cast<::android::PixelFormat>(format), usage, nullptr,
-        getFrameTimestamps ? &outTimestamps : nullptr);
-    hidl_handle tFence;
-    FrameEventHistoryDelta tOutTimestamps;
-
-    native_handle_t* nh = nullptr;
-    if ((fence == nullptr) || !wrapAs(&tFence, &nh, *fence)) {
-        LOG(ERROR) << "TWGraphicBufferProducer::dequeueBuffer - "
-                "Invalid output fence";
-        _hidl_cb(static_cast<int32_t>(status),
-                 static_cast<int32_t>(slot),
-                 tFence,
-                 tOutTimestamps);
-        return Void();
-    }
-    std::vector<std::vector<native_handle_t*> > nhAA;
-    if (getFrameTimestamps && !wrapAs(&tOutTimestamps, &nhAA, outTimestamps)) {
-        LOG(ERROR) << "TWGraphicBufferProducer::dequeueBuffer - "
-                "Invalid output timestamps";
-        _hidl_cb(static_cast<int32_t>(status),
-                 static_cast<int32_t>(slot),
-                 tFence,
-                 tOutTimestamps);
-        native_handle_delete(nh);
-        return Void();
-    }
-
-    _hidl_cb(static_cast<int32_t>(status),
-            static_cast<int32_t>(slot),
-            tFence,
-            tOutTimestamps);
-    native_handle_delete(nh);
-    if (getFrameTimestamps) {
-        for (auto& nhA : nhAA) {
-            for (auto& handle : nhA) {
-                native_handle_delete(handle);
-            }
-        }
-    }
-    return Void();
-}
-
-Return<int32_t> TWGraphicBufferProducer::detachBuffer(int32_t slot) {
-    return static_cast<int32_t>(mBase->detachBuffer(slot));
-}
-
-Return<void> TWGraphicBufferProducer::detachNextBuffer(
-        detachNextBuffer_cb _hidl_cb) {
-    sp<GraphicBuffer> outBuffer;
-    sp<Fence> outFence;
-    status_t status = mBase->detachNextBuffer(&outBuffer, &outFence);
-    AnwBuffer tBuffer;
-    hidl_handle tFence;
-
-    if (outBuffer == nullptr) {
-        LOG(ERROR) << "TWGraphicBufferProducer::detachNextBuffer - "
-                "Invalid output buffer";
-        _hidl_cb(static_cast<int32_t>(status), tBuffer, tFence);
-        return Void();
-    }
-    wrapAs(&tBuffer, *outBuffer);
-    native_handle_t* nh = nullptr;
-    if ((outFence != nullptr) && !wrapAs(&tFence, &nh, *outFence)) {
-        LOG(ERROR) << "TWGraphicBufferProducer::detachNextBuffer - "
-                "Invalid output fence";
-        _hidl_cb(static_cast<int32_t>(status), tBuffer, tFence);
-        return Void();
-    }
-
-    _hidl_cb(static_cast<int32_t>(status), tBuffer, tFence);
-    native_handle_delete(nh);
-    return Void();
-}
-
-Return<void> TWGraphicBufferProducer::attachBuffer(
-        const AnwBuffer& buffer,
-        attachBuffer_cb _hidl_cb) {
-    int outSlot;
-    sp<GraphicBuffer> lBuffer = new GraphicBuffer();
-    if (!convertTo(lBuffer.get(), buffer)) {
-        LOG(ERROR) << "TWGraphicBufferProducer::attachBuffer - "
-                "Invalid input native window buffer";
-        _hidl_cb(static_cast<int32_t>(BAD_VALUE), -1);
-        return Void();
-    }
-    status_t status = mBase->attachBuffer(&outSlot, lBuffer);
-
-    _hidl_cb(static_cast<int32_t>(status), static_cast<int32_t>(outSlot));
-    return Void();
-}
-
-Return<void> TWGraphicBufferProducer::queueBuffer(
-        int32_t slot, const QueueBufferInput& input,
-        queueBuffer_cb _hidl_cb) {
-    QueueBufferOutput tOutput;
-    BGraphicBufferProducer::QueueBufferInput lInput(
-            0, false, HAL_DATASPACE_UNKNOWN,
-            ::android::Rect(0, 0, 1, 1),
-            NATIVE_WINDOW_SCALING_MODE_FREEZE,
-            0, ::android::Fence::NO_FENCE);
-    if (!convertTo(&lInput, input)) {
-        LOG(ERROR) << "TWGraphicBufferProducer::queueBuffer - "
-                "Invalid input";
-        _hidl_cb(static_cast<int32_t>(BAD_VALUE), tOutput);
-        return Void();
-    }
-    BGraphicBufferProducer::QueueBufferOutput lOutput;
-    status_t status = mBase->queueBuffer(
-            static_cast<int>(slot), lInput, &lOutput);
-
-    std::vector<std::vector<native_handle_t*> > nhAA;
-    if (!wrapAs(&tOutput, &nhAA, lOutput)) {
-        LOG(ERROR) << "TWGraphicBufferProducer::queueBuffer - "
-                "Invalid output";
-        _hidl_cb(static_cast<int32_t>(BAD_VALUE), tOutput);
-        return Void();
-    }
-
-    _hidl_cb(static_cast<int32_t>(status), tOutput);
-    for (auto& nhA : nhAA) {
-        for (auto& nh : nhA) {
-            native_handle_delete(nh);
-        }
-    }
-    return Void();
-}
-
-Return<int32_t> TWGraphicBufferProducer::cancelBuffer(
-        int32_t slot, const hidl_handle& fence) {
-    sp<Fence> lFence = new Fence();
-    if (!convertTo(lFence.get(), fence)) {
-        LOG(ERROR) << "TWGraphicBufferProducer::cancelBuffer - "
-                "Invalid input fence";
-        return static_cast<int32_t>(BAD_VALUE);
-    }
-    return static_cast<int32_t>(mBase->cancelBuffer(static_cast<int>(slot), lFence));
-}
-
-Return<void> TWGraphicBufferProducer::query(int32_t what, query_cb _hidl_cb) {
-    int lValue;
-    int lReturn = mBase->query(static_cast<int>(what), &lValue);
-    _hidl_cb(static_cast<int32_t>(lReturn), static_cast<int32_t>(lValue));
-    return Void();
-}
-
-Return<void> TWGraphicBufferProducer::connect(
-        const sp<HProducerListener>& listener,
-        int32_t api, bool producerControlledByApp, connect_cb _hidl_cb) {
-    sp<BProducerListener> lListener = listener == nullptr ?
-            nullptr : new LWProducerListener(listener);
-    BGraphicBufferProducer::QueueBufferOutput lOutput;
-    status_t status = mBase->connect(lListener,
-            static_cast<int>(api),
-            producerControlledByApp,
-            &lOutput);
-
-    QueueBufferOutput tOutput;
-    std::vector<std::vector<native_handle_t*> > nhAA;
-    if (!wrapAs(&tOutput, &nhAA, lOutput)) {
-        LOG(ERROR) << "TWGraphicBufferProducer::connect - "
-                "Invalid output";
-        _hidl_cb(static_cast<int32_t>(status), tOutput);
-        return Void();
-    }
-
-    _hidl_cb(static_cast<int32_t>(status), tOutput);
-    for (auto& nhA : nhAA) {
-        for (auto& nh : nhA) {
-            native_handle_delete(nh);
-        }
-    }
-    return Void();
-}
-
-Return<int32_t> TWGraphicBufferProducer::disconnect(
-        int32_t api, DisconnectMode mode) {
-    return static_cast<int32_t>(mBase->disconnect(
-            static_cast<int>(api),
-            toGuiDisconnectMode(mode)));
-}
-
-Return<int32_t> TWGraphicBufferProducer::setSidebandStream(const hidl_handle& stream) {
-    return static_cast<int32_t>(mBase->setSidebandStream(NativeHandle::create(
-            stream ? native_handle_clone(stream) : NULL, true)));
-}
-
-Return<void> TWGraphicBufferProducer::allocateBuffers(
-        uint32_t width, uint32_t height, PixelFormat format, uint32_t usage) {
-    mBase->allocateBuffers(
-            width, height,
-            static_cast<::android::PixelFormat>(format),
-            usage);
-    return Void();
-}
-
-Return<int32_t> TWGraphicBufferProducer::allowAllocation(bool allow) {
-    return static_cast<int32_t>(mBase->allowAllocation(allow));
-}
-
-Return<int32_t> TWGraphicBufferProducer::setGenerationNumber(uint32_t generationNumber) {
-    return static_cast<int32_t>(mBase->setGenerationNumber(generationNumber));
-}
-
-Return<void> TWGraphicBufferProducer::getConsumerName(getConsumerName_cb _hidl_cb) {
-    _hidl_cb(mBase->getConsumerName().string());
-    return Void();
-}
-
-Return<int32_t> TWGraphicBufferProducer::setSharedBufferMode(bool sharedBufferMode) {
-    return static_cast<int32_t>(mBase->setSharedBufferMode(sharedBufferMode));
-}
-
-Return<int32_t> TWGraphicBufferProducer::setAutoRefresh(bool autoRefresh) {
-    return static_cast<int32_t>(mBase->setAutoRefresh(autoRefresh));
-}
-
-Return<int32_t> TWGraphicBufferProducer::setDequeueTimeout(int64_t timeoutNs) {
-    return static_cast<int32_t>(mBase->setDequeueTimeout(timeoutNs));
-}
-
-Return<void> TWGraphicBufferProducer::getLastQueuedBuffer(
-        getLastQueuedBuffer_cb _hidl_cb) {
-    sp<GraphicBuffer> lOutBuffer = new GraphicBuffer();
-    sp<Fence> lOutFence = new Fence();
-    float lOutTransformMatrix[16];
-    status_t status = mBase->getLastQueuedBuffer(
-            &lOutBuffer, &lOutFence, lOutTransformMatrix);
-
-    AnwBuffer tOutBuffer;
-    if (lOutBuffer != nullptr) {
-        wrapAs(&tOutBuffer, *lOutBuffer);
-    }
-    hidl_handle tOutFence;
-    native_handle_t* nh = nullptr;
-    if ((lOutFence == nullptr) || !wrapAs(&tOutFence, &nh, *lOutFence)) {
-        LOG(ERROR) << "TWGraphicBufferProducer::getLastQueuedBuffer - "
-                "Invalid output fence";
-        _hidl_cb(static_cast<int32_t>(status),
-                tOutBuffer,
-                tOutFence,
-                hidl_array<float, 16>());
-        return Void();
-    }
-    hidl_array<float, 16> tOutTransformMatrix(lOutTransformMatrix);
-
-    _hidl_cb(static_cast<int32_t>(status), tOutBuffer, tOutFence, tOutTransformMatrix);
-    native_handle_delete(nh);
-    return Void();
-}
-
-Return<void> TWGraphicBufferProducer::getFrameTimestamps(
-        getFrameTimestamps_cb _hidl_cb) {
-    ::android::FrameEventHistoryDelta lDelta;
-    mBase->getFrameTimestamps(&lDelta);
-
-    FrameEventHistoryDelta tDelta;
-    std::vector<std::vector<native_handle_t*> > nhAA;
-    if (!wrapAs(&tDelta, &nhAA, lDelta)) {
-        LOG(ERROR) << "TWGraphicBufferProducer::getFrameTimestamps - "
-                "Invalid output frame timestamps";
-        _hidl_cb(tDelta);
-        return Void();
-    }
-
-    _hidl_cb(tDelta);
-    for (auto& nhA : nhAA) {
-        for (auto& nh : nhA) {
-            native_handle_delete(nh);
-        }
-    }
-    return Void();
-}
-
-Return<void> TWGraphicBufferProducer::getUniqueId(getUniqueId_cb _hidl_cb) {
-    uint64_t outId;
-    status_t status = mBase->getUniqueId(&outId);
-    _hidl_cb(static_cast<int32_t>(status), outId);
-    return Void();
-}
-
-}  // namespace implementation
-}  // namespace V1_0
-}  // namespace omx
-}  // namespace media
-}  // namespace hardware
-}  // namespace android
diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp
index 306a8eb..3eb98f3 100644
--- a/media/libstagefright/omx/Android.bp
+++ b/media/libstagefright/omx/Android.bp
@@ -18,8 +18,6 @@
         "SoftVideoEncoderOMXComponent.cpp",
         "1.0/Omx.cpp",
         "1.0/OmxStore.cpp",
-        "1.0/WGraphicBufferProducer.cpp",
-        "1.0/WProducerListener.cpp",
         "1.0/WGraphicBufferSource.cpp",
         "1.0/WOmxNode.cpp",
         "1.0/WOmxObserver.cpp",
@@ -48,7 +46,7 @@
         "libgui",
         "libcutils",
         "libstagefright_foundation",
-        "libstagefright_gbs",
+        "libstagefright_bufferqueue_helper",
         "libstagefright_xmlparser",
         "libdl",
         "libhidlbase",
diff --git a/media/libstagefright/omx/OmxGraphicBufferSource.cpp b/media/libstagefright/omx/OmxGraphicBufferSource.cpp
index 83feac8..8de1f4f 100644
--- a/media/libstagefright/omx/OmxGraphicBufferSource.cpp
+++ b/media/libstagefright/omx/OmxGraphicBufferSource.cpp
@@ -20,8 +20,8 @@
 //#define LOG_NDEBUG 0
 #include <utils/Log.h>
 
-#include <media/stagefright/gbs/ComponentWrapper.h>
-#include <media/stagefright/gbs/GraphicBufferSource.h>
+#include <media/stagefright/bqhelper/ComponentWrapper.h>
+#include <media/stagefright/bqhelper/GraphicBufferSource.h>
 #include <media/stagefright/omx/OmxGraphicBufferSource.h>
 
 namespace android {
diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
index 1ba5852..d650224 100644
--- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
+++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
@@ -41,7 +41,7 @@
     mLooper->start(
             false, // runOnCallingThread
             false, // canCallJava
-            ANDROID_PRIORITY_FOREGROUND);
+            ANDROID_PRIORITY_AUDIO);
 }
 
 void SimpleSoftOMXComponent::prepareForDestruction() {
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
index a79d403..903a2b6 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
@@ -35,6 +35,7 @@
 #include <media/OMXFenceParcelable.h>
 #include <media/OMXBuffer.h>
 #include <media/hardware/VideoAPI.h>
+#include <media/stagefright/bqhelper/Conversion.h>
 
 #include <android/hidl/memory/1.0/IMemory.h>
 #include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h>
@@ -99,51 +100,13 @@
 typedef ::android::IGraphicBufferProducer
         BGraphicBufferProducer;
 
-// native_handle_t helper functions.
+// We want to use all functions declared in ::android::conversion
+using namespace ::android::conversion;
 
-/**
- * \brief Take an fd and create a native handle containing only the given fd.
- * The created handle will need to be deleted manually with
- * `native_handle_delete()`.
- *
- * \param[in] fd The source file descriptor (of type `int`).
- * \return The create `native_handle_t*` that contains the given \p fd. If the
- * supplied \p fd is negative, the created native handle will contain no file
- * descriptors.
- *
- * If the native handle cannot be created, the return value will be
- * `nullptr`.
- *
- * This function does not duplicate the file descriptor.
- */
-inline native_handle_t* native_handle_create_from_fd(int fd) {
-    if (fd < 0) {
-        return native_handle_create(0, 0);
-    }
-    native_handle_t* nh = native_handle_create(1, 0);
-    if (nh == nullptr) {
-        return nullptr;
-    }
-    nh->data[0] = fd;
-    return nh;
-}
-
-/**
- * \brief Extract a file descriptor from a native handle.
- *
- * \param[in] nh The source `native_handle_t*`.
- * \param[in] index The index of the file descriptor in \p nh to read from. This
- * input has the default value of `0`.
- * \return The `index`-th file descriptor in \p nh. If \p nh does not have
- * enough file descriptors, the returned value will be `-1`.
- *
- * This function does not duplicate the file descriptor.
- */
-inline int native_handle_read_fd(native_handle_t const* nh, int index = 0) {
-    return ((nh == nullptr) || (nh->numFds == 0) ||
-            (nh->numFds <= index) || (index < 0)) ?
-            -1 : nh->data[index];
-}
+// Now specifically inject these two functions here, because we're going to
+// declare functions with the same name in this namespace.
+using ::android::conversion::convertTo;
+using ::android::conversion::toStatusT;
 
 /**
  * Conversion functions
@@ -178,20 +141,6 @@
  */
 
 /**
- * \brief Convert `Return<void>` to `binder::Status`.
- *
- * \param[in] t The source `Return<void>`.
- * \return The corresponding `binder::Status`.
- */
-// convert: Return<void> -> ::android::binder::Status
-inline ::android::binder::Status toBinderStatus(
-        Return<void> const& t) {
-    return ::android::binder::Status::fromExceptionCode(
-            t.isOk() ? OK : UNKNOWN_ERROR,
-            t.description().c_str());
-}
-
-/**
  * \brief Convert `Return<Status>` to `status_t`. This is for legacy binder
  * calls.
  *
@@ -212,17 +161,6 @@
 }
 
 /**
- * \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls.
- *
- * \param[in] t The source `Return<void>`.
- * \return The corresponding `status_t`.
- */
-// convert: Return<void> -> status_t
-inline status_t toStatusT(Return<void> const& t) {
-    return t.isOk() ? OK : UNKNOWN_ERROR;
-}
-
-/**
  * \brief Convert `Status` to `status_t`. This is for legacy binder calls.
  *
  * \param[in] t The source `Status`.
@@ -245,17 +183,6 @@
 }
 
 /**
- * \brief Wrap `native_handle_t*` in `hidl_handle`.
- *
- * \param[in] nh The source `native_handle_t*`.
- * \return The `hidl_handle` that points to \p nh.
- */
-// wrap: native_handle_t* -> hidl_handle
-inline hidl_handle inHidlHandle(native_handle_t const* nh) {
-    return hidl_handle(nh);
-}
-
-/**
  * \brief Wrap an `omx_message` and construct the corresponding `Message`.
  *
  * \param[out] t The wrapper of type `Message`.
@@ -490,128 +417,6 @@
 }
 
 /**
- * \brief Convert `int32_t` to `Dataspace`.
- *
- * \param[in] l The source `int32_t`.
- * \result The corresponding `Dataspace`.
- */
-// convert: int32_t -> Dataspace
-inline Dataspace toHardwareDataspace(int32_t l) {
-    return static_cast<Dataspace>(l);
-}
-
-/**
- * \brief Convert `Dataspace` to `int32_t`.
- *
- * \param[in] t The source `Dataspace`.
- * \result The corresponding `int32_t`.
- */
-// convert: Dataspace -> int32_t
-inline int32_t toRawDataspace(Dataspace const& t) {
-    return static_cast<int32_t>(t);
-}
-
-/**
- * \brief Wrap an opaque buffer inside a `hidl_vec<uint8_t>`.
- *
- * \param[in] l The pointer to the beginning of the opaque buffer.
- * \param[in] size The size of the buffer.
- * \return A `hidl_vec<uint8_t>` that points to the buffer.
- */
-// wrap: void*, size_t -> hidl_vec<uint8_t>
-inline hidl_vec<uint8_t> inHidlBytes(void const* l, size_t size) {
-    hidl_vec<uint8_t> t;
-    t.setToExternal(static_cast<uint8_t*>(const_cast<void*>(l)), size, false);
-    return t;
-}
-
-/**
- * \brief Create a `hidl_vec<uint8_t>` that is a copy of an opaque buffer.
- *
- * \param[in] l The pointer to the beginning of the opaque buffer.
- * \param[in] size The size of the buffer.
- * \return A `hidl_vec<uint8_t>` that is a copy of the input buffer.
- */
-// convert: void*, size_t -> hidl_vec<uint8_t>
-inline hidl_vec<uint8_t> toHidlBytes(void const* l, size_t size) {
-    hidl_vec<uint8_t> t;
-    t.resize(size);
-    uint8_t const* src = static_cast<uint8_t const*>(l);
-    std::copy(src, src + size, t.data());
-    return t;
-}
-
-/**
- * \brief Wrap `GraphicBuffer` in `AnwBuffer`.
- *
- * \param[out] t The wrapper of type `AnwBuffer`.
- * \param[in] l The source `GraphicBuffer`.
- */
-// wrap: GraphicBuffer -> AnwBuffer
-inline void wrapAs(AnwBuffer* t, GraphicBuffer const& l) {
-    t->attr.width = l.getWidth();
-    t->attr.height = l.getHeight();
-    t->attr.stride = l.getStride();
-    t->attr.format = static_cast<PixelFormat>(l.getPixelFormat());
-    t->attr.layerCount = l.getLayerCount();
-    t->attr.usage = l.getUsage();
-    t->attr.id = l.getId();
-    t->attr.generationNumber = l.getGenerationNumber();
-    t->nativeHandle = hidl_handle(l.handle);
-}
-
-/**
- * \brief Convert `AnwBuffer` to `GraphicBuffer`.
- *
- * \param[out] l The destination `GraphicBuffer`.
- * \param[in] t The source `AnwBuffer`.
- *
- * This function will duplicate all file descriptors in \p t.
- */
-// convert: AnwBuffer -> GraphicBuffer
-// Ref: frameworks/native/libs/ui/GraphicBuffer.cpp: GraphicBuffer::flatten
-inline bool convertTo(GraphicBuffer* l, AnwBuffer const& t) {
-    native_handle_t* handle = t.nativeHandle == nullptr ?
-            nullptr : native_handle_clone(t.nativeHandle);
-
-    size_t const numInts = 12 + (handle ? handle->numInts : 0);
-    int32_t* ints = new int32_t[numInts];
-
-    size_t numFds = static_cast<size_t>(handle ? handle->numFds : 0);
-    int* fds = new int[numFds];
-
-    ints[0] = 'GBFR';
-    ints[1] = static_cast<int32_t>(t.attr.width);
-    ints[2] = static_cast<int32_t>(t.attr.height);
-    ints[3] = static_cast<int32_t>(t.attr.stride);
-    ints[4] = static_cast<int32_t>(t.attr.format);
-    ints[5] = static_cast<int32_t>(t.attr.layerCount);
-    ints[6] = static_cast<int32_t>(t.attr.usage);
-    ints[7] = static_cast<int32_t>(t.attr.id >> 32);
-    ints[8] = static_cast<int32_t>(t.attr.id & 0xFFFFFFFF);
-    ints[9] = static_cast<int32_t>(t.attr.generationNumber);
-    ints[10] = 0;
-    ints[11] = 0;
-    if (handle) {
-        ints[10] = static_cast<int32_t>(handle->numFds);
-        ints[11] = static_cast<int32_t>(handle->numInts);
-        int* intsStart = handle->data + handle->numFds;
-        std::copy(handle->data, intsStart, fds);
-        std::copy(intsStart, intsStart + handle->numInts, &ints[12]);
-    }
-
-    void const* constBuffer = static_cast<void const*>(ints);
-    size_t size = numInts * sizeof(int32_t);
-    int const* constFds = static_cast<int const*>(fds);
-    status_t status = l->unflatten(constBuffer, size, constFds, numFds);
-
-    delete [] fds;
-    delete [] ints;
-    native_handle_delete(handle);
-    return status == NO_ERROR;
-}
-
-/**
  * \brief Wrap `GraphicBuffer` in `CodecBuffer`.
  *
  * \param[out] t The wrapper of type `CodecBuffer`.
@@ -896,1288 +701,6 @@
 #endif
 }
 
-/**
- * Conversion functions for types outside media
- * ============================================
- *
- * Some objects in libui and libgui that were made to go through binder calls do
- * not expose ways to read or write their fields to the public. To pass an
- * object of this kind through the HIDL boundary, translation functions need to
- * work around the access restriction by using the publicly available
- * `flatten()` and `unflatten()` functions.
- *
- * All `flatten()` and `unflatten()` overloads follow the same convention as
- * follows:
- *
- *     status_t flatten(ObjectType const& object,
- *                      [OtherType const& other, ...]
- *                      void*& buffer, size_t& size,
- *                      int*& fds, size_t& numFds)
- *
- *     status_t unflatten(ObjectType* object,
- *                        [OtherType* other, ...,]
- *                        void*& buffer, size_t& size,
- *                        int*& fds, size_t& numFds)
- *
- * The number of `other` parameters varies depending on the `ObjectType`. For
- * example, in the process of unflattening an object that contains
- * `hidl_handle`, `other` is needed to hold `native_handle_t` objects that will
- * be created.
- *
- * The last four parameters always work the same way in all overloads of
- * `flatten()` and `unflatten()`:
- * - For `flatten()`, `buffer` is the pointer to the non-fd buffer to be filled,
- *   `size` is the size (in bytes) of the non-fd buffer pointed to by `buffer`,
- *   `fds` is the pointer to the fd buffer to be filled, and `numFds` is the
- *   size (in ints) of the fd buffer pointed to by `fds`.
- * - For `unflatten()`, `buffer` is the pointer to the non-fd buffer to be read
- *   from, `size` is the size (in bytes) of the non-fd buffer pointed to by
- *   `buffer`, `fds` is the pointer to the fd buffer to be read from, and
- *   `numFds` is the size (in ints) of the fd buffer pointed to by `fds`.
- * - After a successful call to `flatten()` or `unflatten()`, `buffer` and `fds`
- *   will be advanced, while `size` and `numFds` will be decreased to reflect
- *   how much storage/data of the two buffers (fd and non-fd) have been used.
- * - After an unsuccessful call, the values of `buffer`, `size`, `fds` and
- *   `numFds` are invalid.
- *
- * The return value of a successful `flatten()` or `unflatten()` call will be
- * `OK` (also aliased as `NO_ERROR`). Any other values indicate a failure.
- *
- * For each object type that supports flattening, there will be two accompanying
- * functions: `getFlattenedSize()` and `getFdCount()`. `getFlattenedSize()` will
- * return the size of the non-fd buffer that the object will need for
- * flattening. `getFdCount()` will return the size of the fd buffer that the
- * object will need for flattening.
- *
- * The set of these four functions, `getFlattenedSize()`, `getFdCount()`,
- * `flatten()` and `unflatten()`, are similar to functions of the same name in
- * the abstract class `Flattenable`. The only difference is that functions in
- * this file are not member functions of the object type. For example, we write
- *
- *     flatten(x, buffer, size, fds, numFds)
- *
- * instead of
- *
- *     x.flatten(buffer, size, fds, numFds)
- *
- * because we cannot modify the type of `x`.
- *
- * There is one exception to the naming convention: `hidl_handle` that
- * represents a fence. The four functions for this "Fence" type have the word
- * "Fence" attched to their names because the object type, which is
- * `hidl_handle`, does not carry the special meaning that the object itself can
- * only contain zero or one file descriptor.
- */
-
-// Ref: frameworks/native/libs/ui/Fence.cpp
-
-/**
- * \brief Return the size of the non-fd buffer required to flatten a fence.
- *
- * \param[in] fence The input fence of type `hidl_handle`.
- * \return The required size of the flat buffer.
- *
- * The current version of this function always returns 4, which is the number of
- * bytes required to store the number of file descriptors contained in the fd
- * part of the flat buffer.
- */
-inline size_t getFenceFlattenedSize(hidl_handle const& /* fence */) {
-    return 4;
-};
-
-/**
- * \brief Return the number of file descriptors contained in a fence.
- *
- * \param[in] fence The input fence of type `hidl_handle`.
- * \return `0` if \p fence does not contain a valid file descriptor, or `1`
- * otherwise.
- */
-inline size_t getFenceFdCount(hidl_handle const& fence) {
-    return native_handle_read_fd(fence) == -1 ? 0 : 1;
-}
-
-/**
- * \brief Unflatten `Fence` to `hidl_handle`.
- *
- * \param[out] fence The destination `hidl_handle`.
- * \param[out] nh The underlying native handle.
- * \param[in,out] buffer The pointer to the flat non-fd buffer.
- * \param[in,out] size The size of the flat non-fd buffer.
- * \param[in,out] fds The pointer to the flat fd buffer.
- * \param[in,out] numFds The size of the flat fd buffer.
- * \return `NO_ERROR` on success; other value on failure.
- *
- * If the return value is `NO_ERROR`, \p nh will point to a newly created
- * native handle, which needs to be deleted with `native_handle_delete()`
- * afterwards.
- */
-inline status_t unflattenFence(hidl_handle* fence, native_handle_t** nh,
-        void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
-    if (size < 4) {
-        return NO_MEMORY;
-    }
-
-    uint32_t numFdsInHandle;
-    FlattenableUtils::read(buffer, size, numFdsInHandle);
-
-    if (numFdsInHandle > 1) {
-        return BAD_VALUE;
-    }
-
-    if (numFds < numFdsInHandle) {
-        return NO_MEMORY;
-    }
-
-    if (numFdsInHandle) {
-        *nh = native_handle_create_from_fd(*fds);
-        if (*nh == nullptr) {
-            return NO_MEMORY;
-        }
-        *fence = *nh;
-        ++fds;
-        --numFds;
-    } else {
-        *nh = nullptr;
-        *fence = hidl_handle();
-    }
-
-    return NO_ERROR;
-}
-
-/**
- * \brief Flatten `hidl_handle` as `Fence`.
- *
- * \param[in] t The source `hidl_handle`.
- * \param[in,out] buffer The pointer to the flat non-fd buffer.
- * \param[in,out] size The size of the flat non-fd buffer.
- * \param[in,out] fds The pointer to the flat fd buffer.
- * \param[in,out] numFds The size of the flat fd buffer.
- * \return `NO_ERROR` on success; other value on failure.
- */
-inline status_t flattenFence(hidl_handle const& fence,
-        void*& buffer, size_t& size, int*& fds, size_t& numFds) {
-    if (size < getFenceFlattenedSize(fence) ||
-            numFds < getFenceFdCount(fence)) {
-        return NO_MEMORY;
-    }
-    // Cast to uint32_t since the size of a size_t can vary between 32- and
-    // 64-bit processes
-    FlattenableUtils::write(buffer, size,
-            static_cast<uint32_t>(getFenceFdCount(fence)));
-    int fd = native_handle_read_fd(fence);
-    if (fd != -1) {
-        *fds = fd;
-        ++fds;
-        --numFds;
-    }
-    return NO_ERROR;
-}
-
-/**
- * \brief Wrap `Fence` in `hidl_handle`.
- *
- * \param[out] t The wrapper of type `hidl_handle`.
- * \param[out] nh The native handle pointed to by \p t.
- * \param[in] l The source `Fence`.
- *
- * On success, \p nh will hold a newly created native handle, which must be
- * deleted manually with `native_handle_delete()` afterwards.
- */
-// wrap: Fence -> hidl_handle
-inline bool wrapAs(hidl_handle* t, native_handle_t** nh, Fence const& l) {
-    size_t const baseSize = l.getFlattenedSize();
-    std::unique_ptr<uint8_t[]> baseBuffer(
-            new (std::nothrow) uint8_t[baseSize]);
-    if (!baseBuffer) {
-        return false;
-    }
-
-    size_t const baseNumFds = l.getFdCount();
-    std::unique_ptr<int[]> baseFds(
-            new (std::nothrow) int[baseNumFds]);
-    if (!baseFds) {
-        return false;
-    }
-
-    void* buffer = static_cast<void*>(baseBuffer.get());
-    size_t size = baseSize;
-    int* fds = static_cast<int*>(baseFds.get());
-    size_t numFds = baseNumFds;
-    if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
-        return false;
-    }
-
-    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
-    size = baseSize;
-    int const* constFds = static_cast<int const*>(baseFds.get());
-    numFds = baseNumFds;
-    if (unflattenFence(t, nh, constBuffer, size, constFds, numFds)
-            != NO_ERROR) {
-        return false;
-    }
-
-    return true;
-}
-
-/**
- * \brief Convert `hidl_handle` to `Fence`.
- *
- * \param[out] l The destination `Fence`. `l` must not have been used
- * (`l->isValid()` must return `false`) before this function is called.
- * \param[in] t The source `hidl_handle`.
- *
- * If \p t contains a valid file descriptor, it will be duplicated.
- */
-// convert: hidl_handle -> Fence
-inline bool convertTo(Fence* l, hidl_handle const& t) {
-    int fd = native_handle_read_fd(t);
-    if (fd != -1) {
-        fd = dup(fd);
-        if (fd == -1) {
-            return false;
-        }
-    }
-    native_handle_t* nh = native_handle_create_from_fd(fd);
-    if (nh == nullptr) {
-        if (fd != -1) {
-            close(fd);
-        }
-        return false;
-    }
-
-    size_t const baseSize = getFenceFlattenedSize(t);
-    std::unique_ptr<uint8_t[]> baseBuffer(
-            new (std::nothrow) uint8_t[baseSize]);
-    if (!baseBuffer) {
-        native_handle_delete(nh);
-        return false;
-    }
-
-    size_t const baseNumFds = getFenceFdCount(t);
-    std::unique_ptr<int[]> baseFds(
-            new (std::nothrow) int[baseNumFds]);
-    if (!baseFds) {
-        native_handle_delete(nh);
-        return false;
-    }
-
-    void* buffer = static_cast<void*>(baseBuffer.get());
-    size_t size = baseSize;
-    int* fds = static_cast<int*>(baseFds.get());
-    size_t numFds = baseNumFds;
-    if (flattenFence(hidl_handle(nh), buffer, size, fds, numFds) != NO_ERROR) {
-        native_handle_delete(nh);
-        return false;
-    }
-    native_handle_delete(nh);
-
-    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
-    size = baseSize;
-    int const* constFds = static_cast<int const*>(baseFds.get());
-    numFds = baseNumFds;
-    if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
-        return false;
-    }
-
-    return true;
-}
-
-// Ref: frameworks/native/libs/ui/FenceTime.cpp: FenceTime::Snapshot
-
-/**
- * \brief Return the size of the non-fd buffer required to flatten
- * `FenceTimeSnapshot`.
- *
- * \param[in] t The input `FenceTimeSnapshot`.
- * \return The required size of the flat buffer.
- */
-inline size_t getFlattenedSize(
-        HGraphicBufferProducer::FenceTimeSnapshot const& t) {
-    constexpr size_t min = sizeof(t.state);
-    switch (t.state) {
-        case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY:
-            return min;
-        case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE:
-            return min + getFenceFlattenedSize(t.fence);
-        case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME:
-            return min + sizeof(
-                    ::android::FenceTime::Snapshot::signalTime);
-    }
-    return 0;
-}
-
-/**
- * \brief Return the number of file descriptors contained in
- * `FenceTimeSnapshot`.
- *
- * \param[in] t The input `FenceTimeSnapshot`.
- * \return The number of file descriptors contained in \p snapshot.
- */
-inline size_t getFdCount(
-        HGraphicBufferProducer::FenceTimeSnapshot const& t) {
-    return t.state ==
-            HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE ?
-            getFenceFdCount(t.fence) : 0;
-}
-
-/**
- * \brief Flatten `FenceTimeSnapshot`.
- *
- * \param[in] t The source `FenceTimeSnapshot`.
- * \param[in,out] buffer The pointer to the flat non-fd buffer.
- * \param[in,out] size The size of the flat non-fd buffer.
- * \param[in,out] fds The pointer to the flat fd buffer.
- * \param[in,out] numFds The size of the flat fd buffer.
- * \return `NO_ERROR` on success; other value on failure.
- *
- * This function will duplicate the file descriptor in `t.fence` if `t.state ==
- * FENCE`.
- */
-inline status_t flatten(HGraphicBufferProducer::FenceTimeSnapshot const& t,
-        void*& buffer, size_t& size, int*& fds, size_t& numFds) {
-    if (size < getFlattenedSize(t)) {
-        return NO_MEMORY;
-    }
-
-    switch (t.state) {
-        case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY:
-            FlattenableUtils::write(buffer, size,
-                    ::android::FenceTime::Snapshot::State::EMPTY);
-            return NO_ERROR;
-        case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE:
-            FlattenableUtils::write(buffer, size,
-                    ::android::FenceTime::Snapshot::State::FENCE);
-            return flattenFence(t.fence, buffer, size, fds, numFds);
-        case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME:
-            FlattenableUtils::write(buffer, size,
-                    ::android::FenceTime::Snapshot::State::SIGNAL_TIME);
-            FlattenableUtils::write(buffer, size, t.signalTimeNs);
-            return NO_ERROR;
-    }
-    return NO_ERROR;
-}
-
-/**
- * \brief Unflatten `FenceTimeSnapshot`.
- *
- * \param[out] t The destination `FenceTimeSnapshot`.
- * \param[out] nh The underlying native handle.
- * \param[in,out] buffer The pointer to the flat non-fd buffer.
- * \param[in,out] size The size of the flat non-fd buffer.
- * \param[in,out] fds The pointer to the flat fd buffer.
- * \param[in,out] numFds The size of the flat fd buffer.
- * \return `NO_ERROR` on success; other value on failure.
- *
- * If the return value is `NO_ERROR` and the constructed snapshot contains a
- * file descriptor, \p nh will be created to hold that file descriptor. In this
- * case, \p nh needs to be deleted with `native_handle_delete()` afterwards.
- */
-inline status_t unflatten(
-        HGraphicBufferProducer::FenceTimeSnapshot* t, native_handle_t** nh,
-        void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
-    if (size < sizeof(t->state)) {
-        return NO_MEMORY;
-    }
-
-    *nh = nullptr;
-    ::android::FenceTime::Snapshot::State state;
-    FlattenableUtils::read(buffer, size, state);
-    switch (state) {
-        case ::android::FenceTime::Snapshot::State::EMPTY:
-            t->state = HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY;
-            return NO_ERROR;
-        case ::android::FenceTime::Snapshot::State::FENCE:
-            t->state = HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE;
-            return unflattenFence(&t->fence, nh, buffer, size, fds, numFds);
-        case ::android::FenceTime::Snapshot::State::SIGNAL_TIME:
-            t->state = HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME;
-            if (size < sizeof(t->signalTimeNs)) {
-                return NO_MEMORY;
-            }
-            FlattenableUtils::read(buffer, size, t->signalTimeNs);
-            return NO_ERROR;
-    }
-    return NO_ERROR;
-}
-
-// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventsDelta
-
-/**
- * \brief Return a lower bound on the size of the non-fd buffer required to
- * flatten `FrameEventsDelta`.
- *
- * \param[in] t The input `FrameEventsDelta`.
- * \return A lower bound on the size of the flat buffer.
- */
-constexpr size_t minFlattenedSize(
-        HGraphicBufferProducer::FrameEventsDelta const& /* t */) {
-    return sizeof(uint64_t) + // mFrameNumber
-            sizeof(uint8_t) + // mIndex
-            sizeof(uint8_t) + // mAddPostCompositeCalled
-            sizeof(uint8_t) + // mAddRetireCalled
-            sizeof(uint8_t) + // mAddReleaseCalled
-            sizeof(nsecs_t) + // mPostedTime
-            sizeof(nsecs_t) + // mRequestedPresentTime
-            sizeof(nsecs_t) + // mLatchTime
-            sizeof(nsecs_t) + // mFirstRefreshStartTime
-            sizeof(nsecs_t); // mLastRefreshStartTime
-}
-
-/**
- * \brief Return the size of the non-fd buffer required to flatten
- * `FrameEventsDelta`.
- *
- * \param[in] t The input `FrameEventsDelta`.
- * \return The required size of the flat buffer.
- */
-inline size_t getFlattenedSize(
-        HGraphicBufferProducer::FrameEventsDelta const& t) {
-    return minFlattenedSize(t) +
-            getFlattenedSize(t.gpuCompositionDoneFence) +
-            getFlattenedSize(t.displayPresentFence) +
-            getFlattenedSize(t.displayRetireFence) +
-            getFlattenedSize(t.releaseFence);
-};
-
-/**
- * \brief Return the number of file descriptors contained in
- * `FrameEventsDelta`.
- *
- * \param[in] t The input `FrameEventsDelta`.
- * \return The number of file descriptors contained in \p t.
- */
-inline size_t getFdCount(
-        HGraphicBufferProducer::FrameEventsDelta const& t) {
-    return getFdCount(t.gpuCompositionDoneFence) +
-            getFdCount(t.displayPresentFence) +
-            getFdCount(t.displayRetireFence) +
-            getFdCount(t.releaseFence);
-};
-
-/**
- * \brief Unflatten `FrameEventsDelta`.
- *
- * \param[out] t The destination `FrameEventsDelta`.
- * \param[out] nh The underlying array of native handles.
- * \param[in,out] buffer The pointer to the flat non-fd buffer.
- * \param[in,out] size The size of the flat non-fd buffer.
- * \param[in,out] fds The pointer to the flat fd buffer.
- * \param[in,out] numFds The size of the flat fd buffer.
- * \return `NO_ERROR` on success; other value on failure.
- *
- * If the return value is `NO_ERROR`, \p nh will have length 4, and it will be
- * populated with `nullptr` or newly created handles. Each non-null slot in \p
- * nh will need to be deleted manually with `native_handle_delete()`.
- */
-inline status_t unflatten(HGraphicBufferProducer::FrameEventsDelta* t,
-        std::vector<native_handle_t*>* nh,
-        void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
-    if (size < minFlattenedSize(*t)) {
-        return NO_MEMORY;
-    }
-    FlattenableUtils::read(buffer, size, t->frameNumber);
-
-    // These were written as uint8_t for alignment.
-    uint8_t temp = 0;
-    FlattenableUtils::read(buffer, size, temp);
-    size_t index = static_cast<size_t>(temp);
-    if (index >= ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
-        return BAD_VALUE;
-    }
-    t->index = static_cast<uint32_t>(index);
-
-    FlattenableUtils::read(buffer, size, temp);
-    t->addPostCompositeCalled = static_cast<bool>(temp);
-    FlattenableUtils::read(buffer, size, temp);
-    t->addRetireCalled = static_cast<bool>(temp);
-    FlattenableUtils::read(buffer, size, temp);
-    t->addReleaseCalled = static_cast<bool>(temp);
-
-    FlattenableUtils::read(buffer, size, t->postedTimeNs);
-    FlattenableUtils::read(buffer, size, t->requestedPresentTimeNs);
-    FlattenableUtils::read(buffer, size, t->latchTimeNs);
-    FlattenableUtils::read(buffer, size, t->firstRefreshStartTimeNs);
-    FlattenableUtils::read(buffer, size, t->lastRefreshStartTimeNs);
-    FlattenableUtils::read(buffer, size, t->dequeueReadyTime);
-
-    // Fences
-    HGraphicBufferProducer::FenceTimeSnapshot* tSnapshot[4];
-    tSnapshot[0] = &t->gpuCompositionDoneFence;
-    tSnapshot[1] = &t->displayPresentFence;
-    tSnapshot[2] = &t->displayRetireFence;
-    tSnapshot[3] = &t->releaseFence;
-    nh->resize(4);
-    for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) {
-        status_t status = unflatten(
-                tSnapshot[snapshotIndex], &((*nh)[snapshotIndex]),
-                buffer, size, fds, numFds);
-        if (status != NO_ERROR) {
-            while (snapshotIndex > 0) {
-                --snapshotIndex;
-                if ((*nh)[snapshotIndex] != nullptr) {
-                    native_handle_delete((*nh)[snapshotIndex]);
-                }
-            }
-            return status;
-        }
-    }
-    return NO_ERROR;
-}
-
-/**
- * \brief Flatten `FrameEventsDelta`.
- *
- * \param[in] t The source `FrameEventsDelta`.
- * \param[in,out] buffer The pointer to the flat non-fd buffer.
- * \param[in,out] size The size of the flat non-fd buffer.
- * \param[in,out] fds The pointer to the flat fd buffer.
- * \param[in,out] numFds The size of the flat fd buffer.
- * \return `NO_ERROR` on success; other value on failure.
- *
- * This function will duplicate file descriptors contained in \p t.
- */
-// Ref: frameworks/native/libs/gui/FrameTimestamp.cpp:
-//      FrameEventsDelta::flatten
-inline status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t,
-        void*& buffer, size_t& size, int*& fds, size_t numFds) {
-    // Check that t.index is within a valid range.
-    if (t.index >= static_cast<uint32_t>(FrameEventHistory::MAX_FRAME_HISTORY)
-            || t.index > std::numeric_limits<uint8_t>::max()) {
-        return BAD_VALUE;
-    }
-
-    FlattenableUtils::write(buffer, size, t.frameNumber);
-
-    // These are static_cast to uint8_t for alignment.
-    FlattenableUtils::write(buffer, size, static_cast<uint8_t>(t.index));
-    FlattenableUtils::write(
-            buffer, size, static_cast<uint8_t>(t.addPostCompositeCalled));
-    FlattenableUtils::write(
-            buffer, size, static_cast<uint8_t>(t.addRetireCalled));
-    FlattenableUtils::write(
-            buffer, size, static_cast<uint8_t>(t.addReleaseCalled));
-
-    FlattenableUtils::write(buffer, size, t.postedTimeNs);
-    FlattenableUtils::write(buffer, size, t.requestedPresentTimeNs);
-    FlattenableUtils::write(buffer, size, t.latchTimeNs);
-    FlattenableUtils::write(buffer, size, t.firstRefreshStartTimeNs);
-    FlattenableUtils::write(buffer, size, t.lastRefreshStartTimeNs);
-    FlattenableUtils::write(buffer, size, t.dequeueReadyTime);
-
-    // Fences
-    HGraphicBufferProducer::FenceTimeSnapshot const* tSnapshot[4];
-    tSnapshot[0] = &t.gpuCompositionDoneFence;
-    tSnapshot[1] = &t.displayPresentFence;
-    tSnapshot[2] = &t.displayRetireFence;
-    tSnapshot[3] = &t.releaseFence;
-    for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) {
-        status_t status = flatten(
-                *(tSnapshot[snapshotIndex]), buffer, size, fds, numFds);
-        if (status != NO_ERROR) {
-            return status;
-        }
-    }
-    return NO_ERROR;
-}
-
-// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventHistoryDelta
-
-/**
- * \brief Return the size of the non-fd buffer required to flatten
- * `HGraphicBufferProducer::FrameEventHistoryDelta`.
- *
- * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
- * \return The required size of the flat buffer.
- */
-inline size_t getFlattenedSize(
-        HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
-    size_t size = 4 + // mDeltas.size()
-            sizeof(t.compositorTiming);
-    for (size_t i = 0; i < t.deltas.size(); ++i) {
-        size += getFlattenedSize(t.deltas[i]);
-    }
-    return size;
-}
-
-/**
- * \brief Return the number of file descriptors contained in
- * `HGraphicBufferProducer::FrameEventHistoryDelta`.
- *
- * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`.
- * \return The number of file descriptors contained in \p t.
- */
-inline size_t getFdCount(
-        HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
-    size_t numFds = 0;
-    for (size_t i = 0; i < t.deltas.size(); ++i) {
-        numFds += getFdCount(t.deltas[i]);
-    }
-    return numFds;
-}
-
-/**
- * \brief Unflatten `FrameEventHistoryDelta`.
- *
- * \param[out] t The destination `FrameEventHistoryDelta`.
- * \param[out] nh The underlying array of arrays of native handles.
- * \param[in,out] buffer The pointer to the flat non-fd buffer.
- * \param[in,out] size The size of the flat non-fd buffer.
- * \param[in,out] fds The pointer to the flat fd buffer.
- * \param[in,out] numFds The size of the flat fd buffer.
- * \return `NO_ERROR` on success; other value on failure.
- *
- * If the return value is `NO_ERROR`, \p nh will be populated with `nullptr` or
- * newly created handles. The second dimension of \p nh will be 4. Each non-null
- * slot in \p nh will need to be deleted manually with `native_handle_delete()`.
- */
-inline status_t unflatten(
-        HGraphicBufferProducer::FrameEventHistoryDelta* t,
-        std::vector<std::vector<native_handle_t*> >* nh,
-        void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
-    if (size < 4) {
-        return NO_MEMORY;
-    }
-
-    FlattenableUtils::read(buffer, size, t->compositorTiming);
-
-    uint32_t deltaCount = 0;
-    FlattenableUtils::read(buffer, size, deltaCount);
-    if (static_cast<size_t>(deltaCount) >
-            ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
-        return BAD_VALUE;
-    }
-    t->deltas.resize(deltaCount);
-    nh->resize(deltaCount);
-    for (size_t deltaIndex = 0; deltaIndex < deltaCount; ++deltaIndex) {
-        status_t status = unflatten(
-                &(t->deltas[deltaIndex]), &((*nh)[deltaIndex]),
-                buffer, size, fds, numFds);
-        if (status != NO_ERROR) {
-            return status;
-        }
-    }
-    return NO_ERROR;
-}
-
-/**
- * \brief Flatten `FrameEventHistoryDelta`.
- *
- * \param[in] t The source `FrameEventHistoryDelta`.
- * \param[in,out] buffer The pointer to the flat non-fd buffer.
- * \param[in,out] size The size of the flat non-fd buffer.
- * \param[in,out] fds The pointer to the flat fd buffer.
- * \param[in,out] numFds The size of the flat fd buffer.
- * \return `NO_ERROR` on success; other value on failure.
- *
- * This function will duplicate file descriptors contained in \p t.
- */
-inline status_t flatten(
-        HGraphicBufferProducer::FrameEventHistoryDelta const& t,
-        void*& buffer, size_t& size, int*& fds, size_t& numFds) {
-    if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) {
-        return BAD_VALUE;
-    }
-    if (size < getFlattenedSize(t)) {
-        return NO_MEMORY;
-    }
-
-    FlattenableUtils::write(buffer, size, t.compositorTiming);
-
-    FlattenableUtils::write(buffer, size, static_cast<uint32_t>(t.deltas.size()));
-    for (size_t deltaIndex = 0; deltaIndex < t.deltas.size(); ++deltaIndex) {
-        status_t status = flatten(t.deltas[deltaIndex], buffer, size, fds, numFds);
-        if (status != NO_ERROR) {
-            return status;
-        }
-    }
-    return NO_ERROR;
-}
-
-/**
- * \brief Wrap `::android::FrameEventHistoryData` in
- * `HGraphicBufferProducer::FrameEventHistoryDelta`.
- *
- * \param[out] t The wrapper of type
- * `HGraphicBufferProducer::FrameEventHistoryDelta`.
- * \param[out] nh The array of array of native handles that are referred to by
- * members of \p t.
- * \param[in] l The source `::android::FrameEventHistoryDelta`.
- *
- * On success, each member of \p nh will be either `nullptr` or a newly created
- * native handle. All the non-`nullptr` elements must be deleted individually
- * with `native_handle_delete()`.
- */
-inline bool wrapAs(HGraphicBufferProducer::FrameEventHistoryDelta* t,
-        std::vector<std::vector<native_handle_t*> >* nh,
-        ::android::FrameEventHistoryDelta const& l) {
-
-    size_t const baseSize = l.getFlattenedSize();
-    std::unique_ptr<uint8_t[]> baseBuffer(
-            new (std::nothrow) uint8_t[baseSize]);
-    if (!baseBuffer) {
-        return false;
-    }
-
-    size_t const baseNumFds = l.getFdCount();
-    std::unique_ptr<int[]> baseFds(
-            new (std::nothrow) int[baseNumFds]);
-    if (!baseFds) {
-        return false;
-    }
-
-    void* buffer = static_cast<void*>(baseBuffer.get());
-    size_t size = baseSize;
-    int* fds = baseFds.get();
-    size_t numFds = baseNumFds;
-    if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
-        return false;
-    }
-
-    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
-    size = baseSize;
-    int const* constFds = static_cast<int const*>(baseFds.get());
-    numFds = baseNumFds;
-    if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) {
-        return false;
-    }
-
-    return true;
-}
-
-/**
- * \brief Convert `HGraphicBufferProducer::FrameEventHistoryDelta` to
- * `::android::FrameEventHistoryDelta`.
- *
- * \param[out] l The destination `::android::FrameEventHistoryDelta`.
- * \param[in] t The source `HGraphicBufferProducer::FrameEventHistoryDelta`.
- *
- * This function will duplicate all file descriptors contained in \p t.
- */
-inline bool convertTo(
-        ::android::FrameEventHistoryDelta* l,
-        HGraphicBufferProducer::FrameEventHistoryDelta const& t) {
-
-    size_t const baseSize = getFlattenedSize(t);
-    std::unique_ptr<uint8_t[]> baseBuffer(
-            new (std::nothrow) uint8_t[baseSize]);
-    if (!baseBuffer) {
-        return false;
-    }
-
-    size_t const baseNumFds = getFdCount(t);
-    std::unique_ptr<int[]> baseFds(
-            new (std::nothrow) int[baseNumFds]);
-    if (!baseFds) {
-        return false;
-    }
-
-    void* buffer = static_cast<void*>(baseBuffer.get());
-    size_t size = baseSize;
-    int* fds = static_cast<int*>(baseFds.get());
-    size_t numFds = baseNumFds;
-    if (flatten(t, buffer, size, fds, numFds) != NO_ERROR) {
-        return false;
-    }
-
-    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
-    size = baseSize;
-    int const* constFds = static_cast<int const*>(baseFds.get());
-    numFds = baseNumFds;
-    if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
-        return false;
-    }
-
-    return true;
-}
-
-// Ref: frameworks/native/libs/ui/Region.cpp
-
-/**
- * \brief Return the size of the buffer required to flatten `Region`.
- *
- * \param[in] t The input `Region`.
- * \return The required size of the flat buffer.
- */
-inline size_t getFlattenedSize(Region const& t) {
-    return sizeof(uint32_t) + t.size() * sizeof(::android::Rect);
-}
-
-/**
- * \brief Unflatten `Region`.
- *
- * \param[out] t The destination `Region`.
- * \param[in,out] buffer The pointer to the flat buffer.
- * \param[in,out] size The size of the flat buffer.
- * \return `NO_ERROR` on success; other value on failure.
- */
-inline status_t unflatten(Region* t, void const*& buffer, size_t& size) {
-    if (size < sizeof(uint32_t)) {
-        return NO_MEMORY;
-    }
-
-    uint32_t numRects = 0;
-    FlattenableUtils::read(buffer, size, numRects);
-    if (size < numRects * sizeof(Rect)) {
-        return NO_MEMORY;
-    }
-    if (numRects > (UINT32_MAX / sizeof(Rect))) {
-        return NO_MEMORY;
-    }
-
-    t->resize(numRects);
-    for (size_t r = 0; r < numRects; ++r) {
-        ::android::Rect rect(::android::Rect::EMPTY_RECT);
-        status_t status = rect.unflatten(buffer, size);
-        if (status != NO_ERROR) {
-            return status;
-        }
-        FlattenableUtils::advance(buffer, size, sizeof(rect));
-        (*t)[r] = Rect{
-                static_cast<int32_t>(rect.left),
-                static_cast<int32_t>(rect.top),
-                static_cast<int32_t>(rect.right),
-                static_cast<int32_t>(rect.bottom)};
-    }
-    return NO_ERROR;
-}
-
-/**
- * \brief Flatten `Region`.
- *
- * \param[in] t The source `Region`.
- * \param[in,out] buffer The pointer to the flat buffer.
- * \param[in,out] size The size of the flat buffer.
- * \return `NO_ERROR` on success; other value on failure.
- */
-inline status_t flatten(Region const& t, void*& buffer, size_t& size) {
-    if (size < getFlattenedSize(t)) {
-        return NO_MEMORY;
-    }
-
-    FlattenableUtils::write(buffer, size, static_cast<uint32_t>(t.size()));
-    for (size_t r = 0; r < t.size(); ++r) {
-        ::android::Rect rect(
-                static_cast<int32_t>(t[r].left),
-                static_cast<int32_t>(t[r].top),
-                static_cast<int32_t>(t[r].right),
-                static_cast<int32_t>(t[r].bottom));
-        status_t status = rect.flatten(buffer, size);
-        if (status != NO_ERROR) {
-            return status;
-        }
-        FlattenableUtils::advance(buffer, size, sizeof(rect));
-    }
-    return NO_ERROR;
-}
-
-/**
- * \brief Convert `::android::Region` to `Region`.
- *
- * \param[out] t The destination `Region`.
- * \param[in] l The source `::android::Region`.
- */
-// convert: ::android::Region -> Region
-inline bool convertTo(Region* t, ::android::Region const& l) {
-    size_t const baseSize = l.getFlattenedSize();
-    std::unique_ptr<uint8_t[]> baseBuffer(
-            new (std::nothrow) uint8_t[baseSize]);
-    if (!baseBuffer) {
-        return false;
-    }
-
-    void* buffer = static_cast<void*>(baseBuffer.get());
-    size_t size = baseSize;
-    if (l.flatten(buffer, size) != NO_ERROR) {
-        return false;
-    }
-
-    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
-    size = baseSize;
-    if (unflatten(t, constBuffer, size) != NO_ERROR) {
-        return false;
-    }
-
-    return true;
-}
-
-/**
- * \brief Convert `Region` to `::android::Region`.
- *
- * \param[out] l The destination `::android::Region`.
- * \param[in] t The source `Region`.
- */
-// convert: Region -> ::android::Region
-inline bool convertTo(::android::Region* l, Region const& t) {
-    size_t const baseSize = getFlattenedSize(t);
-    std::unique_ptr<uint8_t[]> baseBuffer(
-            new (std::nothrow) uint8_t[baseSize]);
-    if (!baseBuffer) {
-        return false;
-    }
-
-    void* buffer = static_cast<void*>(baseBuffer.get());
-    size_t size = baseSize;
-    if (flatten(t, buffer, size) != NO_ERROR) {
-        return false;
-    }
-
-    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
-    size = baseSize;
-    if (l->unflatten(constBuffer, size) != NO_ERROR) {
-        return false;
-    }
-
-    return true;
-}
-
-// Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp:
-//      BGraphicBufferProducer::QueueBufferInput
-
-/**
- * \brief Return a lower bound on the size of the buffer required to flatten
- * `HGraphicBufferProducer::QueueBufferInput`.
- *
- * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`.
- * \return A lower bound on the size of the flat buffer.
- */
-constexpr size_t minFlattenedSize(
-        HGraphicBufferProducer::QueueBufferInput const& /* t */) {
-    return sizeof(int64_t) + // timestamp
-            sizeof(int) + // isAutoTimestamp
-            sizeof(android_dataspace) + // dataSpace
-            sizeof(::android::Rect) + // crop
-            sizeof(int) + // scalingMode
-            sizeof(uint32_t) + // transform
-            sizeof(uint32_t) + // stickyTransform
-            sizeof(bool); // getFrameTimestamps
-}
-
-/**
- * \brief Return the size of the buffer required to flatten
- * `HGraphicBufferProducer::QueueBufferInput`.
- *
- * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`.
- * \return The required size of the flat buffer.
- */
-inline size_t getFlattenedSize(HGraphicBufferProducer::QueueBufferInput const& t) {
-    return minFlattenedSize(t) +
-            getFenceFlattenedSize(t.fence) +
-            getFlattenedSize(t.surfaceDamage) +
-            sizeof(HdrMetadata::validTypes);
-}
-
-/**
- * \brief Return the number of file descriptors contained in
- * `HGraphicBufferProducer::QueueBufferInput`.
- *
- * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`.
- * \return The number of file descriptors contained in \p t.
- */
-inline size_t getFdCount(
-        HGraphicBufferProducer::QueueBufferInput const& t) {
-    return getFenceFdCount(t.fence);
-}
-
-/**
- * \brief Flatten `HGraphicBufferProducer::QueueBufferInput`.
- *
- * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`.
- * \param[out] nh The native handle cloned from `t.fence`.
- * \param[in,out] buffer The pointer to the flat non-fd buffer.
- * \param[in,out] size The size of the flat non-fd buffer.
- * \param[in,out] fds The pointer to the flat fd buffer.
- * \param[in,out] numFds The size of the flat fd buffer.
- * \return `NO_ERROR` on success; other value on failure.
- *
- * This function will duplicate the file descriptor in `t.fence`. */
-inline status_t flatten(HGraphicBufferProducer::QueueBufferInput const& t,
-        native_handle_t** nh,
-        void*& buffer, size_t& size, int*& fds, size_t& numFds) {
-    if (size < getFlattenedSize(t)) {
-        return NO_MEMORY;
-    }
-
-    FlattenableUtils::write(buffer, size, t.timestamp);
-    FlattenableUtils::write(buffer, size, static_cast<int>(t.isAutoTimestamp));
-    FlattenableUtils::write(buffer, size,
-            static_cast<android_dataspace_t>(t.dataSpace));
-    FlattenableUtils::write(buffer, size, ::android::Rect(
-            static_cast<int32_t>(t.crop.left),
-            static_cast<int32_t>(t.crop.top),
-            static_cast<int32_t>(t.crop.right),
-            static_cast<int32_t>(t.crop.bottom)));
-    FlattenableUtils::write(buffer, size, static_cast<int>(t.scalingMode));
-    FlattenableUtils::write(buffer, size, t.transform);
-    FlattenableUtils::write(buffer, size, t.stickyTransform);
-    FlattenableUtils::write(buffer, size, t.getFrameTimestamps);
-
-    *nh = t.fence.getNativeHandle() == nullptr ?
-            nullptr : native_handle_clone(t.fence);
-    status_t status = flattenFence(hidl_handle(*nh), buffer, size, fds, numFds);
-    if (status != NO_ERROR) {
-        return status;
-    }
-    status = flatten(t.surfaceDamage, buffer, size);
-    if (status != NO_ERROR) {
-        return status;
-    }
-    FlattenableUtils::write(buffer, size, decltype(HdrMetadata::validTypes)(0));
-    return NO_ERROR;
-}
-
-/**
- * \brief Unflatten `HGraphicBufferProducer::QueueBufferInput`.
- *
- * \param[out] t The destination `HGraphicBufferProducer::QueueBufferInput`.
- * \param[out] nh The underlying native handle for `t->fence`.
- * \param[in,out] buffer The pointer to the flat non-fd buffer.
- * \param[in,out] size The size of the flat non-fd buffer.
- * \param[in,out] fds The pointer to the flat fd buffer.
- * \param[in,out] numFds The size of the flat fd buffer.
- * \return `NO_ERROR` on success; other value on failure.
- *
- * If the return value is `NO_ERROR` and `t->fence` contains a valid file
- * descriptor, \p nh will be a newly created native handle holding that file
- * descriptor. \p nh needs to be deleted with `native_handle_delete()`
- * afterwards.
- */
-inline status_t unflatten(
-        HGraphicBufferProducer::QueueBufferInput* t, native_handle_t** nh,
-        void const*& buffer, size_t& size, int const*& fds, size_t& numFds) {
-    if (size < minFlattenedSize(*t)) {
-        return NO_MEMORY;
-    }
-
-    FlattenableUtils::read(buffer, size, t->timestamp);
-    int lIsAutoTimestamp;
-    FlattenableUtils::read(buffer, size, lIsAutoTimestamp);
-    t->isAutoTimestamp = static_cast<int32_t>(lIsAutoTimestamp);
-    android_dataspace_t lDataSpace;
-    FlattenableUtils::read(buffer, size, lDataSpace);
-    t->dataSpace = static_cast<Dataspace>(lDataSpace);
-    Rect lCrop;
-    FlattenableUtils::read(buffer, size, lCrop);
-    t->crop = Rect{
-            static_cast<int32_t>(lCrop.left),
-            static_cast<int32_t>(lCrop.top),
-            static_cast<int32_t>(lCrop.right),
-            static_cast<int32_t>(lCrop.bottom)};
-    int lScalingMode;
-    FlattenableUtils::read(buffer, size, lScalingMode);
-    t->scalingMode = static_cast<int32_t>(lScalingMode);
-    FlattenableUtils::read(buffer, size, t->transform);
-    FlattenableUtils::read(buffer, size, t->stickyTransform);
-    FlattenableUtils::read(buffer, size, t->getFrameTimestamps);
-
-    status_t status = unflattenFence(&(t->fence), nh,
-            buffer, size, fds, numFds);
-    if (status != NO_ERROR) {
-        return status;
-    }
-    // HdrMetadata ignored
-    return unflatten(&(t->surfaceDamage), buffer, size);
-}
-
-/**
- * \brief Wrap `BGraphicBufferProducer::QueueBufferInput` in
- * `HGraphicBufferProducer::QueueBufferInput`.
- *
- * \param[out] t The wrapper of type
- * `HGraphicBufferProducer::QueueBufferInput`.
- * \param[out] nh The underlying native handle for `t->fence`.
- * \param[in] l The source `BGraphicBufferProducer::QueueBufferInput`.
- *
- * If the return value is `true` and `t->fence` contains a valid file
- * descriptor, \p nh will be a newly created native handle holding that file
- * descriptor. \p nh needs to be deleted with `native_handle_delete()`
- * afterwards.
- */
-inline bool wrapAs(
-        HGraphicBufferProducer::QueueBufferInput* t,
-        native_handle_t** nh,
-        BGraphicBufferProducer::QueueBufferInput const& l) {
-
-    size_t const baseSize = l.getFlattenedSize();
-    std::unique_ptr<uint8_t[]> baseBuffer(
-            new (std::nothrow) uint8_t[baseSize]);
-    if (!baseBuffer) {
-        return false;
-    }
-
-    size_t const baseNumFds = l.getFdCount();
-    std::unique_ptr<int[]> baseFds(
-            new (std::nothrow) int[baseNumFds]);
-    if (!baseFds) {
-        return false;
-    }
-
-    void* buffer = static_cast<void*>(baseBuffer.get());
-    size_t size = baseSize;
-    int* fds = baseFds.get();
-    size_t numFds = baseNumFds;
-    if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
-        return false;
-    }
-
-    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
-    size = baseSize;
-    int const* constFds = static_cast<int const*>(baseFds.get());
-    numFds = baseNumFds;
-    if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) {
-        return false;
-    }
-
-    return true;
-}
-
-/**
- * \brief Convert `HGraphicBufferProducer::QueueBufferInput` to
- * `BGraphicBufferProducer::QueueBufferInput`.
- *
- * \param[out] l The destination `BGraphicBufferProducer::QueueBufferInput`.
- * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`.
- *
- * If `t.fence` has a valid file descriptor, it will be duplicated.
- */
-inline bool convertTo(
-        BGraphicBufferProducer::QueueBufferInput* l,
-        HGraphicBufferProducer::QueueBufferInput const& t) {
-
-    size_t const baseSize = getFlattenedSize(t);
-    std::unique_ptr<uint8_t[]> baseBuffer(
-            new (std::nothrow) uint8_t[baseSize]);
-    if (!baseBuffer) {
-        return false;
-    }
-
-    size_t const baseNumFds = getFdCount(t);
-    std::unique_ptr<int[]> baseFds(
-            new (std::nothrow) int[baseNumFds]);
-    if (!baseFds) {
-        return false;
-    }
-
-    void* buffer = static_cast<void*>(baseBuffer.get());
-    size_t size = baseSize;
-    int* fds = baseFds.get();
-    size_t numFds = baseNumFds;
-    native_handle_t* nh;
-    if (flatten(t, &nh, buffer, size, fds, numFds) != NO_ERROR) {
-        return false;
-    }
-
-    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
-    size = baseSize;
-    int const* constFds = static_cast<int const*>(baseFds.get());
-    numFds = baseNumFds;
-    if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
-        if (nh != nullptr) {
-            native_handle_close(nh);
-            native_handle_delete(nh);
-        }
-        return false;
-    }
-
-    native_handle_delete(nh);
-    return true;
-}
-
-// Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp:
-//      BGraphicBufferProducer::QueueBufferOutput
-
-/**
- * \brief Wrap `BGraphicBufferProducer::QueueBufferOutput` in
- * `HGraphicBufferProducer::QueueBufferOutput`.
- *
- * \param[out] t The wrapper of type
- * `HGraphicBufferProducer::QueueBufferOutput`.
- * \param[out] nh The array of array of native handles that are referred to by
- * members of \p t.
- * \param[in] l The source `BGraphicBufferProducer::QueueBufferOutput`.
- *
- * On success, each member of \p nh will be either `nullptr` or a newly created
- * native handle. All the non-`nullptr` elements must be deleted individually
- * with `native_handle_delete()`.
- */
-// wrap: BGraphicBufferProducer::QueueBufferOutput ->
-// HGraphicBufferProducer::QueueBufferOutput
-inline bool wrapAs(HGraphicBufferProducer::QueueBufferOutput* t,
-        std::vector<std::vector<native_handle_t*> >* nh,
-        BGraphicBufferProducer::QueueBufferOutput const& l) {
-    if (!wrapAs(&(t->frameTimestamps), nh, l.frameTimestamps)) {
-        return false;
-    }
-    t->width = l.width;
-    t->height = l.height;
-    t->transformHint = l.transformHint;
-    t->numPendingBuffers = l.numPendingBuffers;
-    t->nextFrameNumber = l.nextFrameNumber;
-    t->bufferReplaced = l.bufferReplaced;
-    return true;
-}
-
-/**
- * \brief Convert `HGraphicBufferProducer::QueueBufferOutput` to
- * `BGraphicBufferProducer::QueueBufferOutput`.
- *
- * \param[out] l The destination `BGraphicBufferProducer::QueueBufferOutput`.
- * \param[in] t The source `HGraphicBufferProducer::QueueBufferOutput`.
- *
- * This function will duplicate all file descriptors contained in \p t.
- */
-// convert: HGraphicBufferProducer::QueueBufferOutput ->
-// BGraphicBufferProducer::QueueBufferOutput
-inline bool convertTo(
-        BGraphicBufferProducer::QueueBufferOutput* l,
-        HGraphicBufferProducer::QueueBufferOutput const& t) {
-    if (!convertTo(&(l->frameTimestamps), t.frameTimestamps)) {
-        return false;
-    }
-    l->width = t.width;
-    l->height = t.height;
-    l->transformHint = t.transformHint;
-    l->numPendingBuffers = t.numPendingBuffers;
-    l->nextFrameNumber = t.nextFrameNumber;
-    l->bufferReplaced = t.bufferReplaced;
-    return true;
-}
-
-/**
- * \brief Convert `BGraphicBufferProducer::DisconnectMode` to
- * `HGraphicBufferProducer::DisconnectMode`.
- *
- * \param[in] l The source `BGraphicBufferProducer::DisconnectMode`.
- * \return The corresponding `HGraphicBufferProducer::DisconnectMode`.
- */
-inline HGraphicBufferProducer::DisconnectMode toOmxDisconnectMode(
-        BGraphicBufferProducer::DisconnectMode l) {
-    switch (l) {
-        case BGraphicBufferProducer::DisconnectMode::Api:
-            return HGraphicBufferProducer::DisconnectMode::API;
-        case BGraphicBufferProducer::DisconnectMode::AllLocal:
-            return HGraphicBufferProducer::DisconnectMode::ALL_LOCAL;
-    }
-    return HGraphicBufferProducer::DisconnectMode::API;
-}
-
-/**
- * \brief Convert `HGraphicBufferProducer::DisconnectMode` to
- * `BGraphicBufferProducer::DisconnectMode`.
- *
- * \param[in] l The source `HGraphicBufferProducer::DisconnectMode`.
- * \return The corresponding `BGraphicBufferProducer::DisconnectMode`.
- */
-inline BGraphicBufferProducer::DisconnectMode toGuiDisconnectMode(
-        HGraphicBufferProducer::DisconnectMode t) {
-    switch (t) {
-        case HGraphicBufferProducer::DisconnectMode::API:
-            return BGraphicBufferProducer::DisconnectMode::Api;
-        case HGraphicBufferProducer::DisconnectMode::ALL_LOCAL:
-            return BGraphicBufferProducer::DisconnectMode::AllLocal;
-    }
-    return BGraphicBufferProducer::DisconnectMode::Api;
-}
-
 }  // namespace implementation
 }  // namespace V1_0
 }  // namespace omx
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/WGraphicBufferProducer.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/WGraphicBufferProducer.h
index 4a3fe0c..322a699 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/WGraphicBufferProducer.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/WGraphicBufferProducer.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016, The Android Open Source Project
+ * 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.
@@ -17,14 +17,7 @@
 #ifndef ANDROID_HARDWARE_MEDIA_OMX_V1_0_WGRAPHICBUFFERPRODUCER_H
 #define ANDROID_HARDWARE_MEDIA_OMX_V1_0_WGRAPHICBUFFERPRODUCER_H
 
-#include <hidl/MQDescriptor.h>
-#include <hidl/Status.h>
-
-#include <binder/Binder.h>
-#include <gui/IGraphicBufferProducer.h>
-#include <gui/IProducerListener.h>
-
-#include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h>
+#include <media/stagefright/bqhelper/WGraphicBufferProducer.h>
 
 namespace android {
 namespace hardware {
@@ -33,67 +26,8 @@
 namespace V1_0 {
 namespace implementation {
 
-using ::android::hardware::graphics::common::V1_0::PixelFormat;
-using ::android::hardware::media::V1_0::AnwBuffer;
-using ::android::hidl::base::V1_0::IBase;
-using ::android::hardware::hidl_array;
-using ::android::hardware::hidl_memory;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::sp;
-
-typedef ::android::hardware::graphics::bufferqueue::V1_0::
-        IGraphicBufferProducer HGraphicBufferProducer;
-typedef ::android::hardware::graphics::bufferqueue::V1_0::
-        IProducerListener HProducerListener;
-
-typedef ::android::IGraphicBufferProducer BGraphicBufferProducer;
-typedef ::android::IProducerListener BProducerListener;
-using ::android::BnGraphicBufferProducer;
-
-struct TWGraphicBufferProducer : public HGraphicBufferProducer {
-    sp<BGraphicBufferProducer> mBase;
-    TWGraphicBufferProducer(sp<BGraphicBufferProducer> const& base);
-    Return<void> requestBuffer(int32_t slot, requestBuffer_cb _hidl_cb)
-            override;
-    Return<int32_t> setMaxDequeuedBufferCount(int32_t maxDequeuedBuffers)
-            override;
-    Return<int32_t> setAsyncMode(bool async) override;
-    Return<void> dequeueBuffer(
-            uint32_t width, uint32_t height, PixelFormat format, uint32_t usage,
-            bool getFrameTimestamps, dequeueBuffer_cb _hidl_cb) override;
-    Return<int32_t> detachBuffer(int32_t slot) override;
-    Return<void> detachNextBuffer(detachNextBuffer_cb _hidl_cb) override;
-    Return<void> attachBuffer(const AnwBuffer& buffer, attachBuffer_cb _hidl_cb)
-            override;
-    Return<void> queueBuffer(
-            int32_t slot, const HGraphicBufferProducer::QueueBufferInput& input,
-            queueBuffer_cb _hidl_cb) override;
-    Return<int32_t> cancelBuffer(int32_t slot, const hidl_handle& fence)
-            override;
-    Return<void> query(int32_t what, query_cb _hidl_cb) override;
-    Return<void> connect(const sp<HProducerListener>& listener,
-            int32_t api, bool producerControlledByApp,
-            connect_cb _hidl_cb) override;
-    Return<int32_t> disconnect(
-            int32_t api,
-            HGraphicBufferProducer::DisconnectMode mode) override;
-    Return<int32_t> setSidebandStream(const hidl_handle& stream) override;
-    Return<void> allocateBuffers(
-            uint32_t width, uint32_t height,
-            PixelFormat format, uint32_t usage) override;
-    Return<int32_t> allowAllocation(bool allow) override;
-    Return<int32_t> setGenerationNumber(uint32_t generationNumber) override;
-    Return<void> getConsumerName(getConsumerName_cb _hidl_cb) override;
-    Return<int32_t> setSharedBufferMode(bool sharedBufferMode) override;
-    Return<int32_t> setAutoRefresh(bool autoRefresh) override;
-    Return<int32_t> setDequeueTimeout(int64_t timeoutNs) override;
-    Return<void> getLastQueuedBuffer(getLastQueuedBuffer_cb _hidl_cb) override;
-    Return<void> getFrameTimestamps(getFrameTimestamps_cb _hidl_cb) override;
-    Return<void> getUniqueId(getUniqueId_cb _hidl_cb) override;
-};
+using TWGraphicBufferProducer = ::android::TWGraphicBufferProducer<
+    ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer>;
 
 }  // namespace implementation
 }  // namespace V1_0
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/OmxGraphicBufferSource.h b/media/libstagefright/omx/include/media/stagefright/omx/OmxGraphicBufferSource.h
index 4b0f3d2..518e0cb 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/OmxGraphicBufferSource.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/OmxGraphicBufferSource.h
@@ -18,7 +18,7 @@
 
 #define OMX_GRAPHIC_BUFFER_SOURCE_H_
 
-#include <media/stagefright/gbs/GraphicBufferSource.h>
+#include <media/stagefright/bqhelper/GraphicBufferSource.h>
 #include <media/stagefright/foundation/ABase.h>
 
 #include <android/BnGraphicBufferSource.h>
@@ -34,7 +34,7 @@
  * This class is used to feed OMX codecs from a Surface via BufferQueue or
  * HW producer.
  *
- * See media/stagefright/gbs/GraphicBufferSource.h for documentation.
+ * See media/stagefright/bqhelper/GraphicBufferSource.h for documentation.
  */
 class OmxGraphicBufferSource : public GraphicBufferSource {
 public:
diff --git a/media/ndk/NdkMediaDataSource.cpp b/media/ndk/NdkMediaDataSource.cpp
index 0cae6f4..9d00e5e 100644
--- a/media/ndk/NdkMediaDataSource.cpp
+++ b/media/ndk/NdkMediaDataSource.cpp
@@ -40,10 +40,19 @@
     void *userdata;
     AMediaDataSourceReadAt readAt;
     AMediaDataSourceGetSize getSize;
+    AMediaDataSourceClose close;
 };
 
 NdkDataSource::NdkDataSource(AMediaDataSource *dataSource)
-    : mDataSource(dataSource) {
+    : mDataSource(AMediaDataSource_new()) {
+      AMediaDataSource_setReadAt(mDataSource, dataSource->readAt);
+      AMediaDataSource_setGetSize(mDataSource, dataSource->getSize);
+      AMediaDataSource_setClose(mDataSource, dataSource->close);
+      AMediaDataSource_setUserdata(mDataSource, dataSource->userdata);
+}
+
+NdkDataSource::~NdkDataSource() {
+    AMediaDataSource_delete(mDataSource);
 }
 
 status_t NdkDataSource::initCheck() const {
@@ -77,6 +86,12 @@
     return String8("application/octet-stream");
 }
 
+void NdkDataSource::close() {
+    if (mDataSource->close != NULL && mDataSource->userdata != NULL) {
+        mDataSource->close(mDataSource->userdata);
+    }
+}
+
 extern "C" {
 
 EXPORT
@@ -85,6 +100,7 @@
     mSource->userdata = NULL;
     mSource->readAt = NULL;
     mSource->getSize = NULL;
+    mSource->close = NULL;
     return mSource;
 }
 
@@ -111,5 +127,10 @@
     mSource->getSize = getSize;
 }
 
+EXPORT
+void AMediaDataSource_setClose(AMediaDataSource *mSource, AMediaDataSourceClose close) {
+    mSource->close = close;
+}
+
 } // extern "C"
 
diff --git a/media/ndk/NdkMediaDataSourcePriv.h b/media/ndk/NdkMediaDataSourcePriv.h
index a1cb331..ea9c865 100644
--- a/media/ndk/NdkMediaDataSourcePriv.h
+++ b/media/ndk/NdkMediaDataSourcePriv.h
@@ -47,6 +47,10 @@
     virtual status_t getSize(off64_t *);
     virtual String8 toString();
     virtual String8 getMIMEType() const;
+    virtual void close();
+
+protected:
+    virtual ~NdkDataSource();
 
 private:
 
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index eecc858..fe08ab9 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -240,7 +240,7 @@
         return AMEDIA_ERROR_INVALID_PARAMETER;
     }
     Vector<uint8_t> session;
-    status_t status = mObj->mDrm->openSession(session);
+    status_t status = mObj->mDrm->openSession(DrmPlugin::kSecurityLevelMax, session);
     if (status == OK) {
         mObj->mIds.push_front(session);
         List<idvec_t>::iterator iter = mObj->mIds.begin();
diff --git a/media/ndk/include/media/NdkMediaDataSource.h b/media/ndk/include/media/NdkMediaDataSource.h
index 752b684..9e2e351 100644
--- a/media/ndk/include/media/NdkMediaDataSource.h
+++ b/media/ndk/include/media/NdkMediaDataSource.h
@@ -74,6 +74,13 @@
 typedef ssize_t (*AMediaDataSourceGetSize)(void *userdata);
 
 /**
+ * Called to close the data source and release associated resources.
+ * The NDK media framework guarantees that after |close| is called
+ * no future callbacks will be invoked on the data source.
+ */
+typedef void (*AMediaDataSourceClose)(void *userdata);
+
+/**
  * Create new media data source. Returns NULL if memory allocation
  * for the new data source object fails.
  */
@@ -117,6 +124,17 @@
         AMediaDataSource*,
         AMediaDataSourceGetSize);
 
+/**
+ * Set a custom callback to receive signal from the NDK media framework
+ * when the data source is closed.
+ *
+ * Please refer to the definition of AMediaDataSourceClose for
+ * additional details.
+ */
+void AMediaDataSource_setClose(
+        AMediaDataSource*,
+        AMediaDataSourceClose);
+
 #endif  /*__ANDROID_API__ >= 28 */
 
 __END_DECLS
diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt
index 37c557a..17c1a0d 100644
--- a/media/ndk/libmediandk.map.txt
+++ b/media/ndk/libmediandk.map.txt
@@ -125,6 +125,7 @@
     AMediaCrypto_requiresSecureDecoderComponent;
     AMediaDataSource_delete;      # introduced=28
     AMediaDataSource_new;         # introduced=28
+    AMediaDataSource_setClose;    # introduced=28
     AMediaDataSource_setGetSize;  # introduced=28
     AMediaDataSource_setReadAt;   # introduced=28
     AMediaDataSource_setUserdata; # introduced=28
diff --git a/packages/MediaComponents/Android.mk b/packages/MediaComponents/Android.mk
index 7c7718e..22eea25 100644
--- a/packages/MediaComponents/Android.mk
+++ b/packages/MediaComponents/Android.mk
@@ -16,6 +16,12 @@
 
 LOCAL_PATH := $(call my-dir)
 
+ifneq ($(TARGET_BUILD_PDK),true)
+# Build MediaComponents only if this is not a PDK build.  MediaComponents won't
+# build in PDK builds because frameworks/base/core/java is not available but
+# IMediaSession2.aidl and IMediaSession2Callback.aidl are using classes from
+# frameworks/base/core/java.
+
 include $(CLEAR_VARS)
 
 LOCAL_PACKAGE_NAME := MediaComponents
@@ -60,4 +66,6 @@
 
 include $(BUILD_PACKAGE)
 
+endif  # ifneq ($(TARGET_BUILD_PDK),true)
+
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/MediaComponents/res/drawable/ic_arrow_back.xml b/packages/MediaComponents/res/drawable/ic_arrow_back.xml
index 5aba8c6..2b5f71e 100644
--- a/packages/MediaComponents/res/drawable/ic_arrow_back.xml
+++ b/packages/MediaComponents/res/drawable/ic_arrow_back.xml
@@ -1,6 +1,6 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="40dp"
-        android:height="40dp"
+        android:width="24dp"
+        android:height="24dp"
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
diff --git a/packages/MediaComponents/res/drawable/ic_launch.xml b/packages/MediaComponents/res/drawable/ic_launch.xml
new file mode 100644
index 0000000..f7af6aa
--- /dev/null
+++ b/packages/MediaComponents/res/drawable/ic_launch.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/MediaComponents/res/layout/media_controller.xml b/packages/MediaComponents/res/layout/media_controller.xml
index dd56e7c..f9ebd44 100644
--- a/packages/MediaComponents/res/layout/media_controller.xml
+++ b/packages/MediaComponents/res/layout/media_controller.xml
@@ -24,35 +24,88 @@
     <RelativeLayout
         android:id="@+id/title_bar"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content">
+        android:layout_height="wrap_content"
+        style="@style/TitleBar">
 
-        <RadioButton
-            android:id="@+id/back"
+        <LinearLayout
+            android:id="@+id/title_bar_left"
+            android:gravity="center"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_alignParentStart="true"
+            android:layout_alignParentLeft="true"
             android:layout_centerVertical="true"
-            android:checked="true"
-            android:visibility="gone"/>
+            android:orientation="horizontal">
 
-        <TextView
-            android:id="@+id/title_text"
+            <ImageButton
+                android:id="@+id/back"
+                android:clickable="true"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentStart="true"
+                android:layout_centerVertical="true"
+                android:paddingLeft="5dip"
+                android:visibility="visible"
+                style="@style/TitleBarButton.Back"/>
+
+            <TextView
+                android:id="@+id/title_text"
+                android:ellipsize="end"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_toRightOf="@id/back"
+                android:layout_centerVertical="true"
+                android:maxLines="1"
+                android:paddingLeft="5dip"
+                android:paddingRight="5dip"
+                android:textSize="15sp"
+                android:textColor="#FFFFFFFF"/>
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/title_bar_right"
+            android:gravity="center"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_toRightOf="@id/back"
+            android:layout_alignParentRight="true"
             android:layout_centerVertical="true"
-            android:layout_marginLeft="15dp"
-            android:paddingTop="4dp"
-            android:paddingStart="4dp"
-            android:paddingEnd="4dp"
-            android:textSize="20sp"
-            android:textColor="#FFFFFFFF" />
+            android:orientation="horizontal">
 
-        <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" />
+            <LinearLayout
+                android:id="@+id/ad"
+                android:clickable="true"
+                android:gravity="center"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentRight="true"
+                android:layout_centerVertical="true"
+                android:paddingLeft="5dip"
+                android:paddingRight="10dip"
+                android:orientation="horizontal">
+
+                <TextView
+                    android:id="@+id/ad_text"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_centerVertical="true"
+                    android:paddingRight="5dip"
+                    android:text="Visit Advertiser"
+                    android:textSize="10sp"
+                    android:textColor="#FFFFFFFF" />
+
+                <ImageButton
+                    android:id="@+id/ad_launch"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_centerVertical="true"
+                    style="@style/TitleBarButton.Launch" />
+            </LinearLayout>
+
+            <view class="com.android.support.mediarouter.app.MediaRouteButton"
+                android:id="@+id/cast"
+                android:layout_centerVertical="true"
+                android:visibility="visible"
+                style="@style/TitleBarButton" />
+        </LinearLayout>
 
     </RelativeLayout>
 
diff --git a/packages/MediaComponents/res/layout/mr_controller_material_dialog_b.xml b/packages/MediaComponents/res/layout/mr_controller_material_dialog_b.xml
index 3751002..b304471 100644
--- a/packages/MediaComponents/res/layout/mr_controller_material_dialog_b.xml
+++ b/packages/MediaComponents/res/layout/mr_controller_material_dialog_b.xml
@@ -23,7 +23,7 @@
         android:layout_height="wrap_content"
         android:layout_gravity="center"
         android:orientation="vertical"
-        android:background="?attr/colorBackgroundFloating">
+        android:background="?android:attr/colorBackgroundFloating">
         <LinearLayout android:id="@+id/mr_title_bar"
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
@@ -44,7 +44,7 @@
                 android:layout_gravity="center_vertical"
                 android:contentDescription="@string/mr_controller_close_description"
                 android:src="?attr/mediaRouteCloseDrawable"
-                android:background="?attr/selectableItemBackgroundBorderless" />
+                android:background="?android:attr/selectableItemBackgroundBorderless" />
         </LinearLayout>
         <FrameLayout android:id="@+id/mr_custom_control"
             android:layout_width="fill_parent"
@@ -58,7 +58,7 @@
                 android:layout_height="wrap_content"
                 android:adjustViewBounds="true"
                 android:scaleType="fitXY"
-                android:background="?attr/colorPrimary"
+                android:background="?android:attr/colorPrimary"
                 android:layout_gravity="top"
                 android:contentDescription="@string/mr_controller_album_art"
                 android:visibility="gone" />
@@ -89,7 +89,7 @@
                             android:layout_alignParentRight="true"
                             android:layout_centerVertical="true"
                             android:contentDescription="@string/mr_controller_play"
-                            android:background="?attr/selectableItemBackgroundBorderless"
+                            android:background="?android:attr/selectableItemBackgroundBorderless"
                             android:visibility="gone" />
                         <LinearLayout android:id="@+id/mr_control_title_container"
                             android:orientation="vertical"
@@ -146,7 +146,7 @@
                             android:layout_width="48dp"
                             android:layout_height="48dp"
                             android:padding="12dp"
-                            android:background="?attr/selectableItemBackgroundBorderless"
+                            android:background="?android:attr/selectableItemBackgroundBorderless"
                             android:visibility="gone"/>
                     </LinearLayout>
                 </LinearLayout>
@@ -181,7 +181,7 @@
                 android:paddingTop="4dp">
                 <Button
                     android:id="@android:id/button3"
-                    style="?attr/buttonBarNeutralButtonStyle"
+                    style="?android:attr/buttonBarNeutralButtonStyle"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"/>
                 <android.support.v4.widget.Space
@@ -197,7 +197,7 @@
                     android:layout_height="wrap_content"/>
                 <Button
                     android:id="@android:id/button1"
-                    style="?attr/buttonBarPositiveButtonStyle"
+                    style="?android:attr/buttonBarPositiveButtonStyle"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"/>
             </android.support.v7.widget.ButtonBarLayout>
diff --git a/packages/MediaComponents/res/values/style.xml b/packages/MediaComponents/res/values/style.xml
index c59380c..db5e8f3 100644
--- a/packages/MediaComponents/res/values/style.xml
+++ b/packages/MediaComponents/res/values/style.xml
@@ -26,12 +26,22 @@
         <item name="android:src">@drawable/ic_rewind_10</item>
     </style>
 
+    <style name="TitleBar">
+        <item name="android:layout_height">46dp</item>
+    </style>
 
     <style name="TitleBarButton">
         <item name="android:background">@null</item>
         <item name="android:layout_width">36dp</item>
         <item name="android:layout_height">36dp</item>
-        <item name="android:layout_margin">10dp</item>
+    </style>
+
+    <style name="TitleBarButton.Back">
+        <item name="android:src">@drawable/ic_arrow_back</item>
+    </style>
+
+    <style name="TitleBarButton.Launch">
+        <item name="android:src">@drawable/ic_launch</item>
     </style>
 
     <style name="BottomBarButton">
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
index 7702bda..9538c3d 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
@@ -63,4 +63,10 @@
     // Get library service specific
     //////////////////////////////////////////////////////////////////////////////////////////////
     void getBrowserRoot(IMediaSession2Callback callback, in Bundle rootHints);
+    void getItem(IMediaSession2Callback callback, String mediaId);
+    void getChildren(IMediaSession2Callback callback, String parentId, int page, int pageSize,
+            in Bundle extras);
+    void search(IMediaSession2Callback callback, String query, in Bundle extras);
+    void getSearchResult(IMediaSession2Callback callback, String query, int page, int pageSize,
+            in Bundle extras);
 }
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
index a443bf8..b3aa59c 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
@@ -48,4 +48,9 @@
     // Browser sepcific
     //////////////////////////////////////////////////////////////////////////////////////////////
     void onGetRootResult(in Bundle rootHints, String rootMediaId, in Bundle rootExtra);
+    void onItemLoaded(String mediaId, in Bundle result);
+    void onChildrenLoaded(String parentId, int page, int pageSize, in Bundle extras,
+            in List<Bundle> result);
+    void onSearchResultLoaded(String query, int page, int pageSize, in Bundle extras,
+            in List<Bundle> result);
 }
diff --git a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
index 3e6d98f..76da42b 100644
--- a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
@@ -19,11 +19,13 @@
 import android.content.Context;
 import android.media.MediaBrowser2;
 import android.media.MediaBrowser2.BrowserCallback;
+import android.media.MediaItem2;
 import android.media.MediaSession2.CommandButton;
 import android.media.SessionToken2;
 import android.media.update.MediaBrowser2Provider;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.text.TextUtils;
 import android.util.Log;
 
 import java.util.List;
@@ -61,28 +63,98 @@
     }
 
     @Override
-    public void subscribe_impl(String parentId, Bundle options) {
+    public void subscribe_impl(String parentId, Bundle extras) {
         // TODO(jaewan): Implement
     }
 
     @Override
-    public void unsubscribe_impl(String parentId, Bundle options) {
+    public void unsubscribe_impl(String parentId, Bundle extras) {
         // TODO(jaewan): Implement
     }
 
     @Override
     public void getItem_impl(String mediaId) {
-        // TODO(jaewan): Implement
+        if (mediaId == null) {
+            throw new IllegalArgumentException("mediaId shouldn't be null");
+        }
+
+        final IMediaSession2 binder = getSessionBinder();
+        if (binder != null) {
+            try {
+                binder.getItem(getControllerStub(), mediaId);
+            } catch (RemoteException e) {
+                // TODO(jaewan): Handle disconnect.
+                if (DEBUG) {
+                    Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+                }
+            }
+        } else {
+            Log.w(TAG, "Session isn't active", new IllegalStateException());
+        }
     }
 
     @Override
-    public void getChildren_impl(String parentId, int page, int pageSize, Bundle options) {
-        // TODO(jaewan): Implement
+    public void getChildren_impl(String parentId, int page, int pageSize, Bundle extras) {
+        if (parentId == null) {
+            throw new IllegalArgumentException("parentId shouldn't be null");
+        }
+        if (page < 1 || pageSize < 1) {
+            throw new IllegalArgumentException("Neither page nor pageSize should be less than 1");
+        }
+
+        final IMediaSession2 binder = getSessionBinder();
+        if (binder != null) {
+            try {
+                binder.getChildren(getControllerStub(), parentId, page, pageSize, extras);
+            } catch (RemoteException e) {
+                // TODO(jaewan): Handle disconnect.
+                if (DEBUG) {
+                    Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+                }
+            }
+        } else {
+            Log.w(TAG, "Session isn't active", new IllegalStateException());
+        }
     }
 
     @Override
-    public void search_impl(String query, int page, int pageSize, Bundle extras) {
-        // TODO(jaewan): Implement
+    public void search_impl(String query, Bundle extras) {
+        if (TextUtils.isEmpty(query)) {
+            throw new IllegalArgumentException("query shouldn't be empty");
+        }
+        final IMediaSession2 binder = getSessionBinder();
+        if (binder != null) {
+            try {
+                binder.search(getControllerStub(), query, extras);
+            } catch (RemoteException e) {
+                // TODO(jaewan): Handle disconnect.
+                if (DEBUG) {
+                    Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+                }
+            }
+        } else {
+            Log.w(TAG, "Session isn't active", new IllegalStateException());
+        }
+    }
+
+    @Override
+    public void getSearchResult_impl(String query, int page, int pageSize, Bundle extras) {
+        if (TextUtils.isEmpty(query)) {
+            throw new IllegalArgumentException("query shouldn't be empty");
+        }
+        final IMediaSession2 binder = getSessionBinder();
+        if (binder != null) {
+            try {
+                binder.getSearchResult(getControllerStub(), query, page, pageSize, extras);
+            } catch (RemoteException e) {
+                // TODO(jaewan): Handle disconnect.
+                if (DEBUG) {
+                    Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+                }
+            }
+        } else {
+            Log.w(TAG, "Session isn't active", new IllegalStateException());
+        }
     }
 
     public void onGetRootResult(
@@ -92,9 +164,23 @@
         });
     }
 
-    public void onCustomLayoutChanged(final List<CommandButton> layout) {
+    public void onItemLoaded(String mediaId, MediaItem2 item) {
         getCallbackExecutor().execute(() -> {
-            mCallback.onCustomLayoutChanged(layout);
+            mCallback.onItemLoaded(mediaId, item);
+        });
+    }
+
+    public void onChildrenLoaded(String parentId, int page, int pageSize, Bundle extras,
+            List<MediaItem2> result) {
+        getCallbackExecutor().execute(() -> {
+            mCallback.onChildrenLoaded(parentId, page, pageSize, extras, result);
+        });
+    }
+
+    public void onSearchResultLoaded(String query, int page, int pageSize, Bundle extras,
+            List<MediaItem2> result) {
+        getCallbackExecutor().execute(() -> {
+            mCallback.onSearchResultLoaded(query, page, pageSize, extras, result);
         });
     }
 }
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index a477d1d..5af4240 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -21,14 +21,15 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.media.AudioAttributes;
+import android.media.MediaController2;
+import android.media.MediaController2.ControllerCallback;
 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.MediaSession2.PlaylistParams;
 import android.media.MediaSessionService2;
 import android.media.PlaybackState2;
@@ -43,9 +44,6 @@
 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;
 import java.util.concurrent.Executor;
@@ -219,7 +217,11 @@
     }
 
     Context getContext() {
-      return mContext;
+        return mContext;
+    }
+
+    MediaController2 getInstance() {
+        return mInstance;
     }
 
     @Override
@@ -502,10 +504,7 @@
         sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS, args);
     }
 
-    ///////////////////////////////////////////////////
-    // Protected or private methods
-    ///////////////////////////////////////////////////
-    private void pushPlaybackStateChanges(final PlaybackState2 state) {
+    void pushPlaybackStateChanges(final PlaybackState2 state) {
         synchronized (mLock) {
             mPlaybackState = state;
         }
@@ -517,7 +516,7 @@
         });
     }
 
-    private void pushPlaylistParamsChanges(final PlaylistParams params) {
+    void pushPlaylistParamsChanges(final PlaylistParams params) {
         synchronized (mLock) {
             mPlaylistParams = params;
         }
@@ -529,7 +528,7 @@
         });
     }
 
-    private void pushPlaybackInfoChanges(final PlaybackInfo info) {
+    void pushPlaybackInfoChanges(final PlaybackInfo info) {
         synchronized (mLock) {
             mPlaybackInfo = info;
         }
@@ -541,7 +540,7 @@
         });
     }
 
-    private void pushPlaylistChanges(final List<Bundle> list) {
+    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));
@@ -562,7 +561,7 @@
     }
 
     // Should be used without a lock to prevent potential deadlock.
-    private void onConnectedNotLocked(IMediaSession2 sessionBinder,
+    void onConnectedNotLocked(IMediaSession2 sessionBinder,
             final CommandGroup commandGroup, final PlaybackState2 state, final PlaybackInfo info,
             final PlaylistParams params, final List<MediaItem2> playlist, final int ratingType,
             final PendingIntent sessionActivity) {
@@ -623,7 +622,7 @@
         }
     }
 
-    private void onCustomCommand(final Command command, final Bundle args,
+    void onCustomCommand(final Command command, final Bundle args,
             final ResultReceiver receiver) {
         if (DEBUG) {
             Log.d(TAG, "onCustomCommand cmd=" + command);
@@ -634,193 +633,10 @@
         });
     }
 
-    // TODO(jaewan): Pull out this from the controller2, and rename it to the MediaController2Stub
-    //               or MediaBrowser2Stub.
-    static class MediaSession2CallbackStub extends IMediaSession2Callback.Stub {
-        private final WeakReference<MediaController2Impl> mController;
-
-        private MediaSession2CallbackStub(MediaController2Impl controller) {
-            mController = new WeakReference<>(controller);
-        }
-
-        private MediaController2Impl getController() throws IllegalStateException {
-            final MediaController2Impl controller = mController.get();
-            if (controller == null) {
-                throw new IllegalStateException("Controller is released");
-            }
-            return controller;
-        }
-
-        // TODO(jaewan): Refactor code to get rid of these pattern.
-        private MediaBrowser2Impl getBrowser() throws IllegalStateException {
-            final MediaController2Impl controller = getController();
-            if (controller instanceof MediaBrowser2Impl) {
-                return (MediaBrowser2Impl) controller;
-            }
-            return null;
-        }
-
-        public void destroy() {
-            mController.clear();
-        }
-
-        @Override
-        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
-        public void onPlaybackInfoChanged(Bundle playbackInfo) throws RuntimeException {
-            if (DEBUG) {
-                Log.d(TAG, "onPlaybackInfoChanged");
-            }
-            final MediaController2Impl controller;
-            try {
-                controller = getController();
-            } catch (IllegalStateException e) {
-                Log.w(TAG, "Don't fail silently here. Highly likely a bug");
-                return;
-            }
-            controller.pushPlaybackInfoChanges(
-                    PlaybackInfoImpl.fromBundle(controller.getContext(), playbackInfo));
-        }
-
-        @Override
-        public void onConnected(IMediaSession2 sessionBinder, Bundle commandGroup,
-                Bundle playbackState, Bundle playbackInfo, Bundle playlistParams, List<Bundle>
-                playlist, int ratingType, PendingIntent sessionActivity) {
-            final MediaController2Impl controller = mController.get();
-            if (controller == null) {
-                if (DEBUG) {
-                    Log.d(TAG, "onConnected after MediaController2.close()");
-                }
-                return;
-            }
-            final Context context = controller.getContext();
-            List<MediaItem2> list = new ArrayList<>();
-            for (int i = 0; i < playlist.size(); i++) {
-                MediaItem2 item = MediaItem2.fromBundle(context, playlist.get(i));
-                if (item != null) {
-                    list.add(item);
-                }
-            }
-            controller.onConnectedNotLocked(sessionBinder,
-                    CommandGroup.fromBundle(context, commandGroup),
-                    PlaybackState2.fromBundle(context, playbackState),
-                    PlaybackInfoImpl.fromBundle(context, playbackInfo),
-                    PlaylistParams.fromBundle(context, playlistParams),
-                    list, ratingType, sessionActivity);
-        }
-
-        @Override
-        public void onDisconnected() {
-            final MediaController2Impl controller = mController.get();
-            if (controller == null) {
-                if (DEBUG) {
-                    Log.d(TAG, "onDisconnected after MediaController2.close()");
-                }
-                return;
-            }
-            controller.mInstance.close();
-        }
-
-        @Override
-        public void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra)
-                throws RuntimeException {
-            final MediaBrowser2Impl browser;
-            try {
-                browser = getBrowser();
-            } catch (IllegalStateException e) {
-                Log.w(TAG, "Don't fail silently here. Highly likely a bug");
-                return;
-            }
-            if (browser == null) {
-                // TODO(jaewan): Revisit here. Could be a bug
-                return;
-            }
-            browser.onGetRootResult(rootHints, rootMediaId, rootExtra);
-        }
-
-        @Override
-        public void onCustomLayoutChanged(List<Bundle> commandButtonlist) {
-            if (commandButtonlist == null) {
-                // Illegal call. Ignore
-                return;
-            }
-            // TODO(jaewan): Fix here. It's controller feature so shouldn't use browser
-            final MediaBrowser2Impl browser;
-            try {
-                browser = getBrowser();
-            } catch (IllegalStateException e) {
-                Log.w(TAG, "Don't fail silently here. Highly likely a bug");
-                return;
-            }
-            if (browser == null) {
-                // TODO(jaewan): Revisit here. Could be a bug
-                return;
-            }
-            List<CommandButton> layout = new ArrayList<>();
-            for (int i = 0; i < commandButtonlist.size(); 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);
-        }
+    void onCustomLayoutChanged(final List<CommandButton> layout) {
+        mCallbackExecutor.execute(() -> {
+            mCallback.onCustomLayoutChanged(layout);
+        });
     }
 
     // This will be called on the main thread.
@@ -859,4 +675,97 @@
             mInstance.close();
         }
     }
+
+    public static 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/MediaItem2Impl.java b/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
index f51e246..4c4ef24 100644
--- a/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaItem2Impl.java
@@ -52,7 +52,7 @@
             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");
+            throw new IllegalArgumentException("metadata's id should be matched with the mediaid");
         }
 
         mContext = context;
@@ -71,9 +71,9 @@
             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");
+            throw new IllegalArgumentException("metadata's id should be matched with the mediaid");
         }
-        mContext =context;
+        mContext = context;
         mId = mediaId;
         mMetadata = metadata;
         mFlags = flags;
@@ -136,24 +136,21 @@
     }
 
     @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");
+    public void setMetadata_impl(@Nullable MediaMetadata2 metadata) {
+        if (metadata != null && !TextUtils.equals(mId, metadata.getMediaId())) {
+            throw new IllegalArgumentException("metadata's id should be matched with the mediaId");
         }
         mMetadata = metadata;
     }
 
     @Override
-    public MediaMetadata2 getMetadata_impl() {
+    public @Nullable MediaMetadata2 getMetadata_impl() {
         return mMetadata;
     }
 
     @Override
-    public @Nullable String getMediaId_impl() {
-        return mMetadata.getMediaId();
+    public @NonNull String getMediaId_impl() {
+        return mId;
     }
 
     @Override
diff --git a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
index 77bd334..52db74e 100644
--- a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
@@ -26,6 +26,7 @@
 import android.media.MediaPlayerInterface;
 import android.media.MediaSession2;
 import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.SessionCallback;
 import android.media.MediaSessionService2;
 import android.media.SessionToken2;
 import android.media.VolumeProvider2;
@@ -65,15 +66,17 @@
 
     public static class MediaLibrarySessionImpl extends MediaSession2Impl
             implements MediaLibrarySessionProvider {
-        private final MediaLibrarySessionCallback mCallback;
-
         public MediaLibrarySessionImpl(Context context,
                 MediaPlayerInterface player, String id, VolumeProvider2 volumeProvider,
                 int ratingType, PendingIntent sessionActivity, Executor callbackExecutor,
                 MediaLibrarySessionCallback callback) {
             super(context, player, id, volumeProvider, ratingType, sessionActivity,
                     callbackExecutor, callback);
-            mCallback = callback;
+            // Don't put any extra initialization here. Here's the reason.
+            // System service will recognize this session inside of the super constructor and would
+            // connect to this session assuming that initialization is finished. However, if any
+            // initialization logic is here, calls from the server would fail.
+            // see: MediaSession2Stub#connect()
         }
 
         @Override
@@ -87,6 +90,11 @@
         }
 
         @Override
+        MediaLibrarySessionCallback getCallback() {
+            return (MediaLibrarySessionCallback) super.getCallback();
+        }
+
+        @Override
         public void notifyChildrenChanged_impl(ControllerInfo controller, String parentId,
                 Bundle options) {
             // TODO(jaewan): Implements
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java b/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
new file mode 100644
index 0000000..852029a
--- /dev/null
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
@@ -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.
+ */
+
+package com.android.media;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.media.MediaItem2;
+import android.media.MediaSession2.Command;
+import android.media.MediaSession2.CommandButton;
+import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.PlaylistParams;
+import android.media.PlaybackState2;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+import com.android.media.MediaController2Impl.PlaybackInfoImpl;
+import com.android.media.MediaSession2Impl.CommandButtonImpl;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+public class MediaSession2CallbackStub extends IMediaSession2Callback.Stub {
+    private static final String TAG = "MS2CallbackStub";
+    private static final boolean DEBUG = true; // TODO(jaewan): Change
+
+    private final WeakReference<MediaController2Impl> mController;
+
+    MediaSession2CallbackStub(MediaController2Impl controller) {
+        mController = new WeakReference<>(controller);
+    }
+
+    private MediaController2Impl getController() throws IllegalStateException {
+        final MediaController2Impl controller = mController.get();
+        if (controller == null) {
+            throw new IllegalStateException("Controller is released");
+        }
+        return controller;
+    }
+
+    private MediaBrowser2Impl getBrowser() throws IllegalStateException {
+        final MediaController2Impl controller = getController();
+        if (controller instanceof MediaBrowser2Impl) {
+            return (MediaBrowser2Impl) controller;
+        }
+        return null;
+    }
+
+    public void destroy() {
+        mController.clear();
+    }
+
+    @Override
+    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
+    public void onPlaybackInfoChanged(Bundle playbackInfo) throws RuntimeException {
+        if (DEBUG) {
+            Log.d(TAG, "onPlaybackInfoChanged");
+        }
+        final MediaController2Impl controller;
+        try {
+            controller = getController();
+        } catch (IllegalStateException e) {
+            Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+            return;
+        }
+        controller.pushPlaybackInfoChanges(
+                PlaybackInfoImpl.fromBundle(controller.getContext(), playbackInfo));
+    }
+
+    @Override
+    public void onConnected(IMediaSession2 sessionBinder, Bundle commandGroup,
+            Bundle playbackState, Bundle playbackInfo, Bundle playlistParams, List<Bundle>
+            playlist, int ratingType, PendingIntent sessionActivity) {
+        final MediaController2Impl controller = mController.get();
+        if (controller == null) {
+            if (DEBUG) {
+                Log.d(TAG, "onConnected after MediaController2.close()");
+            }
+            return;
+        }
+        final Context context = controller.getContext();
+        List<MediaItem2> list = new ArrayList<>();
+        for (int i = 0; i < playlist.size(); i++) {
+            MediaItem2 item = MediaItem2.fromBundle(context, playlist.get(i));
+            if (item != null) {
+                list.add(item);
+            }
+        }
+        controller.onConnectedNotLocked(sessionBinder,
+                CommandGroup.fromBundle(context, commandGroup),
+                PlaybackState2.fromBundle(context, playbackState),
+                PlaybackInfoImpl.fromBundle(context, playbackInfo),
+                PlaylistParams.fromBundle(context, playlistParams),
+                list, ratingType, sessionActivity);
+    }
+
+    @Override
+    public void onDisconnected() {
+        final MediaController2Impl controller = mController.get();
+        if (controller == null) {
+            if (DEBUG) {
+                Log.d(TAG, "onDisconnected after MediaController2.close()");
+            }
+            return;
+        }
+        controller.getInstance().close();
+    }
+
+    @Override
+    public void onCustomLayoutChanged(List<Bundle> commandButtonlist) {
+        if (commandButtonlist == null) {
+            // Illegal call. Ignore
+            return;
+        }
+        final MediaController2Impl controller;
+        try {
+            controller = getController();
+        } catch (IllegalStateException e) {
+            Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+            return;
+        }
+        if (controller == null) {
+            // TODO(jaewan): Revisit here. Could be a bug
+            return;
+        }
+        List<CommandButton> layout = new ArrayList<>();
+        for (int i = 0; i < commandButtonlist.size(); i++) {
+            CommandButton button = CommandButtonImpl.fromBundle(
+                    controller.getContext(), commandButtonlist.get(i));
+            if (button != null) {
+                layout.add(button);
+            }
+        }
+        controller.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);
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////
+    // MediaBrowser specific
+    ////////////////////////////////////////////////////////////////////////////////////////////
+    @Override
+    public void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra)
+            throws RuntimeException {
+        final MediaBrowser2Impl browser;
+        try {
+            browser = getBrowser();
+        } catch (IllegalStateException e) {
+            Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+            return;
+        }
+        if (browser == null) {
+            // TODO(jaewan): Revisit here. Could be a bug
+            return;
+        }
+        browser.onGetRootResult(rootHints, rootMediaId, rootExtra);
+    }
+
+
+    @Override
+    public void onItemLoaded(String mediaId, Bundle itemBundle) throws RuntimeException {
+        final MediaBrowser2Impl browser;
+        try {
+            browser = getBrowser();
+        } catch (IllegalStateException e) {
+            Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+            return;
+        }
+        if (browser == null) {
+            // TODO(jaewan): Revisit here. Could be a bug
+            return;
+        }
+        browser.onItemLoaded(mediaId,
+                MediaItem2Impl.fromBundle(browser.getContext(), itemBundle));
+    }
+
+    @Override
+    public void onChildrenLoaded(String parentId, int page, int pageSize, Bundle extras,
+            List<Bundle> itemBundleList) throws RuntimeException {
+        final MediaBrowser2Impl browser;
+        try {
+            browser = getBrowser();
+        } catch (IllegalStateException e) {
+            Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+            return;
+        }
+        if (browser == null) {
+            // TODO(jaewan): Revisit here. Could be a bug
+            return;
+        }
+
+        List<MediaItem2> result = null;
+        if (itemBundleList != null) {
+            result = new ArrayList<>();
+            for (Bundle bundle : itemBundleList) {
+                result.add(MediaItem2.fromBundle(browser.getContext(), bundle));
+            }
+        }
+        browser.onChildrenLoaded(parentId, page, pageSize, extras, result);
+    }
+
+    @Override
+    public void onSearchResultLoaded(String query, int page, int pageSize, Bundle extras,
+            List<Bundle> itemBundleList) throws RuntimeException {
+        final MediaBrowser2Impl browser;
+        try {
+            browser = getBrowser();
+        } catch (IllegalStateException e) {
+            Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+            return;
+        }
+        if (browser == null) {
+            // TODO(jaewan): Revisit here. Could be a bug
+            return;
+        }
+
+        List<MediaItem2> result = null;
+        if (itemBundleList != null) {
+            result = new ArrayList<>();
+            for (Bundle bundle : itemBundleList) {
+                result.add(MediaItem2.fromBundle(browser.getContext(), bundle));
+            }
+        }
+        browser.onSearchResultLoaded(query, page, pageSize, extras, result);
+    }
+}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index f323a17..4a9a729 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -88,6 +88,22 @@
     private final int mRatingType;
     private final PendingIntent mSessionActivity;
 
+    // mPlayer is set to null when the session is closed, and we shouldn't throw an exception
+    // nor leave log always for using mPlayer when it's null. Here's the reason.
+    // When a MediaSession2 is closed, there could be a pended operation in the session callback
+    // executor that may want to access the player. Here's the sample code snippet for that.
+    //
+    //   public void onFoo() {
+    //     if (mPlayer == null) return; // first check
+    //     mSessionCallbackExecutor.executor(() -> {
+    //       // Error. Session may be closed and mPlayer can be null here.
+    //       mPlayer.foo();
+    //     });
+    //   }
+    //
+    // By adding protective code, we can also protect APIs from being called after the close()
+    //
+    // TODO(jaewan): Should we put volatile here?
     @GuardedBy("mLock")
     private MediaPlayerInterface mPlayer;
     @GuardedBy("mLock")
@@ -96,10 +112,6 @@
     private PlaybackInfo mPlaybackInfo;
     @GuardedBy("mLock")
     private MyPlaybackListener mListener;
-    @GuardedBy("mLock")
-    private PlaylistParams mPlaylistParams;
-    @GuardedBy("mLock")
-    private List<MediaItem2> mPlaylist;
 
     /**
      * Can be only called by the {@link Builder#build()}.
@@ -146,9 +158,7 @@
                     mContext.getPackageName(), null, id, mSessionStub).getInstance();
         }
 
-        setPlayerLocked(player);
-        mVolumeProvider = volumeProvider;
-        mPlaybackInfo = createPlaybackInfo(volumeProvider, player.getAudioAttributes());
+        setPlayer(player, volumeProvider);
 
         // Ask server for the sanity check, and starts
         // Sanity check for making session ID unique 'per package' cannot be done in here.
@@ -198,14 +208,7 @@
         if (player == null) {
             throw new IllegalArgumentException("player shouldn't be null");
         }
-        PlaybackInfo info =
-                createPlaybackInfo(null /* VolumeProvider */, player.getAudioAttributes());
-        synchronized (mLock) {
-            setPlayerLocked(player);
-            mVolumeProvider = null;
-            mPlaybackInfo = info;
-        }
-        mSessionStub.notifyPlaybackInfoChanged(info);
+        setPlayer(player, null);
     }
 
     @Override
@@ -218,25 +221,25 @@
         if (volumeProvider == null) {
             throw new IllegalArgumentException("volumeProvider shouldn't be null");
         }
+        setPlayer(player, volumeProvider);
+    }
+
+    private void setPlayer(MediaPlayerInterface player, VolumeProvider2 volumeProvider) {
         PlaybackInfo info = createPlaybackInfo(volumeProvider, player.getAudioAttributes());
         synchronized (mLock) {
-            setPlayerLocked(player);
+            if (mPlayer != null && mListener != null) {
+                // This might not work for a poorly implemented player.
+                mPlayer.removePlaybackListener(mListener);
+            }
+            mPlayer = player;
+            mListener = new MyPlaybackListener(this, player);
+            player.addPlaybackListener(mCallbackExecutor, mListener);
             mVolumeProvider = volumeProvider;
             mPlaybackInfo = info;
         }
         mSessionStub.notifyPlaybackInfoChanged(info);
     }
 
-    private void setPlayerLocked(MediaPlayerInterface player) {
-        if (mPlayer != null && mListener != null) {
-            // This might not work for a poorly implemented player.
-            mPlayer.removePlaybackListener(mListener);
-        }
-        mPlayer = player;
-        mListener = new MyPlaybackListener(this, player);
-        player.addPlaybackListener(mCallbackExecutor, mListener);
-    }
-
     private PlaybackInfo createPlaybackInfo(VolumeProvider2 volumeProvider, AudioAttributes attrs) {
         PlaybackInfo info;
         if (volumeProvider == null) {
@@ -252,7 +255,7 @@
                     stream = AudioManager.STREAM_MUSIC;
                 }
             }
-            info = PlaybackInfoImpl.createPlaybackInfo(
+            info = MediaController2Impl.PlaybackInfoImpl.createPlaybackInfo(
                     mContext,
                     PlaybackInfo.PLAYBACK_TYPE_LOCAL,
                     attrs,
@@ -262,7 +265,7 @@
                     mAudioManager.getStreamMaxVolume(stream),
                     mAudioManager.getStreamVolume(stream));
         } else {
-            info = PlaybackInfoImpl.createPlaybackInfo(
+            info = MediaController2Impl.PlaybackInfoImpl.createPlaybackInfo(
                     mContext,
                     PlaybackInfo.PLAYBACK_TYPE_REMOTE /* ControlType */,
                     attrs,
@@ -320,36 +323,56 @@
     @Override
     public void play_impl() {
         ensureCallingThread();
-        ensurePlayer();
-        mPlayer.play();
+        final MediaPlayerInterface player = mPlayer;
+        if (player != null) {
+            player.play();
+        } else if (DEBUG) {
+            Log.d(TAG, "API calls after the close()", new IllegalStateException());
+        }
     }
 
     @Override
     public void pause_impl() {
         ensureCallingThread();
-        ensurePlayer();
-        mPlayer.pause();
+        final MediaPlayerInterface player = mPlayer;
+        if (player != null) {
+            player.pause();
+        } else if (DEBUG) {
+            Log.d(TAG, "API calls after the close()", new IllegalStateException());
+        }
     }
 
     @Override
     public void stop_impl() {
         ensureCallingThread();
-        ensurePlayer();
-        mPlayer.stop();
+        final MediaPlayerInterface player = mPlayer;
+        if (player != null) {
+            player.stop();
+        } else if (DEBUG) {
+            Log.d(TAG, "API calls after the close()", new IllegalStateException());
+        }
     }
 
     @Override
     public void skipToPrevious_impl() {
         ensureCallingThread();
-        ensurePlayer();
-        mPlayer.skipToPrevious();
+        final MediaPlayerInterface player = mPlayer;
+        if (player != null) {
+            player.skipToPrevious();
+        } else if (DEBUG) {
+            Log.d(TAG, "API calls after the close()", new IllegalStateException());
+        }
     }
 
     @Override
     public void skipToNext_impl() {
         ensureCallingThread();
-        ensurePlayer();
-        mPlayer.skipToNext();
+        final MediaPlayerInterface player = mPlayer;
+        if (player != null) {
+            player.skipToNext();
+        } else if (DEBUG) {
+            Log.d(TAG, "API calls after the close()", new IllegalStateException());
+        }
     }
 
     @Override
@@ -367,21 +390,27 @@
     @Override
     public void setPlaylistParams_impl(PlaylistParams params) {
         if (params == null) {
-            throw new IllegalArgumentException("PlaylistParams should not be null!");
+            throw new IllegalArgumentException("params shouldn't be null");
         }
         ensureCallingThread();
-        ensurePlayer();
-        synchronized (mLock) {
-            mPlaylistParams = params;
+        final MediaPlayerInterface player = mPlayer;
+        if (player != null) {
+            player.setPlaylistParams(params);
+            mSessionStub.notifyPlaylistParamsChanged(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;
+        final MediaPlayerInterface player = mPlayer;
+        if (player != null) {
+            // TODO(jaewan): Is it safe to be called on any thread?
+            //               Otherwise MediaSession2 should cache parameter of setPlaylistParams.
+            return player.getPlaylistParams();
+        } else if (DEBUG) {
+            Log.d(TAG, "API calls after the close()", new IllegalStateException());
+        }
+        return null;
     }
 
     //////////////////////////////////////////////////////////////////////////////////////
@@ -412,57 +441,84 @@
     @Override
     public void setPlaylist_impl(List<MediaItem2> playlist) {
         if (playlist == null) {
-            throw new IllegalArgumentException("Playlist should not be null!");
+            throw new IllegalArgumentException("playlist shouldn't be null");
         }
         ensureCallingThread();
-        ensurePlayer();
-        synchronized (mLock) {
-            mPlaylist = playlist;
+        final MediaPlayerInterface player = mPlayer;
+        if (player != null) {
+            player.setPlaylist(playlist);
+            mSessionStub.notifyPlaylistChanged(playlist);
+        } else if (DEBUG) {
+            Log.d(TAG, "API calls after the close()", new IllegalStateException());
         }
-        mPlayer.setPlaylist(playlist);
-        mSessionStub.notifyPlaylistChanged(playlist);
     }
 
     @Override
     public List<MediaItem2> getPlaylist_impl() {
-        synchronized (mLock) {
-            return mPlaylist;
+        final MediaPlayerInterface player = mPlayer;
+        if (player != null) {
+            // TODO(jaewan): Is it safe to be called on any thread?
+            //               Otherwise MediaSession2 should cache parameter of setPlaylist.
+            return player.getPlaylist();
+        } else if (DEBUG) {
+            Log.d(TAG, "API calls after the close()", new IllegalStateException());
         }
+        return null;
     }
 
     @Override
     public void prepare_impl() {
         ensureCallingThread();
-        ensurePlayer();
-        mPlayer.prepare();
+        final MediaPlayerInterface player = mPlayer;
+        if (player != null) {
+            player.prepare();
+        } else if (DEBUG) {
+            Log.d(TAG, "API calls after the close()", new IllegalStateException());
+        }
     }
 
     @Override
     public void fastForward_impl() {
         ensureCallingThread();
-        ensurePlayer();
-        mPlayer.fastForward();
+        final MediaPlayerInterface player = mPlayer;
+        if (player != null) {
+            player.fastForward();
+        } else if (DEBUG) {
+            Log.d(TAG, "API calls after the close()", new IllegalStateException());
+        }
     }
 
     @Override
     public void rewind_impl() {
         ensureCallingThread();
-        ensurePlayer();
-        mPlayer.rewind();
+        final MediaPlayerInterface player = mPlayer;
+        if (player != null) {
+            player.rewind();
+        } else if (DEBUG) {
+            Log.d(TAG, "API calls after the close()", new IllegalStateException());
+        }
     }
 
     @Override
     public void seekTo_impl(long pos) {
         ensureCallingThread();
-        ensurePlayer();
-        mPlayer.seekTo(pos);
+        final MediaPlayerInterface player = mPlayer;
+        if (player != null) {
+            player.seekTo(pos);
+        } else if (DEBUG) {
+            Log.d(TAG, "API calls after the close()", new IllegalStateException());
+        }
     }
 
     @Override
     public void setCurrentPlaylistItem_impl(int index) {
         ensureCallingThread();
-        ensurePlayer();
-        mPlayer.setCurrentPlaylistItem(index);
+        final MediaPlayerInterface player = mPlayer;
+        if (player != null) {
+            player.setCurrentPlaylistItem(index);
+        } else if (DEBUG) {
+            Log.d(TAG, "API calls after the close()", new IllegalStateException());
+        }
     }
 
     @Override
@@ -497,10 +553,15 @@
     @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();
+        final MediaPlayerInterface player = mPlayer;
+        if (player != null) {
+            // TODO(jaewan): Is it safe to be called on any thread?
+            //               Otherwise MediaSession2 should cache the result from listener.
+            return player.getPlaybackState();
+        } else if (DEBUG) {
+            Log.d(TAG, "API calls after the close()", new IllegalStateException());
+        }
+        return null;
     }
 
     ///////////////////////////////////////////////////
@@ -527,14 +588,6 @@
         }*/
     }
 
-    private void ensurePlayer() {
-        // TODO(jaewan): Should we pend command instead? Follow the decision from MP2.
-        //               Alternatively we can add a API like setAcceptsPendingCommands(boolean).
-        if (mPlayer == null) {
-            throw new IllegalStateException("Player isn't set");
-        }
-    }
-
     private void notifyPlaybackStateChangedNotLocked(PlaybackState2 state) {
         List<PlaybackListenerHolder> listeners = new ArrayList<>();
         synchronized (mLock) {
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index 2380fc0..759d580 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -19,10 +19,8 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.media.MediaController2;
-import android.media.MediaController2.PlaybackInfo;
 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;
@@ -31,7 +29,6 @@
 import android.media.MediaSession2.PlaylistParams;
 import android.media.PlaybackState2;
 import android.media.VolumeProvider2;
-import android.media.update.MediaSession2Provider.CommandButtonProvider;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -39,9 +36,11 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.support.annotation.GuardedBy;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 
+import com.android.media.MediaLibraryService2Impl.MediaLibrarySessionImpl;
 import com.android.media.MediaSession2Impl.CommandButtonImpl;
 import com.android.media.MediaSession2Impl.ControllerInfoImpl;
 
@@ -96,6 +95,27 @@
         return session;
     }
 
+    private MediaLibrarySessionImpl getLibrarySession() throws IllegalStateException {
+        final MediaSession2Impl session = getSession();
+        if (!(session instanceof MediaLibrarySessionImpl)) {
+            throw new RuntimeException("Session isn't a library session");
+        }
+        return (MediaLibrarySessionImpl) session;
+    }
+
+    private ControllerInfo getController(IMediaSession2Callback caller) {
+        // TODO(jaewan): Find 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());
+        }
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    // AIDL methods for session overrides
+    //////////////////////////////////////////////////////////////////////////////////////////////
+
     @Override
     public void connect(String callingPackage, final IMediaSession2Callback callback)
             throws RuntimeException {
@@ -136,7 +156,7 @@
                 final PlaybackState2 state = session.getInstance().getPlaybackState();
                 final Bundle playbackStateBundle = (state != null) ? state.toBundle() : null;
                 final Bundle playbackInfoBundle =
-                        ((PlaybackInfoImpl) session.getPlaybackInfo().getProvider()).toBundle();
+                        ((MediaController2Impl.PlaybackInfoImpl) session.getPlaybackInfo().getProvider()).toBundle();
                 final PlaylistParams params = session.getInstance().getPlaylistParams();
                 final Bundle paramsBundle = (params != null) ? params.toBundle() : null;
                 final int ratingType = session.getRatingType();
@@ -487,32 +507,28 @@
         });
     }
 
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    // AIDL methods for LibrarySession overrides
+    //////////////////////////////////////////////////////////////////////////////////////////////
+
     @Override
     public void getBrowserRoot(IMediaSession2Callback caller, Bundle rootHints)
             throws RuntimeException {
-        final MediaSession2Impl sessionImpl = getSession();
-        if (!(sessionImpl.getCallback() instanceof MediaLibrarySessionCallback)) {
-            if (DEBUG) {
-                Log.d(TAG, "Session cannot hand getLibraryRoot()");
-            }
-            return;
-        }
+        final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
         final ControllerInfo controller = getController(caller);
         if (controller == null) {
             if (DEBUG) {
-                Log.d(TAG, "getBrowerRoot from a controller that hasn't connected. Ignore");
+                Log.d(TAG, "getBrowerRoot() from a controller that hasn't connected. Ignore");
             }
             return;
         }
         sessionImpl.getCallbackExecutor().execute(() -> {
-            final MediaSession2Impl session = mSession.get();
+            final MediaLibrarySessionImpl session = getLibrarySession();
             if (session == null) {
                 return;
             }
-            final MediaLibrarySessionCallback libraryCallback =
-                    (MediaLibrarySessionCallback) session.getCallback();
             final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
-            LibraryRoot root = libraryCallback.onGetRoot(controller, rootHints);
+            LibraryRoot root = session.getCallback().onGetRoot(controller, rootHints);
             try {
                 controllerImpl.getControllerBinder().onGetRootResult(rootHints,
                         root == null ? null : root.getRootId(),
@@ -524,15 +540,182 @@
         });
     }
 
-    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());
+    @Override
+    public void getItem(IMediaSession2Callback caller, String mediaId) throws RuntimeException {
+        final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
+        final ControllerInfo controller = getController(caller);
+        if (controller == null) {
+            if (DEBUG) {
+                Log.d(TAG, "getItem() from a controller that hasn't connected. Ignore");
+            }
+            return;
         }
+        if (mediaId == null) {
+            if (DEBUG) {
+                Log.d(TAG, "mediaId shouldn't be null");
+            }
+            return;
+        }
+        sessionImpl.getCallbackExecutor().execute(() -> {
+            final MediaLibrarySessionImpl session = getLibrarySession();
+            if (session == null) {
+                return;
+            }
+            final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
+            MediaItem2 result = session.getCallback().onLoadItem(controller, mediaId);
+            try {
+                controllerImpl.getControllerBinder().onItemLoaded(
+                        mediaId, result == null ? null : result.toBundle());
+            } catch (RemoteException e) {
+                // Controller may be died prematurely.
+                // TODO(jaewan): Handle this.
+            }
+        });
     }
 
+    @Override
+    public void getChildren(IMediaSession2Callback caller, String parentId, int page,
+            int pageSize, Bundle extras) throws RuntimeException {
+        final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
+        final ControllerInfo controller = getController(caller);
+        if (controller == null) {
+            if (DEBUG) {
+                Log.d(TAG, "getChildren() from a controller that hasn't connected. Ignore");
+            }
+            return;
+        }
+        if (parentId == null) {
+            if (DEBUG) {
+                Log.d(TAG, "parentId shouldn't be null");
+            }
+            return;
+        }
+        if (page < 1 || pageSize < 1) {
+            if (DEBUG) {
+                Log.d(TAG, "Neither page nor pageSize should be less than 1");
+            }
+            return;
+        }
+
+        sessionImpl.getCallbackExecutor().execute(() -> {
+            final MediaLibrarySessionImpl session = getLibrarySession();
+            if (session == null) {
+                return;
+            }
+            final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
+            List<MediaItem2> result = session.getCallback().onLoadChildren(
+                    controller, parentId, page, pageSize, extras);
+            if (result != null && result.size() > pageSize) {
+                throw new IllegalArgumentException("onLoadChildren() shouldn't return media items "
+                        + "more than pageSize. result.size()=" + result.size() + " pageSize="
+                        + pageSize);
+            }
+
+            List<Bundle> bundleList = null;
+            if (result != null) {
+                bundleList = new ArrayList<>();
+                for (MediaItem2 item : result) {
+                    bundleList.add(item == null ? null : item.toBundle());
+                }
+            }
+
+            try {
+                controllerImpl.getControllerBinder().onChildrenLoaded(
+                        parentId, page, pageSize, extras, bundleList);
+            } catch (RemoteException e) {
+                // Controller may be died prematurely.
+                // TODO(jaewan): Handle this.
+            }
+        });
+    }
+
+    @Override
+    public void search(IMediaSession2Callback caller, String query, Bundle extras) {
+        final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
+        final ControllerInfo controller = getController(caller);
+        if (controller == null) {
+            if (DEBUG) {
+                Log.d(TAG, "search() from a controller that hasn't connected. Ignore");
+            }
+            return;
+        }
+        if (TextUtils.isEmpty(query)) {
+            if (DEBUG) {
+                Log.d(TAG, "query shouldn't be empty");
+            }
+            return;
+        }
+
+        sessionImpl.getCallbackExecutor().execute(() -> {
+            final MediaLibrarySessionImpl session = getLibrarySession();
+            if (session == null) {
+                return;
+            }
+            final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
+            session.getCallback().onSearch(controller, query, extras);
+        });
+    }
+
+    @Override
+    public void getSearchResult(IMediaSession2Callback caller, String query, int page,
+            int pageSize, Bundle extras) {
+        final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
+        final ControllerInfo controller = getController(caller);
+        if (controller == null) {
+            if (DEBUG) {
+                Log.d(TAG, "getSearchResult() from a controller that hasn't connected. Ignore");
+            }
+            return;
+        }
+        if (TextUtils.isEmpty(query)) {
+            if (DEBUG) {
+                Log.d(TAG, "query shouldn't be empty");
+            }
+            return;
+        }
+        if (page < 1 || pageSize < 1) {
+            if (DEBUG) {
+                Log.d(TAG, "Neither page nor pageSize should be less than 1");
+            }
+            return;
+        }
+
+        sessionImpl.getCallbackExecutor().execute(() -> {
+            final MediaLibrarySessionImpl session = getLibrarySession();
+            if (session == null) {
+                return;
+            }
+            final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
+            List<MediaItem2> result = session.getCallback().onLoadSearchResult(
+                    controller, query, page, pageSize, extras);
+            if (result != null && result.size() > pageSize) {
+                throw new IllegalArgumentException("onLoadSearchResult() shouldn't return media "
+                        + "items more than pageSize. result.size()=" + result.size() + " pageSize="
+                        + pageSize);
+            }
+
+            List<Bundle> bundleList = null;
+            if (result != null) {
+                bundleList = new ArrayList<>();
+                for (MediaItem2 item : result) {
+                    bundleList.add(item == null ? null : item.toBundle());
+                }
+            }
+
+            try {
+                controllerImpl.getControllerBinder().onSearchResultLoaded(
+                        query, page, pageSize, extras, bundleList);
+            } catch (RemoteException e) {
+                // Controller may be died prematurely.
+                // TODO(jaewan): Handle this.
+            }
+        });
+    }
+
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    // APIs for MediaSession2Impl
+    //////////////////////////////////////////////////////////////////////////////////////////////
+
     // TODO(jaewan): Need a way to get controller with permissions
     public List<ControllerInfo> getControllers() {
         ArrayList<ControllerInfo> controllers = new ArrayList<>();
@@ -627,7 +810,7 @@
                     ControllerInfoImpl.from(list.get(i)).getControllerBinder();
             try {
                 callbackBinder.onPlaybackInfoChanged(
-                        ((PlaybackInfoImpl) playbackInfo.getProvider()).toBundle());
+                        ((MediaController2Impl.PlaybackInfoImpl) playbackInfo.getProvider()).toBundle());
             } 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/PlaybackInfoImpl.java b/packages/MediaComponents/src/com/android/media/PlaybackInfoImpl.java
deleted file mode 100644
index 0782cf1..0000000
--- a/packages/MediaComponents/src/com/android/media/PlaybackInfoImpl.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-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/SessionToken2Impl.java b/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java
index b2b7959..f36aa43 100644
--- a/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/SessionToken2Impl.java
@@ -34,6 +34,8 @@
 import android.os.IBinder;
 import android.text.TextUtils;
 
+import java.util.List;
+
 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";
@@ -73,25 +75,24 @@
             }
         }
         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) {
+
+        // Infer id and type from package name and service name
+        // TODO(jaewan): Handle multi-user.
+        String id = getSessionIdFromService(manager, MediaLibraryService2.SERVICE_INTERFACE,
+                packageName, serviceName);
+        if (id != null) {
+            mId = id;
+            mType = TYPE_LIBRARY_SERVICE;
+        } else {
             // retry with session service
-            serviceIntent.setClassName(packageName, serviceName);
-            id = getSessionId(manager.resolveService(serviceIntent,
-                    PackageManager.GET_META_DATA));
-            type = TYPE_SESSION_SERVICE;
+            mId = getSessionIdFromService(manager, MediaSessionService2.SERVICE_INTERFACE,
+                    packageName, serviceName);
+            mType = TYPE_SESSION_SERVICE;
         }
-        if (id == null) {
+        if (mId == null) {
             throw new IllegalArgumentException("service " + serviceName + " doesn't implement"
-                    + " session service nor library service");
+                    + " session service nor library service. Use service's full name.");
         }
-        mId = id;
-        mType = type;
         mPackageName = packageName;
         mServiceName = serviceName;
         mSessionBinder = null;
@@ -109,6 +110,29 @@
         mInstance = new SessionToken2(this);
     }
 
+    private static String getSessionIdFromService(PackageManager manager, String serviceInterface,
+            String packageName, String serviceName) {
+        Intent serviceIntent = new Intent(serviceInterface);
+        serviceIntent.setPackage(packageName);
+        // Use queryIntentServices to find services with MediaLibraryService2.SERVICE_INTERFACE.
+        // We cannot use resolveService with intent specified class name, because resolveService
+        // ignores actions if Intent.setClassName() is specified.
+        List<ResolveInfo> list = manager.queryIntentServices(
+                serviceIntent, PackageManager.GET_META_DATA);
+        if (list != null) {
+            for (int i = 0; i < list.size(); i++) {
+                ResolveInfo resolveInfo = list.get(i);
+                if (resolveInfo == null || resolveInfo.serviceInfo == null) {
+                    continue;
+                }
+                if (TextUtils.equals(resolveInfo.serviceInfo.name, serviceName)) {
+                    return getSessionId(resolveInfo);
+                }
+            }
+        }
+        return null;
+    }
+
     public static String getSessionId(ResolveInfo resolveInfo) {
         if (resolveInfo == null || resolveInfo.serviceInfo == null) {
             return null;
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiHelper.java b/packages/MediaComponents/src/com/android/media/update/ApiHelper.java
index 06f463f..7018844 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiHelper.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiHelper.java
@@ -18,9 +18,12 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.ContextWrapper;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.XmlResourceParser;
+import android.support.v4.widget.Space;
+import android.support.v7.widget.ButtonBarLayout;
 import android.util.AttributeSet;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
@@ -28,6 +31,9 @@
 import android.view.ViewGroup;
 
 import com.android.support.mediarouter.app.MediaRouteButton;
+import com.android.support.mediarouter.app.MediaRouteExpandCollapseButton;
+import com.android.support.mediarouter.app.MediaRouteVolumeSlider;
+import com.android.support.mediarouter.app.OverlayListView;
 
 public class ApiHelper {
     private static ApiHelper sInstance;
@@ -68,6 +74,7 @@
     }
 
     public static LayoutInflater getLayoutInflater(Context context, Theme theme) {
+        // TODO (b/72975976): Avoid to use ContextThemeWrapper with app context and lib theme.
         LayoutInflater layoutInflater = LayoutInflater.from(context).cloneInContext(
                 new ContextThemeWrapper(context, theme));
         layoutInflater.setFactory2(new LayoutInflater.Factory2() {
@@ -76,6 +83,16 @@
                     View parent, String name, Context context, AttributeSet attrs) {
                 if (MediaRouteButton.class.getCanonicalName().equals(name)) {
                     return new MediaRouteButton(context, attrs);
+                } else if (MediaRouteVolumeSlider.class.getCanonicalName().equals(name)) {
+                    return new MediaRouteVolumeSlider(context, attrs);
+                } else if (MediaRouteExpandCollapseButton.class.getCanonicalName().equals(name)) {
+                    return new MediaRouteExpandCollapseButton(context, attrs);
+                } else if (OverlayListView.class.getCanonicalName().equals(name)) {
+                    return new OverlayListView(context, attrs);
+                } else if (ButtonBarLayout.class.getCanonicalName().equals(name)) {
+                    return new ButtonBarLayout(context, attrs);
+                } else if (Space.class.getCanonicalName().equals(name)) {
+                    return new Space(context, attrs);
                 }
                 return null;
             }
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java
index aeb4408..6e70eaf 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteChooserDialog.java
@@ -98,9 +98,9 @@
     }
 
     public MediaRouteChooserDialog(Context context, int theme) {
+        // TODO (b/72975976): Avoid to use ContextThemeWrapper with app context and lib theme.
         super(new ContextThemeWrapper(context,
-                ApiHelper.getLibTheme(MediaRouterThemeHelper.getRouterThemeId(context))),
-                theme == 0 ? android.R.style.Animation : theme);
+                ApiHelper.getLibTheme(MediaRouterThemeHelper.getRouterThemeId(context))), theme);
         context = getContext();
 
         mRouter = MediaRouter.getInstance(context);
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java
index 123ab21..269a6e9 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteControllerDialog.java
@@ -21,6 +21,7 @@
 import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_PAUSE;
 import static android.support.v4.media.session.PlaybackStateCompat.ACTION_STOP;
 
+import android.app.AlertDialog;
 import android.app.PendingIntent;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -41,11 +42,10 @@
 import android.support.v4.media.session.PlaybackStateCompat;
 import android.support.v4.util.ObjectsCompat;
 import android.support.v4.view.accessibility.AccessibilityEventCompat;
-import android.support.v7.app.AlertDialog;
 import android.support.v7.graphics.Palette;
-
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.ContextThemeWrapper;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -72,6 +72,7 @@
 import android.widget.SeekBar;
 import android.widget.TextView;
 
+import com.android.media.update.ApiHelper;
 import com.android.media.update.R;
 import com.android.support.mediarouter.media.MediaRouteSelector;
 import com.android.support.mediarouter.media.MediaRouter;
@@ -204,8 +205,9 @@
     }
 
     public MediaRouteControllerDialog(Context context, int theme) {
-        super(context = MediaRouterThemeHelper.createThemedDialogContext(context, theme, true),
-                MediaRouterThemeHelper.createThemedDialogStyle(context));
+        // TODO (b/72975976): Avoid to use ContextThemeWrapper with app context and lib theme.
+        super(new ContextThemeWrapper(context,
+                ApiHelper.getLibTheme(MediaRouterThemeHelper.getRouterThemeId(context))), theme);
         mContext = getContext();
 
         mControllerCallback = new MediaControllerCallback();
@@ -213,16 +215,14 @@
         mCallback = new MediaRouterCallback();
         mRoute = mRouter.getSelectedRoute();
         setMediaSession(mRouter.getMediaSessionToken());
-        mVolumeGroupListPaddingTop = mContext.getResources().getDimensionPixelSize(
+        mVolumeGroupListPaddingTop = ApiHelper.getLibResources().getDimensionPixelSize(
                 R.dimen.mr_controller_volume_group_list_padding_top);
         mAccessibilityManager =
                 (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
-        if (android.os.Build.VERSION.SDK_INT >= 21) {
-            mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
-                    R.interpolator.mr_linear_out_slow_in);
-            mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
-                    R.interpolator.mr_fast_out_slow_in);
-        }
+        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
+                mContext, android.R.interpolator.linear_out_slow_in);
+        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(
+                mContext, android.R.interpolator.fast_out_slow_in);
         mAccelerateDecelerateInterpolator = new AccelerateDecelerateInterpolator();
     }
 
@@ -332,7 +332,10 @@
         super.onCreate(savedInstanceState);
 
         getWindow().setBackgroundDrawableResource(android.R.color.transparent);
-        setContentView(R.layout.mr_controller_material_dialog_b);
+
+        setContentView(ApiHelper.inflateLibLayout(mContext,
+                ApiHelper.getLibTheme(MediaRouterThemeHelper.getRouterThemeId(mContext)),
+                R.layout.mr_controller_material_dialog_b));
 
         // Remove the neutral button.
         findViewById(BUTTON_NEUTRAL_RES_ID).setVisibility(View.GONE);
@@ -355,12 +358,14 @@
         });
         int color = MediaRouterThemeHelper.getButtonTextColor(mContext);
         mDisconnectButton = findViewById(BUTTON_DISCONNECT_RES_ID);
-        mDisconnectButton.setText(R.string.mr_controller_disconnect);
+        mDisconnectButton.setText(
+                ApiHelper.getLibResources().getString(R.string.mr_controller_disconnect));
         mDisconnectButton.setTextColor(color);
         mDisconnectButton.setOnClickListener(listener);
 
         mStopCastingButton = findViewById(BUTTON_STOP_RES_ID);
-        mStopCastingButton.setText(R.string.mr_controller_stop_casting);
+        mStopCastingButton.setText(
+                ApiHelper.getLibResources().getString(R.string.mr_controller_stop_casting));
         mStopCastingButton.setTextColor(color);
         mStopCastingButton.setOnClickListener(listener);
 
@@ -435,11 +440,11 @@
             }
         });
         loadInterpolator();
-        mGroupListAnimationDurationMs = mContext.getResources().getInteger(
+        mGroupListAnimationDurationMs = ApiHelper.getLibResources().getInteger(
                 R.integer.mr_controller_volume_group_list_animation_duration_ms);
-        mGroupListFadeInDurationMs = mContext.getResources().getInteger(
+        mGroupListFadeInDurationMs = ApiHelper.getLibResources().getInteger(
                 R.integer.mr_controller_volume_group_list_fade_in_duration_ms);
-        mGroupListFadeOutDurationMs = mContext.getResources().getInteger(
+        mGroupListFadeOutDurationMs = ApiHelper.getLibResources().getInteger(
                 R.integer.mr_controller_volume_group_list_fade_out_duration_ms);
 
         mCustomControlView = onCreateMediaControlView(savedInstanceState);
@@ -461,7 +466,7 @@
         View decorView = getWindow().getDecorView();
         mDialogContentWidth = width - decorView.getPaddingLeft() - decorView.getPaddingRight();
 
-        Resources res = mContext.getResources();
+        Resources res = ApiHelper.getLibResources();
         mVolumeGroupListItemIconSize = res.getDimensionPixelSize(
                 R.dimen.mr_controller_volume_group_list_item_icon_size);
         mVolumeGroupListItemHeight = res.getDimensionPixelSize(
@@ -726,12 +731,8 @@
     }
 
     void loadInterpolator() {
-        if (android.os.Build.VERSION.SDK_INT >= 21) {
-            mInterpolator = mIsGroupExpanded ? mLinearOutSlowInInterpolator
-                    : mFastOutSlowInInterpolator;
-        } else {
-            mInterpolator = mAccelerateDecelerateInterpolator;
-        }
+        mInterpolator =
+                mIsGroupExpanded ? mLinearOutSlowInInterpolator : mFastOutSlowInInterpolator;
     }
 
     private void updateVolumeControlLayout() {
@@ -990,14 +991,17 @@
             if (mRoute.getPresentationDisplayId()
                     != MediaRouter.RouteInfo.PRESENTATION_DISPLAY_ID_NONE) {
                 // The user is currently casting screen.
-                mTitleView.setText(R.string.mr_controller_casting_screen);
+                mTitleView.setText(ApiHelper.getLibResources().getString(
+                        R.string.mr_controller_casting_screen));
                 showTitle = true;
             } else if (mState == null || mState.getState() == PlaybackStateCompat.STATE_NONE) {
                 // Show "No media selected" as we don't yet know the playback state.
-                mTitleView.setText(R.string.mr_controller_no_media_selected);
+                mTitleView.setText(ApiHelper.getLibResources().getString(
+                        R.string.mr_controller_no_media_selected));
                 showTitle = true;
             } else if (!hasTitle && !hasSubtitle) {
-                mTitleView.setText(R.string.mr_controller_no_info_available);
+                mTitleView.setText(ApiHelper.getLibResources().getString(
+                        R.string.mr_controller_no_info_available));
                 showTitle = true;
             } else {
                 if (hasTitle) {
@@ -1219,7 +1223,7 @@
                                 AccessibilityEventCompat.TYPE_ANNOUNCEMENT);
                         event.setPackageName(mContext.getPackageName());
                         event.setClassName(getClass().getName());
-                        event.getText().add(mContext.getString(actionDescResId));
+                        event.getText().add(ApiHelper.getLibResources().getString(actionDescResId));
                         mAccessibilityManager.sendAccessibilityEvent(event);
                     }
                 }
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java
index 392b39d..defeedb 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteExpandCollapseButton.java
@@ -26,12 +26,13 @@
 import android.view.View;
 import android.widget.ImageButton;
 
+import com.android.media.update.ApiHelper;
 import com.android.media.update.R;
 
 /**
  * Chevron/Caret button to expand/collapse group volume list with animation.
  */
-class MediaRouteExpandCollapseButton extends ImageButton {
+public class MediaRouteExpandCollapseButton extends ImageButton {
     final AnimationDrawable mExpandAnimationDrawable;
     final AnimationDrawable mCollapseAnimationDrawable;
     final String mExpandGroupDescription;
@@ -49,10 +50,10 @@
 
     public MediaRouteExpandCollapseButton(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mExpandAnimationDrawable = (AnimationDrawable) ContextCompat.getDrawable(
-                context, R.drawable.mr_group_expand);
-        mCollapseAnimationDrawable = (AnimationDrawable) ContextCompat.getDrawable(
-                context, R.drawable.mr_group_collapse);
+        mExpandAnimationDrawable = (AnimationDrawable)
+                ApiHelper.getLibResources().getDrawable(R.drawable.mr_group_expand);
+        mCollapseAnimationDrawable = (AnimationDrawable)
+                ApiHelper.getLibResources().getDrawable(R.drawable.mr_group_collapse);
 
         ColorFilter filter = new PorterDuffColorFilter(
                 MediaRouterThemeHelper.getControllerColor(context, defStyleAttr),
@@ -60,8 +61,10 @@
         mExpandAnimationDrawable.setColorFilter(filter);
         mCollapseAnimationDrawable.setColorFilter(filter);
 
-        mExpandGroupDescription = context.getString(R.string.mr_controller_expand_group);
-        mCollapseGroupDescription = context.getString(R.string.mr_controller_collapse_group);
+        mExpandGroupDescription =
+                ApiHelper.getLibResources().getString(R.string.mr_controller_expand_group);
+        mCollapseGroupDescription =
+                ApiHelper.getLibResources().getString(R.string.mr_controller_collapse_group);
 
         setImageDrawable(mExpandAnimationDrawable.getFrame(0));
         setContentDescription(mExpandGroupDescription);
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteVolumeSlider.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteVolumeSlider.java
index 7a34fb5..d05d20e 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteVolumeSlider.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouteVolumeSlider.java
@@ -20,14 +20,14 @@
 import android.graphics.Color;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
-import android.support.v7.widget.AppCompatSeekBar;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.widget.SeekBar;
 
 /**
  * Volume slider with showing, hiding, and applying alpha supports to the thumb.
  */
-class MediaRouteVolumeSlider extends AppCompatSeekBar {
+public class MediaRouteVolumeSlider extends SeekBar {
     private static final String TAG = "MediaRouteVolumeSlider";
 
     private final float mDisabledAlpha;
@@ -41,7 +41,8 @@
     }
 
     public MediaRouteVolumeSlider(Context context, AttributeSet attrs) {
-        this(context, attrs, android.support.v7.appcompat.R.attr.seekBarStyle);
+        super(context, attrs);
+        mDisabledAlpha = MediaRouterThemeHelper.getDisabledAlpha(context);
     }
 
     public MediaRouteVolumeSlider(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -56,8 +57,10 @@
 
         // The thumb drawable is a collection of drawables and its current drawables are changed per
         // state. Apply the color filter and alpha on every state change.
-        mThumb.setColorFilter(mColor, PorterDuff.Mode.SRC_IN);
-        mThumb.setAlpha(alpha);
+        if (mThumb != null) {
+            mThumb.setColorFilter(mColor, PorterDuff.Mode.SRC_IN);
+            mThumb.setAlpha(alpha);
+        }
 
         getProgressDrawable().setColorFilter(mColor, PorterDuff.Mode.SRC_IN);
         getProgressDrawable().setAlpha(alpha);
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java
index 7440130..63f042f 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/MediaRouterThemeHelper.java
@@ -74,9 +74,8 @@
 
         // 2) If no theme is supplied, look it up from the context (dialogTheme/alertDialogTheme)
         if (theme == 0) {
-            theme = getThemeResource(context, !alertDialog
-                    ? android.support.v7.appcompat.R.attr.dialogTheme
-                    : android.support.v7.appcompat.R.attr.alertDialogTheme);
+            theme = getThemeResource(context,
+                    !alertDialog ? android.R.attr.dialogTheme : android.R.attr.alertDialogTheme);
         }
         //    Apply it
         context = new ContextThemeWrapper(context, theme);
@@ -93,7 +92,7 @@
         // 4) Apply the custom Media Router theme
         int theme = getThemeResource(context, R.attr.mediaRouteTheme);
         if (theme == 0) {
-            // 3) No custom MediaRouther theme was provided so apply the base theme instead
+            // 3) No custom MediaRouter theme was provided so apply the base theme instead
             theme = getRouterThemeId(context);
         }
 
@@ -113,8 +112,7 @@
     }
 
     static @ControllerColorType int getControllerColor(Context context, int style) {
-        int primaryColor = getThemeColor(context, style,
-                android.support.v7.appcompat.R.attr.colorPrimary);
+        int primaryColor = getThemeColor(context, style, android.R.attr.colorPrimary);
         if (primaryColor == 0) {
             primaryColor = getThemeColor(context, style, android.R.attr.colorPrimary);
             if (primaryColor == 0) {
@@ -129,23 +127,20 @@
     }
 
     static int getButtonTextColor(Context context) {
-        int primaryColor = getThemeColor(context, 0,
-                android.support.v7.appcompat.R.attr.colorPrimary);
+        int primaryColor = getThemeColor(context, 0, android.R.attr.colorPrimary);
         int backgroundColor = getThemeColor(context, 0, android.R.attr.colorBackground);
 
         if (ColorUtils.calculateContrast(primaryColor, backgroundColor) < MIN_CONTRAST) {
             // Default to colorAccent if the contrast ratio is low.
-            return getThemeColor(context, 0, android.support.v7.appcompat.R.attr.colorAccent);
+            return getThemeColor(context, 0, android.R.attr.colorAccent);
         }
         return primaryColor;
     }
 
     static void setMediaControlsBackgroundColor(
             Context context, View mainControls, View groupControls, boolean hasGroup) {
-        int primaryColor = getThemeColor(context, 0,
-                android.support.v7.appcompat.R.attr.colorPrimary);
-        int primaryDarkColor = getThemeColor(context, 0,
-                android.support.v7.appcompat.R.attr.colorPrimaryDark);
+        int primaryColor = getThemeColor(context, 0, android.R.attr.colorPrimary);
+        int primaryDarkColor = getThemeColor(context, 0, android.R.attr.colorPrimaryDark);
         if (hasGroup && getControllerColor(context, 0) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
             // Instead of showing dark controls in a possibly dark (i.e. the primary dark), model
             // the white dialog and use the primary color for the group controls.
@@ -174,6 +169,7 @@
 
     private static boolean isLightTheme(Context context) {
         TypedValue value = new TypedValue();
+        // TODO(sungsoo): Switch to com.android.internal.R.attr.isLightTheme
         return context.getTheme().resolveAttribute(android.support.v7.appcompat.R.attr.isLightTheme,
                 value, true) && value.data != 0;
     }
diff --git a/packages/MediaComponents/src/com/android/support/mediarouter/app/OverlayListView.java b/packages/MediaComponents/src/com/android/support/mediarouter/app/OverlayListView.java
index 59019ff..b00dee2 100644
--- a/packages/MediaComponents/src/com/android/support/mediarouter/app/OverlayListView.java
+++ b/packages/MediaComponents/src/com/android/support/mediarouter/app/OverlayListView.java
@@ -32,7 +32,7 @@
  * A ListView which has an additional overlay layer. {@link BitmapDrawable}
  * can be added to the layer and can be animated.
  */
-final class OverlayListView extends ListView {
+public final class OverlayListView extends ListView {
     private final List<OverlayObject> mOverlayObjects = new ArrayList<>();
 
     public OverlayListView(Context context) {
diff --git a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
index dfc0792..318cbf9 100644
--- a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
@@ -54,6 +54,7 @@
 
     static final String ARGUMENT_KEY_FULLSCREEN = "fullScreen";
 
+    // TODO: Move these constants to public api to support custom video view.
     static final String KEY_STATE_CONTAINS_SUBTITLE = "StateContainsSubtitle";
     static final String EVENT_UPDATE_SUBTITLE_STATUS = "UpdateSubtitleStatus";
 
@@ -72,9 +73,7 @@
     private TextView mTitleView;
     private int mDuration;
     private int mPrevState;
-    private int mCurrentVisibility;
     private long mPlaybackActions;
-    private boolean mShowing;
     private boolean mDragging;
     private boolean mIsFullScreen;
     private boolean mOverflowExpanded;
@@ -233,6 +232,7 @@
     public void setEnabled_impl(boolean enabled) {
         super.setEnabled_impl(enabled);
 
+        // TODO: Merge the below code with disableUnsupportedButtons().
         if (mPlayPauseButton != null) {
             mPlayPauseButton.setEnabled(enabled);
         }
@@ -255,24 +255,15 @@
     }
 
     @Override
-    public void onVisibilityAggregated_impl(boolean invisible) {
-        super.onVisibilityAggregated_impl(invisible);
+    public void onVisibilityAggregated_impl(boolean isVisible) {
+        super.onVisibilityAggregated_impl(isVisible);
 
-        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);
-            } else if (visibility == View.GONE) {
-                mInstance.removeCallbacks(mShowProgress);
-            }
+        if (isVisible) {
+            disableUnsupportedButtons();
+            mInstance.removeCallbacks(mUpdateProgress);
+            mInstance.post(mUpdateProgress);
+        } else {
+            mInstance.removeCallbacks(mUpdateProgress);
         }
     }
 
@@ -465,12 +456,13 @@
         }
     }
 
-    private final Runnable mShowProgress = new Runnable() {
+    private final Runnable mUpdateProgress = new Runnable() {
         @Override
         public void run() {
             int pos = setProgress();
-            if (!mDragging && mShowing && isPlaying()) {
-                mInstance.postDelayed(mShowProgress,
+            boolean isShowing = mInstance.getVisibility() == View.VISIBLE;
+            if (!mDragging && isShowing && isPlaying()) {
+                mInstance.postDelayed(mUpdateProgress,
                         DEFAULT_PROGRESS_UPDATE_TIME_MS - (pos % DEFAULT_PROGRESS_UPDATE_TIME_MS));
             }
         }
@@ -557,7 +549,7 @@
             // the seekbar and b) once the user is done dragging the thumb
             // we will post one of these messages to the queue again and
             // this ensures that there will be exactly one message queued up.
-            mInstance.removeCallbacks(mShowProgress);
+            mInstance.removeCallbacks(mUpdateProgress);
 
             // Check if playback is currently stopped. In this case, update the pause button to
             // show the play image instead of the replay image.
@@ -602,7 +594,7 @@
             // Ensure that progress is properly updated in the future,
             // the call to show() does not guarantee this because it is a
             // no-op if we are already showing.
-            mInstance.post(mShowProgress);
+            mInstance.post(mUpdateProgress);
         }
     };
 
@@ -735,6 +727,8 @@
                                 ApiHelper.getLibResources().getDrawable(
                                         R.drawable.ic_pause_circle_filled, null));
                         mPlayPauseButton.setContentDescription(mPauseDescription);
+                        mInstance.removeCallbacks(mUpdateProgress);
+                        mInstance.post(mUpdateProgress);
                         break;
                     case PlaybackState.STATE_PAUSED:
                         mPlayPauseButton.setImageDrawable(
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index 256003f..ea7e714 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
@@ -31,7 +31,9 @@
 import android.media.ClosedCaptionRenderer;
 import android.media.Metadata;
 import android.media.PlaybackParams;
+import android.media.SRTRenderer;
 import android.media.SubtitleController;
+import android.media.TimedText;
 import android.media.TtmlRenderer;
 import android.media.WebVttRenderer;
 import android.media.session.MediaController;
@@ -122,6 +124,7 @@
     private int mVideoWidth;
     private int mVideoHeight;
 
+    private ArrayList<Integer> mSubtitleTrackIndices;
     private SubtitleView mSubtitleView;
     private boolean mSubtitleEnabled;
     private int mSelectedTrackIndex;  // selected subtitle track index as MediaPlayer2 returns
@@ -191,13 +194,11 @@
         if (enableControlView) {
             mMediaControlView = new MediaControlView2(mInstance.getContext());
         }
-        boolean enableSubtitle = (attrs == null) || attrs.getAttributeBooleanValue(
+
+        mSubtitleEnabled = (attrs == null) || attrs.getAttributeBooleanValue(
                 "http://schemas.android.com/apk/res/android",
-                "enableSubtitle", true);
-        if (enableSubtitle) {
-            Log.d(TAG, "enableSubtitle attribute is true.");
-            // TODO: implement
-        }
+                "enableSubtitle", false);
+
         int viewType = (attrs == null) ? VideoView2.VIEW_TYPE_SURFACEVIEW
                 : attrs.getAttributeIntValue(
                 "http://schemas.android.com/apk/res/android",
@@ -240,27 +241,8 @@
 
     @Override
     public void setSubtitleEnabled_impl(boolean enable) {
-        if (enable) {
-            // 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 (subtitleTrackIndices.size() > 0) {
-                // Select first subtitle track
-                mSelectedTrackIndex = subtitleTrackIndices.get(0);
-                mMediaPlayer.selectTrack(mSelectedTrackIndex);
-            }
-        } else {
-            if (mSelectedTrackIndex != INVALID_TRACK_INDEX) {
-                mMediaPlayer.deselectTrack(mSelectedTrackIndex);
-                mSelectedTrackIndex = INVALID_TRACK_INDEX;
-            }
+        if (enable != mSubtitleEnabled) {
+            selectOrDeselectSubtitle(enable);
         }
         mSubtitleEnabled = enable;
     }
@@ -572,7 +554,13 @@
             controller.registerRenderer(new TtmlRenderer(context));
             controller.registerRenderer(new Cea708CaptionRenderer(context));
             controller.registerRenderer(new ClosedCaptionRenderer(context));
-            mMediaPlayer.setSubtitleAnchor(controller, (SubtitleController.Anchor) mSubtitleView);
+            controller.registerRenderer(new SRTRenderer(context));
+            mMediaPlayer.setSubtitleAnchor(
+                    controller, (SubtitleController.Anchor) mSubtitleView);
+            // TODO: Remove timed text related code later once relevant Renderer is defined.
+            // This is just for debugging purpose.
+            mMediaPlayer.setOnTimedTextListener(mTimedTextListener);
+
             mMediaPlayer.setOnPreparedListener(mPreparedListener);
             mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
             mMediaPlayer.setOnCompletionListener(mCompletionListener);
@@ -622,7 +610,6 @@
     /*
      * Reset the media player in any state
      */
-    // TODO: Figure out if the legacy code's boolean parameter: cleartargetstate is necessary.
     private void resetPlayer() {
         if (mMediaPlayer != null) {
             mMediaPlayer.reset();
@@ -774,7 +761,58 @@
             return false;
         }
         PlaybackInfo playbackInfo = mMediaController.getPlaybackInfo();
-        return (playbackInfo != null) && (playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE);
+        return (playbackInfo != null)
+                && (playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE);
+    }
+
+    private void selectOrDeselectSubtitle(boolean select) {
+        if (!isInPlaybackState()) {
+            return;
+        }
+        if (select) {
+            if (mSubtitleTrackIndices.size() > 0) {
+                // Select first subtitle track
+                mSelectedTrackIndex = mSubtitleTrackIndices.get(0);
+                mMediaPlayer.selectTrack(mSelectedTrackIndex);
+                mSubtitleView.setVisibility(View.VISIBLE);
+            }
+        } else {
+            if (mSelectedTrackIndex != INVALID_TRACK_INDEX) {
+                mMediaPlayer.deselectTrack(mSelectedTrackIndex);
+                mSelectedTrackIndex = INVALID_TRACK_INDEX;
+                mSubtitleView.setVisibility(View.GONE);
+            }
+        }
+    }
+
+    private void extractSubtitleTracks() {
+        MediaPlayer.TrackInfo[] trackInfos = mMediaPlayer.getTrackInfo();
+        boolean previouslyNoTracks = mSubtitleTrackIndices == null
+                || mSubtitleTrackIndices.size() == 0;
+        mSubtitleTrackIndices = new ArrayList<>();
+        for (int i = 0; i < trackInfos.length; ++i) {
+            int trackType = trackInfos[i].getTrackType();
+            if (trackType == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE
+                    || trackType == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) {
+                  mSubtitleTrackIndices.add(i);
+            }
+        }
+        if (mSubtitleTrackIndices.size() > 0) {
+            if (previouslyNoTracks) {
+                selectOrDeselectSubtitle(mSubtitleEnabled);
+                // Notify MediaControlView that subtitle track exists
+                // TODO: Send the subtitle track list to MediaSession for MCV2.
+                Bundle data = new Bundle();
+                data.putBoolean(MediaControlView2Impl.KEY_STATE_CONTAINS_SUBTITLE, true);
+                mMediaSession.sendSessionEvent(
+                        MediaControlView2Impl.EVENT_UPDATE_SUBTITLE_STATUS, data);
+            }
+        } else {
+            Bundle data = new Bundle();
+            data.putBoolean(MediaControlView2Impl.KEY_STATE_CONTAINS_SUBTITLE, false);
+            mMediaSession.sendSessionEvent(
+                    MediaControlView2Impl.EVENT_UPDATE_SUBTITLE_STATUS, data);
+        }
     }
 
     MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener =
@@ -805,6 +843,7 @@
             mCurrentState = STATE_PREPARED;
             // Create and set playback state for MediaControlView2
             updatePlaybackState();
+            extractSubtitleTracks();
 
             if (mOnPreparedListener != null) {
                 mOnPreparedListener.onPrepared(mInstance);
@@ -884,6 +923,10 @@
                     if (mOnInfoListener != null) {
                         mOnInfoListener.onInfo(mInstance, what, extra);
                     }
+
+                    if (what == MediaPlayer.MEDIA_INFO_METADATA_UPDATE) {
+                        extractSubtitleTracks();
+                    }
                     return true;
                 }
             };
@@ -954,6 +997,15 @@
                 }
             };
 
+    // TODO: Remove timed text related code later once relevant Renderer is defined.
+    // This is just for debugging purpose.
+    private MediaPlayer.OnTimedTextListener mTimedTextListener =
+            new MediaPlayer.OnTimedTextListener() {
+                public void onTimedText(MediaPlayer mp, TimedText text) {
+                    Log.d(TAG, "TimedText: " + text.getText());
+                }
+            };
+
     private class MediaSessionCallback extends MediaSession.Callback {
         @Override
         public void onCommand(String command, Bundle args, ResultReceiver receiver) {
diff --git a/packages/MediaComponents/test/AndroidManifest.xml b/packages/MediaComponents/test/AndroidManifest.xml
index 48e4292..5ebe31a 100644
--- a/packages/MediaComponents/test/AndroidManifest.xml
+++ b/packages/MediaComponents/test/AndroidManifest.xml
@@ -34,7 +34,7 @@
             <intent-filter>
                 <action android:name="android.media.MediaLibraryService2" />
             </intent-filter>
-            <meta-data android:name="android.media.session" android:value="TestBrowser" />
+            <meta-data android:name="android.media.session" android:value="TestLibrary" />
         </service>
     </application>
 
diff --git a/packages/MediaComponents/test/runtest.sh b/packages/MediaComponents/test/runtest.sh
index 920fa96..edce230 100644
--- a/packages/MediaComponents/test/runtest.sh
+++ b/packages/MediaComponents/test/runtest.sh
@@ -146,9 +146,10 @@
         fi
         target_dir=$(dirname ${target_dir})
         local package=$(sed -n 's/^.*\bpackage\b="\([a-z0-9\._]*\)".*$/\1/p' ${target_dir}/AndroidManifest.xml)
-        local apk_path=$(find ${OUT} -name ${target}.apk)
-        if [[ -z "${apk_path}" ]]; then
-          echo "Cannot locate ${target}.apk" && break
+        local apk_path=$(find ${OUT}/system ${OUT}/data -name ${target}.apk)
+        local apk_num=$(find ${OUT}/system ${OUT}/data -name ${target}.apk | wc -l)
+        if [[ "${apk_num}" != "1" ]]; then
+          echo "Cannot locate a ${target}.apk. Found ${apk_num} apks" && break
         fi
         echo "Installing ${target}.apk. path=${apk_path}"
         ${adb} install -r ${apk_path}
diff --git a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
index 4cd8177..b60fde3 100644
--- a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
@@ -18,12 +18,15 @@
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.assertNull;
 
 import android.annotation.Nullable;
 import android.content.Context;
 import android.media.MediaBrowser2.BrowserCallback;
 import android.media.MediaSession2.Command;
+import android.media.MediaSession2.CommandButton;
 import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.PlaylistParams;
 import android.os.Bundle;
@@ -36,6 +39,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -63,6 +67,9 @@
     interface TestBrowserCallbackInterface extends TestControllerCallbackInterface {
         // Browser specific callbacks
         default void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {}
+        default void onItemLoaded(String mediaId, MediaItem2 result) {}
+        default void onChildrenLoaded(String parentId, int page, int pageSize, Bundle options,
+                List<MediaItem2> result) {}
     }
 
     @Test
@@ -88,6 +95,129 @@
         assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
     }
 
+    @Test
+    public void testGetItem() throws InterruptedException {
+        final String mediaId = MockMediaLibraryService2.MEDIA_ID_GET_ITEM;
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+            @Override
+            public void onItemLoaded(String mediaIdOut, MediaItem2 result) {
+                assertEquals(mediaId, mediaIdOut);
+                assertNotNull(result);
+                assertEquals(mediaId, result.getMediaId());
+                latch.countDown();
+            }
+        };
+
+        final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+        MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+        browser.getItem(mediaId);
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testGetItemNullResult() throws InterruptedException {
+        final String mediaId = "random_media_id";
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+            @Override
+            public void onItemLoaded(String mediaIdOut, MediaItem2 result) {
+                assertEquals(mediaId, mediaIdOut);
+                assertNull(result);
+                latch.countDown();
+            }
+        };
+
+        final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+        MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+        browser.getItem(mediaId);
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testGetChildren() throws InterruptedException {
+        final String parentId = MockMediaLibraryService2.PARENT_ID;
+        final int page = 4;
+        final int pageSize = 10;
+        final Bundle options = new Bundle();
+        options.putString(TAG, TAG);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+            @Override
+            public void onChildrenLoaded(String parentIdOut, int pageOut, int pageSizeOut,
+                    Bundle optionsOut, List<MediaItem2> result) {
+                assertEquals(parentId, parentIdOut);
+                assertEquals(page, pageOut);
+                assertEquals(pageSize, pageSizeOut);
+                assertTrue(TestUtils.equals(options, optionsOut));
+                assertNotNull(result);
+
+                int fromIndex = (page - 1) * pageSize;
+                int toIndex = Math.min(page * pageSize,
+                        MockMediaLibraryService2.GET_CHILDREN_RESULT.size());
+
+                // Compare the given results with originals.
+                for (int originalIndex = fromIndex; originalIndex < toIndex; originalIndex++) {
+                    int relativeIndex = originalIndex - fromIndex;
+                    assertEquals(
+                            MockMediaLibraryService2.GET_CHILDREN_RESULT.get(originalIndex)
+                                    .getMediaId(),
+                            result.get(relativeIndex).getMediaId());
+                }
+                latch.countDown();
+            }
+        };
+
+        final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+        MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+        browser.getChildren(parentId, page, pageSize, options);
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testGetChildrenEmptyResult() throws InterruptedException {
+        final String parentId = MockMediaLibraryService2.PARENT_ID_NO_CHILDREN;
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+            @Override
+            public void onChildrenLoaded(String parentIdOut, int pageOut, int pageSizeOut,
+                    Bundle optionsOut, List<MediaItem2> result) {
+                assertNotNull(result);
+                assertEquals(0, result.size());
+                latch.countDown();
+            }
+        };
+
+        final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+        MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+        browser.getChildren(parentId, 1, 1, null);
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Test
+    public void testGetChildrenNullResult() throws InterruptedException {
+        final String parentId = MockMediaLibraryService2.PARENT_ID_ERROR;
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+            @Override
+            public void onChildrenLoaded(String parentIdOut, int pageOut, int pageSizeOut,
+                    Bundle optionsOut, List<MediaItem2> result) {
+                assertNull(result);
+                latch.countDown();
+            }
+        };
+
+        final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+        MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+        browser.getChildren(parentId, 1, 1, null);
+        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+    }
+
     public static class TestBrowserCallback extends BrowserCallback
             implements WaitForConnectionInterface {
         private final TestControllerCallbackInterface mCallbackProxy;
@@ -104,42 +234,41 @@
         @CallSuper
         @Override
         public void onConnected(CommandGroup commands) {
-            super.onConnected(commands);
             connectLatch.countDown();
         }
 
         @CallSuper
         @Override
         public void onDisconnected() {
-            super.onDisconnected();
             disconnectLatch.countDown();
         }
 
         @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 onPlaybackInfoChanged(MediaController2.PlaybackInfo info) {
-            if (mCallbackProxy != null) {
-                mCallbackProxy.onPlaybackInfoChanged(info);
-            }
+            mCallbackProxy.onPlaybackInfoChanged(info);
         }
 
         @Override
         public void onCustomCommand(Command command, Bundle args, ResultReceiver receiver) {
-            super.onCustomCommand(command, args, receiver);
             mCallbackProxy.onCustomCommand(command, args, receiver);
         }
 
+
+        @Override
+        public void onCustomLayoutChanged(List<CommandButton> layout) {
+            mCallbackProxy.onCustomLayoutChanged(layout);
+        }
+
         @Override
         public void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {
             super.onGetRootResult(rootHints, rootMediaId, rootExtra);
@@ -150,6 +279,24 @@
         }
 
         @Override
+        public void onItemLoaded(String mediaId, MediaItem2 result) {
+            super.onItemLoaded(mediaId, result);
+            if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+                ((TestBrowserCallbackInterface) mCallbackProxy).onItemLoaded(mediaId, result);
+            }
+        }
+
+        @Override
+        public void onChildrenLoaded(String parentId, int page, int pageSize, Bundle options,
+                List<MediaItem2> result) {
+            super.onChildrenLoaded(parentId, page, pageSize, options, result);
+            if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+                ((TestBrowserCallbackInterface) mCallbackProxy)
+                        .onChildrenLoaded(parentId, page, pageSize, options, result);
+            }
+        }
+
+        @Override
         public void waitForConnect(boolean expect) throws InterruptedException {
             if (expect) {
                 assertTrue(connectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
diff --git a/packages/MediaComponents/test/src/android/media/MediaController2Test.java b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
index 07baf58..7bf0fd2 100644
--- a/packages/MediaComponents/test/src/android/media/MediaController2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
@@ -21,9 +21,11 @@
 import android.content.Intent;
 import android.media.MediaPlayerInterface.PlaybackListener;
 import android.media.MediaSession2.Command;
+import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.MediaSession2.PlaylistParams;
 import android.media.MediaSession2.SessionCallback;
+import android.media.TestServiceRegistry.SessionCallbackProxy;
 import android.media.TestUtils.SyncHandler;
 import android.net.Uri;
 import android.os.Bundle;
@@ -34,6 +36,7 @@
 import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.text.TextUtils;
 
 import org.junit.After;
 import org.junit.Before;
@@ -618,24 +621,32 @@
     @Ignore
     @Test
     public void testConnectToService_sessionService() throws InterruptedException {
-        connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
-        testConnectToService();
+        testConnectToService(MockMediaSessionService2.ID);
     }
 
     // TODO(jaewan): Reenable when session manager detects app installs
     @Ignore
     @Test
     public void testConnectToService_libraryService() throws InterruptedException {
-        connectToService(TestUtils.getServiceToken(mContext, MockMediaLibraryService2.ID));
-        testConnectToService();
+        testConnectToService(MockMediaLibraryService2.ID);
     }
 
-    public void testConnectToService() throws InterruptedException {
-        TestServiceRegistry serviceInfo = TestServiceRegistry.getInstance();
-        ControllerInfo info = serviceInfo.getOnConnectControllerInfo();
-        assertEquals(mContext.getPackageName(), info.getPackageName());
-        assertEquals(Process.myUid(), info.getUid());
-        assertFalse(info.isTrusted());
+    public void testConnectToService(String id) throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final SessionCallbackProxy proxy = new SessionCallbackProxy(mContext) {
+            @Override
+            public CommandGroup onConnect(ControllerInfo controller) {
+                if (Process.myUid() == controller.getUid()) {
+                    assertEquals(mContext.getPackageName(), controller.getPackageName());
+                    assertFalse(controller.isTrusted());
+                    latch.countDown();;
+                }
+                return super.onConnect(controller);
+            }
+        };
+        TestServiceRegistry.getInstance().setSessionCallbackProxy(proxy);
+        mController = createController(TestUtils.getServiceToken(mContext, id));
+        assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
         // Test command from controller to session service
         mController.play();
@@ -701,27 +712,26 @@
     @Ignore
     @Test
     public void testClose_sessionService() throws InterruptedException {
-        connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
-        testCloseFromService();
+        testCloseFromService(MockMediaSessionService2.ID);
     }
 
     // TODO(jaewan): Reenable when session manager detects app installs
     @Ignore
     @Test
     public void testClose_libraryService() throws InterruptedException {
-        connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
-        testCloseFromService();
+        testCloseFromService(MockMediaLibraryService2.ID);
     }
 
-    private void testCloseFromService() throws InterruptedException {
-        final String id = mController.getSessionToken().getId();
+    private void testCloseFromService(String id) throws InterruptedException {
         final CountDownLatch latch = new CountDownLatch(1);
-        TestServiceRegistry.getInstance().setServiceInstanceChangedCallback((service) -> {
-            if (service == null) {
-                // Destroying..
+        final SessionCallbackProxy proxy = new SessionCallbackProxy(mContext) {
+            @Override
+            public void onServiceDestroyed() {
                 latch.countDown();
             }
-        });
+        };
+        TestServiceRegistry.getInstance().setSessionCallbackProxy(proxy);
+        mController = createController(TestUtils.getServiceToken(mContext, id));
         mController.close();
         // Wait until close triggers onDestroy() of the session service.
         assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
index b00633b..f5ac6aa 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
@@ -33,6 +33,8 @@
 import android.media.MediaPlayerInterface.PlaybackListener;
 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.SessionCallback;
@@ -353,6 +355,45 @@
     }
 
     @Test
+    public void testSetCustomLayout() throws InterruptedException {
+        final List<CommandButton> buttons = new ArrayList<>();
+        buttons.add(new CommandButton.Builder(mContext)
+                .setCommand(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PLAY))
+                .setDisplayName("button").build());
+        final CountDownLatch latch = new CountDownLatch(1);
+        final SessionCallback sessionCallback = new SessionCallback(mContext) {
+            @Override
+            public CommandGroup onConnect(ControllerInfo controller) {
+                if (mContext.getPackageName().equals(controller.getPackageName())) {
+                    mSession.setCustomLayout(controller, buttons);
+                }
+                return super.onConnect(controller);
+            }
+        };
+
+        try (final MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
+                .setId("testSetCustomLayout")
+                .setSessionCallback(sHandlerExecutor, sessionCallback)
+                .build()) {
+            final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
+                @Override
+                public void onCustomLayoutChanged(List<CommandButton> layout) {
+                    assertEquals(layout.size(), buttons.size());
+                    for (int i = 0; i < layout.size(); i++) {
+                        assertEquals(layout.get(i).getCommand(), buttons.get(i).getCommand());
+                        assertEquals(layout.get(i).getDisplayName(),
+                                buttons.get(i).getDisplayName());
+                    }
+                    latch.countDown();
+                }
+            };
+            final MediaController2 controller =
+                    createController(session.getToken(), true, callback);
+            assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        }
+    }
+
+    @Test
     public void testSendCustomAction() throws InterruptedException {
         final Command testCommand =
                 new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PREPARE);
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
index 513fa29..f5abfff 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.media.MediaController2.ControllerCallback;
 import android.media.MediaSession2.Command;
+import android.media.MediaSession2.CommandButton;
 import android.media.MediaSession2.CommandGroup;
 import android.os.Bundle;
 import android.os.HandlerThread;
@@ -59,15 +60,15 @@
         ControllerCallback getCallback();
     }
 
+    // Any change here should be also reflected to the TestControllerCallback and
+    // TestBrowserCallback
     interface TestControllerCallbackInterface {
-        // Add methods in ControllerCallback/BrowserCallback that you want to test.
+        // Add methods in ControllerCallback that you want to test.
         default void onPlaylistChanged(List<MediaItem2> playlist) {}
         default void onPlaylistParamsChanged(MediaSession2.PlaylistParams params) {}
         default void onPlaybackInfoChanged(MediaController2.PlaybackInfo info) {}
-
-        // Currently empty. Add methods in ControllerCallback/BrowserCallback that you want to test.
-        default void onPlaybackStateChanged(PlaybackState2 state) { }
-
+        default void onPlaybackStateChanged(PlaybackState2 state) {}
+        default void onCustomLayoutChanged(List<CommandButton> layout) {}
         default void onCustomCommand(Command command, Bundle args, ResultReceiver receiver) {}
     }
 
@@ -188,26 +189,22 @@
         @CallSuper
         @Override
         public void onConnected(CommandGroup commands) {
-            super.onConnected(commands);
             connectLatch.countDown();
         }
 
         @CallSuper
         @Override
         public void onDisconnected() {
-            super.onDisconnected();
             disconnectLatch.countDown();
         }
 
         @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);
         }
 
@@ -231,23 +228,22 @@
 
         @Override
         public void onPlaylistChanged(List<MediaItem2> params) {
-            if (mCallbackProxy != null) {
-                mCallbackProxy.onPlaylistChanged(params);
-            }
+            mCallbackProxy.onPlaylistChanged(params);
         }
 
         @Override
         public void onPlaylistParamsChanged(MediaSession2.PlaylistParams params) {
-            if (mCallbackProxy != null) {
-                mCallbackProxy.onPlaylistParamsChanged(params);
-            }
+            mCallbackProxy.onPlaylistParamsChanged(params);
         }
 
         @Override
         public void onPlaybackInfoChanged(MediaController2.PlaybackInfo info) {
-            if (mCallbackProxy != null) {
-                mCallbackProxy.onPlaybackInfoChanged(info);
-            }
+            mCallbackProxy.onPlaybackInfoChanged(info);
+        }
+
+        @Override
+        public void onCustomLayoutChanged(List<CommandButton> layout) {
+            mCallbackProxy.onCustomLayoutChanged(layout);
         }
     }
 
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
index ec69ff6..5fabebc 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
@@ -23,9 +23,17 @@
 import android.content.Context;
 import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
+import android.media.TestServiceRegistry.SessionCallbackProxy;
 import android.media.TestUtils.SyncHandler;
 import android.os.Bundle;
 import android.os.Process;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.util.ArrayList;
+import java.util.List;
+
+import java.util.concurrent.Executor;
 
 import javax.annotation.concurrent.GuardedBy;
 
@@ -38,6 +46,20 @@
 
     public static final String ROOT_ID = "rootId";
     public static final Bundle EXTRA = new Bundle();
+
+    public static final String MEDIA_ID_GET_ITEM = "media_id_get_item";
+
+    public static final String PARENT_ID = "parent_id";
+    public static final String PARENT_ID_NO_CHILDREN = "parent_id_no_children";
+    public static final String PARENT_ID_ERROR = "parent_id_error";
+    public static final List<MediaItem2> GET_CHILDREN_RESULT = new ArrayList<>();
+
+    private static final int CHILDREN_COUNT = 100;
+    private static final DataSourceDesc DATA_SOURCE_DESC =
+            new DataSourceDesc.Builder().setDataSource(new FileDescriptor()).build();
+
+    private static final String TAG = "MockMediaLibrarySvc2";
+
     static {
         EXTRA.putString(ROOT_ID, ROOT_ID);
     }
@@ -46,20 +68,36 @@
 
     private MediaLibrarySession mSession;
 
+    public MockMediaLibraryService2() {
+        super();
+        GET_CHILDREN_RESULT.clear();
+        String mediaIdPrefix = "media_id_";
+        for (int i = 0; i < CHILDREN_COUNT; i++) {
+            GET_CHILDREN_RESULT.add(createMediaItem(mediaIdPrefix + i));
+        }
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        TestServiceRegistry.getInstance().setServiceInstance(this);
+    }
+
     @Override
     public MediaLibrarySession onCreateSession(String sessionId) {
         final MockPlayer player = new MockPlayer(1);
         final SyncHandler handler = (SyncHandler) TestServiceRegistry.getInstance().getHandler();
-        try {
-            handler.postAndSync(() -> {
-                TestLibrarySessionCallback callback = new TestLibrarySessionCallback();
-                mSession = new MediaLibrarySessionBuilder(MockMediaLibraryService2.this,
-                        player, (runnable) -> handler.post(runnable), callback)
-                        .setId(sessionId).build();
-            });
-        } catch (InterruptedException e) {
-            fail(e.toString());
+        final Executor executor = (runnable) -> handler.post(runnable);
+        SessionCallbackProxy sessionCallbackProxy = TestServiceRegistry.getInstance()
+                .getSessionCallbackProxy();
+        if (sessionCallbackProxy == null) {
+            // Ensures non-null
+            sessionCallbackProxy = new SessionCallbackProxy(this) {};
         }
+        TestLibrarySessionCallback callback =
+                new TestLibrarySessionCallback(sessionCallbackProxy);
+        mSession = new MediaLibrarySessionBuilder(MockMediaLibraryService2.this, player,
+                executor, callback).setId(sessionId).build();
         return mSession;
     }
 
@@ -81,24 +119,76 @@
     }
 
     private class TestLibrarySessionCallback extends MediaLibrarySessionCallback {
-        public TestLibrarySessionCallback() {
+        private final SessionCallbackProxy mCallbackProxy;
+
+        public TestLibrarySessionCallback(SessionCallbackProxy callbackProxy) {
             super(MockMediaLibraryService2.this);
+            mCallbackProxy = callbackProxy;
         }
 
         @Override
         public CommandGroup onConnect(ControllerInfo controller) {
-            if (Process.myUid() != controller.getUid()) {
-                // It's system app wants to listen changes. Ignore.
-                return super.onConnect(controller);
-            }
-            TestServiceRegistry.getInstance().setServiceInstance(
-                    MockMediaLibraryService2.this, controller);
-            return super.onConnect(controller);
+            return mCallbackProxy.onConnect(controller);
         }
 
         @Override
         public LibraryRoot onGetRoot(ControllerInfo controller, Bundle rootHints) {
             return new LibraryRoot(MockMediaLibraryService2.this, ROOT_ID, EXTRA);
         }
+
+        @Override
+        public MediaItem2 onLoadItem(ControllerInfo controller, String mediaId) {
+            if (MEDIA_ID_GET_ITEM.equals(mediaId)) {
+                return createMediaItem(mediaId);
+            } else {
+                return null;
+            }
+        }
+
+        @Override
+        public List<MediaItem2> onLoadChildren(ControllerInfo controller, String parentId, int page,
+                int pageSize, Bundle options) {
+            if (PARENT_ID.equals(parentId)) {
+                return getPaginatedResult(GET_CHILDREN_RESULT, page, pageSize);
+            } else if (PARENT_ID_ERROR.equals(parentId)) {
+                return null;
+            }
+            // Includes the case of PARENT_ID_NO_CHILDREN.
+            return new ArrayList<>();
+        }
+    }
+
+    private List<MediaItem2> getPaginatedResult(List<MediaItem2> items, int page, int pageSize) {
+        if (items == null) {
+            return null;
+        } else if (items.size() == 0) {
+            return new ArrayList<>();
+        }
+
+        final int totalItemCount = items.size();
+        int fromIndex = (page - 1) * pageSize;
+        int toIndex = Math.min(page * pageSize, totalItemCount);
+
+        List<MediaItem2> paginatedResult = new ArrayList<>();
+        try {
+            // The case of (fromIndex >= totalItemCount) will throw exception below.
+            paginatedResult = items.subList(fromIndex, toIndex);
+        } catch (IndexOutOfBoundsException | IllegalArgumentException ex) {
+            Log.d(TAG, "Result is empty for given pagination arguments: totalItemCount="
+                    + totalItemCount + ", page=" + page + ", pageSize=" + pageSize, ex);
+        }
+        return paginatedResult;
+    }
+
+    private MediaItem2 createMediaItem(String mediaId) {
+        Context context = MockMediaLibraryService2.this;
+        return new MediaItem2(
+                context,
+                mediaId,
+                DATA_SOURCE_DESC,
+                new MediaMetadata2.Builder(context)
+                        .putString(MediaMetadata2.METADATA_KEY_MEDIA_ID, mediaId)
+                        .build(),
+                0 /* Flags */);
     }
 }
\ 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 c8ed184..12c2c9f 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
@@ -22,11 +22,11 @@
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.content.Context;
+import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.MediaSession2.SessionCallback;
+import android.media.TestServiceRegistry.SessionCallbackProxy;
 import android.media.TestUtils.SyncHandler;
-import android.media.session.PlaybackState;
-import android.os.Process;
 
 import java.util.concurrent.Executor;
 
@@ -45,29 +45,32 @@
     private NotificationManager mNotificationManager;
 
     @Override
+    public void onCreate() {
+        super.onCreate();
+        TestServiceRegistry.getInstance().setServiceInstance(this);
+        mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+    }
+
+    @Override
     public MediaSession2 onCreateSession(String sessionId) {
         final MockPlayer player = new MockPlayer(1);
         final SyncHandler handler = (SyncHandler) TestServiceRegistry.getInstance().getHandler();
         final Executor executor = (runnable) -> handler.post(runnable);
-        try {
-            handler.postAndSync(() -> {
-                mSession = new MediaSession2.Builder(MockMediaSessionService2.this, player)
-                        .setSessionCallback(executor, new MySessionCallback())
-                        .setId(sessionId).build();
-            });
-        } catch (InterruptedException e) {
-            fail(e.toString());
+        SessionCallbackProxy sessionCallbackProxy = TestServiceRegistry.getInstance()
+                .getSessionCallbackProxy();
+        if (sessionCallbackProxy == null) {
+            // Ensures non-null
+            sessionCallbackProxy = new SessionCallbackProxy(this) {};
         }
+        TestSessionServiceCallback callback =
+                new TestSessionServiceCallback(sessionCallbackProxy);
+        mSession = new MediaSession2.Builder(this, player)
+                .setSessionCallback(executor, callback)
+                .setId(sessionId).build();
         return mSession;
     }
 
     @Override
-    public void onCreate() {
-        super.onCreate();
-        mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-    }
-
-    @Override
     public void onDestroy() {
         TestServiceRegistry.getInstance().cleanUp();
         super.onDestroy();
@@ -90,20 +93,17 @@
         return new MediaNotification(this, DEFAULT_MEDIA_NOTIFICATION_ID, notification);
     }
 
-    private class MySessionCallback extends SessionCallback {
-        public MySessionCallback() {
+    private class TestSessionServiceCallback extends SessionCallback {
+        private final SessionCallbackProxy mCallbackProxy;
+
+        public TestSessionServiceCallback(SessionCallbackProxy callbackProxy) {
             super(MockMediaSessionService2.this);
+            mCallbackProxy = callbackProxy;
         }
 
         @Override
-        public MediaSession2.CommandGroup onConnect(ControllerInfo controller) {
-            if (Process.myUid() != controller.getUid()) {
-                // It's system app wants to listen changes. Ignore.
-                return super.onConnect(controller);
-            }
-            TestServiceRegistry.getInstance().setServiceInstance(
-                    MockMediaSessionService2.this, controller);
-            return super.onConnect(controller);
+        public CommandGroup onConnect(ControllerInfo controller) {
+            return mCallbackProxy.onConnect(controller);
         }
     }
 }
diff --git a/packages/MediaComponents/test/src/android/media/SessionToken2Test.java b/packages/MediaComponents/test/src/android/media/SessionToken2Test.java
new file mode 100644
index 0000000..efde78a
--- /dev/null
+++ b/packages/MediaComponents/test/src/android/media/SessionToken2Test.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 android.content.Context;
+import android.os.Process;
+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;
+
+/**
+ * Tests {@link SessionToken2}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SessionToken2Test {
+    private Context mContext;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    @Test
+    public void testConstructor_sessionService() {
+        SessionToken2 token = new SessionToken2(mContext, mContext.getPackageName(),
+                MockMediaSessionService2.class.getCanonicalName());
+        assertEquals(MockMediaSessionService2.ID, token.getId());
+        assertEquals(mContext.getPackageName(), token.getPackageName());
+        assertEquals(Process.myUid(), token.getUid());
+        assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
+    }
+
+    @Test
+    public void testConstructor_libraryService() {
+        SessionToken2 token = new SessionToken2(mContext, mContext.getPackageName(),
+                MockMediaLibraryService2.class.getCanonicalName());
+        assertEquals(MockMediaLibraryService2.ID, token.getId());
+        assertEquals(mContext.getPackageName(), token.getPackageName());
+        assertEquals(Process.myUid(), token.getUid());
+        assertEquals(SessionToken2.TYPE_LIBRARY_SERVICE, token.getType());
+    }
+}
\ No newline at end of file
diff --git a/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java b/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
index 6f5512e..3800c28 100644
--- a/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
+++ b/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
@@ -18,10 +18,12 @@
 
 import static org.junit.Assert.fail;
 
+import android.content.Context;
+import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.TestUtils.SyncHandler;
 import android.os.Handler;
-import android.os.Looper;
+import android.os.Process;
 import android.support.annotation.GuardedBy;
 
 /**
@@ -31,8 +33,46 @@
  * It only support only one service at a time.
  */
 public class TestServiceRegistry {
-    public interface ServiceInstanceChangedCallback {
-        void OnServiceInstanceChanged(MediaSessionService2 service);
+    /**
+     * Proxy for both {@link MediaSession2.SessionCallback} and
+     * {@link MediaLibraryService2.MediaLibrarySessionCallback}.
+     */
+    public static abstract class SessionCallbackProxy {
+        private final Context mContext;
+
+        /**
+         * Constructor
+         */
+        public SessionCallbackProxy(Context context) {
+            mContext = context;
+        }
+
+        public final Context getContext() {
+            return mContext;
+        }
+
+        /**
+         * @param controller
+         * @return
+         */
+        public CommandGroup onConnect(ControllerInfo controller) {
+            if (Process.myUid() == controller.getUid()) {
+                CommandGroup commands = new CommandGroup(mContext);
+                commands.addAllPredefinedCommands();
+                return commands;
+            }
+            return null;
+        }
+
+        /**
+         * Called when enclosing service is created.
+         */
+        public void onServiceCreated(MediaSessionService2 service) { }
+
+        /**
+         * Called when enclosing service is destroyed.
+         */
+        public void onServiceDestroyed() { }
     }
 
     @GuardedBy("TestServiceRegistry.class")
@@ -42,9 +82,7 @@
     @GuardedBy("TestServiceRegistry.class")
     private SyncHandler mHandler;
     @GuardedBy("TestServiceRegistry.class")
-    private ControllerInfo mOnConnectControllerInfo;
-    @GuardedBy("TestServiceRegistry.class")
-    private ServiceInstanceChangedCallback mCallback;
+    private SessionCallbackProxy mCallbackProxy;
 
     public static TestServiceRegistry getInstance() {
         synchronized (TestServiceRegistry.class) {
@@ -61,28 +99,33 @@
         }
     }
 
-    public void setServiceInstanceChangedCallback(ServiceInstanceChangedCallback callback) {
-        synchronized (TestServiceRegistry.class) {
-            mCallback = callback;
-        }
-    }
-
     public Handler getHandler() {
         synchronized (TestServiceRegistry.class) {
             return mHandler;
         }
     }
 
-    public void setServiceInstance(MediaSessionService2 service, ControllerInfo controller) {
+    public void setSessionCallbackProxy(SessionCallbackProxy callbackProxy) {
+        synchronized (TestServiceRegistry.class) {
+            mCallbackProxy = callbackProxy;
+        }
+    }
+
+    public SessionCallbackProxy getSessionCallbackProxy() {
+        synchronized (TestServiceRegistry.class) {
+            return mCallbackProxy;
+        }
+    }
+
+    public void setServiceInstance(MediaSessionService2 service) {
         synchronized (TestServiceRegistry.class) {
             if (mService != null) {
                 fail("Previous service instance is still running. Clean up manually to ensure"
                         + " previoulsy running service doesn't break current test");
             }
             mService = service;
-            mOnConnectControllerInfo = controller;
-            if (mCallback != null) {
-                mCallback.OnServiceInstanceChanged(service);
+            if (mCallbackProxy != null) {
+                mCallbackProxy.onServiceCreated(service);
             }
         }
     }
@@ -93,28 +136,11 @@
         }
     }
 
-    public ControllerInfo getOnConnectControllerInfo() {
-        synchronized (TestServiceRegistry.class) {
-            return mOnConnectControllerInfo;
-        }
-    }
-
-
     public void cleanUp() {
         synchronized (TestServiceRegistry.class) {
-            final ServiceInstanceChangedCallback callback = mCallback;
+            final SessionCallbackProxy callbackProxy = mCallbackProxy;
             if (mService != null) {
-                try {
-                    if (mHandler.getLooper() == Looper.myLooper()) {
-                        mService.getSession().close();
-                    } else {
-                        mHandler.postAndSync(() -> {
-                            mService.getSession().close();
-                        });
-                    }
-                } catch (InterruptedException e) {
-                    // No-op. Service containing session will die, but shouldn't be a huge issue.
-                }
+                mService.getSession().close();
                 // stopSelf() would not kill service while the binder connection established by
                 // bindService() exists, and close() above will do the job instead.
                 // So stopSelf() isn't really needed, but just for sure.
@@ -124,11 +150,10 @@
             if (mHandler != null) {
                 mHandler.removeCallbacksAndMessages(null);
             }
-            mCallback = null;
-            mOnConnectControllerInfo = null;
+            mCallbackProxy = null;
 
-            if (callback != null) {
-                callback.OnServiceInstanceChanged(null);
+            if (callbackProxy != null) {
+                callbackProxy.onServiceDestroyed();
             }
         }
     }
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index e7140c2..a3ce1f6 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -321,6 +321,7 @@
                                               actualSessionId,
                                               client.clientPid,
                                               client.clientUid,
+                                              client.packageName,
                                               config,
                                               AUDIO_INPUT_FLAG_MMAP_NOIRQ, deviceId, &portId);
     }
@@ -340,7 +341,7 @@
         if (direction == MmapStreamInterface::DIRECTION_OUTPUT) {
             AudioSystem::releaseOutput(io, streamType, actualSessionId);
         } else {
-            AudioSystem::releaseInput(io, actualSessionId);
+            AudioSystem::releaseInput(portId);
         }
         ret = NO_INIT;
     }
@@ -658,7 +659,7 @@
     sp<Client> client;
     status_t lStatus;
     audio_stream_type_t streamType;
-    audio_port_handle_t portId;
+    audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
 
     bool updatePid = (input.clientInfo.clientPid == -1);
     const uid_t callingUid = IPCThreadState::self()->getCallingUid();
@@ -1596,7 +1597,7 @@
     sp<Client> client;
     status_t lStatus;
     audio_session_t sessionId = input.sessionId;
-    audio_port_handle_t portId;
+    audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
 
     output.cblk.clear();
     output.buffers.clear();
@@ -1621,12 +1622,6 @@
         clientPid = callingPid;
     }
 
-    // check calling permissions
-    if (!recordingAllowed(input.opPackageName, input.clientInfo.clientTid, clientUid)) {
-        ALOGE("createRecord() permission denied: recording not allowed");
-        lStatus = PERMISSION_DENIED;
-        goto Exit;
-    }
     // we don't yet support anything other than linear PCM
     if (!audio_is_valid_format(input.config.format) || !audio_is_linear_pcm(input.config.format)) {
         ALOGE("createRecord() invalid format %#x", input.config.format);
@@ -1663,14 +1658,16 @@
     // release previously opened input if retrying.
     if (output.inputId != AUDIO_IO_HANDLE_NONE) {
         recordTrack.clear();
-        AudioSystem::releaseInput(output.inputId, sessionId);
+        AudioSystem::releaseInput(portId);
         output.inputId = AUDIO_IO_HANDLE_NONE;
+        portId = AUDIO_PORT_HANDLE_NONE;
     }
     lStatus = AudioSystem::getInputForAttr(&input.attr, &output.inputId,
                                       sessionId,
                                     // FIXME compare to AudioTrack
                                       clientPid,
                                       clientUid,
+                                      input.opPackageName,
                                       &input.config,
                                       output.flags, &output.selectedDeviceId, &portId);
 
@@ -1739,7 +1736,7 @@
         }
         recordTrack.clear();
         if (output.inputId != AUDIO_IO_HANDLE_NONE) {
-            AudioSystem::releaseInput(output.inputId, sessionId);
+            AudioSystem::releaseInput(portId);
         }
     }
 
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 24d862f..d6021b3 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -6936,8 +6936,7 @@
         if (recordTrack->isExternalTrack()) {
             mLock.unlock();
             bool silenced;
-            status = AudioSystem::startInput(mId, recordTrack->sessionId(),
-                    mInDevice, recordTrack->uid(), &silenced);
+            status = AudioSystem::startInput(recordTrack->portId(), &silenced);
             mLock.lock();
             // FIXME should verify that recordTrack is still in mActiveTracks
             if (status != NO_ERROR) {
@@ -6969,7 +6968,7 @@
 
 startError:
     if (recordTrack->isExternalTrack()) {
-        AudioSystem::stopInput(mId, recordTrack->sessionId());
+        AudioSystem::stopInput(recordTrack->portId());
     }
     recordTrack->clearSyncStartEvent();
     // FIXME I wonder why we do not reset the state here?
@@ -7785,7 +7784,7 @@
     if (isOutput()) {
         AudioSystem::releaseOutput(mId, streamType(), mSessionId);
     } else {
-        AudioSystem::releaseInput(mId, mSessionId);
+        AudioSystem::releaseInput(mPortId);
     }
 }
 
@@ -7845,10 +7844,6 @@
         return NO_ERROR;
     }
 
-    if (!isOutput() && !recordingAllowed(client.packageName, client.clientPid, client.clientUid)) {
-        return PERMISSION_DENIED;
-    }
-
     audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
 
     audio_io_handle_t io = mId;
@@ -7880,6 +7875,7 @@
                                               mSessionId,
                                               client.clientPid,
                                               client.clientUid,
+                                              client.packageName,
                                               &config,
                                               AUDIO_INPUT_FLAG_MMAP_NOIRQ,
                                               &deviceId,
@@ -7898,7 +7894,7 @@
     } else {
         // TODO: Block recording for idle UIDs (b/72134552)
         bool silenced;
-        ret = AudioSystem::startInput(mId, mSessionId, mInDevice, client.clientUid, &silenced);
+        ret = AudioSystem::startInput(portId, &silenced);
     }
 
     // abort if start is rejected by audio policy manager
@@ -7908,7 +7904,7 @@
             if (isOutput()) {
                 AudioSystem::releaseOutput(mId, streamType(), mSessionId);
             } else {
-                AudioSystem::releaseInput(mId, mSessionId);
+                AudioSystem::releaseInput(portId);
             }
         } else {
             mHalStream->stop();
@@ -7965,8 +7961,8 @@
         AudioSystem::stopOutput(mId, streamType(), track->sessionId());
         AudioSystem::releaseOutput(mId, streamType(), track->sessionId());
     } else {
-        AudioSystem::stopInput(mId, track->sessionId());
-        AudioSystem::releaseInput(mId, track->sessionId());
+        AudioSystem::stopInput(track->portId());
+        AudioSystem::releaseInput(track->portId());
     }
 
     sp<EffectChain> chain = getEffectChain_l(track->sessionId());
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index ce5c53b..67f27d0 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -1694,7 +1694,7 @@
     if (thread != 0) {
         RecordThread *recordThread = (RecordThread *)thread.get();
         if (recordThread->stop(this) && isExternalTrack()) {
-            AudioSystem::stopInput(mThreadIoHandle, mSessionId);
+            AudioSystem::stopInput(mPortId);
         }
     }
 }
@@ -1706,9 +1706,9 @@
     {
         if (isExternalTrack()) {
             if (mState == ACTIVE || mState == RESUMING) {
-                AudioSystem::stopInput(mThreadIoHandle, mSessionId);
+                AudioSystem::stopInput(mPortId);
             }
-            AudioSystem::releaseInput(mThreadIoHandle, mSessionId);
+            AudioSystem::releaseInput(mPortId);
         }
         sp<ThreadBase> thread = mThread.promote();
         if (thread != 0) {
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 40e0199..54bfcbc 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1158,12 +1158,6 @@
     bool force = !outputDesc->isActive() &&
             (outputDesc->getPatchHandle() == AUDIO_PATCH_HANDLE_NONE);
 
-    // requiresMuteCheck is false when we can bypass mute strategy.
-    // It covers a common case when there is no materially active audio
-    // and muting would result in unnecessary delay and dropped audio.
-    const uint32_t outputLatencyMs = outputDesc->latency();
-    bool requiresMuteCheck = outputDesc->isActive(outputLatencyMs * 2);  // account for drain
-
     // increment usage count for this stream on the requested output:
     // NOTE that the usage count is the same for duplicated output and hardware output which is
     // necessary for a correct control of hardware output routing by startOutput() and stopOutput()
@@ -1187,44 +1181,29 @@
         for (size_t i = 0; i < mOutputs.size(); i++) {
             sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
             if (desc != outputDesc) {
-                // An output has a shared device if
-                // - managed by the same hw module
-                // - supports the currently selected device
-                const bool sharedDevice = outputDesc->sharesHwModuleWith(desc)
-                        && (desc->supportedDevices() & device) != AUDIO_DEVICE_NONE;
-
                 // force a device change if any other output is:
                 // - managed by the same hw module
-                // - supports currently selected device
                 // - has a current device selection that differs from selected device.
+                // - supports currently selected device
                 // - has an active audio patch
                 // In this case, the audio HAL must receive the new device selection so that it can
-                // change the device currently selected by the other output.
-                if (sharedDevice &&
+                // change the device currently selected by the other active output.
+                if (outputDesc->sharesHwModuleWith(desc) &&
                         desc->device() != device &&
+                        desc->supportedDevices() & device &&
                         desc->getPatchHandle() != AUDIO_PATCH_HANDLE_NONE) {
                     force = true;
                 }
                 // wait for audio on other active outputs to be presented when starting
                 // a notification so that audio focus effect can propagate, or that a mute/unmute
                 // event occurred for beacon
-                const uint32_t latencyMs = desc->latency();
-                const bool isActive = desc->isActive(latencyMs * 2);  // account for drain
-
-                if (shouldWait && isActive && (waitMs < latencyMs)) {
-                    waitMs = latencyMs;
+                uint32_t latency = desc->latency();
+                if (shouldWait && desc->isActive(latency * 2) && (waitMs < latency)) {
+                    waitMs = latency;
                 }
-
-                // Require mute check if another output is on a shared device
-                // and currently active to have proper drain and avoid pops.
-                // Note restoring AudioTracks onto this output needs to invoke
-                // a volume ramp if there is no mute.
-                requiresMuteCheck |= sharedDevice && isActive;
             }
         }
-
-        const uint32_t muteWaitMs =
-                setOutputDevice(outputDesc, device, force, 0, NULL, address, requiresMuteCheck);
+        uint32_t muteWaitMs = setOutputDevice(outputDesc, device, force, 0, NULL, address);
 
         // handle special case for sonification while in call
         if (isInCall()) {
@@ -1249,14 +1228,6 @@
         if (waitMs > muteWaitMs) {
             *delayMs = waitMs - muteWaitMs;
         }
-
-        // FIXME: A device change (muteWaitMs > 0) likely introduces a volume change.
-        // A volume change enacted by APM with 0 delay is not synchronous, as it goes
-        // via AudioCommandThread to AudioFlinger.  Hence it is possible that the volume
-        // change occurs after the MixerThread starts and causes a stream volume
-        // glitch.
-        //
-        // We do not introduce additional delay here.
     }
 
     return NO_ERROR;
@@ -4771,24 +4742,21 @@
                                              bool force,
                                              int delayMs,
                                              audio_patch_handle_t *patchHandle,
-                                             const char *address,
-                                             bool requiresMuteCheck)
+                                             const char* address)
 {
     ALOGV("setOutputDevice() device %04x delayMs %d", device, delayMs);
     AudioParameter param;
     uint32_t muteWaitMs;
 
     if (outputDesc->isDuplicated()) {
-        muteWaitMs = setOutputDevice(outputDesc->subOutput1(), device, force, delayMs,
-                nullptr /* patchHandle */, nullptr /* address */, requiresMuteCheck);
-        muteWaitMs += setOutputDevice(outputDesc->subOutput2(), device, force, delayMs,
-                nullptr /* patchHandle */, nullptr /* address */, requiresMuteCheck);
+        muteWaitMs = setOutputDevice(outputDesc->subOutput1(), device, force, delayMs);
+        muteWaitMs += setOutputDevice(outputDesc->subOutput2(), device, force, delayMs);
         return muteWaitMs;
     }
     // no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current
     // output profile
     if ((device != AUDIO_DEVICE_NONE) &&
-            ((device & outputDesc->supportedDevices()) == AUDIO_DEVICE_NONE)) {
+            ((device & outputDesc->supportedDevices()) == 0)) {
         return 0;
     }
 
@@ -4802,14 +4770,7 @@
     if (device != AUDIO_DEVICE_NONE) {
         outputDesc->mDevice = device;
     }
-
-    // if the outputs are not materially active, there is no need to mute.
-    if (requiresMuteCheck) {
-        muteWaitMs = checkDeviceMuteStrategies(outputDesc, prevDevice, delayMs);
-    } else {
-        ALOGV("%s: suppressing checkDeviceMuteStrategies", __func__);
-        muteWaitMs = 0;
-    }
+    muteWaitMs = checkDeviceMuteStrategies(outputDesc, prevDevice, delayMs);
 
     // Do not change the routing if:
     //      the requested device is AUDIO_DEVICE_NONE
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 2b68882..ee339e7 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -317,8 +317,7 @@
                              bool force = false,
                              int delayMs = 0,
                              audio_patch_handle_t *patchHandle = NULL,
-                             const char *address = nullptr,
-                             bool requiresMuteCheck = true);
+                             const char* address = NULL);
         status_t resetOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
                                    int delayMs = 0,
                                    audio_patch_handle_t *patchHandle = NULL);
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index f1d7d86..306de3f 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -287,6 +287,7 @@
                                              audio_session_t session,
                                              pid_t pid,
                                              uid_t uid,
+                                             const String16& opPackageName,
                                              const audio_config_base_t *config,
                                              audio_input_flags_t flags,
                                              audio_port_handle_t *selectedDeviceId,
@@ -295,6 +296,7 @@
     if (mAudioPolicyManager == NULL) {
         return NO_INIT;
     }
+
     // already checked by client, but double-check in case the client wrapper is bypassed
     if (attr->source < AUDIO_SOURCE_DEFAULT && attr->source >= AUDIO_SOURCE_CNT &&
             attr->source != AUDIO_SOURCE_HOTWORD && attr->source != AUDIO_SOURCE_FM_TUNER) {
@@ -318,6 +320,13 @@
         pid = callingPid;
     }
 
+    // check calling permissions
+    if (!recordingAllowed(opPackageName, pid, uid)) {
+        ALOGE("%s permission denied: recording not allowed for uid %d pid %d",
+                __func__, uid, pid);
+        return PERMISSION_DENIED;
+    }
+
     if ((attr->source == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed(pid, uid)) {
         return BAD_VALUE;
     }
@@ -367,6 +376,13 @@
             }
             return status;
         }
+
+        sp<AudioRecordClient> client =
+                new AudioRecordClient(*attr, *input, uid, pid, opPackageName, session);
+        client->active = false;
+        client->isConcurrent = false;
+        client->isVirtualDevice = false; //TODO : update from APM->getInputForAttr()
+        mAudioRecordClients.add(*portId, client);
     }
 
     if (audioPolicyEffects != 0) {
@@ -379,23 +395,38 @@
     return NO_ERROR;
 }
 
-status_t AudioPolicyService::startInput(audio_io_handle_t input,
-                                        audio_session_t session,
-                                        audio_devices_t device,
-                                        uid_t uid,
-                                        bool *silenced)
+status_t AudioPolicyService::startInput(audio_port_handle_t portId, bool *silenced)
 {
-    // If UID inactive it records silence until becoming active
-    *silenced = !mUidPolicy->isUidActive(uid) && !is_virtual_input_device(device);
-
     if (mAudioPolicyManager == NULL) {
         return NO_INIT;
     }
+    sp<AudioRecordClient> client;
+    {
+        Mutex::Autolock _l(mLock);
+
+        ssize_t index = mAudioRecordClients.indexOfKey(portId);
+        if (index < 0) {
+            return INVALID_OPERATION;
+        }
+        client = mAudioRecordClients.valueAt(index);
+    }
+
+    // check calling permissions
+    if (!recordingAllowed(client->opPackageName, client->pid, client->uid)) {
+        ALOGE("%s permission denied: recording not allowed for uid %d pid %d",
+                __func__, client->uid, client->pid);
+        return PERMISSION_DENIED;
+    }
+
+    // If UID inactive it records silence until becoming active
+    *silenced = !mUidPolicy->isUidActive(client->uid) && !client->isVirtualDevice;
 
     Mutex::Autolock _l(mLock);
     AudioPolicyInterface::concurrency_type__mask_t concurrency =
             AudioPolicyInterface::API_INPUT_CONCURRENCY_NONE;
-    status_t status = mAudioPolicyManager->startInput(input, session, *silenced, &concurrency);
+
+    status_t status = mAudioPolicyManager->startInput(
+            client->input, client->session, *silenced, &concurrency);
 
     if (status == NO_ERROR) {
         LOG_ALWAYS_FATAL_IF(concurrency & ~AudioPolicyInterface::API_INPUT_CONCURRENCY_ALL,
@@ -413,38 +444,52 @@
     return status;
 }
 
-status_t AudioPolicyService::stopInput(audio_io_handle_t input,
-                                       audio_session_t session)
+status_t AudioPolicyService::stopInput(audio_port_handle_t portId)
 {
     if (mAudioPolicyManager == NULL) {
         return NO_INIT;
     }
     Mutex::Autolock _l(mLock);
 
-    return mAudioPolicyManager->stopInput(input, session);
+    ssize_t index = mAudioRecordClients.indexOfKey(portId);
+    if (index < 0) {
+        return INVALID_OPERATION;
+    }
+    sp<AudioRecordClient> client = mAudioRecordClients.valueAt(index);
+
+    return mAudioPolicyManager->stopInput(client->input, client->session);
 }
 
-void AudioPolicyService::releaseInput(audio_io_handle_t input,
-                                      audio_session_t session)
+void AudioPolicyService::releaseInput(audio_port_handle_t portId)
 {
     if (mAudioPolicyManager == NULL) {
         return;
     }
     sp<AudioPolicyEffects>audioPolicyEffects;
+    sp<AudioRecordClient> client;
     {
         Mutex::Autolock _l(mLock);
         audioPolicyEffects = mAudioPolicyEffects;
+        ssize_t index = mAudioRecordClients.indexOfKey(portId);
+        if (index < 0) {
+            return;
+        }
+        client = mAudioRecordClients.valueAt(index);
+        mAudioRecordClients.removeItem(portId);
+    }
+    if (client == 0) {
+        return;
     }
     if (audioPolicyEffects != 0) {
         // release audio processors from the input
-        status_t status = audioPolicyEffects->releaseInputEffects(input, session);
+        status_t status = audioPolicyEffects->releaseInputEffects(client->input, client->session);
         if(status != NO_ERROR) {
-            ALOGW("Failed to release effects on input %d", input);
+            ALOGW("Failed to release effects on input %d", client->input);
         }
     }
     {
         Mutex::Autolock _l(mLock);
-        mAudioPolicyManager->releaseInput(input, session);
+        mAudioPolicyManager->releaseInput(client->input, client->session);
     }
 }
 
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index c21aa58..bfa3ef4 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -98,19 +98,15 @@
                                      audio_session_t session,
                                      pid_t pid,
                                      uid_t uid,
+                                     const String16& opPackageName,
                                      const audio_config_base_t *config,
                                      audio_input_flags_t flags,
                                      audio_port_handle_t *selectedDeviceId = NULL,
                                      audio_port_handle_t *portId = NULL);
-    virtual status_t startInput(audio_io_handle_t input,
-                                audio_session_t session,
-                                audio_devices_t device,
-                                uid_t uid,
+    virtual status_t startInput(audio_port_handle_t portId,
                                 bool *silenced);
-    virtual status_t stopInput(audio_io_handle_t input,
-                               audio_session_t session);
-    virtual void releaseInput(audio_io_handle_t input,
-                              audio_session_t session);
+    virtual status_t stopInput(audio_port_handle_t portId);
+    virtual void releaseInput(audio_port_handle_t portId);
     virtual status_t initStreamVolume(audio_stream_type_t stream,
                                       int indexMin,
                                       int indexMax);
@@ -611,6 +607,31 @@
               bool                          mAudioPortCallbacksEnabled;
     };
 
+    // --- AudioRecordClient ---
+    // Information about each registered AudioRecord client
+    // (between calls to getInputForAttr() and releaseInput())
+    class AudioRecordClient : public RefBase {
+    public:
+                AudioRecordClient(const audio_attributes_t attributes,
+                                  const audio_io_handle_t input, uid_t uid, pid_t pid,
+                                  const String16& opPackageName, const audio_session_t session) :
+                                      attributes(attributes),
+                                      input(input), uid(uid), pid(pid),
+                                      opPackageName(opPackageName), session(session),
+                                      active(false), isConcurrent(false), isVirtualDevice(false) {}
+        virtual ~AudioRecordClient() {}
+
+        const audio_attributes_t attributes; // source, flags ...
+        const audio_io_handle_t input;       // audio HAL input IO handle
+        const uid_t uid;                     // client UID
+        const pid_t pid;                     // client PID
+        const String16 opPackageName;        // client package name
+        const audio_session_t session;       // audio session ID
+        bool active;                   // Capture is active or inactive
+        bool isConcurrent;             // is allowed to concurrent capture
+        bool isVirtualDevice;          // uses vitual device: updated by APM::getInputForAttr()
+    };
+
     // Internal dump utilities.
     status_t dumpPermissionDenial(int fd);
 
@@ -636,6 +657,7 @@
     audio_mode_t mPhoneState;
 
     sp<UidPolicy> mUidPolicy;
+    DefaultKeyedVector< audio_port_handle_t, sp<AudioRecordClient> >   mAudioRecordClients;
 };
 
 } // namespace android
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 02a7616..050c3f7 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -1324,8 +1324,8 @@
     {
         const char *fpsRange, *fpsSingle;
 
-        fpsRange = newParams.get(CameraParameters::KEY_PREVIEW_FRAME_RATE);
-        fpsSingle = newParams.get(CameraParameters::KEY_PREVIEW_FPS_RANGE);
+        fpsSingle = newParams.get(CameraParameters::KEY_PREVIEW_FRAME_RATE);
+        fpsRange = newParams.get(CameraParameters::KEY_PREVIEW_FPS_RANGE);
 
         /**
          * Pick either the range or the single key if only one was set.
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 7dba20d..04c2c5b 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -676,8 +676,6 @@
             "TEMPLATE_VIDEO_SNAPSHOT",
             "TEMPLATE_ZERO_SHUTTER_LAG",
             "TEMPLATE_MANUAL",
-            "TEMPLATE_MOTION_TRACKING_PREVIEW",
-            "TEMPALTE_MOTION_TRACKING_BEST"
         };
 
         for (int i = 1; i < CAMERA3_TEMPLATE_COUNT; i++) {
@@ -3435,65 +3433,31 @@
             }
         };
     hardware::Return<void> err;
-    if (mHidlSession_3_4 != nullptr) {
-        device::V3_4::RequestTemplate id;
-        switch (templateId) {
-            case CAMERA3_TEMPLATE_PREVIEW:
-                id = device::V3_4::RequestTemplate::PREVIEW;
-                break;
-            case CAMERA3_TEMPLATE_STILL_CAPTURE:
-                id = device::V3_4::RequestTemplate::STILL_CAPTURE;
-                break;
-            case CAMERA3_TEMPLATE_VIDEO_RECORD:
-                id = device::V3_4::RequestTemplate::VIDEO_RECORD;
-                break;
-            case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
-                id = device::V3_4::RequestTemplate::VIDEO_SNAPSHOT;
-                break;
-            case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
-                id = device::V3_4::RequestTemplate::ZERO_SHUTTER_LAG;
-                break;
-            case CAMERA3_TEMPLATE_MANUAL:
-                id = device::V3_4::RequestTemplate::MANUAL;
-                break;
-            case CAMERA3_TEMPLATE_MOTION_TRACKING_PREVIEW:
-                id = device::V3_4::RequestTemplate::MOTION_TRACKING_PREVIEW;
-                break;
-            case CAMERA3_TEMPLATE_MOTION_TRACKING_BEST:
-                id = device::V3_4::RequestTemplate::MOTION_TRACKING_BEST;
-                break;
-            default:
-                // Unknown template ID
-                return BAD_VALUE;
-        }
-        err = mHidlSession_3_4->constructDefaultRequestSettings_3_4(id, requestCallback);
-    } else {
-        RequestTemplate id;
-        switch (templateId) {
-            case CAMERA3_TEMPLATE_PREVIEW:
-                id = RequestTemplate::PREVIEW;
-                break;
-            case CAMERA3_TEMPLATE_STILL_CAPTURE:
-                id = RequestTemplate::STILL_CAPTURE;
-                break;
-            case CAMERA3_TEMPLATE_VIDEO_RECORD:
-                id = RequestTemplate::VIDEO_RECORD;
-                break;
-            case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
-                id = RequestTemplate::VIDEO_SNAPSHOT;
-                break;
-            case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
-                id = RequestTemplate::ZERO_SHUTTER_LAG;
-                break;
-            case CAMERA3_TEMPLATE_MANUAL:
-                id = RequestTemplate::MANUAL;
-                break;
-            default:
-                // Unknown template ID, or this HAL is too old to support it
-                return BAD_VALUE;
-        }
-        err = mHidlSession->constructDefaultRequestSettings(id, requestCallback);
+    RequestTemplate id;
+    switch (templateId) {
+        case CAMERA3_TEMPLATE_PREVIEW:
+            id = RequestTemplate::PREVIEW;
+            break;
+        case CAMERA3_TEMPLATE_STILL_CAPTURE:
+            id = RequestTemplate::STILL_CAPTURE;
+            break;
+        case CAMERA3_TEMPLATE_VIDEO_RECORD:
+            id = RequestTemplate::VIDEO_RECORD;
+            break;
+        case CAMERA3_TEMPLATE_VIDEO_SNAPSHOT:
+            id = RequestTemplate::VIDEO_SNAPSHOT;
+            break;
+        case CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG:
+            id = RequestTemplate::ZERO_SHUTTER_LAG;
+            break;
+        case CAMERA3_TEMPLATE_MANUAL:
+            id = RequestTemplate::MANUAL;
+            break;
+        default:
+            // Unknown template ID, or this HAL is too old to support it
+            return BAD_VALUE;
     }
+    err = mHidlSession->constructDefaultRequestSettings(id, requestCallback);
 
     if (!err.isOk()) {
         ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk
index 97eaf77..8631c39 100644
--- a/services/mediacodec/Android.mk
+++ b/services/mediacodec/Android.mk
@@ -27,6 +27,7 @@
 include $(CLEAR_VARS)
 # seccomp is not required for coverage build.
 ifneq ($(NATIVE_COVERAGE),true)
+LOCAL_REQUIRED_MODULES := crash_dump.policy
 LOCAL_REQUIRED_MODULES_arm := mediacodec.policy
 LOCAL_REQUIRED_MODULES_x86 := mediacodec.policy
 endif
@@ -53,7 +54,7 @@
 # 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 := \
+LOCAL_REQUIRED_MODULES += \
 $(foreach codec,$(_software_codecs),\
   $(eval _vendor_suffix := $(if $(BOARD_VNDK_VERSION),.vendor))\
   $(codec)$(_vendor_suffix)\
@@ -72,7 +73,11 @@
 # mediacodec runs in 32-bit combatibility mode. For 64 bit architectures,
 # use the 32 bit policy
 ifdef TARGET_2ND_ARCH
+  ifneq ($(TARGET_TRANSLATE_2ND_ARCH),true)
     LOCAL_SRC_FILES := seccomp_policy/mediacodec-$(TARGET_2ND_ARCH).policy
+  else
+    LOCAL_SRC_FILES := seccomp_policy/mediacodec-$(TARGET_ARCH).policy
+  endif
 else
     LOCAL_SRC_FILES := seccomp_policy/mediacodec-$(TARGET_ARCH).policy
 endif
diff --git a/services/mediacodec/seccomp_policy/mediacodec-arm.policy b/services/mediacodec/seccomp_policy/mediacodec-arm.policy
index a751b4c..6ec8895 100644
--- a/services/mediacodec/seccomp_policy/mediacodec-arm.policy
+++ b/services/mediacodec/seccomp_policy/mediacodec-arm.policy
@@ -55,18 +55,4 @@
 getdents64: 1
 getrandom: 1
 
-# for attaching to debuggerd on process crash
-sigaction: 1
-tgkill: 1
-socket: 1
-connect: 1
-fcntl64: 1
-rt_tgsigqueueinfo: 1
-geteuid32: 1
-getgid32: 1
-getegid32: 1
-getgroups32: 1
-recvmsg: 1
-getpid: 1
-gettid: 1
-process_vm_readv: 1
+@include /system/etc/seccomp_policy/crash_dump.arm.policy
diff --git a/services/mediacodec/seccomp_policy/mediacodec-x86.policy b/services/mediacodec/seccomp_policy/mediacodec-x86.policy
index dc2c04f..1553ec7 100644
--- a/services/mediacodec/seccomp_policy/mediacodec-x86.policy
+++ b/services/mediacodec/seccomp_policy/mediacodec-x86.policy
@@ -47,23 +47,10 @@
 write: 1
 nanosleep: 1
 
-# for attaching to debuggerd on process crash
-socketcall: 1
-sigaction: 1
-tgkill: 1
-rt_sigprocmask: 1
-fcntl64: 1
-rt_tgsigqueueinfo: 1
-geteuid32: 1
-getgid32: 1
-getegid32: 1
-getgroups32: 1
-getdents64: 1
-pipe2: 1
-ppoll: 1
-
 # Required by AddressSanitizer
 gettid: 1
 sched_yield: 1
 getpid: 1
 gettid: 1
+
+@include /system/etc/seccomp_policy/crash_dump.x86.policy
diff --git a/services/mediaextractor/Android.mk b/services/mediaextractor/Android.mk
index 9f3746f..5b7571c 100644
--- a/services/mediaextractor/Android.mk
+++ b/services/mediaextractor/Android.mk
@@ -15,12 +15,13 @@
 # service executable
 include $(CLEAR_VARS)
 # seccomp filters are defined for the following architectures:
+LOCAL_REQUIRED_MODULES := crash_dump.policy
 LOCAL_REQUIRED_MODULES_arm := mediaextractor.policy
 LOCAL_REQUIRED_MODULES_arm64 := mediaextractor.policy
 LOCAL_REQUIRED_MODULES_x86 := mediaextractor.policy
 
 # extractor libraries
-LOCAL_REQUIRED_MODULES := \
+LOCAL_REQUIRED_MODULES += \
     libaacextractor \
     libamrextractor \
     libflacextractor \
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy b/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
index a9ee98d..87018ed 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
@@ -48,21 +48,4 @@
 readlinkat: 1
 _llseek: 1
 
-# for attaching to debuggerd on process crash
-sigaction: 1
-tgkill: 1
-socket: 1
-connect: 1
-recvmsg: 1
-fcntl64: 1
-rt_tgsigqueueinfo: 1
-geteuid32: 1
-getgid32: 1
-getegid32: 1
-getgroups32: 1
-getdents64: 1
-pipe2: 1
-ppoll: 1
-getpid: 1
-gettid: 1
-process_vm_readv: 1
+@include /system/etc/seccomp_policy/crash_dump.arm.policy
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy b/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
index dd71ed7..d70e27b 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
@@ -40,23 +40,4 @@
 pread64: 1
 mremap: 1
 
-# for attaching to debuggerd on process crash
-tgkill: 1
-rt_sigprocmask: 1
-rt_sigaction: 1
-# socket: arg0 == AF_LOCAL
-socket: arg0 == 1
-connect: 1
-recvmsg: 1
-rt_tgsigqueueinfo: 1
-writev: 1
-geteuid: 1
-getgid: 1
-getegid: 1
-getgroups: 1
-getdents64: 1
-pipe2: 1
-ppoll: 1
-getpid: 1
-gettid: 1
-process_vm_readv: 1
+@include /system/etc/seccomp_policy/crash_dump.arm64.policy
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy b/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
index ede108e..d739ba1 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
@@ -49,24 +49,10 @@
 readlinkat: 1
 _llseek: 1
 
-# for attaching to debuggerd on process crash
-socketcall: 1
-sigaction: 1
-tgkill: 1
-rt_sigprocmask: 1
-fcntl64: 1
-rt_tgsigqueueinfo: 1
-geteuid32: 1
-getgid32: 1
-getegid32: 1
-getgroups32: 1
-getdents64: 1
-pipe2: 1
-ppoll: 1
-process_vm_readv: 1
-
 # Required by AddressSanitizer
 gettid: 1
 sched_yield: 1
 getpid: 1
 gettid: 1
+
+@include /system/etc/seccomp_policy/crash_dump.x86.policy