Merge "Move codec2 out of frameworks/av" into pi-dev
diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp
index 66e4400..4991e50 100644
--- a/drm/libmediadrm/Android.bp
+++ b/drm/libmediadrm/Android.bp
@@ -54,17 +54,18 @@
 
     proto: {
         export_proto_headers: true,
-	type: "lite",
+        type: "lite",
     },
     shared_libs: [
         "android.hardware.drm@1.0",
         "android.hardware.drm@1.1",
         "libbase",
         "libbinder",
-	"libhidlbase",
+        "libhidlbase",
         "liblog",
         "libmediametrics",
         "libprotobuf-cpp-lite",
+        "libstagefright_foundation",
         "libutils",
     ],
     cflags: [
@@ -86,24 +87,25 @@
 
     proto: {
         export_proto_headers: true,
-	type: "full",
+        type: "full",
     },
     shared_libs: [
         "android.hardware.drm@1.0",
         "android.hardware.drm@1.1",
         "libbase",
         "libbinder",
-	"libhidlbase",
+        "libhidlbase",
         "liblog",
         "libmediametrics",
         "libprotobuf-cpp-full",
+        "libstagefright_foundation",
         "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",
+        // when using the map type in a proto definition.
+        "-Wno-unused-parameter",
+        "-Wno-error",
     ],
 }
 
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index 5d97188..4e8ad52 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -32,6 +32,7 @@
 #include <media/drm/DrmAPI.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/base64.h>
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/MediaErrors.h>
 #include <mediadrm/DrmHal.h>
@@ -63,9 +64,27 @@
 // This constant corresponds to the PROPERTY_DEVICE_UNIQUE_ID constant
 // in the MediaDrm API.
 constexpr char kPropertyDeviceUniqueId[] = "deviceUniqueId";
+constexpr char kEqualsSign[] = "=";
 
+template<typename T>
+std::string toBase64StringNoPad(const T* data, size_t size) {
+    if (size == 0) {
+      return "";
+    }
+    CHECK(sizeof(data[0] == 1));
+
+    android::AString outputString;
+    encodeBase64(data, size, &outputString);
+    // Remove trailing equals padding if it exists.
+    while (outputString.size() > 0 && outputString.endsWith(kEqualsSign)) {
+        outputString.erase(outputString.size() - 1, 1);
+    }
+
+    return std::string(outputString.c_str(), outputString.size());
 }
 
+}  // anonymous namespace
+
 namespace android {
 
 #define INIT_CHECK() {if (mInitCheck != OK) return mInitCheck;}
@@ -101,15 +120,6 @@
     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:
@@ -340,6 +350,7 @@
 
 sp<IDrmPlugin> DrmHal::makeDrmPlugin(const sp<IDrmFactory>& factory,
         const uint8_t uuid[16], const String8& appPackageName) {
+    mAppPackageName = appPackageName;
     mMetrics.SetAppPackageName(appPackageName);
 
     sp<IDrmPlugin> plugin;
@@ -1353,9 +1364,10 @@
     if (result != OK) {
         ALOGE("Failed to serialize framework metrics: %d", result);
     }
-    serializedMetrics = toHexString(serializedMetrics);
-    if (!serializedMetrics.empty()) {
-        item.setCString("serialized_metrics", serializedMetrics.c_str());
+    std::string b64EncodedMetrics = toBase64StringNoPad(serializedMetrics.data(),
+                                                        serializedMetrics.size());
+    if (!b64EncodedMetrics.empty()) {
+        item.setCString("serialized_metrics", b64EncodedMetrics.c_str());
     }
     if (!item.selfrecord()) {
         ALOGE("Failed to self record framework metrics");
@@ -1364,14 +1376,16 @@
 
 void DrmHal::reportPluginMetrics() const
 {
-    Vector<uint8_t> metrics;
+    Vector<uint8_t> metricsVector;
     String8 vendor;
     String8 description;
     if (getPropertyStringInternal(String8("vendor"), vendor) == OK &&
             getPropertyStringInternal(String8("description"), description) == OK &&
-            getPropertyByteArrayInternal(String8("metrics"), metrics) == OK) {
-        status_t res = android::reportDrmPluginMetrics(
-                metrics, vendor, description);
+            getPropertyByteArrayInternal(String8("metrics"), metricsVector) == OK) {
+        std::string metricsString = toBase64StringNoPad(metricsVector.array(),
+                                                        metricsVector.size());
+        status_t res = android::reportDrmPluginMetrics(metricsString, vendor,
+                                                       description, mAppPackageName);
         if (res != OK) {
             ALOGE("Metrics were retrieved but could not be reported: %d", res);
         }
diff --git a/drm/libmediadrm/PluginMetricsReporting.cpp b/drm/libmediadrm/PluginMetricsReporting.cpp
index 6c97f2b..5cb48bf 100644
--- a/drm/libmediadrm/PluginMetricsReporting.cpp
+++ b/drm/libmediadrm/PluginMetricsReporting.cpp
@@ -16,80 +16,35 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "PluginMetricsReporting"
-#include <utils/Log.h>
-#include <inttypes.h>
 
 #include <media/PluginMetricsReporting.h>
 
-#include <media/MediaAnalyticsItem.h>
+#include <inttypes.h>
 
-#include "protos/metrics.pb.h"
+#include <media/MediaAnalyticsItem.h>
+#include <utils/Log.h>
+
 
 namespace android {
 
 namespace {
 
-using android::drm_metrics::MetricsGroup;
-using android::drm_metrics::MetricsGroup_Metric;
-using android::drm_metrics::MetricsGroup_Metric_MetricValue;
+constexpr char kSerializedMetricsField[] = "serialized_metrics";
 
-const char* const kParentAttribute = "/parent/external";
-
-status_t reportMetricsGroup(const MetricsGroup& metricsGroup,
-                            const String8& batchName,
-                            const int64_t* parentId) {
-    MediaAnalyticsItem analyticsItem(batchName.c_str());
+status_t reportVendorMetrics(const std::string& metrics,
+                             const String8& name,
+                             const String8& appPackageName) {
+    MediaAnalyticsItem analyticsItem(name.c_str());
     analyticsItem.generateSessionID();
-    int64_t sessionId = analyticsItem.getSessionID();
-    if (parentId != NULL) {
-        analyticsItem.setInt64(kParentAttribute, *parentId);
-    }
 
-    // Report the package name.
-    if (metricsGroup.has_app_package_name()) {
-        std::string app_package_name(metricsGroup.app_package_name().c_str(),
-                               metricsGroup.app_package_name().size());
-      analyticsItem.setPkgName(app_package_name);
-    }
-
-    for (int i = 0; i < metricsGroup.metric_size(); ++i) {
-        const MetricsGroup_Metric& metric = metricsGroup.metric(i);
-        if (!metric.has_name()) {
-            ALOGE("Metric with no name.");
-            return BAD_VALUE;
-        }
-
-        if (!metric.has_value()) {
-            ALOGE("Metric with no value.");
-            return BAD_VALUE;
-        }
-
-        const MetricsGroup_Metric_MetricValue& value = metric.value();
-        if (value.has_int_value()) {
-            analyticsItem.setInt64(metric.name().c_str(),
-                                   value.int_value());
-        } else if (value.has_double_value()) {
-            analyticsItem.setDouble(metric.name().c_str(),
-                                    value.double_value());
-        } else if (value.has_string_value()) {
-            analyticsItem.setCString(metric.name().c_str(),
-                                     value.string_value().c_str());
-        } else {
-            ALOGE("Metric Value with no actual value.");
-            return BAD_VALUE;
-        }
+    std::string app_package_name(appPackageName.c_str(), appPackageName.size());
+    analyticsItem.setPkgName(app_package_name);
+    if (metrics.size() > 0) {
+        analyticsItem.setCString(kSerializedMetricsField, metrics.c_str());
     }
 
     if (!analyticsItem.selfrecord()) {
-      ALOGE("selfrecord() returned false. sessioId %" PRId64, sessionId);
-    }
-
-    for (int i = 0; i < metricsGroup.metric_sub_group_size(); ++i) {
-        const MetricsGroup& subGroup = metricsGroup.metric_sub_group(i);
-        status_t res = reportMetricsGroup(subGroup, batchName, &sessionId);
-        if (res != OK) {
-            return res;
-        }
+      ALOGE("selfrecord() returned false. sessioId %" PRId64, analyticsItem.getSessionID());
     }
 
     return OK;
@@ -111,21 +66,16 @@
 
 }  // namespace
 
-status_t reportDrmPluginMetrics(const Vector<uint8_t>& serializedMetrics,
+status_t reportDrmPluginMetrics(const std::string& b64EncodedMetrics,
                                 const String8& vendor,
-                                const String8& description) {
-    MetricsGroup root_metrics_group;
-    if (!root_metrics_group.ParseFromArray(serializedMetrics.array(),
-                                           serializedMetrics.size())) {
-        ALOGE("Failure to parse.");
-        return BAD_VALUE;
-    }
+                                const String8& description,
+                                const String8& appPackageName) {
 
     String8 name = String8::format("drm.vendor.%s.%s",
                                    sanitize(vendor).c_str(),
                                    sanitize(description).c_str());
 
-    return reportMetricsGroup(root_metrics_group, name, NULL);
+    return reportVendorMetrics(b64EncodedMetrics, name, appPackageName);
 }
 
 }  // namespace android
diff --git a/drm/libmediadrm/protos/metrics.proto b/drm/libmediadrm/protos/metrics.proto
index aa26f5f..6160e6f 100644
--- a/drm/libmediadrm/protos/metrics.proto
+++ b/drm/libmediadrm/protos/metrics.proto
@@ -18,33 +18,6 @@
 
 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.
@@ -72,7 +45,7 @@
   // The Counter message is used to store a count value with an associated
   // Attribute.
   message Counter {
-    optional int64 count = 1;
+    optional uint64 count = 1;
     // Represents the attributes associated with this counter instance.
     optional Attributes attributes = 2;
   }
@@ -80,11 +53,11 @@
   // 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 float min = 1;
+    optional float max = 2;
+    optional float mean = 3;
     optional double variance = 4;
-    optional double operation_count = 5;
+    optional uint64 operation_count = 5;
 
     // Represents the attributes assocated with this distribution metric
     // instance.
@@ -93,9 +66,9 @@
 
   message SessionLifetime {
     // Start time of the session in milliseconds since epoch.
-    optional int64 start_time_ms = 1;
+    optional uint64 start_time_ms = 1;
     // End time of the session in milliseconds since epoch.
-    optional int64 end_time_ms = 2;
+    optional uint64 end_time_ms = 2;
   }
 
   // The count of open session operations. Each instance has a specific error
diff --git a/include/media/TimeCheck.h b/include/media/TimeCheck.h
new file mode 120000
index 0000000..e3ef134
--- /dev/null
+++ b/include/media/TimeCheck.h
@@ -0,0 +1 @@
+../../media/libmedia/include/media/TimeCheck.h
\ No newline at end of file
diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp
index 666d68a..26b8251 100644
--- a/media/extractors/mp4/ItemTable.cpp
+++ b/media/extractors/mp4/ItemTable.cpp
@@ -76,9 +76,14 @@
 
     Vector<uint32_t> thumbnails;
     Vector<uint32_t> dimgRefs;
+    Vector<uint32_t> cdscRefs;
     size_t nextTileIndex;
 };
 
+struct ExifItem {
+    off64_t offset;
+    size_t size;
+};
 
 /////////////////////////////////////////////////////////////////////
 //
@@ -473,7 +478,9 @@
 
     uint32_t itemId() { return mItemId; }
 
-    void apply(KeyedVector<uint32_t, ImageItem> &itemIdToItemMap) const;
+    void apply(
+            KeyedVector<uint32_t, ImageItem> &itemIdToItemMap,
+            KeyedVector<uint32_t, ExifItem> &itemIdToExifMap) const;
 
 private:
     uint32_t mItemId;
@@ -483,17 +490,20 @@
     DISALLOW_EVIL_CONSTRUCTORS(ItemReference);
 };
 
-void ItemReference::apply(KeyedVector<uint32_t, ImageItem> &itemIdToItemMap) const {
-    ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
-
-    // ignore non-image items
-    if (itemIndex < 0) {
-        return;
-    }
-
+void ItemReference::apply(
+        KeyedVector<uint32_t, ImageItem> &itemIdToItemMap,
+        KeyedVector<uint32_t, ExifItem> &itemIdToExifMap) const {
     ALOGV("attach reference type 0x%x to item id %d)", type(), mItemId);
 
-    if (type() == FOURCC('d', 'i', 'm', 'g')) {
+    switch(type()) {
+    case FOURCC('d', 'i', 'm', 'g'): {
+        ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
+
+        // ignore non-image items
+        if (itemIndex < 0) {
+            return;
+        }
+
         ImageItem &derivedImage = itemIdToItemMap.editValueAt(itemIndex);
         if (!derivedImage.dimgRefs.empty()) {
             ALOGW("dimgRefs if not clean!");
@@ -512,7 +522,16 @@
             // mark the source image of the derivation as hidden
             sourceImage.hidden = true;
         }
-    } else if (type() == FOURCC('t', 'h', 'm', 'b')) {
+        break;
+    }
+    case FOURCC('t', 'h', 'm', 'b'): {
+        ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
+
+        // ignore non-image items
+        if (itemIndex < 0) {
+            return;
+        }
+
         // mark thumbnail image as hidden, these can be retrieved if the client
         // request thumbnail explicitly, but won't be exposed as displayables.
         ImageItem &thumbImage = itemIdToItemMap.editValueAt(itemIndex);
@@ -532,11 +551,43 @@
             }
             masterImage.thumbnails.push_back(mItemId);
         }
-    } else if (type() == FOURCC('a', 'u', 'x', 'l')) {
+        break;
+    }
+    case FOURCC('c', 'd', 's', 'c'): {
+        ssize_t itemIndex = itemIdToExifMap.indexOfKey(mItemId);
+
+        // ignore non-exif block items
+        if (itemIndex < 0) {
+            return;
+        }
+
+        for (size_t i = 0; i < mRefs.size(); i++) {
+            itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
+
+            // ignore non-image items
+            if (itemIndex < 0) {
+                continue;
+            }
+            ALOGV("Image item id %d uses metadata item id %d", mRefs[i], mItemId);
+            ImageItem &image = itemIdToItemMap.editValueAt(itemIndex);
+            image.cdscRefs.push_back(mItemId);
+        }
+        break;
+    }
+    case FOURCC('a', 'u', 'x', 'l'): {
+        ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
+
+        // ignore non-image items
+        if (itemIndex < 0) {
+            return;
+        }
+
         // mark auxiliary image as hidden
         ImageItem &auxImage = itemIdToItemMap.editValueAt(itemIndex);
         auxImage.hidden = true;
-    } else {
+        break;
+    }
+    default:
         ALOGW("ignoring unsupported ref type 0x%x", type());
     }
 }
@@ -1299,10 +1350,13 @@
     for (size_t i = 0; i < mItemInfos.size(); i++) {
         const ItemInfo &info = mItemInfos[i];
 
-
-        // ignore non-image items
+        // Only handle 3 types of items, all others are ignored:
+        //   'grid': derived image from tiles
+        //   'hvc1': coded image (or tile)
+        //   'Exif': EXIF metadata
         if (info.itemType != FOURCC('g', 'r', 'i', 'd') &&
-            info.itemType != FOURCC('h', 'v', 'c', '1')) {
+            info.itemType != FOURCC('h', 'v', 'c', '1') &&
+            info.itemType != FOURCC('E', 'x', 'i', 'f')) {
             continue;
         }
 
@@ -1325,6 +1379,19 @@
             return ERROR_MALFORMED;
         }
 
+        if (info.itemType == FOURCC('E', 'x', 'i', 'f')) {
+            // Only add if the Exif data is non-empty. The first 4 bytes contain
+            // the offset to TIFF header, which the Exif parser doesn't use.
+            if (size > 4) {
+                ExifItem exifItem = {
+                        .offset = offset,
+                        .size = size,
+                };
+                mItemIdToExifMap.add(info.itemId, exifItem);
+            }
+            continue;
+        }
+
         ImageItem image(info.itemType, info.itemId, info.hidden);
 
         ALOGV("adding %s: itemId %d", image.isGrid() ? "grid" : "image", info.itemId);
@@ -1354,7 +1421,7 @@
     }
 
     for (size_t i = 0; i < mItemReferences.size(); i++) {
-        mItemReferences[i]->apply(mItemIdToItemMap);
+        mItemReferences[i]->apply(mItemIdToItemMap, mItemIdToExifMap);
     }
 
     bool foundPrimary = false;
@@ -1574,6 +1641,34 @@
     return OK;
 }
 
+status_t ItemTable::getExifOffsetAndSize(off64_t *offset, size_t *size) {
+    if (!mImageItemsValid) {
+        return INVALID_OPERATION;
+    }
+
+    ssize_t itemIndex = mItemIdToItemMap.indexOfKey(mPrimaryItemId);
+
+    // this should not happen, something's seriously wrong.
+    if (itemIndex < 0) {
+        return INVALID_OPERATION;
+    }
+
+    const ImageItem &image = mItemIdToItemMap[itemIndex];
+    if (image.cdscRefs.size() == 0) {
+        return NAME_NOT_FOUND;
+    }
+
+    ssize_t exifIndex = mItemIdToExifMap.indexOfKey(image.cdscRefs[0]);
+    if (exifIndex < 0) {
+        return NAME_NOT_FOUND;
+    }
+
+    // skip the first 4-byte of the offset to TIFF header
+    *offset = mItemIdToExifMap[exifIndex].offset + 4;
+    *size = mItemIdToExifMap[exifIndex].size - 4;
+    return OK;
+}
+
 } // namespace heif
 
 }  // namespace android
diff --git a/media/extractors/mp4/ItemTable.h b/media/extractors/mp4/ItemTable.h
index f4a69cc..536dcb0 100644
--- a/media/extractors/mp4/ItemTable.h
+++ b/media/extractors/mp4/ItemTable.h
@@ -32,6 +32,7 @@
 
 struct AssociationEntry;
 struct ImageItem;
+struct ExifItem;
 struct ItemLoc;
 struct ItemInfo;
 struct ItemProperty;
@@ -55,6 +56,7 @@
     status_t findThumbnailItem(const uint32_t imageIndex, uint32_t *itemIndex);
     status_t getImageOffsetAndSize(
             uint32_t *itemIndex, off64_t *offset, size_t *size);
+    status_t getExifOffsetAndSize(off64_t *offset, size_t *size);
 
 protected:
     ~ItemTable();
@@ -78,6 +80,7 @@
     bool mImageItemsValid;
     uint32_t mCurrentItemIndex;
     KeyedVector<uint32_t, ImageItem> mItemIdToItemMap;
+    KeyedVector<uint32_t, ExifItem> mItemIdToExifMap;
     Vector<uint32_t> mDisplayables;
 
     status_t parseIlocBox(off64_t offset, size_t size);
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index a9f66fa..07ef0e3 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -586,6 +586,12 @@
     }
 
     if (mIsHeif && (mItemTable != NULL) && (mItemTable->countImages() > 0)) {
+        off64_t exifOffset;
+        size_t exifSize;
+        if (mItemTable->getExifOffsetAndSize(&exifOffset, &exifSize) == OK) {
+            mFileMetaData.setInt64(kKeyExifOffset, (int64_t)exifOffset);
+            mFileMetaData.setInt64(kKeyExifSize, (int64_t)exifSize);
+        }
         for (uint32_t imageIndex = 0;
                 imageIndex < mItemTable->countImages(); imageIndex++) {
             sp<MetaData> meta = mItemTable->getImageMeta(imageIndex);
@@ -601,6 +607,7 @@
                 ALOGW("Extracting still images only");
                 err = OK;
             }
+            mInitCheck = OK;
 
             ALOGV("adding HEIF image track %u", imageIndex);
             Track *track = new Track;
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 6b25302..2a3e668 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -67,8 +67,6 @@
         , mWakeupDelayNanos(AAudioProperty_getWakeupDelayMicros() * AAUDIO_NANOS_PER_MICROSECOND)
         , mMinimumSleepNanos(AAudioProperty_getMinimumSleepMicros() * AAUDIO_NANOS_PER_MICROSECOND)
         {
-    ALOGD("%s - mWakeupDelayNanos = %d, mMinimumSleepNanos = %d",
-          __func__, mWakeupDelayNanos, mMinimumSleepNanos);
 }
 
 AudioStreamInternal::~AudioStreamInternal() {
@@ -231,8 +229,7 @@
 
 aaudio_result_t AudioStreamInternal::close() {
     aaudio_result_t result = AAUDIO_OK;
-    ALOGD("close(): mServiceStreamHandle = 0x%08X",
-             mServiceStreamHandle);
+    ALOGD("%s(): mServiceStreamHandle = 0x%08X", __func__, mServiceStreamHandle);
     if (mServiceStreamHandle != AAUDIO_HANDLE_INVALID) {
         // Don't close a stream while it is running.
         aaudio_stream_state_t currentState = getState();
@@ -243,8 +240,8 @@
             result = waitForStateChange(currentState, &nextState,
                                                        timeoutNanoseconds);
             if (result != AAUDIO_OK) {
-                ALOGE("close() waitForStateChange() returned %d %s",
-                result, AAudio_convertResultToText(result));
+                ALOGE("%s() waitForStateChange() returned %d %s",
+                __func__, result, AAudio_convertResultToText(result));
             }
         }
         setState(AAUDIO_STREAM_STATE_CLOSING);
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index 11b43c3..0c3b1fa 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -57,8 +57,7 @@
         return result;
     }
     if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
-        ALOGE("requestPauseInternal() mServiceStreamHandle invalid = 0x%08X",
-              mServiceStreamHandle);
+        ALOGE("%s() mServiceStreamHandle invalid", __func__);
         return AAUDIO_ERROR_INVALID_STATE;
     }
 
@@ -70,8 +69,7 @@
 
 aaudio_result_t AudioStreamInternalPlay::requestFlush() {
     if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
-        ALOGE("AudioStreamInternal::requestFlush() mServiceStreamHandle invalid = 0x%08X",
-              mServiceStreamHandle);
+        ALOGE("%s() mServiceStreamHandle invalid", __func__);
         return AAUDIO_ERROR_INVALID_STATE;
     }
 
@@ -86,7 +84,7 @@
     // Bump offset so caller does not see the retrograde motion in getFramesRead().
     int64_t offset = writeCounter - readCounter;
     mFramesOffsetFromService += offset;
-    ALOGD("advanceClientToMatchServerPosition() readN = %lld, writeN = %lld, offset = %lld",
+    ALOGV("%s() readN = %lld, writeN = %lld, offset = %lld", __func__,
           (long long)readCounter, (long long)writeCounter, (long long)mFramesOffsetFromService);
 
     // Force writeCounter to match readCounter.
@@ -100,9 +98,7 @@
 
 // Write the data, block if needed and timeoutMillis > 0
 aaudio_result_t AudioStreamInternalPlay::write(const void *buffer, int32_t numFrames,
-                                           int64_t timeoutNanoseconds)
-
-{
+                                               int64_t timeoutNanoseconds) {
     return processData((void *)buffer, numFrames, timeoutNanoseconds);
 }
 
@@ -121,7 +117,7 @@
         // Still haven't got any timestamps from server.
         // Keep waiting until we get some valid timestamps then start writing to the
         // current buffer position.
-        ALOGD("processDataNow() wait for valid timestamps");
+        ALOGD("%s() wait for valid timestamps", __func__);
         // Sleep very briefly and hope we get a timestamp soon.
         *wakeTimePtr = currentNanoTime + (2000 * AAUDIO_NANOS_PER_MICROSECOND);
         ATRACE_END();
@@ -203,8 +199,6 @@
 
 aaudio_result_t AudioStreamInternalPlay::writeNowWithConversion(const void *buffer,
                                                             int32_t numFrames) {
-    // ALOGD("AudioStreamInternal::writeNowWithConversion(%p, %d)",
-    //              buffer, numFrames);
     WrappingBuffer wrappingBuffer;
     uint8_t *byteBuffer = (uint8_t *) buffer;
     int32_t framesLeft = numFrames;
@@ -249,7 +243,6 @@
     int32_t framesWritten = numFrames - framesLeft;
     mAudioEndpoint.advanceWriteIndex(framesWritten);
 
-    // ALOGD("AudioStreamInternal::writeNowWithConversion() returns %d", framesWritten);
     return framesWritten;
 }
 
@@ -268,7 +261,6 @@
     } else {
         mLastFramesRead = framesRead;
     }
-    //ALOGD("AudioStreamInternalPlay::getFramesRead() returns %lld", (long long)framesRead);
     return framesRead;
 }
 
@@ -276,13 +268,13 @@
 {
     int64_t framesWritten = mAudioEndpoint.getDataWriteCounter()
                                + mFramesOffsetFromService;
-    //ALOGD("AudioStreamInternalPlay::getFramesWritten() returns %lld", (long long)framesWritten);
     return framesWritten;
 }
 
 
 // Render audio in the application callback and then write the data to the stream.
 void *AudioStreamInternalPlay::callbackLoop() {
+    ALOGD("%s() entering >>>>>>>>>>>>>>>", __func__);
     aaudio_result_t result = AAUDIO_OK;
     aaudio_data_callback_result_t callbackResult = AAUDIO_CALLBACK_RESULT_CONTINUE;
     if (!isDataCallbackSet()) return NULL;
@@ -297,7 +289,6 @@
             // Write audio data to stream. This is a BLOCKING WRITE!
             result = write(mCallbackBuffer, mCallbackFrames, timeoutNanos);
             if ((result != mCallbackFrames)) {
-                ALOGE("AudioStreamInternalPlay(): callbackLoop: write() returned %d", result);
                 if (result >= 0) {
                     // Only wrote some of the frames requested. Must have timed out.
                     result = AAUDIO_ERROR_TIMEOUT;
@@ -306,13 +297,13 @@
                 break;
             }
         } else if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
-            ALOGD("AudioStreamInternalPlay(): callback returned AAUDIO_CALLBACK_RESULT_STOP");
+            ALOGV("%s(): callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__);
             break;
         }
     }
 
-    ALOGD("AudioStreamInternalPlay(): callbackLoop() exiting, result = %d, isActive() = %d",
-          result, (int) isActive());
+    ALOGD("%s() exiting, result = %d, isActive() = %d <<<<<<<<<<<<<<",
+          __func__, result, (int) isActive());
     return NULL;
 }
 
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index c4465fd..61e03db 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -104,17 +104,17 @@
     mErrorCallbackUserData = builder.getErrorCallbackUserData();
 
     // This is very helpful for debugging in the future. Please leave it in.
-    ALOGI("open() rate = %d, channels = %d, format = %d, sharing = %s, dir = %s",
+    ALOGI("open() rate   = %d, channels    = %d, format   = %d, sharing = %s, dir = %s",
           mSampleRate, mSamplesPerFrame, mFormat,
           AudioStream_convertSharingModeToShortText(mSharingMode),
           (getDirection() == AAUDIO_DIRECTION_OUTPUT) ? "OUTPUT" : "INPUT");
-    ALOGI("open() device = %d, sessionId = %d, perfMode = %d, callback: %s with frames = %d",
+    ALOGI("open() device = %d, sessionId   = %d, perfMode = %d, callback: %s with frames = %d",
           mDeviceId,
           mSessionId,
           mPerformanceMode,
           (isDataCallbackSet() ? "ON" : "OFF"),
           mFramesPerDataCallback);
-    ALOGI("open() usage = %d, contentType = %d, inputPreset = %d",
+    ALOGI("open() usage  = %d, contentType = %d, inputPreset = %d",
           mUsage, mContentType, mInputPreset);
 
     return AAUDIO_OK;
@@ -242,6 +242,22 @@
     return close();
 }
 
+void AudioStream::setState(aaudio_stream_state_t state) {
+    ALOGV("%s(%p) from %d to %d", __func__, this, mState, state);
+    // CLOSED is a final state
+    if (mState == AAUDIO_STREAM_STATE_CLOSED) {
+        ALOGE("%s(%p) tried to set to %d but already CLOSED", __func__, this, state);
+
+    // Once DISCONNECTED, we can only move to CLOSED state.
+    } else if (mState == AAUDIO_STREAM_STATE_DISCONNECTED
+               && state != AAUDIO_STREAM_STATE_CLOSED) {
+        ALOGE("%s(%p) tried to set to %d but already DISCONNECTED", __func__, this, state);
+
+    } else {
+        mState = state;
+    }
+}
+
 aaudio_result_t AudioStream::waitForStateChange(aaudio_stream_state_t currentState,
                                                 aaudio_stream_state_t *nextState,
                                                 int64_t timeoutNanoseconds)
@@ -313,7 +329,9 @@
     setPeriodNanoseconds(periodNanoseconds);
     int err = pthread_create(&mThread, nullptr, AudioStream_internalThreadProc, this);
     if (err != 0) {
-        return AAudioConvert_androidToAAudioResult(-errno);
+        android::status_t status = -errno;
+        ALOGE("createThread() - pthread_create() failed, %d", status);
+        return AAudioConvert_androidToAAudioResult(status);
     } else {
         // TODO Use AAudioThread or maybe AndroidThread
         // Name the thread with an increasing index, "AAudio_#", for debugging.
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index c0db0f9..5273e36 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -471,16 +471,7 @@
         mFormat = format;
     }
 
-    void setState(aaudio_stream_state_t state) {
-        if (mState == AAUDIO_STREAM_STATE_CLOSED) {
-            ; // CLOSED is a final state
-        } else if (mState == AAUDIO_STREAM_STATE_DISCONNECTED
-                && state != AAUDIO_STREAM_STATE_CLOSED) {
-            ; // Once DISCONNECTED, we can only move to CLOSED state.
-        } else {
-            mState = state;
-        }
-    }
+    void setState(aaudio_stream_state_t state);
 
     void setDeviceId(int32_t deviceId) {
         mDeviceId = deviceId;
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 3358e35..873d836 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -24,6 +24,7 @@
 
 #include <binder/IPCThreadState.h>
 #include <binder/Parcel.h>
+#include <media/TimeCheck.h>
 #include <private/android_filesystem_config.h>
 
 #include "IAudioFlinger.h"
@@ -933,6 +934,8 @@
             break;
     }
 
+    TimeCheck check("IAudioFlinger");
+
     switch (code) {
         case CREATE_TRACK: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index 8f5ff30..0b98502 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -27,6 +27,7 @@
 
 #include <media/AudioEffect.h>
 #include <media/IAudioPolicyService.h>
+#include <media/TimeCheck.h>
 #include <private/android_filesystem_config.h>
 #include <system/audio.h>
 
@@ -882,6 +883,8 @@
             break;
     }
 
+    TimeCheck check("IAudioPolicyService");
+
     switch (code) {
         case SET_DEVICE_CONNECTION_STATE: {
             CHECK_INTERFACE(IAudioPolicyService, data, reply);
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 34b15a8..3990e69 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -18,7 +18,7 @@
     vndk: {
         enabled: true,
     },
-    srcs: ["AudioParameter.cpp", "TypeConverter.cpp"],
+    srcs: ["AudioParameter.cpp", "TypeConverter.cpp", "TimeCheck.cpp"],
     cflags: [
         "-Werror",
         "-Wno-error=deprecated-declarations",
diff --git a/media/libmedia/TimeCheck.cpp b/media/libmedia/TimeCheck.cpp
new file mode 100644
index 0000000..dab5d4f
--- /dev/null
+++ b/media/libmedia/TimeCheck.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <media/TimeCheck.h>
+
+namespace android {
+
+/* static */
+sp<TimeCheck::TimeCheckThread> TimeCheck::getTimeCheckThread()
+{
+    static sp<TimeCheck::TimeCheckThread> sTimeCheckThread = new TimeCheck::TimeCheckThread();
+    return sTimeCheckThread;
+}
+
+TimeCheck::TimeCheck(const char *tag, uint32_t timeoutMs)
+    : mEndTimeNs(getTimeCheckThread()->startMonitoring(tag, timeoutMs))
+{
+}
+
+TimeCheck::~TimeCheck() {
+    getTimeCheckThread()->stopMonitoring(mEndTimeNs);
+}
+
+TimeCheck::TimeCheckThread::~TimeCheckThread()
+{
+    AutoMutex _l(mMutex);
+    requestExit();
+    mMonitorRequests.clear();
+    mCond.signal();
+}
+
+nsecs_t TimeCheck::TimeCheckThread::startMonitoring(const char *tag, uint32_t timeoutMs) {
+    Mutex::Autolock _l(mMutex);
+    nsecs_t endTimeNs = systemTime() + milliseconds(timeoutMs);
+    for (; mMonitorRequests.indexOfKey(endTimeNs) >= 0; ++endTimeNs);
+    mMonitorRequests.add(endTimeNs, tag);
+    mCond.signal();
+    return endTimeNs;
+}
+
+void TimeCheck::TimeCheckThread::stopMonitoring(nsecs_t endTimeNs) {
+    Mutex::Autolock _l(mMutex);
+    mMonitorRequests.removeItem(endTimeNs);
+    mCond.signal();
+}
+
+bool TimeCheck::TimeCheckThread::threadLoop()
+{
+    status_t status = TIMED_OUT;
+    const char *tag;
+    {
+        AutoMutex _l(mMutex);
+
+        if (exitPending()) {
+            return false;
+        }
+
+        nsecs_t endTimeNs = INT64_MAX;
+        // KeyedVector mMonitorRequests is ordered so take first entry as next timeout
+        if (mMonitorRequests.size() != 0) {
+            endTimeNs = mMonitorRequests.keyAt(0);
+            tag = mMonitorRequests.valueAt(0);
+        }
+
+        const nsecs_t waitTimeNs = endTimeNs - systemTime();
+        if (waitTimeNs > 0) {
+            status = mCond.waitRelative(mMutex, waitTimeNs);
+        }
+    }
+    LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "TimeCheck timeout for %s", tag);
+    return true;
+}
+
+}; // namespace android
diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h
index b0e1cc8..f267f76 100644
--- a/media/libmedia/include/media/DrmHal.h
+++ b/media/libmedia/include/media/DrmHal.h
@@ -182,6 +182,7 @@
     const Vector<sp<IDrmFactory>> mFactories;
     sp<IDrmPlugin> mPlugin;
     sp<drm::V1_1::IDrmPlugin> mPluginV1_1;
+    String8 mAppPackageName;
 
     // Mutable to allow modification within GetPropertyByteArray.
     mutable MediaDrmMetrics mMetrics;
diff --git a/media/libmedia/include/media/PluginMetricsReporting.h b/media/libmedia/include/media/PluginMetricsReporting.h
index 4a5a363..e00bd43 100644
--- a/media/libmedia/include/media/PluginMetricsReporting.h
+++ b/media/libmedia/include/media/PluginMetricsReporting.h
@@ -20,13 +20,13 @@
 
 #include <utils/Errors.h>
 #include <utils/String8.h>
-#include <utils/Vector.h>
 
 namespace android {
 
-status_t reportDrmPluginMetrics(const Vector<uint8_t>& serializedMetrics,
+status_t reportDrmPluginMetrics(const std::string& b64EncodedMetrics,
                                 const String8& vendorName,
-                                const String8& description);
+                                const String8& description,
+                                const String8& appPackageName);
 
 }  // namespace android
 
diff --git a/media/libmedia/include/media/TimeCheck.h b/media/libmedia/include/media/TimeCheck.h
new file mode 100644
index 0000000..6c5f656
--- /dev/null
+++ b/media/libmedia/include/media/TimeCheck.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_TIME_CHECK_H
+#define ANDROID_TIME_CHECK_H
+
+#include <utils/KeyedVector.h>
+#include <utils/Thread.h>
+
+
+namespace android {
+
+// A class monitoring execution time for a code block (scoped variable) and causing an assert
+// if it exceeds a certain time
+
+class TimeCheck {
+public:
+
+    // The default timeout is chosen to be less than system server watchdog timeout
+    static constexpr uint32_t kDefaultTimeOutMs = 5000;
+
+            TimeCheck(const char *tag, uint32_t timeoutMs = kDefaultTimeOutMs);
+            ~TimeCheck();
+
+private:
+
+    class TimeCheckThread : public Thread {
+    public:
+
+                            TimeCheckThread() {}
+        virtual             ~TimeCheckThread() override;
+
+                nsecs_t     startMonitoring(const char *tag, uint32_t timeoutMs);
+                void        stopMonitoring(nsecs_t endTimeNs);
+
+    private:
+
+                // RefBase
+        virtual void        onFirstRef() override { run("TimeCheckThread", PRIORITY_URGENT_AUDIO); }
+
+                // Thread
+        virtual bool        threadLoop() override;
+
+                Condition           mCond;
+                Mutex               mMutex;
+                // using the end time in ns as key is OK given the risk is low that two entries
+                // are added in such a way that <add time> + <timeout> are the same for both.
+                KeyedVector< nsecs_t, const char*>  mMonitorRequests;
+    };
+
+    static sp<TimeCheckThread> getTimeCheckThread();
+
+    const           nsecs_t mEndTimeNs;
+};
+
+}; // namespace android
+
+#endif  // ANDROID_TIME_CHECK_H
diff --git a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
index 18e63f2..f1b7806 100644
--- a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
+++ b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
@@ -218,6 +218,8 @@
     kKeyIccProfile       = 'prof', // raw data, ICC profile data
     kKeyIsPrimaryImage   = 'prim', // bool (int32_t), image track is the primary image
     kKeyFrameCount       = 'nfrm', // int32_t, total number of frame in video track
+    kKeyExifOffset       = 'exof', // int64_t, Exif data offset
+    kKeyExifSize         = 'exsz', // int64_t, Exif data size
 };
 
 enum {
diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp
index 9e96a2b..e5567dc 100644
--- a/media/libmediaplayer2/mediaplayer2.cpp
+++ b/media/libmediaplayer2/mediaplayer2.cpp
@@ -903,7 +903,6 @@
     if (mPlayer == 0) {
         return INVALID_OPERATION;
     }
-    return mPlayer->getPlaybackSettings(rate);
     status_t ret = mPlayer->getPlaybackSettings(rate);
     if (ret == NO_ERROR) {
         ALOGV("getPlaybackSettings(%f, %f, %d, %d)",
@@ -925,7 +924,9 @@
 status_t MediaPlayer2::getSyncSettings(
         AVSyncSettings* sync /* nonnull */, float* videoFps /* nonnull */) {
     Mutex::Autolock _l(mLock);
-    if (mPlayer == 0) return INVALID_OPERATION;
+    if (mPlayer == 0) {
+        return INVALID_OPERATION;
+    }
     status_t ret = mPlayer->getSyncSettings(sync, videoFps);
     if (ret == NO_ERROR) {
         ALOGV("getSyncSettings(%u, %u, %f, %f)",
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index 27b9a5d..4a7d6ca 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -307,6 +307,26 @@
     return OK;
 }
 
+status_t NuMediaExtractor::getExifOffsetSize(off64_t *offset, size_t *size) const {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mImpl == NULL) {
+        return -EINVAL;
+    }
+
+    sp<MetaData> meta = mImpl->getMetaData();
+
+    int64_t exifOffset, exifSize;
+    if (meta->findInt64(kKeyExifOffset, &exifOffset)
+     && meta->findInt64(kKeyExifSize, &exifSize)) {
+        *offset = (off64_t) exifOffset;
+        *size = (size_t) exifSize;
+
+        return OK;
+    }
+    return ERROR_UNSUPPORTED;
+}
+
 status_t NuMediaExtractor::selectTrack(size_t index,
         int64_t startTimeUs, MediaSource::ReadOptions::SeekMode mode) {
     Mutex::Autolock autoLock(mLock);
diff --git a/media/libstagefright/bqhelper/Android.bp b/media/libstagefright/bqhelper/Android.bp
index 9febe12..4f46be7 100644
--- a/media/libstagefright/bqhelper/Android.bp
+++ b/media/libstagefright/bqhelper/Android.bp
@@ -6,6 +6,7 @@
     },
 
     srcs: [
+        "Conversion.cpp",
         "FrameDropper.cpp",
         "GraphicBufferSource.cpp",
         "WProducerListener.cpp",
diff --git a/media/libstagefright/bqhelper/Conversion.cpp b/media/libstagefright/bqhelper/Conversion.cpp
new file mode 100644
index 0000000..ffed005
--- /dev/null
+++ b/media/libstagefright/bqhelper/Conversion.cpp
@@ -0,0 +1,1542 @@
+/*
+ * Copyright 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/stagefright/bqhelper/Conversion.h>
+
+namespace android {
+namespace conversion {
+
+// 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.
+ */
+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.
+ */
+int native_handle_read_fd(native_handle_t const* nh, int index) {
+    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
+::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
+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
+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
+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
+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>
+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>
+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
+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
+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.
+ */
+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.
+ */
+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.
+ */
+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.
+ */
+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
+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
+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.
+ */
+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.
+ */
+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`.
+ */
+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.
+ */
+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.
+ */
+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.
+ */
+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()`.
+ */
+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
+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.
+ */
+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.
+ */
+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()`.
+ */
+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.
+ */
+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()`.
+ */
+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.
+ */
+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.
+ */
+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.
+ */
+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.
+ */
+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
+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
+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.
+ */
+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.
+ */
+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`. */
+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.
+ */
+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.
+ */
+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.
+ */
+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
+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
+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`.
+ */
+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`.
+ */
+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
+
diff --git a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/Conversion.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/Conversion.h
index c15fafa..60d8c1e 100644
--- a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/Conversion.h
+++ b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/Conversion.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef MEDIA_STAGEFRIGHT_CONVERSION_H_
-#define MEDIA_STAGEFRIGHT_CONVERSION_H_
+#ifndef MEDIA_STAGEFRIGHT_BQHELPER_CONVERSION_H_
+#define MEDIA_STAGEFRIGHT_BQHELPER_CONVERSION_H_
 
 #include <vector>
 #include <list>
@@ -83,17 +83,7 @@
  *
  * 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;
-}
+native_handle_t* native_handle_create_from_fd(int fd);
 
 /**
  * \brief Extract a file descriptor from a native handle.
@@ -106,11 +96,7 @@
  *
  * 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];
-}
+int native_handle_read_fd(native_handle_t const* nh, int index = 0);
 
 /**
  * Conversion functions
@@ -151,12 +137,7 @@
  * \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());
-}
+::android::binder::Status toBinderStatus(Return<void> const& t);
 
 /**
  * \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls.
@@ -165,9 +146,7 @@
  * \return The corresponding `status_t`.
  */
 // convert: Return<void> -> status_t
-inline status_t toStatusT(Return<void> const& t) {
-    return t.isOk() ? OK : UNKNOWN_ERROR;
-}
+status_t toStatusT(Return<void> const& t);
 
 /**
  * \brief Wrap `native_handle_t*` in `hidl_handle`.
@@ -176,9 +155,7 @@
  * \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);
-}
+hidl_handle inHidlHandle(native_handle_t const* nh);
 
 /**
  * \brief Convert `int32_t` to `Dataspace`.
@@ -187,9 +164,7 @@
  * \result The corresponding `Dataspace`.
  */
 // convert: int32_t -> Dataspace
-inline Dataspace toHardwareDataspace(int32_t l) {
-    return static_cast<Dataspace>(l);
-}
+Dataspace toHardwareDataspace(int32_t l);
 
 /**
  * \brief Convert `Dataspace` to `int32_t`.
@@ -198,9 +173,7 @@
  * \result The corresponding `int32_t`.
  */
 // convert: Dataspace -> int32_t
-inline int32_t toRawDataspace(Dataspace const& t) {
-    return static_cast<int32_t>(t);
-}
+int32_t toRawDataspace(Dataspace const& t);
 
 /**
  * \brief Wrap an opaque buffer inside a `hidl_vec<uint8_t>`.
@@ -210,11 +183,7 @@
  * \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;
-}
+hidl_vec<uint8_t> inHidlBytes(void const* l, size_t size);
 
 /**
  * \brief Create a `hidl_vec<uint8_t>` that is a copy of an opaque buffer.
@@ -224,13 +193,7 @@
  * \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;
-}
+hidl_vec<uint8_t> toHidlBytes(void const* l, size_t size);
 
 /**
  * \brief Wrap `GraphicBuffer` in `AnwBuffer`.
@@ -239,17 +202,7 @@
  * \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);
-}
+void wrapAs(AnwBuffer* t, GraphicBuffer const& l);
 
 /**
  * \brief Convert `AnwBuffer` to `GraphicBuffer`.
@@ -261,46 +214,7 @@
  */
 // 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;
-}
+bool convertTo(GraphicBuffer* l, AnwBuffer const& t);
 
 /**
  * Conversion functions for types outside media
@@ -387,9 +301,7 @@
  * 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;
-};
+size_t getFenceFlattenedSize(hidl_handle const& fence);
 
 /**
  * \brief Return the number of file descriptors contained in a fence.
@@ -398,9 +310,7 @@
  * \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;
-}
+size_t getFenceFdCount(hidl_handle const& fence);
 
 /**
  * \brief Unflatten `Fence` to `hidl_handle`.
@@ -417,38 +327,8 @@
  * 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;
-}
+status_t unflattenFence(hidl_handle* fence, native_handle_t** nh,
+        void const*& buffer, size_t& size, int const*& fds, size_t& numFds);
 
 /**
  * \brief Flatten `hidl_handle` as `Fence`.
@@ -460,24 +340,8 @@
  * \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;
-}
+status_t flattenFence(hidl_handle const& fence,
+        void*& buffer, size_t& size, int*& fds, size_t& numFds);
 
 /**
  * \brief Wrap `Fence` in `hidl_handle`.
@@ -490,40 +354,7 @@
  * 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;
-}
+bool wrapAs(hidl_handle* t, native_handle_t** nh, Fence const& l);
 
 /**
  * \brief Convert `hidl_handle` to `Fence`.
@@ -535,58 +366,7 @@
  * 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;
-}
+bool convertTo(Fence* l, hidl_handle const& t);
 
 // Ref: frameworks/native/libs/ui/FenceTime.cpp: FenceTime::Snapshot
 
@@ -597,20 +377,7 @@
  * \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;
-}
+size_t getFlattenedSize(HGraphicBufferProducer::FenceTimeSnapshot const& t);
 
 /**
  * \brief Return the number of file descriptors contained in
@@ -619,12 +386,7 @@
  * \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;
-}
+size_t getFdCount(HGraphicBufferProducer::FenceTimeSnapshot const& t);
 
 /**
  * \brief Flatten `FenceTimeSnapshot`.
@@ -639,29 +401,8 @@
  * 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;
-}
+status_t flatten(HGraphicBufferProducer::FenceTimeSnapshot const& t,
+        void*& buffer, size_t& size, int*& fds, size_t& numFds);
 
 /**
  * \brief Unflatten `FenceTimeSnapshot`.
@@ -678,72 +419,20 @@
  * 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(
+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;
-}
+        void const*& buffer, size_t& size, int const*& fds, size_t& numFds);
 
 // 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);
-};
+size_t getFlattenedSize(HGraphicBufferProducer::FrameEventsDelta const& t);
 
 /**
  * \brief Return the number of file descriptors contained in
@@ -752,13 +441,7 @@
  * \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);
-};
+size_t getFdCount(HGraphicBufferProducer::FrameEventsDelta const& t);
 
 /**
  * \brief Unflatten `FrameEventsDelta`.
@@ -775,60 +458,9 @@
  * 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,
+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;
-}
+        void const*& buffer, size_t& size, int const*& fds, size_t& numFds);
 
 /**
  * \brief Flatten `FrameEventsDelta`.
@@ -844,47 +476,8 @@
  */
 // 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;
-}
+status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t,
+        void*& buffer, size_t& size, int*& fds, size_t numFds);
 
 // Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventHistoryDelta
 
@@ -895,15 +488,8 @@
  * \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;
-}
+size_t getFlattenedSize(
+        HGraphicBufferProducer::FrameEventHistoryDelta const& t);
 
 /**
  * \brief Return the number of file descriptors contained in
@@ -912,14 +498,8 @@
  * \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;
-}
+size_t getFdCount(
+        HGraphicBufferProducer::FrameEventHistoryDelta const& t);
 
 /**
  * \brief Unflatten `FrameEventHistoryDelta`.
@@ -936,34 +516,10 @@
  * 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(
+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;
-}
+        void const*& buffer, size_t& size, int const*& fds, size_t& numFds);
 
 /**
  * \brief Flatten `FrameEventHistoryDelta`.
@@ -977,27 +533,9 @@
  *
  * This function will duplicate file descriptors contained in \p t.
  */
-inline status_t flatten(
+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;
-}
+        void*& buffer, size_t& size, int*& fds, size_t& numFds);
 
 /**
  * \brief Wrap `::android::FrameEventHistoryData` in
@@ -1013,42 +551,9 @@
  * native handle. All the non-`nullptr` elements must be deleted individually
  * with `native_handle_delete()`.
  */
-inline bool wrapAs(HGraphicBufferProducer::FrameEventHistoryDelta* t,
+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;
-}
+        ::android::FrameEventHistoryDelta const& l);
 
 /**
  * \brief Convert `HGraphicBufferProducer::FrameEventHistoryDelta` to
@@ -1059,42 +564,9 @@
  *
  * This function will duplicate all file descriptors contained in \p t.
  */
-inline bool convertTo(
+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;
-}
+        HGraphicBufferProducer::FrameEventHistoryDelta const& t);
 
 // Ref: frameworks/native/libs/ui/Region.cpp
 
@@ -1104,9 +576,7 @@
  * \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);
-}
+size_t getFlattenedSize(Region const& t);
 
 /**
  * \brief Unflatten `Region`.
@@ -1116,36 +586,7 @@
  * \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;
-}
+status_t unflatten(Region* t, void const*& buffer, size_t& size);
 
 /**
  * \brief Flatten `Region`.
@@ -1155,26 +596,7 @@
  * \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;
-}
+status_t flatten(Region const& t, void*& buffer, size_t& size);
 
 /**
  * \brief Convert `::android::Region` to `Region`.
@@ -1183,28 +605,7 @@
  * \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;
-}
+bool convertTo(Region* t, ::android::Region const& l);
 
 /**
  * \brief Convert `Region` to `::android::Region`.
@@ -1213,64 +614,19 @@
  * \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;
-}
+bool convertTo(::android::Region* l, Region const& t);
 
 // 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);
-}
+size_t getFlattenedSize(HGraphicBufferProducer::QueueBufferInput const& t);
 
 /**
  * \brief Return the number of file descriptors contained in
@@ -1279,11 +635,8 @@
  * \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);
-}
-
+size_t getFdCount(
+        HGraphicBufferProducer::QueueBufferInput const& t);
 /**
  * \brief Flatten `HGraphicBufferProducer::QueueBufferInput`.
  *
@@ -1296,40 +649,9 @@
  * \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,
+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;
-}
+        void*& buffer, size_t& size, int*& fds, size_t& numFds);
 
 /**
  * \brief Unflatten `HGraphicBufferProducer::QueueBufferInput`.
@@ -1347,42 +669,9 @@
  * descriptor. \p nh needs to be deleted with `native_handle_delete()`
  * afterwards.
  */
-inline status_t unflatten(
+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);
-}
+        void const*& buffer, size_t& size, int const*& fds, size_t& numFds);
 
 /**
  * \brief Wrap `BGraphicBufferProducer::QueueBufferInput` in
@@ -1398,43 +687,10 @@
  * descriptor. \p nh needs to be deleted with `native_handle_delete()`
  * afterwards.
  */
-inline bool wrapAs(
+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;
-}
+        BGraphicBufferProducer::QueueBufferInput const& l);
 
 /**
  * \brief Convert `HGraphicBufferProducer::QueueBufferInput` to
@@ -1445,48 +701,9 @@
  *
  * If `t.fence` has a valid file descriptor, it will be duplicated.
  */
-inline bool convertTo(
+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;
-}
+        HGraphicBufferProducer::QueueBufferInput const& t);
 
 // Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp:
 //      BGraphicBufferProducer::QueueBufferOutput
@@ -1507,20 +724,9 @@
  */
 // wrap: BGraphicBufferProducer::QueueBufferOutput ->
 // HGraphicBufferProducer::QueueBufferOutput
-inline bool wrapAs(HGraphicBufferProducer::QueueBufferOutput* t,
+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;
-}
+        BGraphicBufferProducer::QueueBufferOutput const& l);
 
 /**
  * \brief Convert `HGraphicBufferProducer::QueueBufferOutput` to
@@ -1533,20 +739,9 @@
  */
 // convert: HGraphicBufferProducer::QueueBufferOutput ->
 // BGraphicBufferProducer::QueueBufferOutput
-inline bool convertTo(
+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;
-}
+        HGraphicBufferProducer::QueueBufferOutput const& t);
 
 /**
  * \brief Convert `BGraphicBufferProducer::DisconnectMode` to
@@ -1555,16 +750,8 @@
  * \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;
-}
+HGraphicBufferProducer::DisconnectMode toHidlDisconnectMode(
+        BGraphicBufferProducer::DisconnectMode l);
 
 /**
  * \brief Convert `HGraphicBufferProducer::DisconnectMode` to
@@ -1573,18 +760,10 @@
  * \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;
-}
+BGraphicBufferProducer::DisconnectMode toGuiDisconnectMode(
+        HGraphicBufferProducer::DisconnectMode t);
 
 }  // namespace conversion
 }  // namespace android
 
-#endif  // MEDIA_STAGEFRIGHT_CONVERSION_H_
+#endif  // MEDIA_STAGEFRIGHT_BQHELPER_CONVERSION_H_
diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.h b/media/libstagefright/codecs/on2/dec/SoftVPX.h
index 9ac2791..b62b526 100644
--- a/media/libstagefright/codecs/on2/dec/SoftVPX.h
+++ b/media/libstagefright/codecs/on2/dec/SoftVPX.h
@@ -44,7 +44,7 @@
 
 private:
     enum {
-        kNumBuffers = 16
+        kNumBuffers = 10
     };
 
     enum {
diff --git a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
index 675c932..54a7095 100644
--- a/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
+++ b/media/libstagefright/include/media/stagefright/NuMediaExtractor.h
@@ -71,6 +71,8 @@
 
     status_t getFileFormat(sp<AMessage> *format) const;
 
+    status_t getExifOffsetSize(off64_t *offset, size_t *size) const;
+
     status_t selectTrack(size_t index, int64_t startTimeUs = -1ll,
             MediaSource::ReadOptions::SeekMode mode =
                 MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);
diff --git a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
index 8ef7620..cd0f75c 100644
--- a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
+++ b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
@@ -563,6 +563,10 @@
             DescribeColorAspectsParams* colorAspectsParams =
                     (DescribeColorAspectsParams *)params;
 
+            if (!isValidOMXParam(colorAspectsParams)) {
+                return OMX_ErrorBadParameter;
+            }
+
             if (colorAspectsParams->nPortIndex != kOutputPortIndex) {
                 return OMX_ErrorBadParameter;
             }
@@ -584,6 +588,10 @@
             DescribeHDRStaticInfoParams* hdrStaticInfoParams =
                     (DescribeHDRStaticInfoParams *)params;
 
+            if (!isValidOMXParam(hdrStaticInfoParams)) {
+                return OMX_ErrorBadParameter;
+            }
+
             if (hdrStaticInfoParams->nPortIndex != kOutputPortIndex) {
                 return OMX_ErrorBadPortIndex;
             }
@@ -635,15 +643,17 @@
             const DescribeHDRStaticInfoParams* hdrStaticInfoParams =
                     (DescribeHDRStaticInfoParams *)params;
 
+            if (!isValidOMXParam(hdrStaticInfoParams)) {
+                return OMX_ErrorBadParameter;
+            }
+
             if (hdrStaticInfoParams->nPortIndex != kOutputPortIndex) {
                 return OMX_ErrorBadPortIndex;
             }
 
-            if (hdrStaticInfoParams != NULL) {
-                mOutputFormat = OMX_COLOR_FormatYUV420Planar16;
-                mHdrStaticInfo = hdrStaticInfoParams->sInfo;
-                updatePortDefinitions(false);
-            }
+            mOutputFormat = OMX_COLOR_FormatYUV420Planar16;
+            mHdrStaticInfo = hdrStaticInfoParams->sInfo;
+            updatePortDefinitions(false);
 
             return OMX_ErrorNone;
         }
diff --git a/packages/MediaComponents/Android.mk b/packages/MediaComponents/Android.mk
index b6480a2..b0d8e7d 100644
--- a/packages/MediaComponents/Android.mk
+++ b/packages/MediaComponents/Android.mk
@@ -40,9 +40,6 @@
 
 LOCAL_PROGUARD_FLAG_FILES := proguard.cfg
 
-# TODO: Enable proguard (b/74090741)
-LOCAL_PROGUARD_ENABLED := disabled
-
 LOCAL_MULTILIB := first
 
 LOCAL_JAVA_LIBRARIES += android-support-annotations
diff --git a/packages/MediaComponents/res/layout/full_transport_controls.xml b/packages/MediaComponents/res/layout/full_transport_controls.xml
index dd41a1f..0914785 100644
--- a/packages/MediaComponents/res/layout/full_transport_controls.xml
+++ b/packages/MediaComponents/res/layout/full_transport_controls.xml
@@ -28,4 +28,4 @@
     <ImageButton android:id="@+id/pause" style="@style/FullTransportControlsButton.Pause" />
     <ImageButton android:id="@+id/ffwd" style="@style/FullTransportControlsButton.Ffwd" />
     <ImageButton android:id="@+id/next" style="@style/FullTransportControlsButton.Next" />
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/packages/MediaComponents/res/layout/media_controller.xml b/packages/MediaComponents/res/layout/media_controller.xml
index c5fbee8..dfda840 100644
--- a/packages/MediaComponents/res/layout/media_controller.xml
+++ b/packages/MediaComponents/res/layout/media_controller.xml
@@ -105,6 +105,7 @@
                 android:id="@+id/cast"
                 android:layout_centerVertical="true"
                 android:visibility="gone"
+                android:contentDescription="@string/mr_button_content_description"
                 style="@style/TitleBarButton" />
         </LinearLayout>
 
@@ -120,6 +121,7 @@
 
     <SeekBar
         android:id="@+id/mediacontroller_progress"
+        android:contentDescription="@string/mcv2_seek_bar_desc"
         android:layout_width="match_parent"
         android:layout_height="12dp"
         android:maxHeight="2dp"
@@ -233,4 +235,4 @@
             </LinearLayout>
         </LinearLayout>
     </RelativeLayout>
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/packages/MediaComponents/res/values/strings.xml b/packages/MediaComponents/res/values/strings.xml
index 69e9dff..aaceac8 100644
--- a/packages/MediaComponents/res/values/strings.xml
+++ b/packages/MediaComponents/res/values/strings.xml
@@ -84,10 +84,6 @@
     <!-- Placeholder text indicating that the user is currently casting screen. [CHAR LIMIT=50] -->
     <string name="mr_controller_casting_screen">Casting screen</string>
 
-    <string name="lockscreen_pause_button_content_description">Pause</string>
-    <string name="lockscreen_play_button_content_description">Play</string>
-    <string name="lockscreen_replay_button_content_description">Replay</string>
-
     <!-- Text for error alert when a video container is not valid for progressive download/playback. -->
     <string name="VideoView2_error_text_invalid_progressive_playback">This video isn\'t valid for streaming to this device.</string>
     <!-- Text for error alert when a video cannot be played. It can be used by any app. -->
@@ -133,4 +129,25 @@
     <string name="MediaControlView2_audio_track_number_text">
         Track <xliff:g id="audio_number" example="1">%1$s</xliff:g>
     </string>
+
+    <!--Content Descriptions -->
+    <string name="mcv2_back_button_desc">Back</string>
+    <string name="mcv2_overflow_left_button_desc">See more buttons</string>
+    <string name="mcv2_overflow_right_button_desc">Back to previous button list</string>
+    <string name="mcv2_seek_bar_desc">Playback progress</string>
+    <string name="mcv2_settings_button_desc">Settings</string>
+    <string name="mcv2_video_quality_button_desc">Video Quality Selection</string>
+    <string name="mcv2_cc_is_on">Subtitle is on. Click to hide it.</string>
+    <string name="mcv2_cc_is_off">Subtitle is off. Click to show it.</string>
+    <string name="mcv2_replay_button_desc">Replay</string>
+    <string name="mcv2_play_button_desc">Play</string>
+    <string name="mcv2_pause_button_desc">Pause</string>
+    <string name="mcv2_previous_button_desc">Previous media</string>
+    <string name="mcv2_next_button_desc">Next media</string>
+    <string name="mcv2_rewind_button_desc">Rewind by 10 seconds</string>
+    <string name="mcv2_ffwd_button_desc">Go forward by 30 seconds</string>
+    <string name="mcv2_launch_button_desc">Launch Link</string>
+    <string name="mcv2_muted_button_desc">Muted. Click to unmute</string>
+    <string name="mcv2_unmuted_button_desc">Click to Mute</string>
+    <string name="mcv2_full_screen_button_desc">Full screen</string>
 </resources>
diff --git a/packages/MediaComponents/res/values/style.xml b/packages/MediaComponents/res/values/style.xml
index b1da137..0be04e6 100644
--- a/packages/MediaComponents/res/values/style.xml
+++ b/packages/MediaComponents/res/values/style.xml
@@ -10,30 +10,35 @@
         <item name="android:src">@drawable/ic_skip_previous</item>
         <item name="android:layout_width">@dimen/mcv2_full_icon_size</item>
         <item name="android:layout_height">@dimen/mcv2_full_icon_size</item>
+        <item name="android:contentDescription">@string/mcv2_previous_button_desc</item>
     </style>
 
     <style name="FullTransportControlsButton.Next">
         <item name="android:src">@drawable/ic_skip_next</item>
         <item name="android:layout_width">@dimen/mcv2_full_icon_size</item>
         <item name="android:layout_height">@dimen/mcv2_full_icon_size</item>
+        <item name="android:contentDescription">@string/mcv2_next_button_desc</item>
     </style>
 
     <style name="FullTransportControlsButton.Pause">
         <item name="android:src">@drawable/ic_pause_circle_filled</item>
         <item name="android:layout_width">@dimen/mcv2_pause_icon_size</item>
         <item name="android:layout_height">@dimen/mcv2_pause_icon_size</item>
+        <item name="android:contentDescription">@string/mcv2_pause_button_desc</item>
     </style>
 
     <style name="FullTransportControlsButton.Ffwd">
         <item name="android:src">@drawable/ic_forward_30</item>
         <item name="android:layout_width">@dimen/mcv2_full_icon_size</item>
         <item name="android:layout_height">@dimen/mcv2_full_icon_size</item>
+        <item name="android:contentDescription">@string/mcv2_ffwd_button_desc</item>
     </style>
 
     <style name="FullTransportControlsButton.Rew">
         <item name="android:src">@drawable/ic_rewind_10</item>
         <item name="android:layout_width">@dimen/mcv2_full_icon_size</item>
         <item name="android:layout_height">@dimen/mcv2_full_icon_size</item>
+        <item name="android:contentDescription">@string/mcv2_rewind_button_desc</item>
     </style>
 
     <style name="EmbeddedTransportControlsButton">
@@ -46,30 +51,35 @@
         <item name="android:src">@drawable/ic_skip_previous</item>
         <item name="android:layout_width">@dimen/mcv2_embedded_icon_size</item>
         <item name="android:layout_height">@dimen/mcv2_embedded_icon_size</item>
+        <item name="android:contentDescription">@string/mcv2_previous_button_desc</item>
     </style>
 
     <style name="EmbeddedTransportControlsButton.Next">
         <item name="android:src">@drawable/ic_skip_next</item>
         <item name="android:layout_width">@dimen/mcv2_embedded_icon_size</item>
         <item name="android:layout_height">@dimen/mcv2_embedded_icon_size</item>
+        <item name="android:contentDescription">@string/mcv2_next_button_desc</item>
     </style>
 
     <style name="EmbeddedTransportControlsButton.Pause">
         <item name="android:src">@drawable/ic_pause_circle_filled</item>
         <item name="android:layout_width">@dimen/mcv2_pause_icon_size</item>
         <item name="android:layout_height">@dimen/mcv2_pause_icon_size</item>
+        <item name="android:contentDescription">@string/mcv2_pause_button_desc</item>
     </style>
 
     <style name="EmbeddedTransportControlsButton.Ffwd">
         <item name="android:src">@drawable/ic_forward_30</item>
         <item name="android:layout_width">@dimen/mcv2_embedded_icon_size</item>
         <item name="android:layout_height">@dimen/mcv2_embedded_icon_size</item>
+        <item name="android:contentDescription">@string/mcv2_ffwd_button_desc</item>
     </style>
 
     <style name="EmbeddedTransportControlsButton.Rew">
         <item name="android:src">@drawable/ic_rewind_10</item>
         <item name="android:layout_width">@dimen/mcv2_embedded_icon_size</item>
         <item name="android:layout_height">@dimen/mcv2_embedded_icon_size</item>
+        <item name="android:contentDescription">@string/mcv2_rewind_button_desc</item>
     </style>
 
     <style name="MinimalTransportControlsButton">
@@ -81,6 +91,7 @@
         <item name="android:src">@drawable/ic_pause_circle_filled</item>
         <item name="android:layout_width">@dimen/mcv2_minimal_icon_size</item>
         <item name="android:layout_height">@dimen/mcv2_minimal_icon_size</item>
+        <item name="android:contentDescription">@string/mcv2_pause_button_desc</item>
     </style>
 
     <style name="TitleBar">
@@ -95,10 +106,12 @@
 
     <style name="TitleBarButton.Back">
         <item name="android:src">@drawable/ic_arrow_back</item>
+        <item name="android:contentDescription">@string/mcv2_back_button_desc</item>
     </style>
 
     <style name="TitleBarButton.Launch">
         <item name="android:src">@drawable/ic_launch</item>
+        <item name="android:contentDescription">@string/mcv2_launch_button_desc</item>
     </style>
 
     <style name="TimeText">
@@ -137,29 +150,36 @@
 
     <style name="BottomBarButton.CC">
         <item name="android:src">@drawable/ic_subtitle_off</item>
+        <item name="android:contentDescription">@string/mcv2_cc_is_off</item>
     </style>
 
     <style name="BottomBarButton.FullScreen">
         <item name="android:src">@drawable/ic_fullscreen</item>
+        <item name="android:contentDescription">@string/mcv2_full_screen_button_desc</item>
     </style>
 
     <style name="BottomBarButton.OverflowRight">
         <item name="android:src">@drawable/ic_chevron_right</item>
+        <item name="android:contentDescription">@string/mcv2_overflow_right_button_desc</item>
     </style>
 
     <style name="BottomBarButton.OverflowLeft">
         <item name="android:src">@drawable/ic_chevron_left</item>
+        <item name="android:contentDescription">@string/mcv2_overflow_left_button_desc</item>
     </style>
 
     <style name="BottomBarButton.Settings">
         <item name="android:src">@drawable/ic_settings</item>
+        <item name="android:contentDescription">@string/mcv2_settings_button_desc</item>
     </style>
 
     <style name="BottomBarButton.Mute">
         <item name="android:src">@drawable/ic_unmute</item>
+        <item name="android:contentDescription">@string/mcv2_unmuted_button_desc</item>
     </style>
 
     <style name="BottomBarButton.VideoQuality">
-        <item name="android:src">@drawable/ic_high_quality</item>
+      <item name="android:src">@drawable/ic_high_quality</item>
+      <item name="android:contentDescription">@string/mcv2_video_quality_button_desc</item>
     </style>
 </resources>
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index 0091816..721e0a2 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -24,12 +24,12 @@
 import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_SET_LIST_METADATA;
 import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE;
 import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE;
-import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_MEDIA_ID;
-import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_SEARCH;
-import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_URI;
-import static android.media.MediaSession2.COMMAND_CODE_PREPARE_FROM_MEDIA_ID;
-import static android.media.MediaSession2.COMMAND_CODE_PREPARE_FROM_SEARCH;
-import static android.media.MediaSession2.COMMAND_CODE_PREPARE_FROM_URI;
+import static android.media.MediaSession2.COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID;
+import static android.media.MediaSession2.COMMAND_CODE_SESSION_PLAY_FROM_SEARCH;
+import static android.media.MediaSession2.COMMAND_CODE_SESSION_PLAY_FROM_URI;
+import static android.media.MediaSession2.COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID;
+import static android.media.MediaSession2.COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH;
+import static android.media.MediaSession2.COMMAND_CODE_SESSION_PREPARE_FROM_URI;
 
 import android.app.PendingIntent;
 import android.content.ComponentName;
@@ -438,7 +438,7 @@
 
     @Override
     public void prepareFromUri_impl(Uri uri, Bundle extras) {
-        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PREPARE_FROM_URI);
+        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_SESSION_PREPARE_FROM_URI);
         if (uri == null) {
             throw new IllegalArgumentException("uri shouldn't be null");
         }
@@ -455,7 +455,8 @@
 
     @Override
     public void prepareFromSearch_impl(String query, Bundle extras) {
-        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PREPARE_FROM_SEARCH);
+        final IMediaSession2 binder = getSessionBinderIfAble(
+                COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH);
         if (TextUtils.isEmpty(query)) {
             throw new IllegalArgumentException("query shouldn't be empty");
         }
@@ -472,7 +473,8 @@
 
     @Override
     public void prepareFromMediaId_impl(String mediaId, Bundle extras) {
-        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PREPARE_FROM_MEDIA_ID);
+        final IMediaSession2 binder = getSessionBinderIfAble(
+                COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID);
         if (mediaId == null) {
             throw new IllegalArgumentException("mediaId shouldn't be null");
         }
@@ -489,7 +491,7 @@
 
     @Override
     public void playFromUri_impl(Uri uri, Bundle extras) {
-        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAY_FROM_URI);
+        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_SESSION_PLAY_FROM_URI);
         if (uri == null) {
             throw new IllegalArgumentException("uri shouldn't be null");
         }
@@ -506,7 +508,7 @@
 
     @Override
     public void playFromSearch_impl(String query, Bundle extras) {
-        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAY_FROM_SEARCH);
+        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_SESSION_PLAY_FROM_SEARCH);
         if (TextUtils.isEmpty(query)) {
             throw new IllegalArgumentException("query shouldn't be empty");
         }
@@ -523,7 +525,8 @@
 
     @Override
     public void playFromMediaId_impl(String mediaId, Bundle extras) {
-        final IMediaSession2 binder = getSessionBinderIfAble(COMMAND_CODE_PLAY_FROM_MEDIA_ID);
+        final IMediaSession2 binder = getSessionBinderIfAble(
+                COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID);
         if (mediaId == null) {
             throw new IllegalArgumentException("mediaId shouldn't be null");
         }
@@ -764,7 +767,7 @@
     }
 
     @Override
-    public long getPosition_impl() {
+    public long getCurrentPosition_impl() {
         synchronized (mLock) {
             long timeDiff = System.currentTimeMillis() - mPositionEventTimeMs;
             long expectedPosition = mPositionMs + (long) (mPlaybackSpeed * timeDiff);
@@ -837,7 +840,8 @@
             if (!mInstance.isConnected()) {
                 return;
             }
-            mCallback.onBufferedPositionChanged(mInstance, bufferedPositionMs);
+            // TODO(jaewan): Fix this -- it's now buffered state
+            //mCallback.onBufferedPositionChanged(mInstance, bufferedPositionMs);
         });
     }
 
@@ -862,8 +866,7 @@
             if (!mInstance.isConnected()) {
                 return;
             }
-            // TODO(jaewan): Fix public API not to take playlistAgent.
-            mCallback.onPlaylistChanged(mInstance, null, playlist, metadata);
+            mCallback.onPlaylistChanged(mInstance, playlist, metadata);
         });
     }
 
@@ -875,8 +878,7 @@
             if (!mInstance.isConnected()) {
                 return;
             }
-            // TODO(jaewan): Fix public API not to take playlistAgent.
-            mCallback.onPlaylistMetadataChanged(mInstance, null, metadata);
+            mCallback.onPlaylistMetadataChanged(mInstance, metadata);
         });
     }
 
@@ -888,8 +890,7 @@
             if (!mInstance.isConnected()) {
                 return;
             }
-            // TODO(jaewan): Fix public API not to take playlistAgent.
-            mCallback.onShuffleModeChanged(mInstance, null, shuffleMode);
+            mCallback.onShuffleModeChanged(mInstance, shuffleMode);
         });
     }
 
@@ -901,8 +902,7 @@
             if (!mInstance.isConnected()) {
                 return;
             }
-            // TODO(jaewan): Fix public API not to take playlistAgent.
-            mCallback.onRepeatModeChanged(mInstance, null, repeatMode);
+            mCallback.onRepeatModeChanged(mInstance, repeatMode);
         });
     }
 
diff --git a/packages/MediaComponents/src/com/android/media/MediaMetadata2Impl.java b/packages/MediaComponents/src/com/android/media/MediaMetadata2Impl.java
index 286f5ee..5fd5812 100644
--- a/packages/MediaComponents/src/com/android/media/MediaMetadata2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaMetadata2Impl.java
@@ -78,8 +78,6 @@
         METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_URI, METADATA_TYPE_TEXT);
         METADATA_KEYS_TYPE.put(METADATA_KEY_ADVERTISEMENT, METADATA_TYPE_LONG);
         METADATA_KEYS_TYPE.put(METADATA_KEY_DOWNLOAD_STATUS, METADATA_TYPE_LONG);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_RADIO_FREQUENCY, METADATA_TYPE_FLOAT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_RADIO_CALLSIGN, METADATA_TYPE_TEXT);
     }
 
     private static final @TextKey
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index 2b7c72a..2d04765 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -48,6 +48,7 @@
 import android.media.MediaSession2.CommandButton;
 import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.OnDataSourceMissingHelper;
 import android.media.MediaSession2.SessionCallback;
 import android.media.MediaSessionService2;
 import android.media.SessionToken2;
@@ -61,15 +62,16 @@
 import android.os.ResultReceiver;
 import android.support.annotation.GuardedBy;
 import android.text.TextUtils;
-import android.util.ArrayMap;
 import android.util.Log;
 
 import java.lang.ref.WeakReference;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.NoSuchElementException;
+import java.util.Set;
 import java.util.concurrent.Executor;
 
 public class MediaSession2Impl implements MediaSession2Provider {
@@ -111,9 +113,13 @@
     @GuardedBy("mLock")
     private MediaPlaylistAgent mPlaylistAgent;
     @GuardedBy("mLock")
+    private SessionPlaylistAgent mSessionPlaylistAgent;
+    @GuardedBy("mLock")
     private VolumeProvider2 mVolumeProvider;
     @GuardedBy("mLock")
     private PlaybackInfo mPlaybackInfo;
+    @GuardedBy("mLock")
+    private OnDataSourceMissingHelper mDsmHelper;
 
     /**
      * Can be only called by the {@link Builder#build()}.
@@ -207,7 +213,7 @@
     }
 
     @Override
-    public void updatePlayer_impl(MediaPlayerBase player, MediaPlaylistAgent playlistAgent,
+    public void updatePlayer_impl(@NonNull MediaPlayerBase player, MediaPlaylistAgent playlistAgent,
             VolumeProvider2 volumeProvider) throws IllegalArgumentException {
         ensureCallingThread();
         if (player == null) {
@@ -225,9 +231,12 @@
             oldPlayer = mPlayer;
             oldAgent = mPlaylistAgent;
             mPlayer = player;
-            // TODO(jaewan): Replace this with the proper default agent (b/74090741)
             if (agent == null) {
-                agent = new MediaPlaylistAgent(mContext) {};
+                mSessionPlaylistAgent = new SessionPlaylistAgent(mContext, this, mPlayer);
+                if (mDsmHelper != null) {
+                    mSessionPlaylistAgent.setOnDataSourceMissingHelper(mDsmHelper);
+                }
+                agent = mSessionPlaylistAgent;
             }
             mPlaylistAgent = agent;
             mVolumeProvider = volumeProvider;
@@ -312,6 +321,7 @@
             mPlayer = null;
             agent = mPlaylistAgent;
             mPlaylistAgent = null;
+            mSessionPlaylistAgent = null;
         }
         if (player != null) {
             player.unregisterPlayerEventCallback(mPlayerEventCallback);
@@ -385,7 +395,7 @@
     }
 
     @Override
-    public void skipToPlaylistItem_impl(MediaItem2 item) {
+    public void skipToPlaylistItem_impl(@NonNull MediaItem2 item) {
         if (item == null) {
             throw new IllegalArgumentException("item shouldn't be null");
         }
@@ -418,7 +428,8 @@
     }
 
     @Override
-    public void setCustomLayout_impl(ControllerInfo controller, List<CommandButton> layout) {
+    public void setCustomLayout_impl(@NonNull ControllerInfo controller,
+            @NonNull List<CommandButton> layout) {
         ensureCallingThread();
         if (controller == null) {
             throw new IllegalArgumentException("controller shouldn't be null");
@@ -434,7 +445,8 @@
     //////////////////////////////////////////////////////////////////////////////////////
 
     @Override
-    public void setAllowedCommands_impl(ControllerInfo controller, CommandGroup commands) {
+    public void setAllowedCommands_impl(@NonNull ControllerInfo controller,
+            @NonNull CommandGroup commands) {
         if (controller == null) {
             throw new IllegalArgumentException("controller shouldn't be null");
         }
@@ -445,8 +457,8 @@
     }
 
     @Override
-    public void sendCustomCommand_impl(ControllerInfo controller, Command command, Bundle args,
-            ResultReceiver receiver) {
+    public void sendCustomCommand_impl(@NonNull ControllerInfo controller, @NonNull Command command,
+            Bundle args, ResultReceiver receiver) {
         if (controller == null) {
             throw new IllegalArgumentException("controller shouldn't be null");
         }
@@ -457,7 +469,7 @@
     }
 
     @Override
-    public void sendCustomCommand_impl(Command command, Bundle args) {
+    public void sendCustomCommand_impl(@NonNull Command command, Bundle args) {
         if (command == null) {
             throw new IllegalArgumentException("command shouldn't be null");
         }
@@ -465,7 +477,7 @@
     }
 
     @Override
-    public void setPlaylist_impl(List<MediaItem2> list, MediaMetadata2 metadata) {
+    public void setPlaylist_impl(@NonNull List<MediaItem2> list, MediaMetadata2 metadata) {
         if (list == null) {
             throw new IllegalArgumentException("list shouldn't be null");
         }
@@ -489,7 +501,7 @@
     }
 
     @Override
-    public void addPlaylistItem_impl(int index, MediaItem2 item) {
+    public void addPlaylistItem_impl(int index, @NonNull MediaItem2 item) {
         if (index < 0) {
             throw new IllegalArgumentException("index shouldn't be negative");
         }
@@ -505,7 +517,7 @@
     }
 
     @Override
-    public void removePlaylistItem_impl(MediaItem2 item) {
+    public void removePlaylistItem_impl(@NonNull MediaItem2 item) {
         if (item == null) {
             throw new IllegalArgumentException("item shouldn't be null");
         }
@@ -518,7 +530,7 @@
     }
 
     @Override
-    public void replacePlaylistItem_impl(int index, MediaItem2 item) {
+    public void replacePlaylistItem_impl(int index, @NonNull MediaItem2 item) {
         if (index < 0) {
             throw new IllegalArgumentException("index shouldn't be negative");
         }
@@ -659,7 +671,7 @@
     }
 
     @Override
-    public long getPosition_impl() {
+    public long getCurrentPosition_impl() {
         final MediaPlayerBase player = mPlayer;
         if (player != null) {
             return mPlayer.getCurrentPosition();
@@ -685,6 +697,29 @@
         mSessionStub.notifyError(errorCode, extras);
     }
 
+    @Override
+    public void setOnDataSourceMissingHelper_impl(@NonNull OnDataSourceMissingHelper helper) {
+        if (helper == null) {
+            throw new IllegalArgumentException("helper shouldn't be null");
+        }
+        synchronized (mLock) {
+            mDsmHelper = helper;
+            if (mSessionPlaylistAgent != null) {
+                mSessionPlaylistAgent.setOnDataSourceMissingHelper(helper);
+            }
+        }
+    }
+
+    @Override
+    public void clearOnDataSourceMissingHelper_impl() {
+        synchronized (mLock) {
+            mDsmHelper = null;
+            if (mSessionPlaylistAgent != null) {
+                mSessionPlaylistAgent.clearOnDataSourceMissingHelper();
+            }
+        }
+    }
+
     ///////////////////////////////////////////////////
     // Protected or private methods
     ///////////////////////////////////////////////////
@@ -1022,7 +1057,7 @@
         /**
          * @return a new Command instance from the Bundle
          */
-        public static Command fromBundle_impl(Context context, Bundle command) {
+        public static Command fromBundle_impl(Context context, @NonNull Bundle command) {
             if (command == null) {
                 throw new IllegalArgumentException("command shouldn't be null");
             }
@@ -1044,8 +1079,7 @@
                 return false;
             }
             CommandImpl other = (CommandImpl) obj;
-            // TODO(jaewan): Should we also compare contents in bundle?
-            //               It may not be possible if the bundle contains private class.
+            // TODO(jaewan): Compare Commands with the generated UUID, as we're doing for the MI2.
             return mCommandCode == other.mCommandCode
                     && TextUtils.equals(mCustomCommand, other.mCustomCommand);
         }
@@ -1074,7 +1108,7 @@
         // Prefix for command codes that will be sent directly to the MediaPlaylistAgent
         private static final String PREFIX_COMMAND_CODE_PLAYLIST = "COMMAND_CODE_PLAYLIST_";
 
-        private List<Command> mCommands = new ArrayList<>();
+        private Set<Command> mCommands = new HashSet<>();
         private final Context mContext;
         private final CommandGroup mInstance;
 
@@ -1092,7 +1126,7 @@
         }
 
         @Override
-        public void addCommand_impl(Command command) {
+        public void addCommand_impl(@NonNull Command command) {
             if (command == null) {
                 throw new IllegalArgumentException("command shouldn't be null");
             }
@@ -1129,7 +1163,7 @@
         }
 
         @Override
-        public void removeCommand_impl(Command command) {
+        public void removeCommand_impl(@NonNull Command command) {
             if (command == null) {
                 throw new IllegalArgumentException("command shouldn't be null");
             }
@@ -1137,7 +1171,7 @@
         }
 
         @Override
-        public boolean hasCommand_impl(Command command) {
+        public boolean hasCommand_impl(@NonNull Command command) {
             if (command == null) {
                 throw new IllegalArgumentException("command shouldn't be null");
             }
@@ -1149,8 +1183,8 @@
             if (code == COMMAND_CODE_CUSTOM) {
                 throw new IllegalArgumentException("Use hasCommand(Command) for custom command");
             }
-            for (int i = 0; i < mCommands.size(); i++) {
-                if (mCommands.get(i).getCommandCode() == code) {
+            for (Command command : mCommands) {
+                if (command.getCommandCode() == code) {
                     return true;
                 }
             }
@@ -1158,12 +1192,12 @@
         }
 
         @Override
-        public List<Command> getCommands_impl() {
+        public Set<Command> getCommands_impl() {
             return getCommands();
         }
 
-        public List<Command> getCommands() {
-            return Collections.unmodifiableList(mCommands);
+        public Set<Command> getCommands() {
+            return Collections.unmodifiableSet(mCommands);
         }
 
         /**
@@ -1173,8 +1207,8 @@
         @Override
         public Bundle toBundle_impl() {
             ArrayList<Bundle> list = new ArrayList<>();
-            for (int i = 0; i < mCommands.size(); i++) {
-                list.add(mCommands.get(i).toBundle());
+            for (Command command : mCommands) {
+                list.add(command.toBundle());
             }
             Bundle bundle = new Bundle();
             bundle.putParcelableArrayList(KEY_COMMANDS, list);
@@ -1217,7 +1251,7 @@
         private final IMediaController2 mControllerBinder;
 
         public ControllerInfoImpl(Context context, ControllerInfo instance, int uid,
-                int pid, String packageName, IMediaController2 callback) {
+                int pid, @NonNull String packageName, @NonNull IMediaController2 callback) {
             if (TextUtils.isEmpty(packageName)) {
                 throw new IllegalArgumentException("packageName shouldn't be empty");
             }
@@ -1234,7 +1268,7 @@
             // Ask server whether the controller is trusted.
             // App cannot know this because apps cannot query enabled notification listener for
             // another package, but system server can do.
-            mIsTrusted = manager.isTrusted(uid, packageName);
+            mIsTrusted = manager.isTrusted(packageName, pid, uid);
         }
 
         @Override
@@ -1468,7 +1502,7 @@
          *      {@link MediaSession2} or {@link MediaController2}.
          */
         // TODO(jaewan): Also need executor
-        public BuilderBaseImpl(Context context) {
+        public BuilderBaseImpl(@NonNull Context context) {
             if (context == null) {
                 throw new IllegalArgumentException("context shouldn't be null");
             }
@@ -1478,7 +1512,7 @@
         }
 
         @Override
-        public void setPlayer_impl(MediaPlayerBase player) {
+        public void setPlayer_impl(@NonNull MediaPlayerBase player) {
             if (player == null) {
                 throw new IllegalArgumentException("player shouldn't be null");
             }
@@ -1486,7 +1520,7 @@
         }
 
         @Override
-        public void setPlaylistAgent_impl(MediaPlaylistAgent playlistAgent) {
+        public void setPlaylistAgent_impl(@NonNull MediaPlaylistAgent playlistAgent) {
             if (playlistAgent == null) {
                 throw new IllegalArgumentException("playlistAgent shouldn't be null");
             }
@@ -1504,7 +1538,7 @@
         }
 
         @Override
-        public void setId_impl(String id) {
+        public void setId_impl(@NonNull String id) {
             if (id == null) {
                 throw new IllegalArgumentException("id shouldn't be null");
             }
@@ -1512,7 +1546,7 @@
         }
 
         @Override
-        public void setSessionCallback_impl(Executor executor, C callback) {
+        public void setSessionCallback_impl(@NonNull Executor executor, @NonNull C callback) {
             if (executor == null) {
                 throw new IllegalArgumentException("executor shouldn't be null");
             }
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index caf834a..a3cb0c6 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -86,9 +86,8 @@
                 CommandGroupImpl group = new CommandGroupImpl(session.getContext());
                 group.addAllPlaybackCommands();
                 group.addAllPlaylistCommands();
-                List<Command> commands = group.getCommands();
-                for (int i = 0; i < commands.size(); i++) {
-                    Command command = commands.get(i);
+                Set<Command> commands = group.getCommands();
+                for (Command command : commands) {
                     sCommandsForOnCommandRequest.append(command.getCommandCode(), command);
                 }
             }
@@ -389,7 +388,7 @@
                 //       use thread poll for incoming calls.
                 final int playerState = session.getInstance().getPlayerState();
                 final long positionEventTimeMs = System.currentTimeMillis();
-                final long positionMs = session.getInstance().getPosition();
+                final long positionMs = session.getInstance().getCurrentPosition();
                 final float playbackSpeed = session.getInstance().getPlaybackSpeed();
                 final long bufferedPositionMs = session.getInstance().getBufferedPosition();
                 final Bundle playbackInfoBundle = ((MediaController2Impl.PlaybackInfoImpl)
@@ -540,7 +539,7 @@
     @Override
     public void prepareFromUri(final IMediaController2 caller, final Uri uri,
             final Bundle extras) {
-        onCommand(caller, MediaSession2.COMMAND_CODE_PREPARE_FROM_URI, (session, controller) -> {
+        onCommand(caller, MediaSession2.COMMAND_CODE_SESSION_PREPARE_FROM_URI, (session, controller) -> {
             if (uri == null) {
                 Log.w(TAG, "prepareFromUri(): Ignoring null uri from " + controller);
                 return;
@@ -552,7 +551,7 @@
     @Override
     public void prepareFromSearch(final IMediaController2 caller, final String query,
             final Bundle extras) {
-        onCommand(caller, MediaSession2.COMMAND_CODE_PREPARE_FROM_SEARCH, (session, controller) -> {
+        onCommand(caller, MediaSession2.COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH, (session, controller) -> {
             if (TextUtils.isEmpty(query)) {
                 Log.w(TAG, "prepareFromSearch(): Ignoring empty query from " + controller);
                 return;
@@ -565,7 +564,7 @@
     @Override
     public void prepareFromMediaId(final IMediaController2 caller, final String mediaId,
             final Bundle extras) {
-        onCommand(caller, MediaSession2.COMMAND_CODE_PREPARE_FROM_MEDIA_ID,
+        onCommand(caller, MediaSession2.COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID,
                 (session, controller) -> {
             if (mediaId == null) {
                 Log.w(TAG, "prepareFromMediaId(): Ignoring null mediaId from " + controller);
@@ -579,7 +578,7 @@
     @Override
     public void playFromUri(final IMediaController2 caller, final Uri uri,
             final Bundle extras) {
-        onCommand(caller, MediaSession2.COMMAND_CODE_PLAY_FROM_URI, (session, controller) -> {
+        onCommand(caller, MediaSession2.COMMAND_CODE_SESSION_PLAY_FROM_URI, (session, controller) -> {
             if (uri == null) {
                 Log.w(TAG, "playFromUri(): Ignoring null uri from " + controller);
                 return;
@@ -591,7 +590,7 @@
     @Override
     public void playFromSearch(final IMediaController2 caller, final String query,
             final Bundle extras) {
-        onCommand(caller, MediaSession2.COMMAND_CODE_PLAY_FROM_SEARCH, (session, controller) -> {
+        onCommand(caller, MediaSession2.COMMAND_CODE_SESSION_PLAY_FROM_SEARCH, (session, controller) -> {
             if (TextUtils.isEmpty(query)) {
                 Log.w(TAG, "playFromSearch(): Ignoring empty query from " + controller);
                 return;
@@ -604,7 +603,7 @@
     @Override
     public void playFromMediaId(final IMediaController2 caller, final String mediaId,
             final Bundle extras) {
-        onCommand(caller, MediaSession2.COMMAND_CODE_PLAY_FROM_MEDIA_ID, (session, controller) -> {
+        onCommand(caller, MediaSession2.COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID, (session, controller) -> {
             if (mediaId == null) {
                 Log.w(TAG, "playFromMediaId(): Ignoring null mediaId from " + controller);
                 return;
@@ -617,8 +616,7 @@
     @Override
     public void setRating(final IMediaController2 caller, final String mediaId,
             final Bundle ratingBundle) {
-        // TODO(jaewan): Define COMMAND_CODE_SET_RATING
-        onCommand(caller, MediaSession2.COMMAND_CODE_SET_RATING, (session, controller) -> {
+        onCommand(caller, MediaSession2.COMMAND_CODE_SESSION_SET_RATING, (session, controller) -> {
             if (mediaId == null) {
                 Log.w(TAG, "setRating(): Ignoring null mediaId from " + controller);
                 return;
@@ -717,7 +715,7 @@
 
     @Override
     public void skipToPreviousItem(IMediaController2 caller) {
-        onCommand(caller, MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM,
+        onCommand(caller, MediaSession2.COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM,
                 (session, controller) -> {
                     session.getInstance().skipToPreviousItem();
                 });
@@ -725,7 +723,7 @@
 
     @Override
     public void skipToNextItem(IMediaController2 caller) {
-        onCommand(caller, MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM,
+        onCommand(caller, MediaSession2.COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM,
                 (session, controller) -> {
                     session.getInstance().skipToNextItem();
                 });
diff --git a/packages/MediaComponents/src/com/android/media/SessionPlaylistAgent.java b/packages/MediaComponents/src/com/android/media/SessionPlaylistAgent.java
index 6805dad..dfbe56a 100644
--- a/packages/MediaComponents/src/com/android/media/SessionPlaylistAgent.java
+++ b/packages/MediaComponents/src/com/android/media/SessionPlaylistAgent.java
@@ -23,8 +23,8 @@
 import android.media.MediaItem2;
 import android.media.MediaMetadata2;
 import android.media.MediaPlayerBase;
+import android.media.MediaPlayerBase.PlayerEventCallback;
 import android.media.MediaPlaylistAgent;
-import android.media.MediaSession2;
 import android.media.MediaSession2.OnDataSourceMissingHelper;
 import android.util.ArrayMap;
 
@@ -47,14 +47,13 @@
     private final PlayItem mEopPlayItem = new PlayItem(END_OF_PLAYLIST, null);
 
     private final Object mLock = new Object();
-    private final MediaSession2 mSession;
+    private final MediaSession2Impl mSessionImpl;
+    private final MyPlayerEventCallback mPlayerCallback;
 
-    // TODO: Set data sources properly into mPlayer (b/74090741)
     @GuardedBy("mLock")
     private MediaPlayerBase mPlayer;
     @GuardedBy("mLock")
-    private OnDataSourceMissingHelper mDsdHelper;
-
+    private OnDataSourceMissingHelper mDsmHelper;
     // TODO: Check if having the same item is okay (b/74090741)
     @GuardedBy("mLock")
     private ArrayList<MediaItem2> mPlaylist = new ArrayList<>();
@@ -71,6 +70,22 @@
     @GuardedBy("mLock")
     private PlayItem mCurrent;
 
+    // Called on session callback executor.
+    private class MyPlayerEventCallback extends PlayerEventCallback {
+        public void onCurrentDataSourceChanged(@NonNull MediaPlayerBase mpb,
+                @Nullable DataSourceDesc dsd) {
+            if (mPlayer != mpb) {
+                return;
+            }
+            synchronized (mLock) {
+                if (dsd == null && mCurrent != null) {
+                    mCurrent = getNextValidPlayItemLocked(mCurrent.shuffledIdx, 1);
+                    updateCurrentIfNeededLocked();
+                }
+            }
+        }
+    }
+
     private class PlayItem {
         int shuffledIdx;
         DataSourceDesc dsd;
@@ -118,31 +133,46 @@
         }
     }
 
-    public SessionPlaylistAgent(@NonNull Context context, @NonNull MediaSession2 session,
+    public SessionPlaylistAgent(@NonNull Context context, @NonNull MediaSession2Impl sessionImpl,
             @NonNull MediaPlayerBase player) {
         super(context);
-        if (session == null) {
-            throw new IllegalArgumentException("session shouldn't be null");
+        if (sessionImpl == null) {
+            throw new IllegalArgumentException("sessionImpl shouldn't be null");
         }
         if (player == null) {
             throw new IllegalArgumentException("player shouldn't be null");
         }
-        mSession = session;
+        mSessionImpl = sessionImpl;
         mPlayer = player;
+        mPlayerCallback = new MyPlayerEventCallback();
+        mPlayer.registerPlayerEventCallback(mSessionImpl.getCallbackExecutor(), mPlayerCallback);
     }
 
-    public void setPlayer(MediaPlayerBase player) {
+    public void setPlayer(@NonNull MediaPlayerBase player) {
         if (player == null) {
             throw new IllegalArgumentException("player shouldn't be null");
         }
         synchronized (mLock) {
+            if (player == mPlayer) {
+                return;
+            }
+            mPlayer.unregisterPlayerEventCallback(mPlayerCallback);
             mPlayer = player;
+            mPlayer.registerPlayerEventCallback(
+                    mSessionImpl.getCallbackExecutor(), mPlayerCallback);
+            updatePlayerDataSourceLocked();
         }
     }
 
     public void setOnDataSourceMissingHelper(OnDataSourceMissingHelper helper) {
         synchronized (mLock) {
-            mDsdHelper = helper;
+            mDsmHelper = helper;
+        }
+    }
+
+    public void clearOnDataSourceMissingHelper() {
+        synchronized (mLock) {
+            mDsmHelper = null;
         }
     }
 
@@ -154,8 +184,7 @@
     }
 
     @Override
-    public void setPlaylist(@NonNull List<MediaItem2> list,
-            @Nullable MediaMetadata2 metadata) {
+    public void setPlaylist(@NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) {
         if (list == null) {
             throw new IllegalArgumentException("list shouldn't be null");
         }
@@ -169,6 +198,7 @@
 
             mMetadata = metadata;
             mCurrent = getNextValidPlayItemLocked(END_OF_PLAYLIST, 1);
+            updatePlayerDataSourceLocked();
         }
         notifyPlaylistChanged();
     }
@@ -207,6 +237,7 @@
             }
             if (!hasValidItem()) {
                 mCurrent = getNextValidPlayItemLocked(END_OF_PLAYLIST, 1);
+                updatePlayerDataSourceLocked();
             } else {
                 updateCurrentIfNeededLocked();
             }
@@ -246,6 +277,7 @@
             mPlaylist.set(index, item);
             if (!hasValidItem()) {
                 mCurrent = getNextValidPlayItemLocked(END_OF_PLAYLIST, 1);
+                updatePlayerDataSourceLocked();
             } else {
                 updateCurrentIfNeededLocked();
             }
@@ -288,7 +320,7 @@
     @Override
     public void skipToNextItem() {
         synchronized (mLock) {
-            if (!hasValidItem()) {
+            if (!hasValidItem() || mCurrent == mEopPlayItem) {
                 return;
             }
             PlayItem next = getNextValidPlayItemLocked(mCurrent.shuffledIdx, 1);
@@ -315,6 +347,23 @@
                 return;
             }
             mRepeatMode = repeatMode;
+            switch (repeatMode) {
+                case MediaPlaylistAgent.REPEAT_MODE_ONE:
+                    if (mCurrent != null && mCurrent != mEopPlayItem) {
+                        mPlayer.loopCurrent(true);
+                    }
+                    break;
+                case MediaPlaylistAgent.REPEAT_MODE_ALL:
+                case MediaPlaylistAgent.REPEAT_MODE_GROUP:
+                    if (mCurrent == mEopPlayItem) {
+                        mCurrent = getNextValidPlayItemLocked(END_OF_PLAYLIST, 1);
+                        updatePlayerDataSourceLocked();
+                    }
+                    // pass through
+                case MediaPlaylistAgent.REPEAT_MODE_NONE:
+                    mPlayer.loopCurrent(false);
+                    break;
+            }
         }
         notifyRepeatModeChanged();
     }
@@ -336,6 +385,7 @@
             }
             mShuffleMode = shuffleMode;
             applyShuffleModeLocked();
+            updateCurrentIfNeededLocked();
         }
         notifyShuffleModeChanged();
     }
@@ -359,10 +409,10 @@
         if (dsd != null) {
             return dsd;
         }
-        OnDataSourceMissingHelper helper = mDsdHelper;
+        OnDataSourceMissingHelper helper = mDsmHelper;
         if (helper != null) {
             // TODO: Do not call onDataSourceMissing with the lock (b/74090741).
-            dsd = helper.onDataSourceMissing(mSession, item);
+            dsd = helper.onDataSourceMissing(mSessionImpl.getInstance(), item);
             if (dsd != null) {
                 mItemDsdMap.put(item, dsd);
             }
@@ -370,6 +420,7 @@
         return dsd;
     }
 
+    // TODO: consider to call updateCurrentIfNeededLocked inside (b/74090741)
     private PlayItem getNextValidPlayItemLocked(int curShuffledIdx, int direction) {
         int size = mPlaylist.size();
         if (curShuffledIdx == END_OF_PLAYLIST) {
@@ -411,9 +462,21 @@
                 mCurrent = getNextValidPlayItemLocked(mCurrent.shuffledIdx, 1);
             }
         }
+        updatePlayerDataSourceLocked();
         return;
     }
 
+    private void updatePlayerDataSourceLocked() {
+        if (mCurrent == null || mCurrent == mEopPlayItem) {
+            return;
+        }
+        if (mPlayer.getCurrentDataSource() != mCurrent.dsd) {
+            mPlayer.setDataSource(mCurrent.dsd);
+            mPlayer.loopCurrent(mRepeatMode == MediaPlaylistAgent.REPEAT_MODE_ONE);
+        }
+        // TODO: Call setNextDataSource (b/74090741)
+    }
+
     private void applyShuffleModeLocked() {
         mShuffledList.clear();
         mShuffledList.addAll(mPlaylist);
diff --git a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
index 9ae75b0..4cdc41d 100644
--- a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
@@ -207,10 +207,6 @@
     private List<String> mPlaybackSpeedTextList;
     private List<Float> mPlaybackSpeedList;
 
-    private CharSequence mPlayDescription;
-    private CharSequence mPauseDescription;
-    private CharSequence mReplayDescription;
-
     public MediaControlView2Impl(MediaControlView2 instance,
             ViewGroupProvider superProvider, ViewGroupProvider privateProvider) {
         super(instance, superProvider, privateProvider);
@@ -479,12 +475,6 @@
     }
 
     private void initControllerView(View v) {
-        mPlayDescription = mResources.getText(R.string.lockscreen_play_button_content_description);
-        mPauseDescription =
-                mResources.getText(R.string.lockscreen_pause_button_content_description);
-        mReplayDescription =
-                mResources.getText(R.string.lockscreen_replay_button_content_description);
-
         // Relating to Title Bar View
         mTitleBar = v.findViewById(R.id.title_bar);
         mTitleView = v.findViewById(R.id.title_text);
@@ -707,12 +697,14 @@
             mControls.pause();
             mPlayPauseButton.setImageDrawable(
                     mResources.getDrawable(R.drawable.ic_play_circle_filled, null));
-            mPlayPauseButton.setContentDescription(mPlayDescription);
+            mPlayPauseButton.setContentDescription(
+                    mResources.getString(R.string.mcv2_play_button_desc));
         } else {
             mControls.play();
             mPlayPauseButton.setImageDrawable(
                     mResources.getDrawable(R.drawable.ic_pause_circle_filled, null));
-            mPlayPauseButton.setContentDescription(mPauseDescription);
+            mPlayPauseButton.setContentDescription(
+                    mResources.getString(R.string.mcv2_pause_button_desc));
         }
     }
 
@@ -748,7 +740,8 @@
             if (mIsStopped) {
                 mPlayPauseButton.setImageDrawable(
                         mResources.getDrawable(R.drawable.ic_play_circle_filled, null));
-                mPlayPauseButton.setContentDescription(mPlayDescription);
+                mPlayPauseButton.setContentDescription(
+                        mResources.getString(R.string.mcv2_play_button_desc));
                 mIsStopped = false;
             }
         }
@@ -897,11 +890,15 @@
             if (!mIsMute) {
                 mMuteButton.setImageDrawable(
                         mResources.getDrawable(R.drawable.ic_mute, null));
+                mMuteButton.setContentDescription(
+                        mResources.getString(R.string.mcv2_muted_button_desc));
                 mIsMute = true;
                 mController.sendCommand(COMMAND_MUTE, null, null);
             } else {
                 mMuteButton.setImageDrawable(
                         mResources.getDrawable(R.drawable.ic_unmute, null));
+                mMuteButton.setContentDescription(
+                        mResources.getString(R.string.mcv2_unmuted_button_desc));
                 mIsMute = false;
                 mController.sendCommand(COMMAND_UNMUTE, null, null);
             }
@@ -975,12 +972,16 @@
                                     MediaControlView2Impl.COMMAND_SHOW_SUBTITLE, extra, null);
                             mSubtitleButton.setImageDrawable(
                                     mResources.getDrawable(R.drawable.ic_subtitle_on, null));
+                            mSubtitleButton.setContentDescription(
+                                    mResources.getString(R.string.mcv2_cc_is_on));
                             mSubtitleIsEnabled = true;
                         } else {
                             mController.sendCommand(
                                     MediaControlView2Impl.COMMAND_HIDE_SUBTITLE, null, null);
                             mSubtitleButton.setImageDrawable(
                                     mResources.getDrawable(R.drawable.ic_subtitle_off, null));
+                            mSubtitleButton.setContentDescription(
+                                    mResources.getString(R.string.mcv2_cc_is_off));
 
                             mSubtitleIsEnabled = false;
                         }
@@ -1106,11 +1107,13 @@
         if (isPlaying()) {
             mPlayPauseButton.setImageDrawable(
                     mResources.getDrawable(R.drawable.ic_pause_circle_filled, null));
-            mPlayPauseButton.setContentDescription(mPauseDescription);
+            mPlayPauseButton.setContentDescription(
+                    mResources.getString(R.string.mcv2_pause_button_desc));
         } else {
             mPlayPauseButton.setImageDrawable(
                     mResources.getDrawable(R.drawable.ic_play_circle_filled, null));
-            mPlayPauseButton.setContentDescription(mPlayDescription);
+            mPlayPauseButton.setContentDescription(
+                    mResources.getString(R.string.mcv2_play_button_desc));
         }
     }
 
@@ -1218,19 +1221,22 @@
                     case PlaybackState.STATE_PLAYING:
                         mPlayPauseButton.setImageDrawable(
                                 mResources.getDrawable(R.drawable.ic_pause_circle_filled, null));
-                        mPlayPauseButton.setContentDescription(mPauseDescription);
+                        mPlayPauseButton.setContentDescription(
+                                mResources.getString(R.string.mcv2_pause_button_desc));
                         mInstance.removeCallbacks(mUpdateProgress);
                         mInstance.post(mUpdateProgress);
                         break;
                     case PlaybackState.STATE_PAUSED:
                         mPlayPauseButton.setImageDrawable(
                                 mResources.getDrawable(R.drawable.ic_play_circle_filled, null));
-                        mPlayPauseButton.setContentDescription(mPlayDescription);
+                        mPlayPauseButton.setContentDescription(
+                                mResources.getString(R.string.mcv2_play_button_desc));
                         break;
                     case PlaybackState.STATE_STOPPED:
                         mPlayPauseButton.setImageDrawable(
                                 mResources.getDrawable(R.drawable.ic_replay_circle_filled, null));
-                        mPlayPauseButton.setContentDescription(mReplayDescription);
+                        mPlayPauseButton.setContentDescription(
+                                mResources.getString(R.string.mcv2_replay_button_desc));
                         mIsStopped = true;
                         break;
                     default:
diff --git a/packages/MediaComponents/tests/Android.mk b/packages/MediaComponents/tests/Android.mk
index bf81efb..dddfd2a 100644
--- a/packages/MediaComponents/tests/Android.mk
+++ b/packages/MediaComponents/tests/Android.mk
@@ -20,6 +20,7 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android.test.runner.stubs \
     android.test.base.stubs \
+    mockito-target-minus-junit4 \
     junit
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/packages/MediaComponents/tests/src/com/android/media/SessionPlaylistAgentTest.java b/packages/MediaComponents/tests/src/com/android/media/SessionPlaylistAgentTest.java
index 80370ec..55a34fd 100644
--- a/packages/MediaComponents/tests/src/com/android/media/SessionPlaylistAgentTest.java
+++ b/packages/MediaComponents/tests/src/com/android/media/SessionPlaylistAgentTest.java
@@ -16,23 +16,27 @@
 
 package com.android.media;
 
+import static org.mockito.Mockito.*;
+
 import android.content.Context;
+import android.media.AudioAttributes;
 import android.media.DataSourceDesc;
 import android.media.MediaItem2;
 import android.media.MediaMetadata2;
-import android.media.MediaPlayer2;
 import android.media.MediaPlayerBase;
+import android.media.MediaPlayerBase.PlayerEventCallback;
 import android.media.MediaPlaylistAgent;
 import android.media.MediaSession2;
+import android.media.MediaSession2.OnDataSourceMissingHelper;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.test.AndroidTestCase;
-import android.util.Log;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Matchers;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -52,10 +56,11 @@
 
     private Object mWaitLock = new Object();
     private Context mContext;
-    private MediaSession2 mSession;
+    private MediaSession2Impl mSessionImpl;
     private MediaPlayerBase mPlayer;
+    private PlayerEventCallback mPlayerEventCallback;
     private SessionPlaylistAgent mAgent;
-    private MyDataSourceHelper mDataSourceHelper;
+    private OnDataSourceMissingHelper mDataSourceHelper;
     private MyPlaylistEventCallback mEventCallback;
 
     public class MyPlaylistEventCallback extends MediaPlaylistAgent.PlaylistEventCallback {
@@ -108,7 +113,7 @@
         }
     }
 
-    public class MyDataSourceHelper implements MediaSession2.OnDataSourceMissingHelper {
+    public class MyDataSourceHelper implements OnDataSourceMissingHelper {
         @Override
         public DataSourceDesc onDataSourceMissing(MediaSession2 session, MediaItem2 item) {
             if (item.getMediaId().contains("WITHOUT_DSD")) {
@@ -121,8 +126,104 @@
         }
     }
 
+    public class MockPlayer extends MediaPlayerBase {
+        @Override
+        public void play() {
+        }
+
+        @Override
+        public void prepare() {
+        }
+
+        @Override
+        public void pause() {
+        }
+
+        @Override
+        public void reset() {
+        }
+
+        @Override
+        public void skipToNext() {
+        }
+
+        @Override
+        public void seekTo(long pos) {
+        }
+
+        @Override
+        public int getPlayerState() {
+            return 0;
+        }
+
+        @Override
+        public int getBufferingState() {
+            return 0;
+        }
+
+        @Override
+        public void setAudioAttributes(AudioAttributes attributes) {
+        }
+
+        @Override
+        public AudioAttributes getAudioAttributes() {
+            return null;
+        }
+
+        @Override
+        public void setDataSource(DataSourceDesc dsd) {
+        }
+
+        @Override
+        public void setNextDataSource(DataSourceDesc dsd) {
+        }
+
+        @Override
+        public void setNextDataSources(List<DataSourceDesc> dsds) {
+        }
+
+        @Override
+        public DataSourceDesc getCurrentDataSource() {
+            return null;
+        }
+
+        @Override
+        public void loopCurrent(boolean loop) {
+        }
+
+        @Override
+        public void setPlaybackSpeed(float speed) {
+        }
+
+        @Override
+        public void setPlayerVolume(float volume) {
+        }
+
+        @Override
+        public float getPlayerVolume() {
+            return 0;
+        }
+
+        @Override
+        public void registerPlayerEventCallback(Executor e, PlayerEventCallback cb) {
+        }
+
+        @Override
+        public void unregisterPlayerEventCallback(PlayerEventCallback cb) {
+        }
+
+        @Override
+        public void close() throws Exception {
+        }
+    }
+
     @Before
     public void setUp() throws Exception {
+        mContext = getContext();
+        // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2
+        // Dexmaker is used by mockito.
+        System.setProperty("dexmaker.dexcache", mContext.getCacheDir().getPath());
+
         HandlerThread handlerThread = new HandlerThread("SessionPlaylistAgent");
         handlerThread.start();
         mHandler = new Handler(handlerThread.getLooper());
@@ -130,15 +231,16 @@
             mHandler.post(runnable);
         };
 
-        mContext = getContext();
-        mPlayer = MediaPlayer2.create();
-        mSession = new MediaSession2.Builder(mContext)
-                .setPlayer(mPlayer)
-                .setSessionCallback(mHandlerExecutor,
-                        new MediaSession2.SessionCallback(mContext) {})
-                .setId(TAG).build();
+        mPlayer = mock(MockPlayer.class);
+        doAnswer(invocation -> {
+            Object[] args = invocation.getArguments();
+            mPlayerEventCallback = (PlayerEventCallback) args[1];
+            return null;
+        }).when(mPlayer).registerPlayerEventCallback(Matchers.any(), Matchers.any());
+
+        mSessionImpl = mock(MediaSession2Impl.class);
         mDataSourceHelper = new MyDataSourceHelper();
-        mAgent = new SessionPlaylistAgent(mContext, mSession, mPlayer);
+        mAgent = new SessionPlaylistAgent(mContext, mSessionImpl, mPlayer);
         mAgent.setOnDataSourceMissingHelper(mDataSourceHelper);
         mEventCallback = new MyPlaylistEventCallback(mWaitLock);
         mAgent.registerPlaylistEventCallback(mHandlerExecutor, mEventCallback);
@@ -146,8 +248,6 @@
 
     @After
     public void tearDown() throws Exception {
-        mSession.close();
-        mPlayer.close();
         mHandler.getLooper().quitSafely();
         mHandler = null;
         mHandlerExecutor = null;
@@ -476,6 +576,33 @@
         assertEquals(SessionPlaylistAgent.END_OF_PLAYLIST, mAgent.getCurShuffledIndex());
     }
 
+    @Test
+    public void testPlaylistAfterOnCurrentDataSourceChanged() throws Exception {
+        int listSize = 2;
+        verify(mPlayer).registerPlayerEventCallback(Matchers.any(), Matchers.any());
+
+        createAndSetPlaylist(listSize);
+        assertEquals(0, mAgent.getCurShuffledIndex());
+
+        mPlayerEventCallback.onCurrentDataSourceChanged(mPlayer, null);
+        assertEquals(1, mAgent.getCurShuffledIndex());
+        mPlayerEventCallback.onCurrentDataSourceChanged(mPlayer, null);
+        assertEquals(SessionPlaylistAgent.END_OF_PLAYLIST, mAgent.getCurShuffledIndex());
+
+        mAgent.skipToNextItem();
+        assertEquals(SessionPlaylistAgent.END_OF_PLAYLIST, mAgent.getCurShuffledIndex());
+
+        mAgent.setRepeatMode(MediaPlaylistAgent.REPEAT_MODE_ONE);
+        assertEquals(SessionPlaylistAgent.END_OF_PLAYLIST, mAgent.getCurShuffledIndex());
+
+        mAgent.setRepeatMode(MediaPlaylistAgent.REPEAT_MODE_ALL);
+        assertEquals(0, mAgent.getCurShuffledIndex());
+        mPlayerEventCallback.onCurrentDataSourceChanged(mPlayer, null);
+        assertEquals(1, mAgent.getCurShuffledIndex());
+        mPlayerEventCallback.onCurrentDataSourceChanged(mPlayer, null);
+        assertEquals(0, mAgent.getCurShuffledIndex());
+    }
+
     private List<MediaItem2> createAndSetPlaylist(int listSize) throws Exception {
         List<MediaItem2> items = new ArrayList<>();
         for (int i = 0; i < listSize; ++i) {
diff --git a/services/audiopolicy/engineconfigurable/wrapper/audio_policy_criteria_conf.h b/services/audiopolicy/engineconfigurable/wrapper/audio_policy_criteria_conf.h
index 31b7e0f..e4fd176 100644
--- a/services/audiopolicy/engineconfigurable/wrapper/audio_policy_criteria_conf.h
+++ b/services/audiopolicy/engineconfigurable/wrapper/audio_policy_criteria_conf.h
@@ -62,7 +62,8 @@
     [AUDIO_POLICY_FORCE_FOR_DOCK] =                 "ForceUseForDock",
     [AUDIO_POLICY_FORCE_FOR_SYSTEM] =               "ForceUseForSystem",
     [AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO] =    "ForceUseForHdmiSystemAudio",
-    [AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND] =     "ForceUseForEncodedSurround"
+    [AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND] =     "ForceUseForEncodedSurround",
+    [AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING] =      "ForceUseForVibrateRinging"
 };
 
 
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 977a396..be7f7ec 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -154,6 +154,13 @@
         }
         mForceUse[usage] = config;
         break;
+    case AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING:
+        if (config != AUDIO_POLICY_FORCE_BT_SCO && config != AUDIO_POLICY_FORCE_NONE) {
+            ALOGW("setForceUse() invalid config %d for FOR_VIBRATE_RINGING", config);
+            return BAD_VALUE;
+        }
+        mForceUse[usage] = config;
+        break;
     default:
         ALOGW("setForceUse() invalid usage %d", usage);
         break; // TODO return BAD_VALUE?
@@ -423,8 +430,7 @@
 
         // if SCO headset is connected and we are told to use it, play ringtone over
         // speaker and BT SCO
-        if (((availableOutputDevicesType & AUDIO_DEVICE_OUT_ALL_SCO) != 0) &&
-                (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION] == AUDIO_POLICY_FORCE_BT_SCO)) {
+        if ((availableOutputDevicesType & AUDIO_DEVICE_OUT_ALL_SCO) != 0) {
             uint32_t device2 = AUDIO_DEVICE_NONE;
             device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
             if (device2 == AUDIO_DEVICE_NONE) {
@@ -433,10 +439,23 @@
             if (device2 == AUDIO_DEVICE_NONE) {
                 device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_SCO;
             }
-
-            if (device2 != AUDIO_DEVICE_NONE) {
-                device |= device2;
-                break;
+            // Use ONLY Bluetooth SCO output when ringing in vibration mode
+            if (!((mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED)
+                    && (strategy == STRATEGY_ENFORCED_AUDIBLE))) {
+                if (mForceUse[AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING]
+                        == AUDIO_POLICY_FORCE_BT_SCO) {
+                    if (device2 != AUDIO_DEVICE_NONE) {
+                        device = device2;
+                        break;
+                    }
+                }
+            }
+            // Use both Bluetooth SCO and phone default output when ringing in normal mode
+            if (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION] == AUDIO_POLICY_FORCE_BT_SCO) {
+                if (device2 != AUDIO_DEVICE_NONE) {
+                    device |= device2;
+                    break;
+                }
             }
         }
         // The second device used for sonification is the same as the device used by media strategy
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 73d92ac..92a2030 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -5141,7 +5141,8 @@
             AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
             AUDIO_DEVICE_OUT_WIRED_HEADSET |
             AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
-            AUDIO_DEVICE_OUT_USB_HEADSET)) &&
+            AUDIO_DEVICE_OUT_USB_HEADSET |
+            AUDIO_DEVICE_OUT_HEARING_AID)) &&
         ((stream_strategy == STRATEGY_SONIFICATION)
                 || (stream_strategy == STRATEGY_SONIFICATION_RESPECTFUL)
                 || (stream == AUDIO_STREAM_SYSTEM)
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index fb9b0ba..9ed6d73 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -1999,14 +1999,17 @@
 // A reference count is kept to determine when we will actually release the
 // media players.
 
-MediaPlayer* CameraService::newMediaPlayer(const char *file) {
-    MediaPlayer* mp = new MediaPlayer();
-    if (mp->setDataSource(NULL /* httpService */, file, NULL) == NO_ERROR) {
+sp<MediaPlayer> CameraService::newMediaPlayer(const char *file) {
+    sp<MediaPlayer> mp = new MediaPlayer();
+    status_t error;
+    if ((error = mp->setDataSource(NULL /* httpService */, file, NULL)) == NO_ERROR) {
         mp->setAudioStreamType(AUDIO_STREAM_ENFORCED_AUDIBLE);
-        mp->prepare();
-    } else {
+        error = mp->prepare();
+    }
+    if (error != NO_ERROR) {
         ALOGE("Failed to load CameraService sounds: %s", file);
-        delete mp;
+        mp->disconnect();
+        mp.clear();
         return nullptr;
     }
     return mp;
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 86a2b81..a7a9264 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -736,7 +736,7 @@
     std::vector<std::string> mNormalDeviceIds;
 
     // sounds
-    MediaPlayer*        newMediaPlayer(const char *file);
+    sp<MediaPlayer>     newMediaPlayer(const char *file);
 
     Mutex               mSoundLock;
     sp<MediaPlayer>     mSoundPlayer[NUM_SOUNDS];
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index 7b8d817..11fd9f6 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -201,14 +201,14 @@
         if (endpoint.get() != nullptr) {
             aaudio_result_t result = endpoint->open(request);
             if (result != AAUDIO_OK) {
-                ALOGE("openSharedEndpoint(), open failed");
+                ALOGE("%s(), open failed", __func__);
                 endpoint.clear();
             } else {
                 mSharedStreams.push_back(endpoint);
             }
         }
-        ALOGD("openSharedEndpoint(), created %p, requested device = %d, dir = %d",
-              endpoint.get(), configuration.getDeviceId(), (int)direction);
+        ALOGD("%s(), created endpoint %p, requested device = %d, dir = %d",
+              __func__, endpoint.get(), configuration.getDeviceId(), (int)direction);
         IPCThreadState::self()->restoreCallingIdentity(token);
     }
 
@@ -244,8 +244,8 @@
                 mExclusiveStreams.end());
 
         serviceEndpoint->close();
-        ALOGD("closeExclusiveEndpoint() %p for device %d",
-              serviceEndpoint.get(), serviceEndpoint->getDeviceId());
+        ALOGD("%s() %p for device %d",
+              __func__, serviceEndpoint.get(), serviceEndpoint->getDeviceId());
     }
 }
 
@@ -266,7 +266,7 @@
                 mSharedStreams.end());
 
         serviceEndpoint->close();
-        ALOGD("closeSharedEndpoint() %p for device %d",
-              serviceEndpoint.get(), serviceEndpoint->getDeviceId());
+        ALOGD("%s() %p for device %d",
+              __func__, serviceEndpoint.get(), serviceEndpoint->getDeviceId());
     }
 }
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index c708fee..ad5bb3a 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -165,7 +165,6 @@
 }
 
 aaudio_result_t AAudioService::closeStream(aaudio_handle_t streamHandle) {
-    ALOGD("closeStream(0x%08X)", streamHandle);
     // Check permission and ownership first.
     sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
     if (serviceStream.get() == nullptr) {
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 33439fc..96e621a 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -39,7 +39,7 @@
 using namespace aaudio;   // TODO just import names needed
 
 AAudioServiceEndpoint::~AAudioServiceEndpoint() {
-    ALOGD("AAudioServiceEndpoint::~AAudioServiceEndpoint() destroying endpoint %p", this);
+    ALOGD("%s(%p) destroyed", __func__, this);
 }
 
 std::string AAudioServiceEndpoint::dump() const {
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index db01c88..52990da 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -98,8 +98,8 @@
             .flags = AUDIO_FLAG_LOW_LATENCY,
             .tags = ""
     };
-    ALOGV("open() MMAP attributes.usage = %d, content_type = %d, source = %d",
-          attributes.usage, attributes.content_type, attributes.source);
+    ALOGD("%s(%p) MMAP attributes.usage = %d, content_type = %d, source = %d",
+          __func__, this, attributes.usage, attributes.content_type, attributes.source);
 
     mMmapClient.clientUid = request.getUserId();
     mMmapClient.clientPid = request.getProcessId();
@@ -135,7 +135,7 @@
         mHardwareTimeOffsetNanos = INPUT_ESTIMATED_HARDWARE_OFFSET_NANOS; // frames at ADC earlier
 
     } else {
-        ALOGE("openMmapStream - invalid direction = %d", direction);
+        ALOGE("%s() invalid direction = %d", __func__, direction);
         return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
     }
 
@@ -157,20 +157,20 @@
                                                           this, // callback
                                                           mMmapStream,
                                                           &mPortHandle);
-    ALOGD("open() mMapClient.uid = %d, pid = %d => portHandle = %d\n",
-          mMmapClient.clientUid,  mMmapClient.clientPid, mPortHandle);
+    ALOGD("%s() mMapClient.uid = %d, pid = %d => portHandle = %d\n",
+          __func__, mMmapClient.clientUid,  mMmapClient.clientPid, mPortHandle);
     if (status != OK) {
-        ALOGE("openMmapStream returned status %d", status);
+        ALOGE("%s() openMmapStream() returned status %d",  __func__, status);
         return AAUDIO_ERROR_UNAVAILABLE;
     }
 
     if (deviceId == AAUDIO_UNSPECIFIED) {
-        ALOGW("open() - openMmapStream() failed to set deviceId");
+        ALOGW("%s() openMmapStream() failed to set deviceId", __func__);
     }
     setDeviceId(deviceId);
 
     if (sessionId == AUDIO_SESSION_ALLOCATE) {
-        ALOGW("open() - openMmapStream() failed to set sessionId");
+        ALOGW("%s() - openMmapStream() failed to set sessionId", __func__);
     }
 
     aaudio_session_id_t actualSessionId =
@@ -178,7 +178,7 @@
             ? AAUDIO_SESSION_ID_NONE
             : (aaudio_session_id_t) sessionId;
     setSessionId(actualSessionId);
-    ALOGD("open() deviceId = %d, sessionId = %d", getDeviceId(), getSessionId());
+    ALOGD("%s() deviceId = %d, sessionId = %d", __func__, getDeviceId(), getSessionId());
 
     // Create MMAP/NOIRQ buffer.
     int32_t minSizeFrames = getBufferCapacity();
@@ -187,14 +187,14 @@
     }
     status = mMmapStream->createMmapBuffer(minSizeFrames, &mMmapBufferinfo);
     if (status != OK) {
-        ALOGE("open() - createMmapBuffer() failed with status %d %s",
-              status, strerror(-status));
+        ALOGE("%s() - createMmapBuffer() failed with status %d %s",
+              __func__, status, strerror(-status));
         result = AAUDIO_ERROR_UNAVAILABLE;
         goto error;
     } else {
-        ALOGD("createMmapBuffer status = %d, buffer_size = %d, burst_size %d"
+        ALOGD("%s() createMmapBuffer() returned = %d, buffer_size = %d, burst_size %d"
                       ", Sharable FD: %s",
-              status,
+              __func__, status,
               abs(mMmapBufferinfo.buffer_size_frames),
               mMmapBufferinfo.burst_size_frames,
               mMmapBufferinfo.buffer_size_frames < 0 ? "Yes" : "No");
@@ -214,7 +214,7 @@
             // Fallback is handled by caller but indicate what is possible in case
             // this is used in the future
             setSharingMode(AAUDIO_SHARING_MODE_SHARED);
-            ALOGW("open() - exclusive FD cannot be used by client");
+            ALOGW("%s() - exclusive FD cannot be used by client", __func__);
             result = AAUDIO_ERROR_UNAVAILABLE;
             goto error;
         }
@@ -229,7 +229,7 @@
     // Assume that AudioFlinger will close the original shared_memory_fd.
     mAudioDataFileDescriptor.reset(dup(mMmapBufferinfo.shared_memory_fd));
     if (mAudioDataFileDescriptor.get() == -1) {
-        ALOGE("open() - could not dup shared_memory_fd");
+        ALOGE("%s() - could not dup shared_memory_fd", __func__);
         result = AAUDIO_ERROR_INTERNAL;
         goto error;
     }
@@ -247,12 +247,12 @@
         burstMicros = mFramesPerBurst * static_cast<int64_t>(1000000) / getSampleRate();
     } while (burstMicros < burstMinMicros);
 
-    ALOGD("open() original burst = %d, minMicros = %d, to burst = %d\n",
-          mMmapBufferinfo.burst_size_frames, burstMinMicros, mFramesPerBurst);
+    ALOGD("%s() original burst = %d, minMicros = %d, to burst = %d\n",
+          __func__, mMmapBufferinfo.burst_size_frames, burstMinMicros, mFramesPerBurst);
 
-    ALOGD("open() actual rate = %d, channels = %d"
+    ALOGD("%s() actual rate = %d, channels = %d"
           ", deviceId = %d, capacity = %d\n",
-          getSampleRate(), getSamplesPerFrame(), deviceId, getBufferCapacity());
+          __func__, getSampleRate(), getSamplesPerFrame(), deviceId, getBufferCapacity());
 
     return result;
 
@@ -262,9 +262,8 @@
 }
 
 aaudio_result_t AAudioServiceEndpointMMAP::close() {
-
     if (mMmapStream != 0) {
-        ALOGD("close() clear() endpoint");
+        ALOGD("%s() clear() endpoint", __func__);
         // Needs to be explicitly cleared or CTS will fail but it is not clear why.
         mMmapStream.clear();
         // Apparently the above close is asynchronous. An attempt to open a new device
@@ -299,20 +298,18 @@
 aaudio_result_t AAudioServiceEndpointMMAP::startClient(const android::AudioClient& client,
                                                        audio_port_handle_t *clientHandle) {
     if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
-    ALOGD("startClient(%p(uid=%d, pid=%d))",
-          &client, client.clientUid, client.clientPid);
+    ALOGV("%s(%p(uid=%d, pid=%d))", __func__, &client, client.clientUid, client.clientPid);
     audio_port_handle_t originalHandle =  *clientHandle;
     status_t status = mMmapStream->start(client, clientHandle);
     aaudio_result_t result = AAudioConvert_androidToAAudioResult(status);
-    ALOGD("startClient() , %d => %d returns %d",
-          originalHandle, *clientHandle, result);
+    ALOGV("%s() , %d => %d returns %d", __func__, originalHandle, *clientHandle, result);
     return result;
 }
 
 aaudio_result_t AAudioServiceEndpointMMAP::stopClient(audio_port_handle_t clientHandle) {
     if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
     aaudio_result_t result = AAudioConvert_androidToAAudioResult(mMmapStream->stop(clientHandle));
-    ALOGD("stopClient(%d) returns %d", clientHandle, result);
+    ALOGV("%s(%d) returns %d", __func__, clientHandle, result);
     return result;
 }
 
@@ -324,13 +321,13 @@
         return AAUDIO_ERROR_NULL;
     }
     status_t status = mMmapStream->getMmapPosition(&position);
-    ALOGV("getFreeRunningPosition() status= %d, pos = %d, nanos = %lld\n",
-          status, position.position_frames, (long long) position.time_nanoseconds);
+    ALOGV("%s() status= %d, pos = %d, nanos = %lld\n",
+          __func__, status, position.position_frames, (long long) position.time_nanoseconds);
     aaudio_result_t result = AAudioConvert_androidToAAudioResult(status);
     if (result == AAUDIO_ERROR_UNAVAILABLE) {
-        ALOGW("sendCurrentTimestamp(): getMmapPosition() has no position data available");
+        ALOGW("%s(): getMmapPosition() has no position data available", __func__);
     } else if (result != AAUDIO_OK) {
-        ALOGE("sendCurrentTimestamp(): getMmapPosition() returned status %d", status);
+        ALOGE("%s(): getMmapPosition() returned status %d", __func__, status);
     } else {
         // Convert 32-bit position to 64-bit position.
         mFramesTransferred.update32(position.position_frames);
@@ -347,15 +344,16 @@
 
 
 void AAudioServiceEndpointMMAP::onTearDown() {
-    ALOGD("onTearDown() called");
+    ALOGD("%s(%p) called", __func__, this);
     disconnectRegisteredStreams();
 };
 
 void AAudioServiceEndpointMMAP::onVolumeChanged(audio_channel_mask_t channels,
                                               android::Vector<float> values) {
-    // TODO do we really need a different volume for each channel?
+    // TODO Do we really need a different volume for each channel?
+    // We get called with an array filled with a single value!
     float volume = values[0];
-    ALOGD("onVolumeChanged() volume[0] = %f", volume);
+    ALOGD("%s(%p) volume[0] = %f", __func__, this, volume);
     std::lock_guard<std::mutex> lock(mLockStreams);
     for(const auto stream : mRegisteredStreams) {
         stream->onVolumeChanged(volume);
@@ -363,8 +361,7 @@
 };
 
 void AAudioServiceEndpointMMAP::onRoutingChanged(audio_port_handle_t deviceId) {
-    ALOGD("onRoutingChanged() called with dev %d, old = %d",
-          deviceId, getDeviceId());
+    ALOGD("%s(%p) called with dev %d, old = %d", __func__, this, deviceId, getDeviceId());
     if (getDeviceId() != AUDIO_PORT_HANDLE_NONE  && getDeviceId() != deviceId) {
         disconnectRegisteredStreams();
     }
diff --git a/services/oboeservice/AAudioServiceEndpointPlay.cpp b/services/oboeservice/AAudioServiceEndpointPlay.cpp
index 2601f3f..a274466 100644
--- a/services/oboeservice/AAudioServiceEndpointPlay.cpp
+++ b/services/oboeservice/AAudioServiceEndpointPlay.cpp
@@ -43,10 +43,12 @@
 
 AAudioServiceEndpointPlay::AAudioServiceEndpointPlay(AAudioService &audioService)
         : mStreamInternalPlay(audioService, true) {
+    ALOGD("%s(%p) created", __func__, this);
     mStreamInternal = &mStreamInternalPlay;
 }
 
 AAudioServiceEndpointPlay::~AAudioServiceEndpointPlay() {
+    ALOGD("%s(%p) destroyed", __func__, this);
 }
 
 aaudio_result_t AAudioServiceEndpointPlay::open(const aaudio::AAudioStreamRequest &request) {
@@ -68,6 +70,7 @@
 
 // Mix data from each application stream and write result to the shared MMAP stream.
 void *AAudioServiceEndpointPlay::callbackLoop() {
+    ALOGD("%s() entering >>>>>>>>>>>>>>> MIXER", __func__);
     aaudio_result_t result = AAUDIO_OK;
     int64_t timeoutNanos = getStreamInternal()->calculateReasonableTimeout();
 
@@ -152,5 +155,7 @@
         }
     }
 
+    ALOGD("%s() exiting, enabled = %d, state = %d, result = %d <<<<<<<<<<<<< MIXER",
+          __func__, mCallbackEnabled.load(), getStreamInternal()->getState(), result);
     return NULL; // TODO review
 }
diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp
index 584efe5..f08a52f 100644
--- a/services/oboeservice/AAudioServiceEndpointShared.cpp
+++ b/services/oboeservice/AAudioServiceEndpointShared.cpp
@@ -89,18 +89,22 @@
 }
 
 // Glue between C and C++ callbacks.
-static void *aaudio_endpoint_thread_proc(void *context) {
-    AAudioServiceEndpointShared *endpoint = (AAudioServiceEndpointShared *) context;
-    if (endpoint != NULL) {
-        void *result = endpoint->callbackLoop();
-        // Close now so that the HW resource is freed and we can open a new device.
-        if (!endpoint->isConnected()) {
-            endpoint->close();
-        }
-        return result;
-    } else {
-        return NULL;
+static void *aaudio_endpoint_thread_proc(void *arg) {
+    assert(arg != nullptr);
+
+    // The caller passed in a smart pointer to prevent the endpoint from getting deleted
+    // while the thread was launching.
+    sp<AAudioServiceEndpointShared> *endpointForThread =
+            static_cast<sp<AAudioServiceEndpointShared> *>(arg);
+    sp<AAudioServiceEndpointShared> endpoint = *endpointForThread;
+    delete endpointForThread; // Just use scoped smart pointer. Don't need this anymore.
+    void *result = endpoint->callbackLoop();
+    // Close now so that the HW resource is freed and we can open a new device.
+    if (!endpoint->isConnected()) {
+        endpoint->close();
     }
+
+    return result;
 }
 
 aaudio_result_t aaudio::AAudioServiceEndpointShared::startSharingThread_l() {
@@ -109,7 +113,16 @@
                           * AAUDIO_NANOS_PER_SECOND
                           / getSampleRate();
     mCallbackEnabled.store(true);
-    return getStreamInternal()->createThread(periodNanos, aaudio_endpoint_thread_proc, this);
+    // Pass a smart pointer so the thread can hold a reference.
+    sp<AAudioServiceEndpointShared> *endpointForThread = new sp<AAudioServiceEndpointShared>(this);
+    aaudio_result_t result = getStreamInternal()->createThread(periodNanos,
+                                                               aaudio_endpoint_thread_proc,
+                                                               endpointForThread);
+    if (result != AAUDIO_OK) {
+        // The thread can't delete it so we have to do it here.
+        delete endpointForThread;
+    }
+    return result;
 }
 
 aaudio_result_t aaudio::AAudioServiceEndpointShared::stopSharingThread() {
diff --git a/services/oboeservice/AAudioServiceEndpointShared.h b/services/oboeservice/AAudioServiceEndpointShared.h
index 74cd817..227250c 100644
--- a/services/oboeservice/AAudioServiceEndpointShared.h
+++ b/services/oboeservice/AAudioServiceEndpointShared.h
@@ -54,12 +54,13 @@
 
     virtual void            *callbackLoop() = 0;
 
+
+protected:
+
     AudioStreamInternal *getStreamInternal() const {
         return mStreamInternal;
     };
 
-protected:
-
     aaudio_result_t          startSharingThread_l();
 
     aaudio_result_t          stopSharingThread();
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index e8c9e41..18f14ee 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -93,7 +93,7 @@
     {
         std::lock_guard<std::mutex> lock(mUpMessageQueueLock);
         if (mUpMessageQueue != nullptr) {
-            ALOGE("open() called twice");
+            ALOGE("%s() called twice", __func__);
             return AAUDIO_ERROR_INVALID_STATE;
         }
 
@@ -108,7 +108,7 @@
                                                          request,
                                                          sharingMode);
         if (mServiceEndpoint == nullptr) {
-            ALOGE("open() openEndpoint() failed");
+            ALOGE("%s() openEndpoint() failed", __func__);
             result = AAUDIO_ERROR_UNAVAILABLE;
             goto error;
         }
@@ -167,7 +167,7 @@
     }
 
     if (mServiceEndpoint == nullptr) {
-        ALOGE("start() missing endpoint");
+        ALOGE("%s() missing endpoint", __func__);
         result = AAUDIO_ERROR_INVALID_STATE;
         goto error;
     }
@@ -201,7 +201,7 @@
         return result;
     }
     if (mServiceEndpoint == nullptr) {
-        ALOGE("pause() missing endpoint");
+        ALOGE("%s() missing endpoint", __func__);
         return AAUDIO_ERROR_INVALID_STATE;
     }
 
@@ -217,7 +217,7 @@
 
     result = mServiceEndpoint->stopStream(this, mClientHandle);
     if (result != AAUDIO_OK) {
-        ALOGE("pause() mServiceEndpoint returned %d", result);
+        ALOGE("%s() mServiceEndpoint returned %d, %s", __func__, result, getTypeText());
         disconnect(); // TODO should we return or pause Base first?
     }
 
@@ -233,7 +233,7 @@
     }
 
     if (mServiceEndpoint == nullptr) {
-        ALOGE("stop() missing endpoint");
+        ALOGE("%s() missing endpoint", __func__);
         return AAUDIO_ERROR_INVALID_STATE;
     }
 
@@ -251,7 +251,7 @@
     // TODO wait for data to be played out
     result = mServiceEndpoint->stopStream(this, mClientHandle);
     if (result != AAUDIO_OK) {
-        ALOGE("stop() mServiceEndpoint returned %d", result);
+        ALOGE("%s() mServiceEndpoint returned %d, %s", __func__, result, getTypeText());
         disconnect();
         // TODO what to do with result here?
     }
@@ -284,7 +284,7 @@
 
 // implement Runnable, periodically send timestamps to client
 void AAudioServiceStreamBase::run() {
-    ALOGD("run() entering ----------------");
+    ALOGD("%s() %s entering >>>>>>>>>>>>>> TIMESTAMPS", __func__, getTypeText());
     TimestampScheduler timestampScheduler;
     timestampScheduler.setBurstPeriod(mFramesPerBurst, getSampleRate());
     timestampScheduler.start(AudioClock::getNanoseconds());
@@ -302,7 +302,7 @@
             AudioClock::sleepUntilNanoTime(nextTime);
         }
     }
-    ALOGD("run() exiting ----------------");
+    ALOGD("%s() %s exiting <<<<<<<<<<<<<< TIMESTAMPS", __func__, getTypeText());
 }
 
 void AAudioServiceStreamBase::disconnect() {
@@ -333,12 +333,12 @@
 aaudio_result_t AAudioServiceStreamBase::writeUpMessageQueue(AAudioServiceMessage *command) {
     std::lock_guard<std::mutex> lock(mUpMessageQueueLock);
     if (mUpMessageQueue == nullptr) {
-        ALOGE("writeUpMessageQueue(): mUpMessageQueue null! - stream not open");
+        ALOGE("%s(): mUpMessageQueue null! - stream not open", __func__);
         return AAUDIO_ERROR_NULL;
     }
     int32_t count = mUpMessageQueue->getFifoBuffer()->write(command, 1);
     if (count != 1) {
-        ALOGE("writeUpMessageQueue(): Queue full. Did client die?");
+        ALOGE("%s(): Queue full. Did client die? %s", __func__, getTypeText());
         return AAUDIO_ERROR_WOULD_BLOCK;
     } else {
         return AAUDIO_OK;
@@ -355,7 +355,7 @@
     aaudio_result_t result = getFreeRunningPosition(&command.timestamp.position,
                                                     &command.timestamp.timestamp);
     if (result == AAUDIO_OK) {
-        ALOGV("sendCurrentTimestamp() SERVICE  %8lld at %lld",
+        ALOGV("%s() SERVICE  %8lld at %lld", __func__,
               (long long) command.timestamp.position,
               (long long) command.timestamp.timestamp);
         command.what = AAudioServiceMessage::code::TIMESTAMP_SERVICE;
@@ -366,7 +366,7 @@
             result = getHardwareTimestamp(&command.timestamp.position,
                                           &command.timestamp.timestamp);
             if (result == AAUDIO_OK) {
-                ALOGV("sendCurrentTimestamp() HARDWARE %8lld at %lld",
+                ALOGV("%s() HARDWARE %8lld at %lld", __func__,
                       (long long) command.timestamp.position,
                       (long long) command.timestamp.timestamp);
                 command.what = AAudioServiceMessage::code::TIMESTAMP_HARDWARE;
@@ -389,7 +389,7 @@
     {
         std::lock_guard<std::mutex> lock(mUpMessageQueueLock);
         if (mUpMessageQueue == nullptr) {
-            ALOGE("getDescription(): mUpMessageQueue null! - stream not open");
+            ALOGE("%s(): mUpMessageQueue null! - stream not open", __func__);
             return AAUDIO_ERROR_NULL;
         }
         // Gather information on the message queue.
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index 5f5bb98..3720596 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -55,7 +55,7 @@
     , public Runnable  {
 
 public:
-    AAudioServiceStreamBase(android::AAudioService &aAudioService);
+    explicit AAudioServiceStreamBase(android::AAudioService &aAudioService);
 
     virtual ~AAudioServiceStreamBase();
 
@@ -219,6 +219,8 @@
         mCloseNeeded.store(needed);
     }
 
+    virtual const char *getTypeText() const { return "Base"; }
+
 protected:
 
     /**
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index e2415d0..1509f7d 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -69,9 +69,7 @@
 
     aaudio_result_t close() override;
 
-    /**
-     * Send a MMAP/NOIRQ buffer timestamp to the client.
-     */
+    const char *getTypeText() const override { return "MMAP"; }
 
 protected:
 
diff --git a/services/oboeservice/AAudioServiceStreamShared.h b/services/oboeservice/AAudioServiceStreamShared.h
index 3b12e61..61769b5 100644
--- a/services/oboeservice/AAudioServiceStreamShared.h
+++ b/services/oboeservice/AAudioServiceStreamShared.h
@@ -43,7 +43,7 @@
 class AAudioServiceStreamShared : public AAudioServiceStreamBase {
 
 public:
-    AAudioServiceStreamShared(android::AAudioService &aAudioService);
+    explicit AAudioServiceStreamShared(android::AAudioService &aAudioService);
     virtual ~AAudioServiceStreamShared() = default;
 
     static std::string dumpHeader();
@@ -87,6 +87,8 @@
         return mXRunCount.load();
     }
 
+    const char *getTypeText() const override { return "Shared"; }
+
 protected:
 
     aaudio_result_t getAudioDataDescription(AudioEndpointParcelable &parcelable) override;
diff --git a/services/oboeservice/AAudioThread.h b/services/oboeservice/AAudioThread.h
index ffc9b7b..dcce68a 100644
--- a/services/oboeservice/AAudioThread.h
+++ b/services/oboeservice/AAudioThread.h
@@ -44,7 +44,7 @@
 public:
     AAudioThread();
 
-    AAudioThread(const char *prefix);
+    explicit AAudioThread(const char *prefix);
 
     virtual ~AAudioThread() = default;