Integration of the aptX and aptX-HD codecs for A2DP source

Each of the codecs can be used if the corresponding encoding
shared library is installed on the device:
 - aptX: libaptX.so
 - aptX-HD: libaptXHD.so

Test: A2DP streaming to aptX and aptX-HD headsets
Bug: 30958229
Change-Id: I1ca36da653721576f5a5b6bdac186b9ea2790c85
diff --git a/stack/Android.mk b/stack/Android.mk
index 64a5ca2..0b1615f 100644
--- a/stack/Android.mk
+++ b/stack/Android.mk
@@ -39,6 +39,10 @@
     ./a2dp/a2dp_sbc_encoder.cc \
     ./a2dp/a2dp_sbc_up_sample.cc \
     ./a2dp/a2dp_vendor.cc \
+    ./a2dp/a2dp_vendor_aptx.cc \
+    ./a2dp/a2dp_vendor_aptx_hd.cc \
+    ./a2dp/a2dp_vendor_aptx_encoder.cc \
+    ./a2dp/a2dp_vendor_aptx_hd_encoder.cc \
     ./avct/avct_api.cc \
     ./avct/avct_bcb_act.cc \
     ./avct/avct_ccb.cc \
diff --git a/stack/a2dp/a2dp_codec_config.cc b/stack/a2dp/a2dp_codec_config.cc
index 73e1f67..a0182c3 100644
--- a/stack/a2dp/a2dp_codec_config.cc
+++ b/stack/a2dp/a2dp_codec_config.cc
@@ -26,6 +26,8 @@
 
 #include "a2dp_sbc.h"
 #include "a2dp_vendor.h"
+#include "a2dp_vendor_aptx.h"
+#include "a2dp_vendor_aptx_hd.h"
 #include "osi/include/log.h"
 
 /* The Media Type offset within the codec info byte array */
@@ -82,6 +84,12 @@
     case BTAV_A2DP_CODEC_INDEX_SINK_SBC:
       codec_config = new A2dpCodecConfigSbcSink();
       break;
+    case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX:
+      codec_config = new A2dpCodecConfigAptx();
+      break;
+    case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD:
+      codec_config = new A2dpCodecConfigAptxHd();
+      break;
     case BTAV_A2DP_CODEC_INDEX_MAX:
       break;
   }
diff --git a/stack/a2dp/a2dp_vendor.cc b/stack/a2dp/a2dp_vendor.cc
index 387f445..7224140 100644
--- a/stack/a2dp/a2dp_vendor.cc
+++ b/stack/a2dp/a2dp_vendor.cc
@@ -21,13 +21,27 @@
 #define LOG_TAG "a2dp_vendor"
 
 #include "a2dp_vendor.h"
+#include "a2dp_vendor_aptx.h"
+#include "a2dp_vendor_aptx_hd.h"
 #include "bt_target.h"
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
 
-bool A2DP_IsVendorSourceCodecValid(UNUSED_ATTR const uint8_t* p_codec_info) {
-  // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
-  // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+bool A2DP_IsVendorSourceCodecValid(const uint8_t* p_codec_info) {
+  uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+  uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+  // Check for aptX
+  if (vendor_id == A2DP_APTX_VENDOR_ID &&
+      codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+    return A2DP_IsVendorSourceCodecValidAptx(p_codec_info);
+  }
+
+  // Check for aptX-HD
+  if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+      codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+    return A2DP_IsVendorSourceCodecValidAptxHd(p_codec_info);
+  }
 
   // Add checks based on <vendor_id, codec_id>
 
@@ -55,9 +69,21 @@
   return false;
 }
 
-bool A2DP_IsVendorPeerSinkCodecValid(UNUSED_ATTR const uint8_t* p_codec_info) {
-  // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
-  // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+bool A2DP_IsVendorPeerSinkCodecValid(const uint8_t* p_codec_info) {
+  uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+  uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+  // Check for aptX
+  if (vendor_id == A2DP_APTX_VENDOR_ID &&
+      codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+    return A2DP_IsVendorPeerSinkCodecValidAptx(p_codec_info);
+  }
+
+  // Check for aptX-HD
+  if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+      codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+    return A2DP_IsVendorPeerSinkCodecValidAptxHd(p_codec_info);
+  }
 
   // Add checks based on <vendor_id, codec_id>
 
@@ -114,10 +140,24 @@
   return codec_id;
 }
 
-bool A2DP_VendorUsesRtpHeader(UNUSED_ATTR bool content_protection_enabled,
-                              UNUSED_ATTR const uint8_t* p_codec_info) {
-  // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
-  // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+bool A2DP_VendorUsesRtpHeader(bool content_protection_enabled,
+                              const uint8_t* p_codec_info) {
+  uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+  uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+  // Check for aptX
+  if (vendor_id == A2DP_APTX_VENDOR_ID &&
+      codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorUsesRtpHeaderAptx(content_protection_enabled,
+                                        p_codec_info);
+  }
+
+  // Check for aptX-HD
+  if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+      codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorUsesRtpHeaderAptxHd(content_protection_enabled,
+                                          p_codec_info);
+  }
 
   // Add checks based on <content_protection_enabled, vendor_id, codec_id>
 
@@ -125,8 +165,20 @@
 }
 
 const char* A2DP_VendorCodecName(UNUSED_ATTR const uint8_t* p_codec_info) {
-  // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_src_config);
-  // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_src_config);
+  uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+  uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+  // Check for aptX
+  if (vendor_id == A2DP_APTX_VENDOR_ID &&
+      codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorCodecNameAptx(p_codec_info);
+  }
+
+  // Check for aptX-HD
+  if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+      codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorCodecNameAptxHd(p_codec_info);
+  }
 
   // Add checks based on <vendor_id, codec_id>
 
@@ -148,10 +200,24 @@
   uint32_t vendor_id_b = A2DP_VendorCodecGetVendorId(p_codec_info_b);
   uint16_t codec_id_b = A2DP_VendorCodecGetCodecId(p_codec_info_b);
 
+  if (vendor_id_a != vendor_id_b || codec_id_a != codec_id_b) return false;
+
+  // Check for aptX
+  if (vendor_id_a == A2DP_APTX_VENDOR_ID &&
+      codec_id_a == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorCodecTypeEqualsAptx(p_codec_info_a, p_codec_info_b);
+  }
+
+  // Check for aptX-HD
+  if (vendor_id_a == A2DP_APTX_HD_VENDOR_ID &&
+      codec_id_a == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorCodecTypeEqualsAptxHd(p_codec_info_a, p_codec_info_b);
+  }
+
   // OPTIONAL: Add extra vendor-specific checks based on the
   // vendor-specific data stored in "p_codec_info_a" and "p_codec_info_b".
 
-  return (vendor_id_a == vendor_id_b) && (codec_id_a == codec_id_b);
+  return true;
 }
 
 bool A2DP_VendorCodecEquals(const uint8_t* p_codec_info_a,
@@ -171,33 +237,81 @@
 
   if ((vendor_id_a != vendor_id_b) || (codec_id_a != codec_id_b)) return false;
 
+  // Check for aptX
+  if (vendor_id_a == A2DP_APTX_VENDOR_ID &&
+      codec_id_a == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorCodecEqualsAptx(p_codec_info_a, p_codec_info_b);
+  }
+
+  // Check for aptX-HD
+  if (vendor_id_a == A2DP_APTX_HD_VENDOR_ID &&
+      codec_id_a == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorCodecEqualsAptxHd(p_codec_info_a, p_codec_info_b);
+  }
+
   // Add extra vendor-specific checks based on the
   // vendor-specific data stored in "p_codec_info_a" and "p_codec_info_b".
 
   return false;
 }
 
-int A2DP_VendorGetTrackSampleRate(UNUSED_ATTR const uint8_t* p_codec_info) {
-  // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
-  // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+int A2DP_VendorGetTrackSampleRate(const uint8_t* p_codec_info) {
+  uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+  uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+  // Check for aptX
+  if (vendor_id == A2DP_APTX_VENDOR_ID &&
+      codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorGetTrackSampleRateAptx(p_codec_info);
+  }
+
+  // Check for aptX-HD
+  if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+      codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorGetTrackSampleRateAptxHd(p_codec_info);
+  }
 
   // Add checks based on <vendor_id, codec_id>
 
   return -1;
 }
 
-int A2DP_VendorGetTrackBitsPerSample(UNUSED_ATTR const uint8_t* p_codec_info) {
-  // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
-  // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+int A2DP_VendorGetTrackBitsPerSample(const uint8_t* p_codec_info) {
+  uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+  uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+  // Check for aptX
+  if (vendor_id == A2DP_APTX_VENDOR_ID &&
+      codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorGetTrackBitsPerSampleAptx(p_codec_info);
+  }
+
+  // Check for aptX-HD
+  if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+      codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorGetTrackBitsPerSampleAptxHd(p_codec_info);
+  }
 
   // Add checks based on <vendor_id, codec_id>
 
   return -1;
 }
 
-int A2DP_VendorGetTrackChannelCount(UNUSED_ATTR const uint8_t* p_codec_info) {
-  // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
-  // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+int A2DP_VendorGetTrackChannelCount(const uint8_t* p_codec_info) {
+  uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+  uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+  // Check for aptX
+  if (vendor_id == A2DP_APTX_VENDOR_ID &&
+      codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorGetTrackChannelCountAptx(p_codec_info);
+  }
+
+  // Check for aptX-HD
+  if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+      codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorGetTrackChannelCountAptxHd(p_codec_info);
+  }
 
   // Add checks based on <vendor_id, codec_id>
 
@@ -227,22 +341,48 @@
   return -1;
 }
 
-bool A2DP_VendorGetPacketTimestamp(UNUSED_ATTR const uint8_t* p_codec_info,
-                                   UNUSED_ATTR const uint8_t* p_data,
-                                   UNUSED_ATTR uint32_t* p_timestamp) {
-  // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
-  // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+bool A2DP_VendorGetPacketTimestamp(const uint8_t* p_codec_info,
+                                   const uint8_t* p_data,
+                                   uint32_t* p_timestamp) {
+  uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+  uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+  // Check for aptX
+  if (vendor_id == A2DP_APTX_VENDOR_ID &&
+      codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorGetPacketTimestampAptx(p_codec_info, p_data, p_timestamp);
+  }
+
+  // Check for aptX-HD
+  if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+      codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorGetPacketTimestampAptxHd(p_codec_info, p_data,
+                                               p_timestamp);
+  }
 
   // Add checks based on <vendor_id, codec_id>
 
   return false;
 }
 
-bool A2DP_VendorBuildCodecHeader(UNUSED_ATTR const uint8_t* p_codec_info,
-                                 UNUSED_ATTR BT_HDR* p_buf,
-                                 UNUSED_ATTR uint16_t frames_per_packet) {
-  // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
-  // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+bool A2DP_VendorBuildCodecHeader(const uint8_t* p_codec_info, BT_HDR* p_buf,
+                                 uint16_t frames_per_packet) {
+  uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+  uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+  // Check for aptX
+  if (vendor_id == A2DP_APTX_VENDOR_ID &&
+      codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorBuildCodecHeaderAptx(p_codec_info, p_buf,
+                                           frames_per_packet);
+  }
+
+  // Check for aptX-HD
+  if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+      codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorBuildCodecHeaderAptxHd(p_codec_info, p_buf,
+                                             frames_per_packet);
+  }
 
   // Add checks based on <vendor_id, codec_id>
 
@@ -251,8 +391,20 @@
 
 const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterface(
     const uint8_t* p_codec_info) {
-  // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
-  // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+  uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+  uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+  // Check for aptX
+  if (vendor_id == A2DP_APTX_VENDOR_ID &&
+      codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorGetEncoderInterfaceAptx(p_codec_info);
+  }
+
+  // Check for aptX-HD
+  if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+      codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorGetEncoderInterfaceAptxHd(p_codec_info);
+  }
 
   // Add checks based on <vendor_id, codec_id>
 
@@ -260,8 +412,20 @@
 }
 
 bool A2DP_VendorAdjustCodec(uint8_t* p_codec_info) {
-  // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
-  // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+  uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+  uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+  // Check for aptX
+  if (vendor_id == A2DP_APTX_VENDOR_ID &&
+      codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorAdjustCodecAptx(p_codec_info);
+  }
+
+  // Check for aptX-HD
+  if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+      codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorAdjustCodecAptxHd(p_codec_info);
+  }
 
   // Add checks based on <vendor_id, codec_id>
 
@@ -269,9 +433,21 @@
 }
 
 btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndex(
-    UNUSED_ATTR const uint8_t* p_codec_info) {
-  // uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
-  // uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+    const uint8_t* p_codec_info) {
+  uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+  uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+
+  // Check for aptX
+  if (vendor_id == A2DP_APTX_VENDOR_ID &&
+      codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorSourceCodecIndexAptx(p_codec_info);
+  }
+
+  // Check for aptX-HD
+  if (vendor_id == A2DP_APTX_HD_VENDOR_ID &&
+      codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+    return A2DP_VendorSourceCodecIndexAptxHd(p_codec_info);
+  }
 
   // Add checks based on <vendor_id, codec_id>
 
@@ -284,6 +460,10 @@
     case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC:
     case BTAV_A2DP_CODEC_INDEX_SINK_SBC:
       break;  // This is not a vendor-specific codec
+    case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX:
+      return A2DP_VendorCodecIndexStrAptx();
+    case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD:
+      return A2DP_VendorCodecIndexStrAptxHd();
     // Add a switch statement for each vendor-specific codec
     case BTAV_A2DP_CODEC_INDEX_MAX:
       break;
@@ -299,6 +479,10 @@
     case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC:
     case BTAV_A2DP_CODEC_INDEX_SINK_SBC:
       break;  // This is not a vendor-specific codec
+    case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX:
+      return A2DP_VendorInitCodecConfigAptx(p_cfg);
+    case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD:
+      return A2DP_VendorInitCodecConfigAptxHd(p_cfg);
     // Add a switch statement for each vendor-specific codec
     case BTAV_A2DP_CODEC_INDEX_MAX:
       break;
diff --git a/stack/a2dp/a2dp_vendor_aptx.cc b/stack/a2dp/a2dp_vendor_aptx.cc
new file mode 100644
index 0000000..0a74529
--- /dev/null
+++ b/stack/a2dp/a2dp_vendor_aptx.cc
@@ -0,0 +1,843 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/******************************************************************************
+ *
+ *  Utility functions to help build and parse the aptX Codec Information
+ *  Element and Media Payload.
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "a2dp_vendor_aptx"
+
+#include "bt_target.h"
+
+#include "a2dp_vendor_aptx.h"
+
+#include <string.h>
+
+#include <base/logging.h>
+#include "a2dp_vendor.h"
+#include "a2dp_vendor_aptx_encoder.h"
+#include "bt_utils.h"
+#include "osi/include/log.h"
+#include "osi/include/osi.h"
+
+// data type for the aptX Codec Information Element */
+typedef struct {
+  uint32_t vendorId;
+  uint16_t codecId;    /* Codec ID for aptX */
+  uint8_t sampleRate;  /* Sampling Frequency */
+  uint8_t channelMode; /* STEREO/DUAL/MONO */
+  uint8_t future1;
+  uint8_t future2;
+  btav_a2dp_codec_bits_per_sample_t bits_per_sample;
+} tA2DP_APTX_CIE;
+
+/* aptX Source codec capabilities */
+static const tA2DP_APTX_CIE a2dp_aptx_caps = {
+    A2DP_APTX_VENDOR_ID,                                       /* vendorId */
+    A2DP_APTX_CODEC_ID_BLUETOOTH,                              /* codecId */
+    (A2DP_APTX_SAMPLERATE_44100 | A2DP_APTX_SAMPLERATE_48000), /* sampleRate */
+    A2DP_APTX_CHANNELS_STEREO,                                 /* channelMode */
+    A2DP_APTX_FUTURE_1,                                        /* future1 */
+    A2DP_APTX_FUTURE_2,                                        /* future2 */
+    BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16 /* bits_per_sample */
+};
+
+/* Default aptX codec configuration */
+static const tA2DP_APTX_CIE a2dp_aptx_default_config = {
+    A2DP_APTX_VENDOR_ID,               /* vendorId */
+    A2DP_APTX_CODEC_ID_BLUETOOTH,      /* codecId */
+    A2DP_APTX_SAMPLERATE_44100,        /* sampleRate */
+    A2DP_APTX_CHANNELS_STEREO,         /* channelMode */
+    A2DP_APTX_FUTURE_1,                /* future1 */
+    A2DP_APTX_FUTURE_2,                /* future2 */
+    BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16 /* bits_per_sample */
+};
+
+static const tA2DP_ENCODER_INTERFACE a2dp_encoder_interface_aptx = {
+    a2dp_vendor_aptx_encoder_init,  a2dp_vendor_aptx_encoder_cleanup,
+    a2dp_vendor_aptx_feeding_init,  a2dp_vendor_aptx_feeding_reset,
+    a2dp_vendor_aptx_feeding_flush, a2dp_vendor_aptx_get_encoder_interval_ms,
+    a2dp_vendor_aptx_send_frames,   a2dp_vendor_aptx_debug_codec_dump};
+
+UNUSED_ATTR static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilityAptx(
+    const tA2DP_APTX_CIE* p_cap, const uint8_t* p_codec_info,
+    bool is_peer_codec_info);
+
+// Builds the aptX Media Codec Capabilities byte sequence beginning from the
+// LOSC octet. |media_type| is the media type |AVDT_MEDIA_TYPE_*|.
+// |p_ie| is a pointer to the aptX Codec Information Element information.
+// The result is stored in |p_result|. Returns A2DP_SUCCESS on success,
+// otherwise the corresponding A2DP error status code.
+static tA2DP_STATUS A2DP_BuildInfoAptx(uint8_t media_type,
+                                       const tA2DP_APTX_CIE* p_ie,
+                                       uint8_t* p_result) {
+  tA2DP_STATUS status;
+
+  if (p_ie == NULL || p_result == NULL) {
+    status = A2DP_INVALID_PARAMS;
+  } else {
+    status = A2DP_SUCCESS;
+    *p_result++ = A2DP_APTX_CODEC_LEN;
+    *p_result++ = media_type;
+    *p_result++ = A2DP_MEDIA_CT_NON_A2DP;
+    *p_result++ = (uint8_t)(p_ie->vendorId & 0x000000FF);
+    *p_result++ = (uint8_t)((p_ie->vendorId & 0x0000FF00) >> 8);
+    *p_result++ = (uint8_t)((p_ie->vendorId & 0x00FF0000) >> 16);
+    *p_result++ = (uint8_t)((p_ie->vendorId & 0xFF000000) >> 24);
+    *p_result++ = (uint8_t)(p_ie->codecId & 0x00FF);
+    *p_result++ = (uint8_t)((p_ie->codecId & 0xFF00) >> 8);
+    *p_result++ = p_ie->sampleRate | p_ie->channelMode;
+  }
+  return status;
+}
+
+// Parses the aptX Media Codec Capabilities byte sequence beginning from the
+// LOSC octet. The result is stored in |p_ie|. The byte sequence to parse is
+// |p_codec_info|. If |is_capability| is true, the byte sequence is
+// codec capabilities, otherwise is codec configuration.
+// Returns A2DP_SUCCESS on success, otherwise the corresponding A2DP error
+// status code.
+static tA2DP_STATUS A2DP_ParseInfoAptx(tA2DP_APTX_CIE* p_ie,
+                                       const uint8_t* p_codec_info,
+                                       bool is_capability) {
+  tA2DP_STATUS status = A2DP_SUCCESS;
+  uint8_t losc;
+  uint8_t media_type;
+  tA2DP_CODEC_TYPE codec_type;
+
+  if (p_ie == NULL || p_codec_info == NULL) return A2DP_INVALID_PARAMS;
+
+  // Check the codec capability length
+  losc = *p_codec_info++;
+  if (losc != A2DP_APTX_CODEC_LEN) return A2DP_WRONG_CODEC;
+
+  media_type = (*p_codec_info++) >> 4;
+  codec_type = *p_codec_info++;
+  /* Check the Media Type and Media Codec Type */
+  if (media_type != AVDT_MEDIA_TYPE_AUDIO ||
+      codec_type != A2DP_MEDIA_CT_NON_A2DP) {
+    return A2DP_WRONG_CODEC;
+  }
+
+  // Check the Vendor ID and Codec ID */
+  p_ie->vendorId = (*p_codec_info & 0x000000FF) |
+                   (*(p_codec_info + 1) << 8 & 0x0000FF00) |
+                   (*(p_codec_info + 2) << 16 & 0x00FF0000) |
+                   (*(p_codec_info + 3) << 24 & 0xFF000000);
+  p_codec_info = p_codec_info + 4;
+  p_ie->codecId =
+      (*p_codec_info & 0x00FF) | (*(p_codec_info + 1) << 8 & 0xFF00);
+  p_codec_info = p_codec_info + 2;
+  if (p_ie->vendorId != A2DP_APTX_VENDOR_ID ||
+      p_ie->codecId != A2DP_APTX_CODEC_ID_BLUETOOTH) {
+    return A2DP_WRONG_CODEC;
+  }
+
+  p_ie->channelMode = *p_codec_info & 0x0F;
+  p_ie->sampleRate = *p_codec_info & 0xF0;
+
+  status = A2DP_SUCCESS;
+
+  if (is_capability) return status;
+
+  if (A2DP_BitsSet(p_ie->sampleRate) != A2DP_SET_ONE_BIT)
+    status = A2DP_BAD_SAMP_FREQ;
+  if (A2DP_BitsSet(p_ie->channelMode) != A2DP_SET_ONE_BIT)
+    status = A2DP_BAD_CH_MODE;
+
+  return status;
+}
+
+bool A2DP_IsVendorSourceCodecValidAptx(const uint8_t* p_codec_info) {
+  tA2DP_APTX_CIE cfg_cie;
+
+  /* Use a liberal check when parsing the codec info */
+  return (A2DP_ParseInfoAptx(&cfg_cie, p_codec_info, false) == A2DP_SUCCESS) ||
+         (A2DP_ParseInfoAptx(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS);
+}
+
+bool A2DP_IsVendorPeerSinkCodecValidAptx(const uint8_t* p_codec_info) {
+  tA2DP_APTX_CIE cfg_cie;
+
+  /* Use a liberal check when parsing the codec info */
+  return (A2DP_ParseInfoAptx(&cfg_cie, p_codec_info, false) == A2DP_SUCCESS) ||
+         (A2DP_ParseInfoAptx(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS);
+}
+
+// Checks whether A2DP aptX codec configuration matches with a device's codec
+// capabilities. |p_cap| is the aptX codec configuration. |p_codec_info| is
+// the device's codec capabilities.
+// If |is_capability| is true, the byte sequence is codec capabilities,
+// otherwise is codec configuration.
+// |p_codec_info| contains the codec capabilities for a peer device that
+// is acting as an A2DP source.
+// Returns A2DP_SUCCESS if the codec configuration matches with capabilities,
+// otherwise the corresponding A2DP error status code.
+static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilityAptx(
+    const tA2DP_APTX_CIE* p_cap, const uint8_t* p_codec_info,
+    bool is_capability) {
+  tA2DP_STATUS status;
+  tA2DP_APTX_CIE cfg_cie;
+
+  /* parse configuration */
+  status = A2DP_ParseInfoAptx(&cfg_cie, p_codec_info, is_capability);
+  if (status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: parsing failed %d", __func__, status);
+    return status;
+  }
+
+  /* verify that each parameter is in range */
+
+  LOG_DEBUG(LOG_TAG, "FREQ peer: 0x%x, capability 0x%x", cfg_cie.sampleRate,
+            p_cap->sampleRate);
+  LOG_DEBUG(LOG_TAG, "CH_MODE peer: 0x%x, capability 0x%x", cfg_cie.channelMode,
+            p_cap->channelMode);
+
+  /* sampling frequency */
+  if ((cfg_cie.sampleRate & p_cap->sampleRate) == 0) return A2DP_NS_SAMP_FREQ;
+
+  /* channel mode */
+  if ((cfg_cie.channelMode & p_cap->channelMode) == 0) return A2DP_NS_CH_MODE;
+
+  return A2DP_SUCCESS;
+}
+
+bool A2DP_VendorUsesRtpHeaderAptx(UNUSED_ATTR bool content_protection_enabled,
+                                  UNUSED_ATTR const uint8_t* p_codec_info) {
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+  return true;
+#else
+  // no RTP header for aptX classic and no Copy Protection byte
+  return false;
+#endif
+}
+
+const char* A2DP_VendorCodecNameAptx(UNUSED_ATTR const uint8_t* p_codec_info) {
+  return "aptX";
+}
+
+bool A2DP_VendorCodecTypeEqualsAptx(const uint8_t* p_codec_info_a,
+                                    const uint8_t* p_codec_info_b) {
+  tA2DP_APTX_CIE aptx_cie_a;
+  tA2DP_APTX_CIE aptx_cie_b;
+
+  // Check whether the codec info contains valid data
+  tA2DP_STATUS a2dp_status =
+      A2DP_ParseInfoAptx(&aptx_cie_a, p_codec_info_a, true);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+              a2dp_status);
+    return false;
+  }
+  a2dp_status = A2DP_ParseInfoAptx(&aptx_cie_b, p_codec_info_b, true);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+              a2dp_status);
+    return false;
+  }
+
+  return true;
+}
+
+bool A2DP_VendorCodecEqualsAptx(const uint8_t* p_codec_info_a,
+                                const uint8_t* p_codec_info_b) {
+  tA2DP_APTX_CIE aptx_cie_a;
+  tA2DP_APTX_CIE aptx_cie_b;
+
+  // Check whether the codec info contains valid data
+  tA2DP_STATUS a2dp_status =
+      A2DP_ParseInfoAptx(&aptx_cie_a, p_codec_info_a, true);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+              a2dp_status);
+    return false;
+  }
+  a2dp_status = A2DP_ParseInfoAptx(&aptx_cie_b, p_codec_info_b, true);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+              a2dp_status);
+    return false;
+  }
+
+  return (aptx_cie_a.sampleRate == aptx_cie_b.sampleRate) &&
+         (aptx_cie_a.channelMode == aptx_cie_b.channelMode);
+}
+
+int A2DP_VendorGetTrackSampleRateAptx(const uint8_t* p_codec_info) {
+  tA2DP_APTX_CIE aptx_cie;
+
+  // Check whether the codec info contains valid data
+  tA2DP_STATUS a2dp_status = A2DP_ParseInfoAptx(&aptx_cie, p_codec_info, false);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+              a2dp_status);
+    return -1;
+  }
+
+  if (aptx_cie.sampleRate == A2DP_APTX_SAMPLERATE_44100) return 44100;
+  if (aptx_cie.sampleRate == A2DP_APTX_SAMPLERATE_48000) return 48000;
+
+  return -1;
+}
+
+int A2DP_VendorGetTrackBitsPerSampleAptx(const uint8_t* p_codec_info) {
+  tA2DP_APTX_CIE aptx_cie;
+
+  // Check whether the codec info contains valid data
+  tA2DP_STATUS a2dp_status = A2DP_ParseInfoAptx(&aptx_cie, p_codec_info, false);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+              a2dp_status);
+    return -1;
+  }
+
+  return 16;  // For aptX we always use 16 bits per audio sample
+}
+
+int A2DP_VendorGetTrackChannelCountAptx(const uint8_t* p_codec_info) {
+  tA2DP_APTX_CIE aptx_cie;
+
+  // Check whether the codec info contains valid data
+  tA2DP_STATUS a2dp_status = A2DP_ParseInfoAptx(&aptx_cie, p_codec_info, false);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+              a2dp_status);
+    return -1;
+  }
+
+  switch (aptx_cie.channelMode) {
+    case A2DP_APTX_CHANNELS_MONO:
+      return 1;
+    case A2DP_APTX_CHANNELS_STEREO:
+      return 2;
+  }
+
+  return -1;
+}
+
+bool A2DP_VendorGetPacketTimestampAptx(const uint8_t* p_codec_info,
+                                       const uint8_t* p_data,
+                                       uint32_t* p_timestamp) {
+  // TODO: Is this function really codec-specific?
+  *p_timestamp = *(const uint32_t*)p_data;
+  return true;
+}
+
+bool A2DP_VendorBuildCodecHeaderAptx(UNUSED_ATTR const uint8_t* p_codec_info,
+                                     UNUSED_ATTR BT_HDR* p_buf,
+                                     UNUSED_ATTR uint16_t frames_per_packet) {
+  // Nothing to do
+  return true;
+}
+
+void A2DP_VendorDumpCodecInfoAptx(const uint8_t* p_codec_info) {
+  tA2DP_STATUS a2dp_status;
+  tA2DP_APTX_CIE aptx_cie;
+
+  LOG_DEBUG(LOG_TAG, "%s", __func__);
+
+  a2dp_status = A2DP_ParseInfoAptx(&aptx_cie, p_codec_info, true);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: A2DP_ParseInfoAptx fail:%d", __func__, a2dp_status);
+    return;
+  }
+
+  LOG_DEBUG(LOG_TAG, "\tsamp_freq: 0x%x", aptx_cie.sampleRate);
+  if (aptx_cie.sampleRate == A2DP_APTX_SAMPLERATE_44100) {
+    LOG_DEBUG(LOG_TAG, "\tsamp_freq: (44100)");
+  }
+  if (aptx_cie.sampleRate == A2DP_APTX_SAMPLERATE_48000) {
+    LOG_DEBUG(LOG_TAG, "\tsamp_freq: (48000)");
+  }
+
+  LOG_DEBUG(LOG_TAG, "\tch_mode: 0x%x", aptx_cie.channelMode);
+  if (aptx_cie.channelMode == A2DP_APTX_CHANNELS_MONO) {
+    LOG_DEBUG(LOG_TAG, "\tch_mode: (Mono)");
+  }
+  if (aptx_cie.channelMode == A2DP_APTX_CHANNELS_STEREO) {
+    LOG_DEBUG(LOG_TAG, "\tch_mode: (Stereo)");
+  }
+}
+
+const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterfaceAptx(
+    const uint8_t* p_codec_info) {
+  if (!A2DP_IsVendorSourceCodecValidAptx(p_codec_info)) return NULL;
+
+  return &a2dp_encoder_interface_aptx;
+}
+
+bool A2DP_VendorAdjustCodecAptx(uint8_t* p_codec_info) {
+  tA2DP_APTX_CIE cfg_cie;
+
+  // Nothing to do: just verify the codec info is valid
+  if (A2DP_ParseInfoAptx(&cfg_cie, p_codec_info, true) != A2DP_SUCCESS)
+    return false;
+
+  return true;
+}
+
+btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndexAptx(
+    UNUSED_ATTR const uint8_t* p_codec_info) {
+  return BTAV_A2DP_CODEC_INDEX_SOURCE_APTX;
+}
+
+const char* A2DP_VendorCodecIndexStrAptx(void) { return "aptX"; }
+
+bool A2DP_VendorInitCodecConfigAptx(tAVDT_CFG* p_cfg) {
+  if (A2DP_BuildInfoAptx(AVDT_MEDIA_TYPE_AUDIO, &a2dp_aptx_caps,
+                         p_cfg->codec_info) != A2DP_SUCCESS) {
+    return false;
+  }
+
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+  /* Content protection info - support SCMS-T */
+  uint8_t* p = p_cfg->protect_info;
+  *p++ = AVDT_CP_LOSC;
+  UINT16_TO_STREAM(p, AVDT_CP_SCMS_T_ID);
+  p_cfg->num_protect = 1;
+#endif
+
+  return true;
+}
+
+A2dpCodecConfigAptx::A2dpCodecConfigAptx()
+    : A2dpCodecConfig(BTAV_A2DP_CODEC_INDEX_SOURCE_APTX, "aptX") {}
+
+A2dpCodecConfigAptx::~A2dpCodecConfigAptx() {}
+
+bool A2dpCodecConfigAptx::init() {
+  if (!isValid()) return false;
+
+  // Load the encoder
+  if (!A2DP_VendorLoadEncoderAptx()) {
+    LOG_ERROR(LOG_TAG, "%s: cannot load the encoder", __func__);
+    return false;
+  }
+
+  return true;
+}
+
+//
+// Selects the best sample rate from |sampleRate|.
+// The result is stored in |p_result| and p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_best_sample_rate(uint8_t sampleRate,
+                                    tA2DP_APTX_CIE* p_result,
+                                    btav_a2dp_codec_config_t* p_codec_config) {
+  if (sampleRate & A2DP_APTX_SAMPLERATE_48000) {
+    p_result->sampleRate = A2DP_APTX_SAMPLERATE_48000;
+    p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+    return true;
+  }
+  if (sampleRate & A2DP_APTX_SAMPLERATE_44100) {
+    p_result->sampleRate = A2DP_APTX_SAMPLERATE_44100;
+    p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
+    return true;
+  }
+  return false;
+}
+
+//
+// Selects the audio sample rate from |p_codec_audio_config|.
+// |sampleRate| contains the capability.
+// The result is stored in |p_result| and |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_audio_sample_rate(
+    const btav_a2dp_codec_config_t* p_codec_audio_config, uint8_t sampleRate,
+    tA2DP_APTX_CIE* p_result, btav_a2dp_codec_config_t* p_codec_config) {
+  switch (p_codec_audio_config->sample_rate) {
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_44100:
+      if (sampleRate & A2DP_APTX_SAMPLERATE_44100) {
+        p_result->sampleRate = A2DP_APTX_SAMPLERATE_44100;
+        p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
+        return true;
+      }
+      break;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_48000:
+      if (sampleRate & A2DP_APTX_SAMPLERATE_48000) {
+        p_result->sampleRate = A2DP_APTX_SAMPLERATE_48000;
+        p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+        return true;
+      }
+      break;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_88200:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_96000:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_176400:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_192000:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_NONE:
+      break;
+  }
+  return false;
+}
+
+//
+// Selects the best bits per sample.
+// The result is stored in |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_best_bits_per_sample(
+    btav_a2dp_codec_config_t* p_codec_config) {
+  p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16;
+  return true;
+}
+
+//
+// Selects the audio bits per sample from |p_codec_audio_config|.
+// The result is stored in |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_audio_bits_per_sample(
+    const btav_a2dp_codec_config_t* p_codec_audio_config,
+    btav_a2dp_codec_config_t* p_codec_config) {
+  switch (p_codec_audio_config->bits_per_sample) {
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16:
+      p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16;
+      return true;
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24:
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE:
+      break;
+  }
+  return false;
+}
+
+//
+// Selects the best channel mode from |channelMode|.
+// The result is stored in |p_result| and |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_best_channel_mode(uint8_t channelMode,
+                                     tA2DP_APTX_CIE* p_result,
+                                     btav_a2dp_codec_config_t* p_codec_config) {
+  if (channelMode & A2DP_APTX_CHANNELS_STEREO) {
+    p_result->channelMode = A2DP_APTX_CHANNELS_STEREO;
+    p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+    return true;
+  }
+  if (channelMode & A2DP_APTX_CHANNELS_MONO) {
+    p_result->channelMode = A2DP_APTX_CHANNELS_MONO;
+    p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+    return true;
+  }
+  return false;
+}
+
+//
+// Selects the audio channel mode from |p_codec_audio_config|.
+// |channelMode| contains the capability.
+// The result is stored in |p_result| and |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_audio_channel_mode(
+    const btav_a2dp_codec_config_t* p_codec_audio_config, uint8_t channelMode,
+    tA2DP_APTX_CIE* p_result, btav_a2dp_codec_config_t* p_codec_config) {
+  switch (p_codec_audio_config->channel_mode) {
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_MONO:
+      if (channelMode & A2DP_APTX_CHANNELS_MONO) {
+        p_result->channelMode = A2DP_APTX_CHANNELS_MONO;
+        p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+        return true;
+      }
+      break;
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO:
+      if (channelMode & A2DP_APTX_CHANNELS_STEREO) {
+        p_result->channelMode = A2DP_APTX_CHANNELS_STEREO;
+        p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+        return true;
+      }
+      break;
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_NONE:
+      break;
+  }
+
+  return false;
+}
+
+bool A2dpCodecConfigAptx::setCodecConfig(const uint8_t* p_peer_codec_info,
+                                         bool is_capability,
+                                         uint8_t* p_result_codec_config) {
+  std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
+  tA2DP_APTX_CIE sink_info_cie;
+  tA2DP_APTX_CIE result_config_cie;
+  uint8_t sampleRate;
+  uint8_t channelMode;
+
+  // Save the internal state
+  btav_a2dp_codec_config_t saved_codec_config = codec_config_;
+  btav_a2dp_codec_config_t saved_codec_capability = codec_capability_;
+  btav_a2dp_codec_config_t saved_codec_user_config = codec_user_config_;
+  btav_a2dp_codec_config_t saved_codec_audio_config = codec_audio_config_;
+  uint8_t saved_ota_codec_config[AVDT_CODEC_SIZE];
+  uint8_t saved_ota_codec_peer_capability[AVDT_CODEC_SIZE];
+  uint8_t saved_ota_codec_peer_config[AVDT_CODEC_SIZE];
+  memcpy(saved_ota_codec_config, ota_codec_config_, sizeof(ota_codec_config_));
+  memcpy(saved_ota_codec_peer_capability, ota_codec_peer_capability_,
+         sizeof(ota_codec_peer_capability_));
+  memcpy(saved_ota_codec_peer_config, ota_codec_peer_config_,
+         sizeof(ota_codec_peer_config_));
+
+  tA2DP_STATUS status =
+      A2DP_ParseInfoAptx(&sink_info_cie, p_peer_codec_info, is_capability);
+  if (status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: can't parse peer's Sink capabilities: error = %d",
+              __func__, status);
+    goto fail;
+  }
+
+  //
+  // Build the preferred configuration
+  //
+  memset(&result_config_cie, 0, sizeof(result_config_cie));
+  result_config_cie.vendorId = a2dp_aptx_caps.vendorId;
+  result_config_cie.codecId = a2dp_aptx_caps.codecId;
+
+  //
+  // Select the sample frequency
+  //
+  sampleRate = a2dp_aptx_caps.sampleRate & sink_info_cie.sampleRate;
+  codec_config_.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE;
+  switch (codec_user_config_.sample_rate) {
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_44100:
+      if (sampleRate & A2DP_APTX_SAMPLERATE_44100) {
+        result_config_cie.sampleRate = A2DP_APTX_SAMPLERATE_44100;
+        codec_capability_.sample_rate = codec_user_config_.sample_rate;
+        codec_config_.sample_rate = codec_user_config_.sample_rate;
+      }
+      break;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_48000:
+      if (sampleRate & A2DP_APTX_SAMPLERATE_48000) {
+        result_config_cie.sampleRate = A2DP_APTX_SAMPLERATE_48000;
+        codec_capability_.sample_rate = codec_user_config_.sample_rate;
+        codec_config_.sample_rate = codec_user_config_.sample_rate;
+      }
+      break;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_88200:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_96000:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_176400:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_192000:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_NONE:
+      codec_capability_.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE;
+      codec_config_.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE;
+      break;
+  }
+
+  // Select the sample frequency if there is no user preference
+  do {
+    if (codec_config_.sample_rate != BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) break;
+
+    // Compute the common capability
+    if (sampleRate & A2DP_APTX_SAMPLERATE_44100)
+      codec_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
+    if (sampleRate & A2DP_APTX_SAMPLERATE_48000)
+      codec_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+
+    // No user preference - try the codec audio config
+    if (select_audio_sample_rate(&codec_audio_config_, sampleRate,
+                                 &result_config_cie, &codec_config_)) {
+      break;
+    }
+
+    // No user preference - try the default config
+    if (select_best_sample_rate(
+            a2dp_aptx_default_config.sampleRate & sink_info_cie.sampleRate,
+            &result_config_cie, &codec_config_)) {
+      break;
+    }
+
+    // No user preference - use the best match
+    if (select_best_sample_rate(sampleRate, &result_config_cie,
+                                &codec_config_)) {
+      break;
+    }
+  } while (false);
+  if (codec_config_.sample_rate == BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) {
+    LOG_ERROR(LOG_TAG,
+              "%s: cannot match sample frequency: source caps = 0x%x "
+              "sink info = 0x%x",
+              __func__, a2dp_aptx_caps.sampleRate, sink_info_cie.sampleRate);
+    goto fail;
+  }
+
+  //
+  // Select the bits per sample
+  //
+  // NOTE: this information is NOT included in the aptX A2DP codec
+  // description that is sent OTA.
+  codec_config_.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE;
+  switch (codec_user_config_.bits_per_sample) {
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16:
+      codec_capability_.bits_per_sample = codec_user_config_.bits_per_sample;
+      codec_config_.bits_per_sample = codec_user_config_.bits_per_sample;
+      break;
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24:
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE:
+      codec_capability_.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE;
+      codec_config_.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE;
+      break;
+  }
+
+  // Select the bits per sample if there is no user preference
+  do {
+    if (codec_config_.bits_per_sample != BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE)
+      break;
+
+    // Compute the common capability
+    codec_capability_.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16;
+
+    // No user preference - try the codec audio config
+    if (select_audio_bits_per_sample(&codec_audio_config_, &codec_config_)) {
+      break;
+    }
+
+    // No user preference - try the default config
+    if (select_best_bits_per_sample(&codec_config_)) {
+      break;
+    }
+
+    // No user preference - use the best match
+    // NOTE: no-op - kept here for consistency
+    if (select_best_bits_per_sample(&codec_config_)) {
+      break;
+    }
+  } while (false);
+  if (codec_config_.bits_per_sample == BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) {
+    LOG_ERROR(LOG_TAG,
+              "%s: cannot match bits per sample: user preference = 0x%x",
+              __func__, codec_user_config_.bits_per_sample);
+    goto fail;
+  }
+
+  //
+  // Select the channel mode
+  //
+  channelMode = a2dp_aptx_caps.channelMode & sink_info_cie.channelMode;
+  codec_config_.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE;
+  switch (codec_user_config_.channel_mode) {
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_MONO:
+      if (channelMode & A2DP_APTX_CHANNELS_MONO) {
+        result_config_cie.channelMode = A2DP_APTX_CHANNELS_MONO;
+        codec_capability_.channel_mode = codec_user_config_.channel_mode;
+        codec_config_.channel_mode = codec_user_config_.channel_mode;
+      }
+      break;
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO:
+      if (channelMode & A2DP_APTX_CHANNELS_STEREO) {
+        result_config_cie.channelMode = A2DP_APTX_CHANNELS_STEREO;
+        codec_capability_.channel_mode = codec_user_config_.channel_mode;
+        codec_config_.channel_mode = codec_user_config_.channel_mode;
+      }
+      break;
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_NONE:
+      codec_capability_.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE;
+      codec_config_.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE;
+      break;
+  }
+
+  // Select the channel mode if there is no user preference
+  do {
+    if (codec_config_.channel_mode != BTAV_A2DP_CODEC_CHANNEL_MODE_NONE) break;
+
+    // Compute the common capability
+    if (channelMode & A2DP_APTX_CHANNELS_MONO)
+      codec_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+    if (channelMode & A2DP_APTX_CHANNELS_STEREO)
+      codec_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+
+    // No user preference - try the codec audio config
+    if (select_audio_channel_mode(&codec_audio_config_, channelMode,
+                                  &result_config_cie, &codec_config_)) {
+      break;
+    }
+
+    // No user preference - try the default config
+    if (select_best_channel_mode(
+            a2dp_aptx_default_config.channelMode & sink_info_cie.channelMode,
+            &result_config_cie, &codec_config_)) {
+      break;
+    }
+
+    // No user preference - use the best match
+    if (select_best_channel_mode(channelMode, &result_config_cie,
+                                 &codec_config_)) {
+      break;
+    }
+  } while (false);
+  if (codec_config_.channel_mode == BTAV_A2DP_CODEC_CHANNEL_MODE_NONE) {
+    LOG_ERROR(LOG_TAG,
+              "%s: cannot match channel mode: source caps = 0x%x "
+              "sink info = 0x%x",
+              __func__, a2dp_aptx_caps.channelMode, sink_info_cie.channelMode);
+    goto fail;
+  }
+
+  //
+  // Set the rest of the fields as bit-wise AND operation
+  //
+  result_config_cie.future1 = a2dp_aptx_caps.future1 & sink_info_cie.future1;
+  result_config_cie.future2 = a2dp_aptx_caps.future2 & sink_info_cie.future2;
+
+  if (A2DP_BuildInfoAptx(AVDT_MEDIA_TYPE_AUDIO, &result_config_cie,
+                         p_result_codec_config) != A2DP_SUCCESS) {
+    goto fail;
+  }
+
+  //
+  // Copy the codec-specific fields if they are not zero
+  //
+  if (codec_user_config_.codec_specific_1 != 0)
+    codec_config_.codec_specific_1 = codec_user_config_.codec_specific_1;
+  if (codec_user_config_.codec_specific_2 != 0)
+    codec_config_.codec_specific_2 = codec_user_config_.codec_specific_2;
+  if (codec_user_config_.codec_specific_3 != 0)
+    codec_config_.codec_specific_3 = codec_user_config_.codec_specific_3;
+  if (codec_user_config_.codec_specific_4 != 0)
+    codec_config_.codec_specific_4 = codec_user_config_.codec_specific_4;
+
+  // Create a local copy of the peer codec capability/config, and the
+  // result codec config.
+  if (is_capability) {
+    status = A2DP_BuildInfoAptx(AVDT_MEDIA_TYPE_AUDIO, &sink_info_cie,
+                                ota_codec_peer_capability_);
+  } else {
+    status = A2DP_BuildInfoAptx(AVDT_MEDIA_TYPE_AUDIO, &sink_info_cie,
+                                ota_codec_peer_config_);
+  }
+  CHECK(status == A2DP_SUCCESS);
+  status = A2DP_BuildInfoAptx(AVDT_MEDIA_TYPE_AUDIO, &result_config_cie,
+                              ota_codec_config_);
+  CHECK(status == A2DP_SUCCESS);
+
+  return true;
+
+fail:
+  // Restore the internal state
+  codec_config_ = saved_codec_config;
+  codec_capability_ = saved_codec_capability;
+  codec_user_config_ = saved_codec_user_config;
+  codec_audio_config_ = saved_codec_audio_config;
+  memcpy(ota_codec_config_, saved_ota_codec_config, sizeof(ota_codec_config_));
+  memcpy(ota_codec_peer_capability_, saved_ota_codec_peer_capability,
+         sizeof(ota_codec_peer_capability_));
+  memcpy(ota_codec_peer_config_, saved_ota_codec_peer_config,
+         sizeof(ota_codec_peer_config_));
+  return false;
+}
diff --git a/stack/a2dp/a2dp_vendor_aptx_encoder.cc b/stack/a2dp/a2dp_vendor_aptx_encoder.cc
new file mode 100644
index 0000000..05a9114
--- /dev/null
+++ b/stack/a2dp/a2dp_vendor_aptx_encoder.cc
@@ -0,0 +1,581 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "a2dp_vendor_aptx_encoder"
+
+#include "a2dp_vendor_aptx_encoder.h"
+
+#include <dlfcn.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "a2dp_vendor.h"
+#include "a2dp_vendor_aptx.h"
+#include "bt_common.h"
+#include "osi/include/log.h"
+#include "osi/include/osi.h"
+
+//
+// Encoder for aptX Source Codec
+//
+
+//
+// The aptX encoder shared library, and the functions to use
+//
+static const char* APTX_ENCODER_LIB_NAME = "libaptX.so";
+static void* aptx_encoder_lib_handle = NULL;
+
+static const char* APTX_ENCODER_INIT_NAME = "aptxbtenc_init";
+typedef int (*tAPTX_ENCODER_INIT)(void* state, short endian);
+
+static const char* APTX_ENCODER_ENCODE_STEREO_NAME = "aptxbtenc_encodestereo";
+typedef int (*tAPTX_ENCODER_ENCODE_STEREO)(void* state, void* pcmL, void* pcmR,
+                                           void* buffer);
+
+static const char* APTX_ENCODER_SIZEOF_PARAMS_NAME = "SizeofAptxbtenc";
+typedef int (*tAPTX_ENCODER_SIZEOF_PARAMS)(void);
+
+static tAPTX_ENCODER_INIT aptx_encoder_init_func;
+static tAPTX_ENCODER_ENCODE_STEREO aptx_encoder_encode_stereo_func;
+static tAPTX_ENCODER_SIZEOF_PARAMS aptx_encoder_sizeof_params_func;
+
+// offset
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+#define A2DP_APTX_OFFSET (AVDT_MEDIA_OFFSET + 1)
+#else
+// no RTP header for aptX classic
+#define A2DP_APTX_OFFSET (AVDT_MEDIA_OFFSET - AVDT_MEDIA_HDR_SIZE)
+#endif
+
+#define A2DP_APTX_MAX_PCM_BYTES_PER_READ 1024
+
+typedef struct {
+  uint64_t sleep_time_ns;
+  uint32_t pcm_reads;
+  uint32_t pcm_bytes_per_read;
+  uint32_t aptx_bytes;
+  uint32_t frame_size_counter;
+} tAPTX_FRAMING_PARAMS;
+
+typedef struct {
+  uint64_t session_start_us;
+
+  size_t media_read_total_expected_packets;
+  size_t media_read_total_expected_reads_count;
+  size_t media_read_total_expected_read_bytes;
+
+  size_t media_read_total_dropped_packets;
+  size_t media_read_total_actual_reads_count;
+  size_t media_read_total_actual_read_bytes;
+} a2dp_aptx_encoder_stats_t;
+
+typedef struct {
+  a2dp_source_read_callback_t read_callback;
+  a2dp_source_enqueue_callback_t enqueue_callback;
+
+  bool use_SCMS_T;
+  bool is_peer_edr;          // True if the peer device supports EDR
+  bool peer_supports_3mbps;  // True if the peer device supports 3Mbps EDR
+  uint16_t peer_mtu;         // MTU of the A2DP peer
+  uint32_t timestamp;        // Timestamp for the A2DP frames
+
+  tA2DP_FEEDING_PARAMS feeding_params;
+  tAPTX_FRAMING_PARAMS framing_params;
+  void* aptx_encoder_state;
+  a2dp_aptx_encoder_stats_t stats;
+} tA2DP_APTX_ENCODER_CB;
+
+static tA2DP_APTX_ENCODER_CB a2dp_aptx_encoder_cb;
+
+static void a2dp_vendor_aptx_encoder_update(uint16_t peer_mtu,
+                                            A2dpCodecConfig* a2dp_codec_config,
+                                            bool* p_restart_input,
+                                            bool* p_restart_output,
+                                            bool* p_config_updated);
+static void aptx_init_framing_params(tAPTX_FRAMING_PARAMS* framing_params);
+static void aptx_update_framing_params(tAPTX_FRAMING_PARAMS* framing_params);
+static size_t aptx_encode_16bit(tAPTX_FRAMING_PARAMS* framing_params,
+                                size_t* data_out_index, uint16_t* data16_in,
+                                uint8_t* data_out);
+
+bool A2DP_VendorLoadEncoderAptx(void) {
+  if (aptx_encoder_lib_handle != NULL) return true;  // Already loaded
+
+  // Open the encoder library
+  aptx_encoder_lib_handle = dlopen(APTX_ENCODER_LIB_NAME, RTLD_NOW);
+  if (aptx_encoder_lib_handle == NULL) {
+    LOG_ERROR(LOG_TAG, "%s: cannot open aptX encoder library %s: %s", __func__,
+              APTX_ENCODER_LIB_NAME, dlerror());
+    return false;
+  }
+
+  aptx_encoder_init_func = (tAPTX_ENCODER_INIT)dlsym(aptx_encoder_lib_handle,
+                                                     APTX_ENCODER_INIT_NAME);
+  if (aptx_encoder_init_func == NULL) {
+    LOG_ERROR(LOG_TAG,
+              "%s: cannot find function '%s' in the encoder library: %s",
+              __func__, APTX_ENCODER_INIT_NAME, dlerror());
+    A2DP_VendorUnloadEncoderAptx();
+    return false;
+  }
+
+  aptx_encoder_encode_stereo_func = (tAPTX_ENCODER_ENCODE_STEREO)dlsym(
+      aptx_encoder_lib_handle, APTX_ENCODER_ENCODE_STEREO_NAME);
+  if (aptx_encoder_encode_stereo_func == NULL) {
+    LOG_ERROR(LOG_TAG,
+              "%s: cannot find function '%s' in the encoder library: %s",
+              __func__, APTX_ENCODER_ENCODE_STEREO_NAME, dlerror());
+    A2DP_VendorUnloadEncoderAptx();
+    return false;
+  }
+
+  aptx_encoder_sizeof_params_func = (tAPTX_ENCODER_SIZEOF_PARAMS)dlsym(
+      aptx_encoder_lib_handle, APTX_ENCODER_SIZEOF_PARAMS_NAME);
+  if (aptx_encoder_sizeof_params_func == NULL) {
+    LOG_ERROR(LOG_TAG,
+              "%s: cannot find function '%s' in the encoder library: %s",
+              __func__, APTX_ENCODER_SIZEOF_PARAMS_NAME, dlerror());
+    A2DP_VendorUnloadEncoderAptx();
+    return false;
+  }
+
+  return true;
+}
+
+void A2DP_VendorUnloadEncoderAptx(void) {
+  aptx_encoder_init_func = NULL;
+  aptx_encoder_encode_stereo_func = NULL;
+  aptx_encoder_sizeof_params_func = NULL;
+
+  if (aptx_encoder_lib_handle != NULL) {
+    dlclose(aptx_encoder_lib_handle);
+    aptx_encoder_lib_handle = NULL;
+  }
+}
+
+void a2dp_vendor_aptx_encoder_init(
+    const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+    A2dpCodecConfig* a2dp_codec_config,
+    a2dp_source_read_callback_t read_callback,
+    a2dp_source_enqueue_callback_t enqueue_callback) {
+  memset(&a2dp_aptx_encoder_cb, 0, sizeof(a2dp_aptx_encoder_cb));
+
+  a2dp_aptx_encoder_cb.stats.session_start_us = time_get_os_boottime_us();
+
+  a2dp_aptx_encoder_cb.read_callback = read_callback;
+  a2dp_aptx_encoder_cb.enqueue_callback = enqueue_callback;
+  a2dp_aptx_encoder_cb.is_peer_edr = p_peer_params->is_peer_edr;
+  a2dp_aptx_encoder_cb.peer_supports_3mbps = p_peer_params->peer_supports_3mbps;
+  a2dp_aptx_encoder_cb.peer_mtu = p_peer_params->peer_mtu;
+  a2dp_aptx_encoder_cb.timestamp = 0;
+
+  /* aptX encoder config */
+  a2dp_aptx_encoder_cb.use_SCMS_T = false;  // TODO: should be a parameter
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+  a2dp_aptx_encoder_cb.use_SCMS_T = true;
+#endif
+
+  a2dp_aptx_encoder_cb.aptx_encoder_state =
+      osi_malloc(aptx_encoder_sizeof_params_func());
+  if (a2dp_aptx_encoder_cb.aptx_encoder_state != NULL) {
+    aptx_encoder_init_func(a2dp_aptx_encoder_cb.aptx_encoder_state, 0);
+  } else {
+    LOG_ERROR(LOG_TAG, "%s: Cannot allocate aptX encoder state", __func__);
+    // TODO: Return an error?
+  }
+
+  // NOTE: Ignore the restart_input / restart_output flags - this initization
+  // happens when the connection is (re)started.
+  bool restart_input = false;
+  bool restart_output = false;
+  bool config_updated = false;
+  a2dp_vendor_aptx_encoder_update(a2dp_aptx_encoder_cb.peer_mtu,
+                                  a2dp_codec_config, &restart_input,
+                                  &restart_output, &config_updated);
+  aptx_init_framing_params(&a2dp_aptx_encoder_cb.framing_params);
+}
+
+bool A2dpCodecConfigAptx::updateEncoderUserConfig(
+    const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params, bool* p_restart_input,
+    bool* p_restart_output, bool* p_config_updated) {
+  a2dp_aptx_encoder_cb.is_peer_edr = p_peer_params->is_peer_edr;
+  a2dp_aptx_encoder_cb.peer_supports_3mbps = p_peer_params->peer_supports_3mbps;
+  a2dp_aptx_encoder_cb.peer_mtu = p_peer_params->peer_mtu;
+  a2dp_aptx_encoder_cb.timestamp = 0;
+
+  if (a2dp_aptx_encoder_cb.peer_mtu == 0) {
+    LOG_ERROR(LOG_TAG,
+              "%s: Cannot update the codec encoder for %s: "
+              "invalid peer MTU",
+              __func__, name().c_str());
+    return false;
+  }
+
+  a2dp_vendor_aptx_encoder_update(a2dp_aptx_encoder_cb.peer_mtu, this,
+                                  p_restart_input, p_restart_output,
+                                  p_config_updated);
+  return true;
+}
+
+// Update the A2DP aptX encoder.
+// |peer_mtu| is the peer MTU.
+// |a2dp_codec_config| is the A2DP codec to use for the update.
+static void a2dp_vendor_aptx_encoder_update(uint16_t peer_mtu,
+                                            A2dpCodecConfig* a2dp_codec_config,
+                                            bool* p_restart_input,
+                                            bool* p_restart_output,
+                                            bool* p_config_updated) {
+  uint8_t codec_info[AVDT_CODEC_SIZE];
+
+  *p_restart_input = false;
+  *p_restart_output = false;
+  *p_config_updated = false;
+  if (!a2dp_codec_config->copyOutOtaCodecConfig(codec_info)) {
+    LOG_ERROR(LOG_TAG,
+              "%s: Cannot update the codec encoder for %s: "
+              "invalid codec config",
+              __func__, a2dp_codec_config->name().c_str());
+    return;
+  }
+  const uint8_t* p_codec_info = codec_info;
+
+  // The feeding parameters
+  a2dp_aptx_encoder_cb.feeding_params.sample_rate =
+      A2DP_VendorGetTrackSampleRateAptx(p_codec_info);
+  a2dp_aptx_encoder_cb.feeding_params.bits_per_sample =
+      A2DP_VendorGetTrackBitsPerSampleAptx(p_codec_info);
+  a2dp_aptx_encoder_cb.feeding_params.channel_count =
+      A2DP_VendorGetTrackChannelCountAptx(p_codec_info);
+}
+
+void a2dp_vendor_aptx_encoder_cleanup(void) {
+  osi_free(a2dp_aptx_encoder_cb.aptx_encoder_state);
+  memset(&a2dp_aptx_encoder_cb, 0, sizeof(a2dp_aptx_encoder_cb));
+}
+
+void a2dp_vendor_aptx_feeding_init(
+    const tA2DP_FEEDING_PARAMS* p_feeding_params) {
+  LOG_DEBUG(LOG_TAG,
+            "%s: PCM feeding: sample_rate:%d bits_per_sample:%d "
+            "channel_count:%d",
+            __func__, p_feeding_params->sample_rate,
+            p_feeding_params->bits_per_sample, p_feeding_params->channel_count);
+
+  // Check the PCM feeding sample_rate
+  switch (p_feeding_params->sample_rate) {
+    case 44100:
+    case 48000:
+      break;
+    default:
+      LOG_WARN(LOG_TAG, "%s: feeding PCM sample_rate %u is not supported",
+               __func__, p_feeding_params->sample_rate);
+      return;
+  }
+
+  // Check the bits per sample
+  switch (p_feeding_params->bits_per_sample) {
+    case 16:
+      break;
+    default:
+      LOG_WARN(LOG_TAG, "%s: feeding PCM bits_per_sample %u is not supported",
+               __func__, p_feeding_params->bits_per_sample);
+      return;
+  }
+
+  // Check the number of channels
+  switch (p_feeding_params->channel_count) {
+    case 1:  // Mono
+    case 2:  // Stereo
+      break;
+    default:
+      LOG_WARN(LOG_TAG, "%s: feeding PCM channel_count %u is not supported",
+               __func__, p_feeding_params->channel_count);
+      return;
+  }
+
+  bool reconfig_needed = false;
+  if (a2dp_aptx_encoder_cb.feeding_params.sample_rate !=
+      p_feeding_params->sample_rate) {
+    LOG_DEBUG(LOG_TAG,
+              "%s: codec reconfiguration: feeding PCM sample_rate "
+              "from %u to %u",
+              __func__, a2dp_aptx_encoder_cb.feeding_params.sample_rate,
+              p_feeding_params->sample_rate);
+    reconfig_needed = true;
+    a2dp_aptx_encoder_cb.feeding_params.sample_rate =
+        p_feeding_params->sample_rate;
+  }
+  if (a2dp_aptx_encoder_cb.feeding_params.bits_per_sample !=
+      p_feeding_params->bits_per_sample) {
+    LOG_DEBUG(LOG_TAG,
+              "%s: aptX reconfiguration needed: "
+              "feeding PCM bits_per_sample from %u to %u",
+              __func__, a2dp_aptx_encoder_cb.feeding_params.bits_per_sample,
+              p_feeding_params->bits_per_sample);
+    a2dp_aptx_encoder_cb.feeding_params.bits_per_sample =
+        p_feeding_params->bits_per_sample;
+    reconfig_needed = true;
+  }
+  if (a2dp_aptx_encoder_cb.feeding_params.channel_count !=
+      p_feeding_params->channel_count) {
+    LOG_DEBUG(LOG_TAG,
+              "%s: aptX reconfiguration needed: "
+              "feeding PCM channel_count from %u to %u",
+              __func__, a2dp_aptx_encoder_cb.feeding_params.channel_count,
+              p_feeding_params->channel_count);
+    a2dp_aptx_encoder_cb.feeding_params.channel_count =
+        p_feeding_params->channel_count;
+    reconfig_needed = true;
+  }
+
+  if (reconfig_needed) {
+    LOG_DEBUG(LOG_TAG, "%s: sample_rate=%u bits_per_sample=%u channel_count=%u",
+              __func__, p_feeding_params->sample_rate,
+              p_feeding_params->bits_per_sample,
+              p_feeding_params->channel_count);
+    aptx_init_framing_params(&a2dp_aptx_encoder_cb.framing_params);
+  } else {
+    LOG_DEBUG(LOG_TAG, "%s: no aptX reconfiguration needed", __func__);
+  }
+}
+
+//
+// Initialize the framing parameters, and set those that don't change
+// while streaming (e.g., 'sleep_time_ns').
+//
+static void aptx_init_framing_params(tAPTX_FRAMING_PARAMS* framing_params) {
+  framing_params->sleep_time_ns = 0;
+  framing_params->pcm_reads = 0;
+  framing_params->pcm_bytes_per_read = 0;
+  framing_params->aptx_bytes = 0;
+  framing_params->frame_size_counter = 0;
+
+  if (a2dp_aptx_encoder_cb.feeding_params.sample_rate == 48000) {
+    if (a2dp_aptx_encoder_cb.use_SCMS_T) {
+      framing_params->sleep_time_ns = 13000000;
+    } else {
+      framing_params->sleep_time_ns = 14000000;
+    }
+  } else {
+    // Assume the sample rate is 44100
+    if (a2dp_aptx_encoder_cb.use_SCMS_T) {
+      framing_params->sleep_time_ns = 14000000;
+    } else {
+      framing_params->sleep_time_ns = 15000000;
+    }
+  }
+
+  LOG_DEBUG(LOG_TAG, "%s: sleep_time_ns = %" PRIu64, __func__,
+            framing_params->sleep_time_ns);
+}
+
+//
+// Set frame size and transmission interval needed to stream the required
+// sample rate using 2-DH5 packets for aptX and 2-DH3 packets for aptX-LL.
+// With SCMS-T enabled we need to reserve room for extra headers added later.
+// Packets are always sent at equals time intervals but to achieve the
+// required sample rate, the frame size needs to change on occasion.
+//
+// Also need to specify how many of the required PCM samples are read at a
+// time:
+//     aptx_bytes = pcm_reads * pcm_bytes_per_read / 4
+// and
+//     number of aptX samples produced = pcm_bytes_per_read / 16
+//
+static void aptx_update_framing_params(tAPTX_FRAMING_PARAMS* framing_params) {
+  if (a2dp_aptx_encoder_cb.feeding_params.sample_rate == 48000) {
+    if (a2dp_aptx_encoder_cb.use_SCMS_T) {
+      framing_params->aptx_bytes = 624;
+      framing_params->pcm_bytes_per_read = 208;
+      framing_params->pcm_reads = 12;
+    } else {
+      framing_params->aptx_bytes = 672;
+      framing_params->pcm_bytes_per_read = 224;
+      framing_params->pcm_reads = 12;
+    }
+  } else {
+    // Assume the sample rate is 44100
+    if (a2dp_aptx_encoder_cb.use_SCMS_T) {
+      if (++framing_params->frame_size_counter < 20) {
+        framing_params->aptx_bytes = 616;
+        framing_params->pcm_bytes_per_read = 224;
+        framing_params->pcm_reads = 11;
+      } else {
+        framing_params->aptx_bytes = 644;
+        framing_params->pcm_bytes_per_read = 368;
+        framing_params->pcm_reads = 7;
+        framing_params->frame_size_counter = 0;
+      }
+    } else {
+      if (++framing_params->frame_size_counter < 8) {
+        framing_params->aptx_bytes = 660;
+        framing_params->pcm_bytes_per_read = 240;
+        framing_params->pcm_reads = 11;
+      } else {
+        framing_params->aptx_bytes = 672;
+        framing_params->pcm_bytes_per_read = 224;
+        framing_params->pcm_reads = 12;
+        framing_params->frame_size_counter = 0;
+      }
+    }
+  }
+
+  LOG_DEBUG(LOG_TAG,
+            "%s: sleep_time_ns = %" PRIu64
+            " aptx_bytes = %u "
+            "pcm_bytes_per_read = %u pcm_reads = %u frame_size_counter = %u",
+            __func__, framing_params->sleep_time_ns, framing_params->aptx_bytes,
+            framing_params->pcm_bytes_per_read, framing_params->pcm_reads,
+            framing_params->frame_size_counter);
+}
+
+void a2dp_vendor_aptx_feeding_reset(void) {
+  aptx_init_framing_params(&a2dp_aptx_encoder_cb.framing_params);
+}
+
+void a2dp_vendor_aptx_feeding_flush(void) {
+  aptx_init_framing_params(&a2dp_aptx_encoder_cb.framing_params);
+}
+
+period_ms_t a2dp_vendor_aptx_get_encoder_interval_ms(void) {
+  return a2dp_aptx_encoder_cb.framing_params.sleep_time_ns / (1000 * 1000);
+}
+
+void a2dp_vendor_aptx_send_frames(uint64_t timestamp_us) {
+  tAPTX_FRAMING_PARAMS* framing_params = &a2dp_aptx_encoder_cb.framing_params;
+
+  // Prepare the packet to send
+  BT_HDR* p_buf = (BT_HDR*)osi_malloc(BT_DEFAULT_BUFFER_SIZE);
+  p_buf->offset = A2DP_APTX_OFFSET;
+  p_buf->len = 0;
+  p_buf->layer_specific = 0;
+
+  uint8_t* encoded_ptr = (uint8_t*)(p_buf + 1);
+  encoded_ptr += p_buf->offset;
+
+  aptx_update_framing_params(framing_params);
+
+  //
+  // Read the PCM data and encode it
+  //
+  LOG_VERBOSE(LOG_TAG, "%s: %u PCM reads of size %u", __func__,
+              framing_params->pcm_reads, framing_params->pcm_bytes_per_read);
+  size_t encoded_ptr_index = 0;
+  size_t pcm_bytes_encoded = 0;
+  a2dp_aptx_encoder_cb.stats.media_read_total_expected_packets++;
+  a2dp_aptx_encoder_cb.stats.media_read_total_expected_reads_count +=
+      framing_params->pcm_reads;
+  a2dp_aptx_encoder_cb.stats.media_read_total_expected_read_bytes +=
+      framing_params->pcm_reads * framing_params->pcm_bytes_per_read;
+  for (size_t reads = 0; reads < framing_params->pcm_reads; reads++) {
+    uint16_t read_buffer16[A2DP_APTX_MAX_PCM_BYTES_PER_READ / sizeof(uint16_t)];
+    size_t pcm_bytes_read = a2dp_aptx_encoder_cb.read_callback(
+        (uint8_t*)read_buffer16, framing_params->pcm_bytes_per_read);
+    a2dp_aptx_encoder_cb.stats.media_read_total_actual_read_bytes +=
+        pcm_bytes_read;
+    if (pcm_bytes_read < framing_params->pcm_bytes_per_read) {
+      LOG_WARN(LOG_TAG,
+               "%s: underflow at PCM reading iteration %zu: read %zu "
+               "instead of %d",
+               __func__, reads, pcm_bytes_read,
+               framing_params->pcm_bytes_per_read);
+      break;
+    }
+    a2dp_aptx_encoder_cb.stats.media_read_total_actual_reads_count++;
+    pcm_bytes_encoded += aptx_encode_16bit(framing_params, &encoded_ptr_index,
+                                           read_buffer16, encoded_ptr);
+  }
+
+  // Compute the number of encoded bytes
+  const int COMPRESSION_RATIO = 4;
+  size_t encoded_bytes = pcm_bytes_encoded / COMPRESSION_RATIO;
+  p_buf->len += encoded_bytes;
+  LOG_DEBUG(LOG_TAG, "%s: encoded %zu PCM bytes to %zu", __func__,
+            pcm_bytes_encoded, encoded_bytes);
+
+  // Update the RTP timestamp
+  *((uint32_t*)(p_buf + 1)) = a2dp_aptx_encoder_cb.timestamp;
+  const uint8_t BYTES_PER_FRAME = 2;
+  uint32_t rtp_timestamp =
+      (pcm_bytes_encoded / a2dp_aptx_encoder_cb.feeding_params.channel_count) /
+      BYTES_PER_FRAME;
+  a2dp_aptx_encoder_cb.timestamp += rtp_timestamp;
+
+  if (p_buf->len > 0) {
+    a2dp_aptx_encoder_cb.enqueue_callback(p_buf, 1);
+  } else {
+    a2dp_aptx_encoder_cb.stats.media_read_total_dropped_packets++;
+    osi_free(p_buf);
+  }
+}
+
+static size_t aptx_encode_16bit(tAPTX_FRAMING_PARAMS* framing_params,
+                                size_t* data_out_index, uint16_t* data16_in,
+                                uint8_t* data_out) {
+  size_t pcm_bytes_encoded = 0;
+  size_t frame = 0;
+
+  for (size_t aptx_samples = 0;
+       aptx_samples < framing_params->pcm_bytes_per_read / 16; aptx_samples++) {
+    uint32_t pcmL[4];
+    uint32_t pcmR[4];
+    uint16_t encoded_sample[2];
+
+    for (size_t i = 0, j = frame; i < 4; i++, j++) {
+      pcmL[i] = (uint16_t) * (data16_in + (2 * j));
+      pcmR[i] = (uint16_t) * (data16_in + ((2 * j) + 1));
+    }
+
+    aptx_encoder_encode_stereo_func(a2dp_aptx_encoder_cb.aptx_encoder_state,
+                                    &pcmL, &pcmR, &encoded_sample);
+
+    data_out[*data_out_index + 0] = (uint8_t)((encoded_sample[0] >> 8) & 0xff);
+    data_out[*data_out_index + 1] = (uint8_t)((encoded_sample[0] >> 0) & 0xff);
+    data_out[*data_out_index + 2] = (uint8_t)((encoded_sample[1] >> 8) & 0xff);
+    data_out[*data_out_index + 3] = (uint8_t)((encoded_sample[1] >> 0) & 0xff);
+
+    frame += 4;
+    pcm_bytes_encoded += 16;
+    *data_out_index += 4;
+  }
+
+  return pcm_bytes_encoded;
+}
+
+void a2dp_vendor_aptx_debug_codec_dump(int fd) {
+  a2dp_aptx_encoder_stats_t* stats = &a2dp_aptx_encoder_cb.stats;
+
+  dprintf(fd, "\nA2DP aptX State:\n");
+
+  dprintf(fd,
+          "  Packets expected/dropped                                : %zu / "
+          "%zu\n",
+          stats->media_read_total_expected_packets,
+          stats->media_read_total_dropped_packets);
+
+  dprintf(fd,
+          "  PCM reads count expected/actual                         : %zu / "
+          "%zu\n",
+          stats->media_read_total_expected_reads_count,
+          stats->media_read_total_actual_reads_count);
+
+  dprintf(fd,
+          "  PCM read bytes expected/actual                          : %zu / "
+          "%zu\n",
+          stats->media_read_total_expected_read_bytes,
+          stats->media_read_total_actual_read_bytes);
+}
diff --git a/stack/a2dp/a2dp_vendor_aptx_hd.cc b/stack/a2dp/a2dp_vendor_aptx_hd.cc
new file mode 100644
index 0000000..f86f4c2
--- /dev/null
+++ b/stack/a2dp/a2dp_vendor_aptx_hd.cc
@@ -0,0 +1,876 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/******************************************************************************
+ *
+ *  Utility functions to help build and parse the aptX-HD Codec Information
+ *  Element and Media Payload.
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "a2dp_vendor_aptx_hd"
+
+#include "bt_target.h"
+
+#include "a2dp_vendor_aptx_hd.h"
+
+#include <string.h>
+
+#include <base/logging.h>
+#include "a2dp_vendor.h"
+#include "a2dp_vendor_aptx_hd_encoder.h"
+#include "bt_utils.h"
+#include "osi/include/log.h"
+#include "osi/include/osi.h"
+
+// data type for the aptX-HD Codec Information Element */
+typedef struct {
+  uint32_t vendorId;
+  uint16_t codecId;    /* Codec ID for aptX-HD */
+  uint8_t sampleRate;  /* Sampling Frequency */
+  uint8_t channelMode; /* STEREO/DUAL/MONO */
+  uint8_t acl_sprint_reserved0;
+  uint8_t acl_sprint_reserved1;
+  uint8_t acl_sprint_reserved2;
+  uint8_t acl_sprint_reserved3;
+  btav_a2dp_codec_bits_per_sample_t bits_per_sample;
+} tA2DP_APTX_HD_CIE;
+
+/* aptX-HD Source codec capabilities */
+static const tA2DP_APTX_HD_CIE a2dp_aptx_hd_caps = {
+    A2DP_APTX_HD_VENDOR_ID,          /* vendorId */
+    A2DP_APTX_HD_CODEC_ID_BLUETOOTH, /* codecId */
+    (A2DP_APTX_HD_SAMPLERATE_44100 |
+     A2DP_APTX_HD_SAMPLERATE_48000),   /* sampleRate */
+    A2DP_APTX_HD_CHANNELS_STEREO,      /* channelMode */
+    A2DP_APTX_HD_ACL_SPRINT_RESERVED0, /* acl_sprint_reserved0 */
+    A2DP_APTX_HD_ACL_SPRINT_RESERVED1, /* acl_sprint_reserved1 */
+    A2DP_APTX_HD_ACL_SPRINT_RESERVED2, /* acl_sprint_reserved2 */
+    A2DP_APTX_HD_ACL_SPRINT_RESERVED3, /* acl_sprint_reserved3 */
+    BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24 /* bits_per_sample */
+};
+
+/* Default aptX-HD codec configuration */
+static const tA2DP_APTX_HD_CIE a2dp_aptx_hd_default_config = {
+    A2DP_APTX_HD_VENDOR_ID,            /* vendorId */
+    A2DP_APTX_HD_CODEC_ID_BLUETOOTH,   /* codecId */
+    A2DP_APTX_HD_SAMPLERATE_44100,     /* sampleRate */
+    A2DP_APTX_HD_CHANNELS_STEREO,      /* channelMode */
+    A2DP_APTX_HD_ACL_SPRINT_RESERVED0, /* acl_sprint_reserved0 */
+    A2DP_APTX_HD_ACL_SPRINT_RESERVED1, /* acl_sprint_reserved1 */
+    A2DP_APTX_HD_ACL_SPRINT_RESERVED2, /* acl_sprint_reserved2 */
+    A2DP_APTX_HD_ACL_SPRINT_RESERVED3, /* acl_sprint_reserved3 */
+    BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24 /* bits_per_sample */
+};
+
+static const tA2DP_ENCODER_INTERFACE a2dp_encoder_interface_aptx_hd = {
+    a2dp_vendor_aptx_hd_encoder_init,
+    a2dp_vendor_aptx_hd_encoder_cleanup,
+    a2dp_vendor_aptx_hd_feeding_init,
+    a2dp_vendor_aptx_hd_feeding_reset,
+    a2dp_vendor_aptx_hd_feeding_flush,
+    a2dp_vendor_aptx_hd_get_encoder_interval_ms,
+    a2dp_vendor_aptx_hd_send_frames,
+    a2dp_vendor_aptx_hd_debug_codec_dump};
+
+UNUSED_ATTR static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilityAptxHd(
+    const tA2DP_APTX_HD_CIE* p_cap, const uint8_t* p_codec_info,
+    bool is_peer_codec_info);
+
+// Builds the aptX-HD Media Codec Capabilities byte sequence beginning from the
+// LOSC octet. |media_type| is the media type |AVDT_MEDIA_TYPE_*|.
+// |p_ie| is a pointer to the aptX-HD Codec Information Element information.
+// The result is stored in |p_result|. Returns A2DP_SUCCESS on success,
+// otherwise the corresponding A2DP error status code.
+static tA2DP_STATUS A2DP_BuildInfoAptxHd(uint8_t media_type,
+                                         const tA2DP_APTX_HD_CIE* p_ie,
+                                         uint8_t* p_result) {
+  tA2DP_STATUS status;
+
+  if (p_ie == NULL || p_result == NULL) {
+    status = A2DP_INVALID_PARAMS;
+  } else {
+    status = A2DP_SUCCESS;
+    *p_result++ = A2DP_APTX_HD_CODEC_LEN;
+    *p_result++ = media_type;
+    *p_result++ = A2DP_MEDIA_CT_NON_A2DP;
+    *p_result++ = (uint8_t)(p_ie->vendorId & 0x000000FF);
+    *p_result++ = (uint8_t)((p_ie->vendorId & 0x0000FF00) >> 8);
+    *p_result++ = (uint8_t)((p_ie->vendorId & 0x00FF0000) >> 16);
+    *p_result++ = (uint8_t)((p_ie->vendorId & 0xFF000000) >> 24);
+    *p_result++ = (uint8_t)(p_ie->codecId & 0x00FF);
+    *p_result++ = (uint8_t)((p_ie->codecId & 0xFF00) >> 8);
+    *p_result++ = p_ie->sampleRate | p_ie->channelMode;
+    *p_result++ = p_ie->acl_sprint_reserved0;
+    *p_result++ = p_ie->acl_sprint_reserved1;
+    *p_result++ = p_ie->acl_sprint_reserved2;
+    *p_result++ = p_ie->acl_sprint_reserved3;
+  }
+  return status;
+}
+
+// Parses the aptX-HD Media Codec Capabilities byte sequence beginning from the
+// LOSC octet. The result is stored in |p_ie|. The byte sequence to parse is
+// |p_codec_info|. If |is_capability| is true, the byte sequence is
+// codec capabilities, otherwise is codec configuration.
+// Returns A2DP_SUCCESS on success, otherwise the corresponding A2DP error
+// status code.
+static tA2DP_STATUS A2DP_ParseInfoAptxHd(tA2DP_APTX_HD_CIE* p_ie,
+                                         const uint8_t* p_codec_info,
+                                         bool is_capability) {
+  tA2DP_STATUS status = A2DP_SUCCESS;
+  uint8_t losc;
+  uint8_t media_type;
+  tA2DP_CODEC_TYPE codec_type;
+
+  if (p_ie == NULL || p_codec_info == NULL) return A2DP_INVALID_PARAMS;
+
+  // Check the codec capability length
+  losc = *p_codec_info++;
+  if (losc != A2DP_APTX_HD_CODEC_LEN) return A2DP_WRONG_CODEC;
+
+  media_type = (*p_codec_info++) >> 4;
+  codec_type = *p_codec_info++;
+  /* Check the Media Type and Media Codec Type */
+  if (media_type != AVDT_MEDIA_TYPE_AUDIO ||
+      codec_type != A2DP_MEDIA_CT_NON_A2DP) {
+    return A2DP_WRONG_CODEC;
+  }
+
+  // Check the Vendor ID and Codec ID */
+  p_ie->vendorId = (*p_codec_info & 0x000000FF) |
+                   (*(p_codec_info + 1) << 8 & 0x0000FF00) |
+                   (*(p_codec_info + 2) << 16 & 0x00FF0000) |
+                   (*(p_codec_info + 3) << 24 & 0xFF000000);
+  p_codec_info = p_codec_info + 4;
+  p_ie->codecId =
+      (*p_codec_info & 0x00FF) | (*(p_codec_info + 1) << 8 & 0xFF00);
+  p_codec_info = p_codec_info + 2;
+  if (p_ie->vendorId != A2DP_APTX_HD_VENDOR_ID ||
+      p_ie->codecId != A2DP_APTX_HD_CODEC_ID_BLUETOOTH) {
+    return A2DP_WRONG_CODEC;
+  }
+
+  p_ie->channelMode = *p_codec_info & 0x0F;
+  p_ie->sampleRate = *p_codec_info & 0xF0;
+
+  p_codec_info++;
+  p_ie->acl_sprint_reserved0 = *(p_codec_info++);
+  p_ie->acl_sprint_reserved1 = *(p_codec_info++);
+  p_ie->acl_sprint_reserved2 = *(p_codec_info++);
+  p_ie->acl_sprint_reserved3 = *(p_codec_info++);
+
+  status = A2DP_SUCCESS;
+
+  if (is_capability) return status;
+
+  if (A2DP_BitsSet(p_ie->sampleRate) != A2DP_SET_ONE_BIT)
+    status = A2DP_BAD_SAMP_FREQ;
+  if (A2DP_BitsSet(p_ie->channelMode) != A2DP_SET_ONE_BIT)
+    status = A2DP_BAD_CH_MODE;
+
+  return status;
+}
+
+bool A2DP_IsVendorSourceCodecValidAptxHd(const uint8_t* p_codec_info) {
+  tA2DP_APTX_HD_CIE cfg_cie;
+
+  /* Use a liberal check when parsing the codec info */
+  return (A2DP_ParseInfoAptxHd(&cfg_cie, p_codec_info, false) ==
+          A2DP_SUCCESS) ||
+         (A2DP_ParseInfoAptxHd(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS);
+}
+
+bool A2DP_IsVendorPeerSinkCodecValidAptxHd(const uint8_t* p_codec_info) {
+  tA2DP_APTX_HD_CIE cfg_cie;
+
+  /* Use a liberal check when parsing the codec info */
+  return (A2DP_ParseInfoAptxHd(&cfg_cie, p_codec_info, false) ==
+          A2DP_SUCCESS) ||
+         (A2DP_ParseInfoAptxHd(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS);
+}
+
+// Checks whether A2DP aptX-HD codec configuration matches with a device's
+// codec capabilities. |p_cap| is the aptX-HD codec configuration.
+// |p_codec_info| is the device's codec capabilities.
+// If |is_capability| is true, the byte sequence is codec capabilities,
+// otherwise is codec configuration.
+// |p_codec_info| contains the codec capabilities for a peer device that
+// is acting as an A2DP source.
+// Returns A2DP_SUCCESS if the codec configuration matches with capabilities,
+// otherwise the corresponding A2DP error status code.
+static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilityAptxHd(
+    const tA2DP_APTX_HD_CIE* p_cap, const uint8_t* p_codec_info,
+    bool is_capability) {
+  tA2DP_STATUS status;
+  tA2DP_APTX_HD_CIE cfg_cie;
+
+  /* parse configuration */
+  status = A2DP_ParseInfoAptxHd(&cfg_cie, p_codec_info, is_capability);
+  if (status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: parsing failed %d", __func__, status);
+    return status;
+  }
+
+  /* verify that each parameter is in range */
+
+  LOG_DEBUG(LOG_TAG, "FREQ peer: 0x%x, capability 0x%x", cfg_cie.sampleRate,
+            p_cap->sampleRate);
+  LOG_DEBUG(LOG_TAG, "CH_MODE peer: 0x%x, capability 0x%x", cfg_cie.channelMode,
+            p_cap->channelMode);
+
+  /* sampling frequency */
+  if ((cfg_cie.sampleRate & p_cap->sampleRate) == 0) return A2DP_NS_SAMP_FREQ;
+
+  /* channel mode */
+  if ((cfg_cie.channelMode & p_cap->channelMode) == 0) return A2DP_NS_CH_MODE;
+
+  return A2DP_SUCCESS;
+}
+
+bool A2DP_VendorUsesRtpHeaderAptxHd(UNUSED_ATTR bool content_protection_enabled,
+                                    UNUSED_ATTR const uint8_t* p_codec_info) {
+  return true;
+}
+
+const char* A2DP_VendorCodecNameAptxHd(
+    UNUSED_ATTR const uint8_t* p_codec_info) {
+  return "aptX-HD";
+}
+
+bool A2DP_VendorCodecTypeEqualsAptxHd(const uint8_t* p_codec_info_a,
+                                      const uint8_t* p_codec_info_b) {
+  tA2DP_APTX_HD_CIE aptx_hd_cie_a;
+  tA2DP_APTX_HD_CIE aptx_hd_cie_b;
+
+  // Check whether the codec info contains valid data
+  tA2DP_STATUS a2dp_status =
+      A2DP_ParseInfoAptxHd(&aptx_hd_cie_a, p_codec_info_a, true);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+              a2dp_status);
+    return false;
+  }
+  a2dp_status = A2DP_ParseInfoAptxHd(&aptx_hd_cie_b, p_codec_info_b, true);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+              a2dp_status);
+    return false;
+  }
+
+  return true;
+}
+
+bool A2DP_VendorCodecEqualsAptxHd(const uint8_t* p_codec_info_a,
+                                  const uint8_t* p_codec_info_b) {
+  tA2DP_APTX_HD_CIE aptx_hd_cie_a;
+  tA2DP_APTX_HD_CIE aptx_hd_cie_b;
+
+  // Check whether the codec info contains valid data
+  tA2DP_STATUS a2dp_status =
+      A2DP_ParseInfoAptxHd(&aptx_hd_cie_a, p_codec_info_a, true);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+              a2dp_status);
+    return false;
+  }
+  a2dp_status = A2DP_ParseInfoAptxHd(&aptx_hd_cie_b, p_codec_info_b, true);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+              a2dp_status);
+    return false;
+  }
+
+  return (aptx_hd_cie_a.sampleRate == aptx_hd_cie_b.sampleRate) &&
+         (aptx_hd_cie_a.channelMode == aptx_hd_cie_b.channelMode);
+}
+
+int A2DP_VendorGetTrackSampleRateAptxHd(const uint8_t* p_codec_info) {
+  tA2DP_APTX_HD_CIE aptx_hd_cie;
+
+  // Check whether the codec info contains valid data
+  tA2DP_STATUS a2dp_status =
+      A2DP_ParseInfoAptxHd(&aptx_hd_cie, p_codec_info, false);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+              a2dp_status);
+    return -1;
+  }
+
+  if (aptx_hd_cie.sampleRate == A2DP_APTX_HD_SAMPLERATE_44100) return 44100;
+  if (aptx_hd_cie.sampleRate == A2DP_APTX_HD_SAMPLERATE_48000) return 48000;
+
+  return -1;
+}
+
+int A2DP_VendorGetTrackBitsPerSampleAptxHd(const uint8_t* p_codec_info) {
+  tA2DP_APTX_HD_CIE aptx_hd_cie;
+
+  // Check whether the codec info contains valid data
+  tA2DP_STATUS a2dp_status =
+      A2DP_ParseInfoAptxHd(&aptx_hd_cie, p_codec_info, false);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+              a2dp_status);
+    return -1;
+  }
+
+  return 24;  // For aptX-HD we always use 24 bits per audio sample
+}
+
+int A2DP_VendorGetTrackChannelCountAptxHd(const uint8_t* p_codec_info) {
+  tA2DP_APTX_HD_CIE aptx_hd_cie;
+
+  // Check whether the codec info contains valid data
+  tA2DP_STATUS a2dp_status =
+      A2DP_ParseInfoAptxHd(&aptx_hd_cie, p_codec_info, false);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: cannot decode codec information: %d", __func__,
+              a2dp_status);
+    return -1;
+  }
+
+  switch (aptx_hd_cie.channelMode) {
+    case A2DP_APTX_HD_CHANNELS_MONO:
+      return 1;
+    case A2DP_APTX_HD_CHANNELS_STEREO:
+      return 2;
+  }
+
+  return -1;
+}
+
+bool A2DP_VendorGetPacketTimestampAptxHd(const uint8_t* p_codec_info,
+                                         const uint8_t* p_data,
+                                         uint32_t* p_timestamp) {
+  // TODO: Is this function really codec-specific?
+  *p_timestamp = *(const uint32_t*)p_data;
+  return true;
+}
+
+bool A2DP_VendorBuildCodecHeaderAptxHd(UNUSED_ATTR const uint8_t* p_codec_info,
+                                       UNUSED_ATTR BT_HDR* p_buf,
+                                       UNUSED_ATTR uint16_t frames_per_packet) {
+  // Nothing to do
+  return true;
+}
+
+void A2DP_VendorDumpCodecInfoAptxHd(const uint8_t* p_codec_info) {
+  tA2DP_STATUS a2dp_status;
+  tA2DP_APTX_HD_CIE aptx_hd_cie;
+
+  LOG_DEBUG(LOG_TAG, "%s", __func__);
+
+  a2dp_status = A2DP_ParseInfoAptxHd(&aptx_hd_cie, p_codec_info, true);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: A2DP_ParseInfoAptxHd fail:%d", __func__,
+              a2dp_status);
+    return;
+  }
+
+  LOG_DEBUG(LOG_TAG, "\tsamp_freq: 0x%x", aptx_hd_cie.sampleRate);
+  if (aptx_hd_cie.sampleRate == A2DP_APTX_HD_SAMPLERATE_44100) {
+    LOG_DEBUG(LOG_TAG, "\tsamp_freq: (44100)");
+  }
+  if (aptx_hd_cie.sampleRate == A2DP_APTX_HD_SAMPLERATE_48000) {
+    LOG_DEBUG(LOG_TAG, "\tsamp_freq: (48000)");
+  }
+
+  LOG_DEBUG(LOG_TAG, "\tch_mode: 0x%x", aptx_hd_cie.channelMode);
+  if (aptx_hd_cie.channelMode == A2DP_APTX_HD_CHANNELS_MONO) {
+    LOG_DEBUG(LOG_TAG, "\tch_mode: (Mono)");
+  }
+  if (aptx_hd_cie.channelMode == A2DP_APTX_HD_CHANNELS_STEREO) {
+    LOG_DEBUG(LOG_TAG, "\tch_mode: (Stereo)");
+  }
+}
+
+const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterfaceAptxHd(
+    const uint8_t* p_codec_info) {
+  if (!A2DP_IsVendorSourceCodecValidAptxHd(p_codec_info)) return NULL;
+
+  return &a2dp_encoder_interface_aptx_hd;
+}
+
+bool A2DP_VendorAdjustCodecAptxHd(uint8_t* p_codec_info) {
+  tA2DP_APTX_HD_CIE cfg_cie;
+
+  // Nothing to do: just verify the codec info is valid
+  if (A2DP_ParseInfoAptxHd(&cfg_cie, p_codec_info, true) != A2DP_SUCCESS)
+    return false;
+
+  return true;
+}
+
+btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndexAptxHd(
+    const uint8_t* p_codec_info) {
+  return BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD;
+}
+
+const char* A2DP_VendorCodecIndexStrAptxHd(void) { return "aptX-HD"; }
+
+bool A2DP_VendorInitCodecConfigAptxHd(tAVDT_CFG* p_cfg) {
+  if (A2DP_BuildInfoAptxHd(AVDT_MEDIA_TYPE_AUDIO, &a2dp_aptx_hd_caps,
+                           p_cfg->codec_info) != A2DP_SUCCESS) {
+    return false;
+  }
+
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+  /* Content protection info - support SCMS-T */
+  uint8_t* p = p_cfg->protect_info;
+  *p++ = AVDT_CP_LOSC;
+  UINT16_TO_STREAM(p, AVDT_CP_SCMS_T_ID);
+  p_cfg->num_protect = 1;
+#endif
+
+  return true;
+}
+
+A2dpCodecConfigAptxHd::A2dpCodecConfigAptxHd()
+    : A2dpCodecConfig(BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD, "aptX-HD") {}
+
+A2dpCodecConfigAptxHd::~A2dpCodecConfigAptxHd() {}
+
+bool A2dpCodecConfigAptxHd::init() {
+  if (!isValid()) return false;
+
+  // Load the encoder
+  if (!A2DP_VendorLoadEncoderAptxHd()) {
+    LOG_ERROR(LOG_TAG, "%s: cannot load the encoder", __func__);
+    return false;
+  }
+
+  return true;
+}
+
+//
+// Selects the best sample rate from |sampleRate|.
+// The result is stored in |p_result| and p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_best_sample_rate(uint8_t sampleRate,
+                                    tA2DP_APTX_HD_CIE* p_result,
+                                    btav_a2dp_codec_config_t* p_codec_config) {
+  if (sampleRate & A2DP_APTX_HD_SAMPLERATE_48000) {
+    p_result->sampleRate = A2DP_APTX_HD_SAMPLERATE_48000;
+    p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+    return true;
+  }
+  if (sampleRate & A2DP_APTX_HD_SAMPLERATE_44100) {
+    p_result->sampleRate = A2DP_APTX_HD_SAMPLERATE_44100;
+    p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
+    return true;
+  }
+  return false;
+}
+
+//
+// Selects the audio sample rate from |p_codec_audio_config|.
+// |sampleRate| contains the capability.
+// The result is stored in |p_result| and |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_audio_sample_rate(
+    const btav_a2dp_codec_config_t* p_codec_audio_config, uint8_t sampleRate,
+    tA2DP_APTX_HD_CIE* p_result, btav_a2dp_codec_config_t* p_codec_config) {
+  switch (p_codec_audio_config->sample_rate) {
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_44100:
+      if (sampleRate & A2DP_APTX_HD_SAMPLERATE_44100) {
+        p_result->sampleRate = A2DP_APTX_HD_SAMPLERATE_44100;
+        p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
+        return true;
+      }
+      break;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_48000:
+      if (sampleRate & A2DP_APTX_HD_SAMPLERATE_48000) {
+        p_result->sampleRate = A2DP_APTX_HD_SAMPLERATE_48000;
+        p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+        return true;
+      }
+      break;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_88200:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_96000:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_176400:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_192000:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_NONE:
+      break;
+  }
+  return false;
+}
+
+//
+// Selects the best bits per sample.
+// The result is stored in |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_best_bits_per_sample(
+    btav_a2dp_codec_config_t* p_codec_config) {
+  p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24;
+  return true;
+}
+
+//
+// Selects the audio bits per sample from |p_codec_audio_config|.
+// The result is stored in |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_audio_bits_per_sample(
+    const btav_a2dp_codec_config_t* p_codec_audio_config,
+    btav_a2dp_codec_config_t* p_codec_config) {
+  switch (p_codec_audio_config->bits_per_sample) {
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24:
+      p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24;
+      return true;
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16:
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE:
+      break;
+  }
+  return false;
+}
+
+//
+// Selects the best channel mode from |channelMode|.
+// The result is stored in |p_result| and |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_best_channel_mode(uint8_t channelMode,
+                                     tA2DP_APTX_HD_CIE* p_result,
+                                     btav_a2dp_codec_config_t* p_codec_config) {
+  if (channelMode & A2DP_APTX_HD_CHANNELS_STEREO) {
+    p_result->channelMode = A2DP_APTX_HD_CHANNELS_STEREO;
+    p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+    return true;
+  }
+  if (channelMode & A2DP_APTX_HD_CHANNELS_MONO) {
+    p_result->channelMode = A2DP_APTX_HD_CHANNELS_MONO;
+    p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+    return true;
+  }
+  return false;
+}
+
+//
+// Selects the audio channel mode from |p_codec_audio_config|.
+// |channelMode| contains the capability.
+// The result is stored in |p_result| and |p_codec_config|.
+// Returns true if a selection was made, otherwise false.
+//
+static bool select_audio_channel_mode(
+    const btav_a2dp_codec_config_t* p_codec_audio_config, uint8_t channelMode,
+    tA2DP_APTX_HD_CIE* p_result, btav_a2dp_codec_config_t* p_codec_config) {
+  switch (p_codec_audio_config->channel_mode) {
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_MONO:
+      if (channelMode & A2DP_APTX_HD_CHANNELS_MONO) {
+        p_result->channelMode = A2DP_APTX_HD_CHANNELS_MONO;
+        p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+        return true;
+      }
+      break;
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO:
+      if (channelMode & A2DP_APTX_HD_CHANNELS_STEREO) {
+        p_result->channelMode = A2DP_APTX_HD_CHANNELS_STEREO;
+        p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+        return true;
+      }
+      break;
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_NONE:
+      break;
+  }
+
+  return false;
+}
+
+bool A2dpCodecConfigAptxHd::setCodecConfig(const uint8_t* p_peer_codec_info,
+                                           bool is_capability,
+                                           uint8_t* p_result_codec_config) {
+  std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
+  tA2DP_APTX_HD_CIE sink_info_cie;
+  tA2DP_APTX_HD_CIE result_config_cie;
+  uint8_t sampleRate;
+  uint8_t channelMode;
+
+  // Save the internal state
+  btav_a2dp_codec_config_t saved_codec_config = codec_config_;
+  btav_a2dp_codec_config_t saved_codec_capability = codec_capability_;
+  btav_a2dp_codec_config_t saved_codec_user_config = codec_user_config_;
+  btav_a2dp_codec_config_t saved_codec_audio_config = codec_audio_config_;
+  uint8_t saved_ota_codec_config[AVDT_CODEC_SIZE];
+  uint8_t saved_ota_codec_peer_capability[AVDT_CODEC_SIZE];
+  uint8_t saved_ota_codec_peer_config[AVDT_CODEC_SIZE];
+  memcpy(saved_ota_codec_config, ota_codec_config_, sizeof(ota_codec_config_));
+  memcpy(saved_ota_codec_peer_capability, ota_codec_peer_capability_,
+         sizeof(ota_codec_peer_capability_));
+  memcpy(saved_ota_codec_peer_config, ota_codec_peer_config_,
+         sizeof(ota_codec_peer_config_));
+
+  tA2DP_STATUS status =
+      A2DP_ParseInfoAptxHd(&sink_info_cie, p_peer_codec_info, is_capability);
+  if (status != A2DP_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: can't parse peer's Sink capabilities: error = %d",
+              __func__, status);
+    goto fail;
+  }
+
+  //
+  // Build the preferred configuration
+  //
+  memset(&result_config_cie, 0, sizeof(result_config_cie));
+  result_config_cie.vendorId = a2dp_aptx_hd_caps.vendorId;
+  result_config_cie.codecId = a2dp_aptx_hd_caps.codecId;
+
+  //
+  // Select the sample frequency
+  //
+  sampleRate = a2dp_aptx_hd_caps.sampleRate & sink_info_cie.sampleRate;
+  codec_config_.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE;
+  switch (codec_user_config_.sample_rate) {
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_44100:
+      if (sampleRate & A2DP_APTX_HD_SAMPLERATE_44100) {
+        result_config_cie.sampleRate = A2DP_APTX_HD_SAMPLERATE_44100;
+        codec_capability_.sample_rate = codec_user_config_.sample_rate;
+        codec_config_.sample_rate = codec_user_config_.sample_rate;
+      }
+      break;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_48000:
+      if (sampleRate & A2DP_APTX_HD_SAMPLERATE_48000) {
+        result_config_cie.sampleRate = A2DP_APTX_HD_SAMPLERATE_48000;
+        codec_capability_.sample_rate = codec_user_config_.sample_rate;
+        codec_config_.sample_rate = codec_user_config_.sample_rate;
+      }
+      break;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_88200:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_96000:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_176400:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_192000:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_NONE:
+      codec_capability_.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE;
+      codec_config_.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE;
+      break;
+  }
+
+  // Select the sample frequency if there is no user preference
+  do {
+    if (codec_config_.sample_rate != BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) break;
+
+    // Compute the common capability
+    if (sampleRate & A2DP_APTX_HD_SAMPLERATE_44100)
+      codec_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
+    if (sampleRate & A2DP_APTX_HD_SAMPLERATE_48000)
+      codec_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+
+    // No user preference - try the codec audio config
+    if (select_audio_sample_rate(&codec_audio_config_, sampleRate,
+                                 &result_config_cie, &codec_config_)) {
+      break;
+    }
+
+    // No user preference - try the default config
+    if (select_best_sample_rate(
+            a2dp_aptx_hd_default_config.sampleRate & sink_info_cie.sampleRate,
+            &result_config_cie, &codec_config_)) {
+      break;
+    }
+
+    // No user preference - use the best match
+    if (select_best_sample_rate(sampleRate, &result_config_cie,
+                                &codec_config_)) {
+      break;
+    }
+  } while (false);
+  if (codec_config_.sample_rate == BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) {
+    LOG_ERROR(LOG_TAG,
+              "%s: cannot match sample frequency: source caps = 0x%x "
+              "sink info = 0x%x",
+              __func__, a2dp_aptx_hd_caps.sampleRate, sink_info_cie.sampleRate);
+    goto fail;
+  }
+
+  //
+  // Select the bits per sample
+  //
+  // NOTE: this information is NOT included in the aptX-HD A2DP codec
+  // description that is sent OTA.
+  codec_config_.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE;
+  switch (codec_user_config_.bits_per_sample) {
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24:
+      codec_capability_.bits_per_sample = codec_user_config_.bits_per_sample;
+      codec_config_.bits_per_sample = codec_user_config_.bits_per_sample;
+      break;
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16:
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE:
+      codec_capability_.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE;
+      codec_config_.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE;
+      break;
+  }
+
+  // Select the bits per sample if there is no user preference
+  do {
+    if (codec_config_.bits_per_sample != BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE)
+      break;
+
+    // Compute the common capability
+    codec_capability_.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24;
+
+    // No user preference - try the codec audio config
+    if (select_audio_bits_per_sample(&codec_audio_config_, &codec_config_)) {
+      break;
+    }
+
+    // No user preference - try the default config
+    if (select_best_bits_per_sample(&codec_config_)) {
+      break;
+    }
+
+    // No user preference - use the best match
+    // NOTE: no-op - kept here for consistency
+    if (select_best_bits_per_sample(&codec_config_)) {
+      break;
+    }
+  } while (false);
+  if (codec_config_.bits_per_sample == BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) {
+    LOG_ERROR(LOG_TAG,
+              "%s: cannot match bits per sample: user preference = 0x%x",
+              __func__, codec_user_config_.bits_per_sample);
+    goto fail;
+  }
+
+  //
+  // Select the channel mode
+  //
+  channelMode = a2dp_aptx_hd_caps.channelMode & sink_info_cie.channelMode;
+  codec_config_.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE;
+  switch (codec_user_config_.channel_mode) {
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_MONO:
+      if (channelMode & A2DP_APTX_HD_CHANNELS_MONO) {
+        result_config_cie.channelMode = A2DP_APTX_HD_CHANNELS_MONO;
+        codec_capability_.channel_mode = codec_user_config_.channel_mode;
+        codec_config_.channel_mode = codec_user_config_.channel_mode;
+      }
+      break;
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO:
+      if (channelMode & A2DP_APTX_HD_CHANNELS_STEREO) {
+        result_config_cie.channelMode = A2DP_APTX_HD_CHANNELS_STEREO;
+        codec_capability_.channel_mode = codec_user_config_.channel_mode;
+        codec_config_.channel_mode = codec_user_config_.channel_mode;
+      }
+      break;
+    case BTAV_A2DP_CODEC_CHANNEL_MODE_NONE:
+      codec_capability_.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE;
+      codec_config_.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE;
+      break;
+  }
+
+  // Select the channel mode if there is no user preference
+  do {
+    if (codec_config_.channel_mode != BTAV_A2DP_CODEC_CHANNEL_MODE_NONE) break;
+
+    // Compute the common capability
+    if (channelMode & A2DP_APTX_HD_CHANNELS_MONO)
+      codec_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+    if (channelMode & A2DP_APTX_HD_CHANNELS_STEREO)
+      codec_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+
+    // No user preference - try the codec audio config
+    if (select_audio_channel_mode(&codec_audio_config_, channelMode,
+                                  &result_config_cie, &codec_config_)) {
+      break;
+    }
+
+    // No user preference - try the default config
+    if (select_best_channel_mode(
+            a2dp_aptx_hd_default_config.channelMode & sink_info_cie.channelMode,
+            &result_config_cie, &codec_config_)) {
+      break;
+    }
+
+    // No user preference - use the best match
+    if (select_best_channel_mode(channelMode, &result_config_cie,
+                                 &codec_config_)) {
+      break;
+    }
+  } while (false);
+  if (codec_config_.channel_mode == BTAV_A2DP_CODEC_CHANNEL_MODE_NONE) {
+    LOG_ERROR(LOG_TAG,
+              "%s: cannot match channel mode: source caps = 0x%x "
+              "sink info = 0x%x",
+              __func__, a2dp_aptx_hd_caps.channelMode,
+              sink_info_cie.channelMode);
+    goto fail;
+  }
+
+  //
+  // Set the rest of the fields as bit-wise AND operation
+  //
+  result_config_cie.acl_sprint_reserved0 =
+      a2dp_aptx_hd_caps.acl_sprint_reserved0 &
+      sink_info_cie.acl_sprint_reserved0;
+  result_config_cie.acl_sprint_reserved1 =
+      a2dp_aptx_hd_caps.acl_sprint_reserved1 &
+      sink_info_cie.acl_sprint_reserved1;
+  result_config_cie.acl_sprint_reserved2 =
+      a2dp_aptx_hd_caps.acl_sprint_reserved2 &
+      sink_info_cie.acl_sprint_reserved2;
+  result_config_cie.acl_sprint_reserved3 =
+      a2dp_aptx_hd_caps.acl_sprint_reserved3 &
+      sink_info_cie.acl_sprint_reserved3;
+
+  if (A2DP_BuildInfoAptxHd(AVDT_MEDIA_TYPE_AUDIO, &result_config_cie,
+                           p_result_codec_config) != A2DP_SUCCESS) {
+    goto fail;
+  }
+
+  //
+  // Copy the codec-specific fields if they are not zero
+  //
+  if (codec_user_config_.codec_specific_1 != 0)
+    codec_config_.codec_specific_1 = codec_user_config_.codec_specific_1;
+  if (codec_user_config_.codec_specific_2 != 0)
+    codec_config_.codec_specific_2 = codec_user_config_.codec_specific_2;
+  if (codec_user_config_.codec_specific_3 != 0)
+    codec_config_.codec_specific_3 = codec_user_config_.codec_specific_3;
+  if (codec_user_config_.codec_specific_4 != 0)
+    codec_config_.codec_specific_4 = codec_user_config_.codec_specific_4;
+
+  // Create a local copy of the peer codec capability/config, and the
+  // result codec config.
+  if (is_capability) {
+    status = A2DP_BuildInfoAptxHd(AVDT_MEDIA_TYPE_AUDIO, &sink_info_cie,
+                                  ota_codec_peer_capability_);
+  } else {
+    status = A2DP_BuildInfoAptxHd(AVDT_MEDIA_TYPE_AUDIO, &sink_info_cie,
+                                  ota_codec_peer_config_);
+  }
+  CHECK(status == A2DP_SUCCESS);
+  status = A2DP_BuildInfoAptxHd(AVDT_MEDIA_TYPE_AUDIO, &result_config_cie,
+                                ota_codec_config_);
+  CHECK(status == A2DP_SUCCESS);
+  return true;
+
+fail:
+  // Restore the internal state
+  codec_config_ = saved_codec_config;
+  codec_capability_ = saved_codec_capability;
+  codec_user_config_ = saved_codec_user_config;
+  codec_audio_config_ = saved_codec_audio_config;
+  memcpy(ota_codec_config_, saved_ota_codec_config, sizeof(ota_codec_config_));
+  memcpy(ota_codec_peer_capability_, saved_ota_codec_peer_capability,
+         sizeof(ota_codec_peer_capability_));
+  memcpy(ota_codec_peer_config_, saved_ota_codec_peer_config,
+         sizeof(ota_codec_peer_config_));
+  return false;
+}
diff --git a/stack/a2dp/a2dp_vendor_aptx_hd_encoder.cc b/stack/a2dp/a2dp_vendor_aptx_hd_encoder.cc
new file mode 100644
index 0000000..0370302
--- /dev/null
+++ b/stack/a2dp/a2dp_vendor_aptx_hd_encoder.cc
@@ -0,0 +1,575 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "a2dp_vendor_aptx_hd_encoder"
+
+#include "a2dp_vendor_aptx_hd_encoder.h"
+
+#include <dlfcn.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "a2dp_vendor.h"
+#include "a2dp_vendor_aptx_hd.h"
+#include "bt_common.h"
+#include "osi/include/log.h"
+#include "osi/include/osi.h"
+
+//
+// Encoder for aptX-HD Source Codec
+//
+
+//
+// The aptX-HD encoder shared library, and the functions to use
+//
+static const char* APTX_HD_ENCODER_LIB_NAME = "libaptXHD.so";
+static void* aptx_hd_encoder_lib_handle = NULL;
+
+static const char* APTX_HD_ENCODER_INIT_NAME = "aptxhdbtenc_init";
+typedef int (*tAPTX_HD_ENCODER_INIT)(void* state, short endian);
+
+static const char* APTX_HD_ENCODER_ENCODE_STEREO_NAME =
+    "aptxhdbtenc_encodestereo";
+typedef int (*tAPTX_HD_ENCODER_ENCODE_STEREO)(void* state, void* pcmL,
+                                              void* pcmR, void* buffer);
+
+static const char* APTX_HD_ENCODER_SIZEOF_PARAMS_NAME = "SizeofAptxhdbtenc";
+typedef int (*tAPTX_HD_ENCODER_SIZEOF_PARAMS)(void);
+
+static tAPTX_HD_ENCODER_INIT aptx_hd_encoder_init_func;
+static tAPTX_HD_ENCODER_ENCODE_STEREO aptx_hd_encoder_encode_stereo_func;
+static tAPTX_HD_ENCODER_SIZEOF_PARAMS aptx_hd_encoder_sizeof_params_func;
+
+// offset
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+#define A2DP_APTX_HD_OFFSET (AVDT_MEDIA_OFFSET + 1)
+#else
+#define A2DP_APTX_HD_OFFSET AVDT_MEDIA_OFFSET
+#endif
+
+#define A2DP_APTX_HD_MAX_PCM_BYTES_PER_READ 1024
+
+typedef struct {
+  uint64_t sleep_time_ns;
+  uint32_t pcm_reads;
+  uint32_t pcm_bytes_per_read;
+  uint32_t aptx_hd_bytes;
+  uint32_t frame_size_counter;
+} tAPTX_HD_FRAMING_PARAMS;
+
+typedef struct {
+  uint64_t session_start_us;
+
+  size_t media_read_total_expected_packets;
+  size_t media_read_total_expected_reads_count;
+  size_t media_read_total_expected_read_bytes;
+
+  size_t media_read_total_dropped_packets;
+  size_t media_read_total_actual_reads_count;
+  size_t media_read_total_actual_read_bytes;
+} a2dp_aptx_hd_encoder_stats_t;
+
+typedef struct {
+  a2dp_source_read_callback_t read_callback;
+  a2dp_source_enqueue_callback_t enqueue_callback;
+
+  bool use_SCMS_T;
+  bool is_peer_edr;          // True if the peer device supports EDR
+  bool peer_supports_3mbps;  // True if the peer device supports 3Mbps EDR
+  uint16_t peer_mtu;         // // MTU of the A2DP peer
+  uint32_t timestamp;        // Timestamp for the A2DP frames
+
+  tA2DP_FEEDING_PARAMS feeding_params;
+  tAPTX_HD_FRAMING_PARAMS framing_params;
+  void* aptx_hd_encoder_state;
+  a2dp_aptx_hd_encoder_stats_t stats;
+} tA2DP_APTX_HD_ENCODER_CB;
+
+static tA2DP_APTX_HD_ENCODER_CB a2dp_aptx_hd_encoder_cb;
+
+static void a2dp_vendor_aptx_hd_encoder_update(
+    uint16_t peer_mtu, A2dpCodecConfig* a2dp_codec_config,
+    bool* p_restart_input, bool* p_restart_output, bool* p_config_updated);
+static void aptx_hd_init_framing_params(
+    tAPTX_HD_FRAMING_PARAMS* framing_params);
+static void aptx_hd_update_framing_params(
+    tAPTX_HD_FRAMING_PARAMS* framing_params);
+static size_t aptx_hd_encode_24bit(tAPTX_HD_FRAMING_PARAMS* framing_params,
+                                   size_t* data_out_index, uint32_t* data32_in,
+                                   uint8_t* data_out);
+
+bool A2DP_VendorLoadEncoderAptxHd(void) {
+  if (aptx_hd_encoder_lib_handle != NULL) return true;  // Already loaded
+
+  // Open the encoder library
+  aptx_hd_encoder_lib_handle = dlopen(APTX_HD_ENCODER_LIB_NAME, RTLD_NOW);
+  if (aptx_hd_encoder_lib_handle == NULL) {
+    LOG_ERROR(LOG_TAG, "%s: cannot open aptX-HD encoder library %s: %s",
+              __func__, APTX_HD_ENCODER_LIB_NAME, dlerror());
+    return false;
+  }
+
+  aptx_hd_encoder_init_func = (tAPTX_HD_ENCODER_INIT)dlsym(
+      aptx_hd_encoder_lib_handle, APTX_HD_ENCODER_INIT_NAME);
+  if (aptx_hd_encoder_init_func == NULL) {
+    LOG_ERROR(LOG_TAG,
+              "%s: cannot find function '%s' in the encoder library: %s",
+              __func__, APTX_HD_ENCODER_INIT_NAME, dlerror());
+    A2DP_VendorUnloadEncoderAptxHd();
+    return false;
+  }
+
+  aptx_hd_encoder_encode_stereo_func = (tAPTX_HD_ENCODER_ENCODE_STEREO)dlsym(
+      aptx_hd_encoder_lib_handle, APTX_HD_ENCODER_ENCODE_STEREO_NAME);
+  if (aptx_hd_encoder_encode_stereo_func == NULL) {
+    LOG_ERROR(LOG_TAG,
+              "%s: cannot find function '%s' in the encoder library: %s",
+              __func__, APTX_HD_ENCODER_ENCODE_STEREO_NAME, dlerror());
+    A2DP_VendorUnloadEncoderAptxHd();
+    return false;
+  }
+
+  aptx_hd_encoder_sizeof_params_func = (tAPTX_HD_ENCODER_SIZEOF_PARAMS)dlsym(
+      aptx_hd_encoder_lib_handle, APTX_HD_ENCODER_SIZEOF_PARAMS_NAME);
+  if (aptx_hd_encoder_sizeof_params_func == NULL) {
+    LOG_ERROR(LOG_TAG,
+              "%s: cannot find function '%s' in the encoder library: %s",
+              __func__, APTX_HD_ENCODER_SIZEOF_PARAMS_NAME, dlerror());
+    A2DP_VendorUnloadEncoderAptxHd();
+    return false;
+  }
+
+  return true;
+}
+
+void A2DP_VendorUnloadEncoderAptxHd(void) {
+  aptx_hd_encoder_init_func = NULL;
+  aptx_hd_encoder_encode_stereo_func = NULL;
+  aptx_hd_encoder_sizeof_params_func = NULL;
+
+  if (aptx_hd_encoder_lib_handle != NULL) {
+    dlclose(aptx_hd_encoder_lib_handle);
+    aptx_hd_encoder_lib_handle = NULL;
+  }
+}
+
+void a2dp_vendor_aptx_hd_encoder_init(
+    const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+    A2dpCodecConfig* a2dp_codec_config,
+    a2dp_source_read_callback_t read_callback,
+    a2dp_source_enqueue_callback_t enqueue_callback) {
+  memset(&a2dp_aptx_hd_encoder_cb, 0, sizeof(a2dp_aptx_hd_encoder_cb));
+
+  a2dp_aptx_hd_encoder_cb.stats.session_start_us = time_get_os_boottime_us();
+
+  a2dp_aptx_hd_encoder_cb.read_callback = read_callback;
+  a2dp_aptx_hd_encoder_cb.enqueue_callback = enqueue_callback;
+  a2dp_aptx_hd_encoder_cb.is_peer_edr = p_peer_params->is_peer_edr;
+  a2dp_aptx_hd_encoder_cb.peer_supports_3mbps =
+      p_peer_params->peer_supports_3mbps;
+  a2dp_aptx_hd_encoder_cb.peer_mtu = p_peer_params->peer_mtu;
+  a2dp_aptx_hd_encoder_cb.timestamp = 0;
+
+  /* aptX-HD encoder config */
+  a2dp_aptx_hd_encoder_cb.use_SCMS_T = false;  // TODO: should be a parameter
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+  a2dp_aptx_hd_encoder_cb.use_SCMS_T = true;
+#endif
+
+  a2dp_aptx_hd_encoder_cb.aptx_hd_encoder_state =
+      osi_malloc(aptx_hd_encoder_sizeof_params_func());
+  if (a2dp_aptx_hd_encoder_cb.aptx_hd_encoder_state != NULL) {
+    aptx_hd_encoder_init_func(a2dp_aptx_hd_encoder_cb.aptx_hd_encoder_state, 0);
+  } else {
+    LOG_ERROR(LOG_TAG, "%s: Cannot allocate aptX-HD encoder state", __func__);
+    // TODO: Return an error?
+  }
+
+  // NOTE: Ignore the restart_input / restart_output flags - this initization
+  // happens when the connection is (re)started.
+  bool restart_input = false;
+  bool restart_output = false;
+  bool config_updated = false;
+  a2dp_vendor_aptx_hd_encoder_update(a2dp_aptx_hd_encoder_cb.peer_mtu,
+                                     a2dp_codec_config, &restart_input,
+                                     &restart_output, &config_updated);
+  aptx_hd_init_framing_params(&a2dp_aptx_hd_encoder_cb.framing_params);
+}
+
+bool A2dpCodecConfigAptxHd::updateEncoderUserConfig(
+    const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params, bool* p_restart_input,
+    bool* p_restart_output, bool* p_config_updated) {
+  a2dp_aptx_hd_encoder_cb.is_peer_edr = p_peer_params->is_peer_edr;
+  a2dp_aptx_hd_encoder_cb.peer_supports_3mbps =
+      p_peer_params->peer_supports_3mbps;
+  a2dp_aptx_hd_encoder_cb.peer_mtu = p_peer_params->peer_mtu;
+  a2dp_aptx_hd_encoder_cb.timestamp = 0;
+
+  if (a2dp_aptx_hd_encoder_cb.peer_mtu == 0) {
+    LOG_ERROR(LOG_TAG,
+              "%s: Cannot update the codec encoder for %s: "
+              "invalid peer MTU",
+              __func__, name().c_str());
+    return false;
+  }
+
+  a2dp_vendor_aptx_hd_encoder_update(a2dp_aptx_hd_encoder_cb.peer_mtu, this,
+                                     p_restart_input, p_restart_output,
+                                     p_config_updated);
+  return true;
+}
+
+// Update the A2DP aptX-HD encoder.
+// |peer_mtu| is the peer MTU.
+// |a2dp_codec_config| is the A2DP codec to use for the update.
+static void a2dp_vendor_aptx_hd_encoder_update(
+    uint16_t peer_mtu, A2dpCodecConfig* a2dp_codec_config,
+    bool* p_restart_input, bool* p_restart_output, bool* p_config_updated) {
+  uint8_t codec_info[AVDT_CODEC_SIZE];
+
+  *p_restart_input = false;
+  *p_restart_output = false;
+  *p_config_updated = false;
+  if (!a2dp_codec_config->copyOutOtaCodecConfig(codec_info)) {
+    LOG_ERROR(LOG_TAG,
+              "%s: Cannot update the codec encoder for %s: "
+              "invalid codec config",
+              __func__, a2dp_codec_config->name().c_str());
+    return;
+  }
+  const uint8_t* p_codec_info = codec_info;
+
+  // The feeding parameters
+  a2dp_aptx_hd_encoder_cb.feeding_params.sample_rate =
+      A2DP_VendorGetTrackSampleRateAptxHd(p_codec_info);
+  a2dp_aptx_hd_encoder_cb.feeding_params.bits_per_sample =
+      A2DP_VendorGetTrackBitsPerSampleAptxHd(p_codec_info);
+  a2dp_aptx_hd_encoder_cb.feeding_params.channel_count =
+      A2DP_VendorGetTrackChannelCountAptxHd(p_codec_info);
+}
+
+void a2dp_vendor_aptx_hd_encoder_cleanup(void) {
+  osi_free(a2dp_aptx_hd_encoder_cb.aptx_hd_encoder_state);
+  memset(&a2dp_aptx_hd_encoder_cb, 0, sizeof(a2dp_aptx_hd_encoder_cb));
+}
+
+void a2dp_vendor_aptx_hd_feeding_init(
+    const tA2DP_FEEDING_PARAMS* p_feeding_params) {
+  LOG_DEBUG(LOG_TAG,
+            "%s: PCM feeding: sample_rate:%d bits_per_sample:%d"
+            "channel_count:%d",
+            __func__, p_feeding_params->sample_rate,
+            p_feeding_params->bits_per_sample, p_feeding_params->channel_count);
+
+  // Check the PCM feeding sample_rate
+  switch (p_feeding_params->sample_rate) {
+    case 44100:
+    case 48000:
+      break;
+    default:
+      LOG_WARN(LOG_TAG, "%s: feeding PCM sample_rate %u is not supported",
+               __func__, p_feeding_params->sample_rate);
+      return;
+  }
+
+  // Check the bits per sample
+  switch (p_feeding_params->bits_per_sample) {
+    case 24:
+      break;
+    default:
+      LOG_WARN(LOG_TAG, "%s: feeding PCM bits_per_sample %u is not supported",
+               __func__, p_feeding_params->bits_per_sample);
+      return;
+  }
+
+  // Check the number of channels
+  switch (p_feeding_params->channel_count) {
+    case 1:  // Mono
+    case 2:  // Stereo
+      break;
+    default:
+      LOG_WARN(LOG_TAG, "%s: feeding PCM channel_count %u is not supported",
+               __func__, p_feeding_params->channel_count);
+      return;
+  }
+
+  bool reconfig_needed = false;
+  if (a2dp_aptx_hd_encoder_cb.feeding_params.sample_rate !=
+      p_feeding_params->sample_rate) {
+    LOG_DEBUG(LOG_TAG,
+              "%s: codec reconfiguration: feeding PCM sample_rate "
+              "from %u to %u",
+              __func__, a2dp_aptx_hd_encoder_cb.feeding_params.sample_rate,
+              p_feeding_params->sample_rate);
+    reconfig_needed = true;
+    a2dp_aptx_hd_encoder_cb.feeding_params.sample_rate =
+        p_feeding_params->sample_rate;
+  }
+  if (a2dp_aptx_hd_encoder_cb.feeding_params.bits_per_sample !=
+      p_feeding_params->bits_per_sample) {
+    LOG_DEBUG(LOG_TAG,
+              "%s: aptX-HD reconfiguration needed: "
+              "feeding PCM bits_per_sample from %u to %u",
+              __func__, a2dp_aptx_hd_encoder_cb.feeding_params.bits_per_sample,
+              p_feeding_params->bits_per_sample);
+    a2dp_aptx_hd_encoder_cb.feeding_params.bits_per_sample =
+        p_feeding_params->bits_per_sample;
+    reconfig_needed = true;
+  }
+  if (a2dp_aptx_hd_encoder_cb.feeding_params.channel_count !=
+      p_feeding_params->channel_count) {
+    LOG_DEBUG(LOG_TAG,
+              "%s: aptX-HD reconfiguration needed: "
+              "feeding PCM channel_count from %u to %u",
+              __func__, a2dp_aptx_hd_encoder_cb.feeding_params.channel_count,
+              p_feeding_params->channel_count);
+    a2dp_aptx_hd_encoder_cb.feeding_params.channel_count =
+        p_feeding_params->channel_count;
+    reconfig_needed = true;
+  }
+
+  if (reconfig_needed) {
+    LOG_DEBUG(LOG_TAG, "%s: sample_rate=%u bits_per_sample=%u channel_count=%u",
+              __func__, p_feeding_params->sample_rate,
+              p_feeding_params->bits_per_sample,
+              p_feeding_params->channel_count);
+    aptx_hd_init_framing_params(&a2dp_aptx_hd_encoder_cb.framing_params);
+  } else {
+    LOG_DEBUG(LOG_TAG, "%s: no aptX-HD reconfiguration needed", __func__);
+  }
+}
+
+//
+// Initialize the framing parameters, and set those that don't change
+// while streaming (e.g., 'sleep_time_ns').
+//
+static void aptx_hd_init_framing_params(
+    tAPTX_HD_FRAMING_PARAMS* framing_params) {
+  framing_params->sleep_time_ns = 0;
+  framing_params->pcm_reads = 0;
+  framing_params->pcm_bytes_per_read = 0;
+  framing_params->aptx_hd_bytes = 0;
+  framing_params->frame_size_counter = 0;
+
+  framing_params->sleep_time_ns = 9000000;
+
+  LOG_DEBUG(LOG_TAG, "%s: sleep_time_ns = %" PRIu64, __func__,
+            framing_params->sleep_time_ns);
+}
+
+//
+// Set frame size and transmission interval needed to stream the required
+// sample rate using 2-DH5 packets for aptX and 2-DH3 packets for aptX-LL.
+// With SCMS-T enabled we need to reserve room for extra headers added later.
+// Packets are always sent at equals time intervals but to achieve the
+// required sample rate, the frame size needs to change on occasion.
+//
+// Also need to specify how many of the required PCM samples are read at a
+// time:
+//     aptx_bytes = pcm_reads * pcm_bytes_per_read / 4
+// and
+//     number of aptX samples produced = pcm_bytes_per_read / 16
+//
+static void aptx_hd_update_framing_params(
+    tAPTX_HD_FRAMING_PARAMS* framing_params) {
+  if (a2dp_aptx_hd_encoder_cb.feeding_params.sample_rate == 48000) {
+    framing_params->aptx_hd_bytes = 648;
+    framing_params->pcm_bytes_per_read = 24;
+    framing_params->pcm_reads = 108;
+  } else {
+    // Assume the sample rate is 44100
+
+    //
+    // Total of 80 iterations:
+    // - Iteration 80: packet size 648, with 108 reads of 24 PCM bytes
+    // - Iterations 20, 40, 60: packet size 612, with 102 reads of 24 PCM bytes
+    // - All other iterations: packet size 594, with 99 reads of 24 PCM bytes
+    //
+    if (framing_params->frame_size_counter + 1 == 80) {
+      framing_params->aptx_hd_bytes = 648;
+      framing_params->pcm_bytes_per_read = 24;
+      framing_params->pcm_reads = 108;
+    } else if (((framing_params->frame_size_counter + 1) % 20) == 0) {
+      framing_params->aptx_hd_bytes = 612;
+      framing_params->pcm_bytes_per_read = 24;
+      framing_params->pcm_reads = 102;
+    } else {
+      framing_params->aptx_hd_bytes = 594;
+      framing_params->pcm_bytes_per_read = 24;
+      framing_params->pcm_reads = 99;
+    }
+    framing_params->frame_size_counter++;
+    if (framing_params->frame_size_counter == 80)
+      framing_params->frame_size_counter = 0;
+  }
+
+  LOG_DEBUG(LOG_TAG,
+            "%s: sleep_time_ns = %" PRIu64
+            " aptx_hd_bytes = %u "
+            "pcm_bytes_per_read = %u pcm_reads = %u frame_size_counter = %u",
+            __func__, framing_params->sleep_time_ns,
+            framing_params->aptx_hd_bytes, framing_params->pcm_bytes_per_read,
+            framing_params->pcm_reads, framing_params->frame_size_counter);
+}
+
+void a2dp_vendor_aptx_hd_feeding_reset(void) {
+  aptx_hd_init_framing_params(&a2dp_aptx_hd_encoder_cb.framing_params);
+}
+
+void a2dp_vendor_aptx_hd_feeding_flush(void) {
+  aptx_hd_init_framing_params(&a2dp_aptx_hd_encoder_cb.framing_params);
+}
+
+period_ms_t a2dp_vendor_aptx_hd_get_encoder_interval_ms(void) {
+  return a2dp_aptx_hd_encoder_cb.framing_params.sleep_time_ns / (1000 * 1000);
+}
+
+void a2dp_vendor_aptx_hd_send_frames(uint64_t timestamp_us) {
+  tAPTX_HD_FRAMING_PARAMS* framing_params =
+      &a2dp_aptx_hd_encoder_cb.framing_params;
+
+  // Prepare the packet to send
+  BT_HDR* p_buf = (BT_HDR*)osi_malloc(BT_DEFAULT_BUFFER_SIZE);
+  p_buf->offset = A2DP_APTX_HD_OFFSET;
+  p_buf->len = 0;
+  p_buf->layer_specific = 0;
+
+  uint8_t* encoded_ptr = (uint8_t*)(p_buf + 1);
+  encoded_ptr += p_buf->offset;
+
+  aptx_hd_update_framing_params(framing_params);
+
+  //
+  // Read the PCM data and encode it
+  //
+  LOG_DEBUG(LOG_TAG, "%s: %u PCM reads of size %u", __func__,
+            framing_params->pcm_reads, framing_params->pcm_bytes_per_read);
+  size_t encoded_ptr_index = 0;
+  size_t pcm_bytes_encoded = 0;
+  a2dp_aptx_hd_encoder_cb.stats.media_read_total_expected_packets++;
+  a2dp_aptx_hd_encoder_cb.stats.media_read_total_expected_reads_count +=
+      framing_params->pcm_reads;
+  a2dp_aptx_hd_encoder_cb.stats.media_read_total_expected_read_bytes +=
+      framing_params->pcm_reads * framing_params->pcm_bytes_per_read;
+  for (size_t reads = 0; reads < framing_params->pcm_reads; reads++) {
+    uint32_t
+        read_buffer32[A2DP_APTX_HD_MAX_PCM_BYTES_PER_READ / sizeof(uint32_t)];
+    size_t pcm_bytes_read = a2dp_aptx_hd_encoder_cb.read_callback(
+        (uint8_t*)read_buffer32, framing_params->pcm_bytes_per_read);
+    a2dp_aptx_hd_encoder_cb.stats.media_read_total_actual_read_bytes +=
+        pcm_bytes_read;
+    if (pcm_bytes_read < framing_params->pcm_bytes_per_read) {
+      LOG_WARN(LOG_TAG,
+               "%s: underflow at PCM reading iteration %zu: read %zu "
+               "instead of %d",
+               __func__, reads, pcm_bytes_read,
+               framing_params->pcm_bytes_per_read);
+      break;
+    }
+    a2dp_aptx_hd_encoder_cb.stats.media_read_total_actual_reads_count++;
+    pcm_bytes_encoded += aptx_hd_encode_24bit(
+        framing_params, &encoded_ptr_index, read_buffer32, encoded_ptr);
+  }
+
+  // Compute the number of encoded bytes
+  const int COMPRESSION_RATIO = 4;
+  size_t encoded_bytes = pcm_bytes_encoded / COMPRESSION_RATIO;
+  p_buf->len += encoded_bytes;
+  LOG_DEBUG(LOG_TAG, "%s: encoded %zu PCM bytes to %zu", __func__,
+            pcm_bytes_encoded, encoded_bytes);
+
+  // Update the RTP timestamp
+  *((uint32_t*)(p_buf + 1)) = a2dp_aptx_hd_encoder_cb.timestamp;
+  const uint8_t BYTES_PER_FRAME = 3;
+  uint32_t rtp_timestamp =
+      (pcm_bytes_encoded /
+       a2dp_aptx_hd_encoder_cb.feeding_params.channel_count) /
+      BYTES_PER_FRAME;
+  a2dp_aptx_hd_encoder_cb.timestamp += rtp_timestamp;
+
+  if (p_buf->len > 0) {
+    a2dp_aptx_hd_encoder_cb.enqueue_callback(p_buf, 1);
+  } else {
+    a2dp_aptx_hd_encoder_cb.stats.media_read_total_dropped_packets++;
+    osi_free(p_buf);
+  }
+}
+
+static size_t aptx_hd_encode_24bit(tAPTX_HD_FRAMING_PARAMS* framing_params,
+                                   size_t* data_out_index, uint32_t* data32_in,
+                                   uint8_t* data_out) {
+  size_t pcm_bytes_encoded = 0;
+  const uint8_t* p = (const uint8_t*)(data32_in);
+
+  for (size_t aptx_hd_samples = 0;
+       aptx_hd_samples < framing_params->pcm_bytes_per_read / 24;
+       aptx_hd_samples++) {
+    uint32_t pcmL[4];
+    uint32_t pcmR[4];
+    uint32_t encoded_sample[2];
+
+    // Expand from AUDIO_FORMAT_PCM_24_BIT_PACKED data (3 bytes per sample)
+    // into AUDIO_FORMAT_PCM_8_24_BIT (4 bytes per sample).
+    for (size_t i = 0; i < 4; i++) {
+      pcmL[i] = ((p[0] << 0) | (p[1] << 8) | (((int8_t)p[2]) << 16));
+      p += 3;
+      pcmR[i] = ((p[0] << 0) | (p[1] << 8) | (((int8_t)p[2]) << 16));
+      p += 3;
+    }
+
+    aptx_hd_encoder_encode_stereo_func(
+        a2dp_aptx_hd_encoder_cb.aptx_hd_encoder_state, &pcmL, &pcmR,
+        &encoded_sample);
+
+    uint8_t* encoded_ptr = (uint8_t*)&encoded_sample[0];
+    data_out[*data_out_index + 0] = *(encoded_ptr + 2);
+    data_out[*data_out_index + 1] = *(encoded_ptr + 1);
+    data_out[*data_out_index + 2] = *(encoded_ptr + 0);
+    data_out[*data_out_index + 3] = *(encoded_ptr + 6);
+    data_out[*data_out_index + 4] = *(encoded_ptr + 5);
+    data_out[*data_out_index + 5] = *(encoded_ptr + 4);
+
+    pcm_bytes_encoded += 24;
+    *data_out_index += 6;
+  }
+
+  return pcm_bytes_encoded;
+}
+
+void a2dp_vendor_aptx_hd_debug_codec_dump(int fd) {
+  a2dp_aptx_hd_encoder_stats_t* stats = &a2dp_aptx_hd_encoder_cb.stats;
+
+  dprintf(fd, "\nA2DP aptX-HD State:\n");
+
+  dprintf(fd,
+          "  Packets expected/dropped                                : %zu / "
+          "%zu\n",
+          stats->media_read_total_expected_packets,
+          stats->media_read_total_dropped_packets);
+
+  dprintf(fd,
+          "  PCM reads count expected/actual                         : %zu / "
+          "%zu\n",
+          stats->media_read_total_expected_reads_count,
+          stats->media_read_total_actual_reads_count);
+
+  dprintf(fd,
+          "  PCM read bytes expected/actual                          : %zu / "
+          "%zu\n",
+          stats->media_read_total_expected_read_bytes,
+          stats->media_read_total_actual_read_bytes);
+}
diff --git a/stack/include/a2dp_vendor_aptx.h b/stack/include/a2dp_vendor_aptx.h
new file mode 100644
index 0000000..ce9ab9c
--- /dev/null
+++ b/stack/include/a2dp_vendor_aptx.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// A2DP Codec API for aptX
+//
+
+#ifndef A2DP_VENDOR_APTX_H
+#define A2DP_VENDOR_APTX_H
+
+#include "a2dp_codec_api.h"
+#include "a2dp_vendor_aptx_constants.h"
+#include "avdt_api.h"
+
+class A2dpCodecConfigAptx : public A2dpCodecConfig {
+ public:
+  A2dpCodecConfigAptx();
+  virtual ~A2dpCodecConfigAptx();
+
+  bool init() override;
+  bool setCodecConfig(const uint8_t* p_peer_codec_info, bool is_capability,
+                      uint8_t* p_result_codec_config) override;
+
+ private:
+  bool updateEncoderUserConfig(
+      const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+      bool* p_restart_input, bool* p_restart_output,
+      bool* p_config_updated) override;
+};
+
+// Checks whether the codec capabilities contain a valid A2DP aptX Source
+// codec.
+// NOTE: only codecs that are implemented are considered valid.
+// Returns true if |p_codec_info| contains information about a valid aptX
+// codec, otherwise false.
+bool A2DP_IsVendorSourceCodecValidAptx(const uint8_t* p_codec_info);
+
+// Checks whether the codec capabilities contain a valid peer A2DP aptX Sink
+// codec.
+// NOTE: only codecs that are implemented are considered valid.
+// Returns true if |p_codec_info| contains information about a valid aptX
+// codec, otherwise false.
+bool A2DP_IsVendorPeerSinkCodecValidAptx(const uint8_t* p_codec_info);
+
+// Checks whether the A2DP data packets should contain RTP header.
+// |content_protection_enabled| is true if Content Protection is
+// enabled. |p_codec_info| contains information about the codec capabilities.
+// Returns true if the A2DP data packets should contain RTP header, otherwise
+// false.
+bool A2DP_VendorUsesRtpHeaderAptx(bool content_protection_enabled,
+                                  const uint8_t* p_codec_info);
+
+// Gets the A2DP aptX codec name for a given |p_codec_info|.
+const char* A2DP_VendorCodecNameAptx(const uint8_t* p_codec_info);
+
+// Checks whether two A2DP aptX codecs |p_codec_info_a| and |p_codec_info_b|
+// have the same type.
+// Returns true if the two codecs have the same type, otherwise false.
+bool A2DP_VendorCodecTypeEqualsAptx(const uint8_t* p_codec_info_a,
+                                    const uint8_t* p_codec_info_b);
+
+// Checks whether two A2DP aptX codecs |p_codec_info_a| and |p_codec_info_b|
+// are exactly the same.
+// Returns true if the two codecs are exactly the same, otherwise false.
+// If the codec type is not aptX, the return value is false.
+bool A2DP_VendorCodecEqualsAptx(const uint8_t* p_codec_info_a,
+                                const uint8_t* p_codec_info_b);
+
+// Gets the track sample rate value for the A2DP aptX codec.
+// |p_codec_info| is a pointer to the aptX codec_info to decode.
+// Returns the track sample rate on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetTrackSampleRateAptx(const uint8_t* p_codec_info);
+
+// Gets the bits per audio sample for the A2DP aptX codec.
+// |p_codec_info| is a pointer to the aptX codec_info to decode.
+// Returns the bits per audio sample on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetTrackBitsPerSampleAptx(const uint8_t* p_codec_info);
+
+// Gets the channel count for the A2DP aptX codec.
+// |p_codec_info| is a pointer to the aptX codec_info to decode.
+// Returns the channel count on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetTrackChannelCountAptx(const uint8_t* p_codec_info);
+
+// Gets the A2DP aptX audio data timestamp from an audio packet.
+// |p_codec_info| contains the codec information.
+// |p_data| contains the audio data.
+// The timestamp is stored in |p_timestamp|.
+// Returns true on success, otherwise false.
+bool A2DP_VendorGetPacketTimestampAptx(const uint8_t* p_codec_info,
+                                       const uint8_t* p_data,
+                                       uint32_t* p_timestamp);
+
+// Builds A2DP aptX codec header for audio data.
+// |p_codec_info| contains the codec information.
+// |p_buf| contains the audio data.
+// |frames_per_packet| is the number of frames in this packet.
+// Returns true on success, otherwise false.
+bool A2DP_VendorBuildCodecHeaderAptx(const uint8_t* p_codec_info, BT_HDR* p_buf,
+                                     uint16_t frames_per_packet);
+
+// Decodes and displays aptX codec info (for debugging).
+// |p_codec_info| is a pointer to the aptX codec_info to decode and display.
+void A2DP_VendorDumpCodecInfoAptx(const uint8_t* p_codec_info);
+
+// Gets the A2DP aptX encoder interface that can be used to encode and prepare
+// A2DP packets for transmission - see |tA2DP_ENCODER_INTERFACE|.
+// |p_codec_info| contains the codec information.
+// Returns the A2DP aptX encoder interface if the |p_codec_info| is valid and
+// supported, otherwise NULL.
+const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterfaceAptx(
+    const uint8_t* p_codec_info);
+
+// Adjusts the A2DP aptX codec, based on local support and Bluetooth
+// specification.
+// |p_codec_info| contains the codec information to adjust.
+// Returns true if |p_codec_info| is valid and supported, otherwise false.
+bool A2DP_VendorAdjustCodecAptx(uint8_t* p_codec_info);
+
+// Gets the A2DP aptX Source codec index for a given |p_codec_info|.
+// Returns the corresponding |btav_a2dp_codec_index_t| on success,
+// otherwise |BTAV_A2DP_CODEC_INDEX_MAX|.
+btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndexAptx(
+    const uint8_t* p_codec_info);
+
+// Gets the A2DP aptX Source codec name.
+const char* A2DP_VendorCodecIndexStrAptx(void);
+
+// Initializes A2DP aptX Source codec information into |tAVDT_CFG|
+// configuration entry pointed by |p_cfg|.
+bool A2DP_VendorInitCodecConfigAptx(tAVDT_CFG* p_cfg);
+
+#endif  // A2DP_VENDOR_APTX_H
diff --git a/stack/include/a2dp_vendor_aptx_constants.h b/stack/include/a2dp_vendor_aptx_constants.h
new file mode 100644
index 0000000..0f2c7c6
--- /dev/null
+++ b/stack/include/a2dp_vendor_aptx_constants.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// A2DP constants for aptX codec
+//
+
+#ifndef A2DP_VENDOR_APTX_CONSTANTS_H
+#define A2DP_VENDOR_APTX_CONSTANTS_H
+
+/* aptX codec specific settings */
+#define A2DP_APTX_CODEC_LEN 9
+
+#define A2DP_APTX_VENDOR_ID 0x0000004F
+#define A2DP_APTX_CODEC_ID_BLUETOOTH 0x0001
+#define A2DP_APTX_SAMPLERATE_44100 0x20
+#define A2DP_APTX_SAMPLERATE_48000 0x10
+#define A2DP_APTX_CHANNELS_STEREO 0x02
+#define A2DP_APTX_CHANNELS_MONO 0x01
+#define A2DP_APTX_FUTURE_1 0x00
+#define A2DP_APTX_FUTURE_2 0x00
+
+#endif  // A2DP_VENDOR_APTX_CONSTANTS_H
diff --git a/stack/include/a2dp_vendor_aptx_encoder.h b/stack/include/a2dp_vendor_aptx_encoder.h
new file mode 100644
index 0000000..d444b63
--- /dev/null
+++ b/stack/include/a2dp_vendor_aptx_encoder.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Interface to the A2DP aptX Encoder
+//
+
+#ifndef A2DP_VENDOR_APTX_ENCODER_H
+#define A2DP_VENDOR_APTX_ENCODER_H
+
+#include "a2dp_codec_api.h"
+#include "osi/include/time.h"
+
+// Loads the A2DP aptX encoder.
+// Return true on success, otherwise false.
+bool A2DP_VendorLoadEncoderAptx(void);
+
+// Unloads the A2DP aptX encoder.
+void A2DP_VendorUnloadEncoderAptx(void);
+
+// Initialize the A2DP aptX encoder.
+// |p_peer_params| contains the A2DP peer information.
+// The current A2DP codec config is in |a2dp_codec_config|.
+// |read_callback| is the callback for reading the input audio data.
+// |enqueue_callback| is the callback for enqueueing the encoded audio data.
+void a2dp_vendor_aptx_encoder_init(
+    const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+    A2dpCodecConfig* a2dp_codec_config,
+    a2dp_source_read_callback_t read_callback,
+    a2dp_source_enqueue_callback_t enqueue_callback);
+
+// Cleanup the A2DP aptX encoder.
+void a2dp_vendor_aptx_encoder_cleanup(void);
+
+// Initialize the feeding for the A2DP aptX encoder.
+// The feeding initialization parameters are in |p_feeding_params|.
+void a2dp_vendor_aptx_feeding_init(
+    const tA2DP_FEEDING_PARAMS* p_feeding_params);
+
+// Reset the feeding for the A2DP aptX encoder.
+void a2dp_vendor_aptx_feeding_reset(void);
+
+// Flush the feeding for the A2DP aptX encoder.
+void a2dp_vendor_aptx_feeding_flush(void);
+
+// Get the A2DP aptX encoder interval (in milliseconds).
+period_ms_t a2dp_vendor_aptx_get_encoder_interval_ms(void);
+
+// Prepare and send A2DP aptX encoded frames.
+// |timestamp_us| is the current timestamp (in microseconds).
+void a2dp_vendor_aptx_send_frames(uint64_t timestamp_us);
+
+// Dump aptX codec-related statistics.
+// |fd| is the file descriptor to use to dump the statistics information
+// in user-friendly test format.
+void a2dp_vendor_aptx_debug_codec_dump(int fd);
+
+#endif  // A2DP_VENDOR_APTX_ENCODER_H
diff --git a/stack/include/a2dp_vendor_aptx_hd.h b/stack/include/a2dp_vendor_aptx_hd.h
new file mode 100644
index 0000000..443ea90
--- /dev/null
+++ b/stack/include/a2dp_vendor_aptx_hd.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// A2DP Codec API for aptX-HD
+//
+
+#ifndef A2DP_VENDOR_APTX_HD_H
+#define A2DP_VENDOR_APTX_HD_H
+
+#include "a2dp_codec_api.h"
+#include "a2dp_vendor_aptx_hd_constants.h"
+#include "avdt_api.h"
+
+class A2dpCodecConfigAptxHd : public A2dpCodecConfig {
+ public:
+  A2dpCodecConfigAptxHd();
+  virtual ~A2dpCodecConfigAptxHd();
+
+  bool init() override;
+  bool setCodecConfig(const uint8_t* p_peer_codec_info, bool is_capability,
+                      uint8_t* p_result_codec_config) override;
+
+ private:
+  bool updateEncoderUserConfig(
+      const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+      bool* p_restart_input, bool* p_restart_output,
+      bool* p_config_updated) override;
+};
+
+// Checks whether the codec capabilities contain a valid A2DP aptX-HD Source
+// codec.
+// NOTE: only codecs that are implemented are considered valid.
+// Returns true if |p_codec_info| contains information about a valid aptX-HD
+// codec, otherwise false.
+bool A2DP_IsVendorSourceCodecValidAptxHd(const uint8_t* p_codec_info);
+
+// Checks whether the codec capabilities contain a valid peer A2DP aptX-HD Sink
+// codec.
+// NOTE: only codecs that are implemented are considered valid.
+// Returns true if |p_codec_info| contains information about a valid aptX-HD
+// codec, otherwise false.
+bool A2DP_IsVendorPeerSinkCodecValidAptxHd(const uint8_t* p_codec_info);
+
+// Checks whether the A2DP data packets should contain RTP header.
+// |content_protection_enabled| is true if Content Protection is
+// enabled. |p_codec_info| contains information about the codec capabilities.
+// Returns true if the A2DP data packets should contain RTP header, otherwise
+// false.
+bool A2DP_VendorUsesRtpHeaderAptxHd(bool content_protection_enabled,
+                                    const uint8_t* p_codec_info);
+
+// Gets the A2DP aptX-HD codec name for a given |p_codec_info|.
+const char* A2DP_VendorCodecNameAptxHd(const uint8_t* p_codec_info);
+
+// Checks whether two A2DP aptX-HD codecs |p_codec_info_a| and |p_codec_info_b|
+// have the same type.
+// Returns true if the two codecs have the same type, otherwise false.
+bool A2DP_VendorCodecTypeEqualsAptxHd(const uint8_t* p_codec_info_a,
+                                      const uint8_t* p_codec_info_b);
+
+// Checks whether two A2DP aptX-HD codecs |p_codec_info_a| and |p_codec_info_b|
+// are exactly the same.
+// Returns true if the two codecs are exactly the same, otherwise false.
+// If the codec type is not aptX-HD, the return value is false.
+bool A2DP_VendorCodecEqualsAptxHd(const uint8_t* p_codec_info_a,
+                                  const uint8_t* p_codec_info_b);
+
+// Gets the track sample rate value for the A2DP aptX-HD codec.
+// |p_codec_info| is a pointer to the aptX-HD codec_info to decode.
+// Returns the track sample rate on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetTrackSampleRateAptxHd(const uint8_t* p_codec_info);
+
+// Gets the bits per audio sample for the A2DP aptX-HD codec.
+// |p_codec_info| is a pointer to the aptX-HD codec_info to decode.
+// Returns the bits per audio sample on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetTrackBitsPerSampleAptxHd(const uint8_t* p_codec_info);
+
+// Gets the channel count for the A2DP aptX-HD codec.
+// |p_codec_info| is a pointer to the aptX-HD codec_info to decode.
+// Returns the channel count on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetTrackChannelCountAptxHd(const uint8_t* p_codec_info);
+
+// Gets the A2DP aptX-HD audio data timestamp from an audio packet.
+// |p_codec_info| contains the codec information.
+// |p_data| contains the audio data.
+// The timestamp is stored in |p_timestamp|.
+// Returns true on success, otherwise false.
+bool A2DP_VendorGetPacketTimestampAptxHd(const uint8_t* p_codec_info,
+                                         const uint8_t* p_data,
+                                         uint32_t* p_timestamp);
+
+// Builds A2DP aptX-HD codec header for audio data.
+// |p_codec_info| contains the codec information.
+// |p_buf| contains the audio data.
+// |frames_per_packet| is the number of frames in this packet.
+// Returns true on success, otherwise false.
+bool A2DP_VendorBuildCodecHeaderAptxHd(const uint8_t* p_codec_info,
+                                       BT_HDR* p_buf,
+                                       uint16_t frames_per_packet);
+
+// Decodes and displays aptX-HD codec info (for debugging).
+// |p_codec_info| is a pointer to the aptX-HD codec_info to decode and display.
+void A2DP_VendorDumpCodecInfoAptxHd(const uint8_t* p_codec_info);
+
+// Gets the A2DP aptX-HD encoder interface that can be used to encode and
+// prepare A2DP packets for transmission - see |tA2DP_ENCODER_INTERFACE|.
+// |p_codec_info| contains the codec information.
+// Returns the A2DP aptX-HD encoder interface if the |p_codec_info| is valid
+// and supported, otherwise NULL.
+const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterfaceAptxHd(
+    const uint8_t* p_codec_info);
+
+// Adjusts the A2DP aptX-HD codec, based on local support and Bluetooth
+// specification.
+// |p_codec_info| contains the codec information to adjust.
+// Returns true if |p_codec_info| is valid and supported, otherwise false.
+bool A2DP_VendorAdjustCodecAptxHd(uint8_t* p_codec_info);
+
+// Gets the A2DP aptX-HD Source codec index for a given |p_codec_info|.
+// Returns the corresponding |btav_a2dp_codec_index_t| on success,
+// otherwise |BTAV_A2DP_CODEC_INDEX_MAX|.
+btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndexAptxHd(
+    const uint8_t* p_codec_info);
+
+// Gets the A2DP aptX-HD Source codec name.
+const char* A2DP_VendorCodecIndexStrAptxHd(void);
+
+// Initializes A2DP aptX-HD Source codec information into |tAVDT_CFG|
+// configuration entry pointed by |p_cfg|.
+bool A2DP_VendorInitCodecConfigAptxHd(tAVDT_CFG* p_cfg);
+
+#endif  // A2DP_VENDOR_APTX_HD_H
diff --git a/stack/include/a2dp_vendor_aptx_hd_constants.h b/stack/include/a2dp_vendor_aptx_hd_constants.h
new file mode 100644
index 0000000..40ac8b5
--- /dev/null
+++ b/stack/include/a2dp_vendor_aptx_hd_constants.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// A2DP constants for aptX-HD codec
+//
+
+#ifndef A2DP_VENDOR_APTX_HD_CONSTANTS_H
+#define A2DP_VENDOR_APTX_HD_CONSTANTS_H
+
+/* aptX-HD codec specific settings */
+#define A2DP_APTX_HD_CODEC_LEN 13
+
+#define A2DP_APTX_HD_VENDOR_ID 0x000000D7
+#define A2DP_APTX_HD_CODEC_ID_BLUETOOTH 0x0024
+#define A2DP_APTX_HD_SAMPLERATE_44100 0x20
+#define A2DP_APTX_HD_SAMPLERATE_48000 0x10
+#define A2DP_APTX_HD_CHANNELS_STEREO 0x02
+#define A2DP_APTX_HD_CHANNELS_MONO 0x01
+#define A2DP_APTX_HD_ACL_SPRINT_RESERVED0 0x00
+#define A2DP_APTX_HD_ACL_SPRINT_RESERVED1 0x00
+#define A2DP_APTX_HD_ACL_SPRINT_RESERVED2 0x00
+#define A2DP_APTX_HD_ACL_SPRINT_RESERVED3 0x00
+
+#endif  // A2DP_VENDOR_APTX_HD_CONSTANTS_H
diff --git a/stack/include/a2dp_vendor_aptx_hd_encoder.h b/stack/include/a2dp_vendor_aptx_hd_encoder.h
new file mode 100644
index 0000000..032d933
--- /dev/null
+++ b/stack/include/a2dp_vendor_aptx_hd_encoder.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Interface to the A2DP aptX-HD Encoder
+//
+
+#ifndef A2DP_VENDOR_APTX_HD_ENCODER_H
+#define A2DP_VENDOR_APTX_HD_ENCODER_H
+
+#include "a2dp_codec_api.h"
+#include "osi/include/time.h"
+
+// Loads the A2DP aptX-HD encoder.
+// Return true on success, otherwise false.
+bool A2DP_VendorLoadEncoderAptxHd(void);
+
+// Unloads the A2DP aptX-HD encoder.
+void A2DP_VendorUnloadEncoderAptxHd(void);
+
+// Initialize the A2DP aptX-HD encoder.
+// |p_peer_params| contains the A2DP peer information.
+// The current A2DP codec config is in |a2dp_codec_config|.
+// |read_callback| is the callback for reading the input audio data.
+// |enqueue_callback| is the callback for enqueueing the encoded audio data.
+void a2dp_vendor_aptx_hd_encoder_init(
+    const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+    A2dpCodecConfig* a2dp_codec_config,
+    a2dp_source_read_callback_t read_callback,
+    a2dp_source_enqueue_callback_t enqueue_callback);
+
+// Cleanup the A2DP aptX-HD encoder.
+void a2dp_vendor_aptx_hd_encoder_cleanup(void);
+
+// Initialize the feeding for the A2DP aptX-HD encoder.
+// The feeding initialization parameters are in |p_feeding_params|.
+void a2dp_vendor_aptx_hd_feeding_init(
+    const tA2DP_FEEDING_PARAMS* p_feeding_params);
+
+// Reset the feeding for the A2DP aptX-HD encoder.
+void a2dp_vendor_aptx_hd_feeding_reset(void);
+
+// Flush the feeding for the A2DP aptX-HD encoder.
+void a2dp_vendor_aptx_hd_feeding_flush(void);
+
+// Get the A2DP aptX-HD encoder interval (in milliseconds).
+period_ms_t a2dp_vendor_aptx_hd_get_encoder_interval_ms(void);
+
+// Prepare and send A2DP aptX-HD encoded frames.
+// |timestamp_us| is the current timestamp (in microseconds).
+void a2dp_vendor_aptx_hd_send_frames(uint64_t timestamp_us);
+
+// Dump aptX-HD codec-related statistics.
+// |fd| is the file descriptor to use to dump the statistics information
+// in user-friendly test format.
+void a2dp_vendor_aptx_hd_debug_codec_dump(int fd);
+
+#endif  // A2DP_VENDOR_APTX_HD_ENCODER_H