diff --git a/system/audio_hal_interface/fuzzer/Android.bp b/system/audio_hal_interface/fuzzer/Android.bp
index ac74f1382fcfe97c4745ac1835830771f5758781..844fdd35846e0367ee1ad20c2d68704d2b29bb47 100644
--- a/system/audio_hal_interface/fuzzer/Android.bp
+++ b/system/audio_hal_interface/fuzzer/Android.bp
@@ -72,6 +72,7 @@ cc_defaults {
         "libudrv-uipc",
         "libbt-common",
         "liblc3",
+        "libopus",
         "libstatslog_bt",
         "libvndksupport",
         "libprocessgroup",
diff --git a/system/btif/Android.bp b/system/btif/Android.bp
index 079d12cc04eefa8da2d6f3272132ef0f28ef156a..0a43f0601773b99a52964053a5665a5ff6113dca 100644
--- a/system/btif/Android.bp
+++ b/system/btif/Android.bp
@@ -238,6 +238,7 @@ cc_test {
         "libFraunhoferAAC",
         "libg722codec",
         "liblc3",
+        "libopus",
         "libosi",
         "libudrv-uipc",
    ],
diff --git a/system/build/Android.bp b/system/build/Android.bp
index 9a0c996a7d17d1d1a2b5d62fb0c74a26384fa724..79f7d65632e4d4911194ffd63d9e92f0e23dd76e 100644
--- a/system/build/Android.bp
+++ b/system/build/Android.bp
@@ -207,6 +207,7 @@ fluoride_defaults {
         "libFraunhoferAAC",
         "libg722codec",
         "liblc3",
+        "libopus",
         "libprotobuf-cpp-lite",
         "libstatslog_bt",
         "libudrv-uipc",
diff --git a/system/gd/rust/topshim/facade/Android.bp b/system/gd/rust/topshim/facade/Android.bp
index 5d2b06b2ca7e39daaffe35228b7d5ec05cdf66b9..058b781b3bc4324f6f34b82e8325cb6ea9f7062e 100644
--- a/system/gd/rust/topshim/facade/Android.bp
+++ b/system/gd/rust/topshim/facade/Android.bp
@@ -55,6 +55,7 @@ rust_binary_host {
         "libFraunhoferAAC",
         "libg722codec",
         "liblc3",
+        "libopus",
         "libudrv-uipc",
         "libbluetooth_gd", // Gabeldorsche
         "libbluetooth-dumpsys",
diff --git a/system/service/Android.bp b/system/service/Android.bp
index 91dcff058dd7367dbd8c3dfac63b32b3febdbd4b..560ab2b83305ce66edd52cd952ce649a7469982c 100644
--- a/system/service/Android.bp
+++ b/system/service/Android.bp
@@ -119,6 +119,7 @@ cc_binary {
         "libFraunhoferAAC",
         "libg722codec",
         "liblc3",
+        "libopus",
         "libosi",
         "libudrv-uipc",
     ],
@@ -202,6 +203,7 @@ cc_test {
         "libFraunhoferAAC",
         "libg722codec",
         "liblc3",
+        "libopus",
         "libosi",
         "libudrv-uipc",
     ],
diff --git a/system/stack/Android.bp b/system/stack/Android.bp
index 8d1b8e544725e336b584d5f7e800c54d0da2e530..94e0f4ea80f0064a001eb7f8783fa4d404b7df87 100644
--- a/system/stack/Android.bp
+++ b/system/stack/Android.bp
@@ -55,6 +55,7 @@ cc_library_static {
         "external/aac/libSYS/include",
         "external/libldac/inc",
         "external/libldac/abr/inc",
+        "external/libopus/include",
         "packages/modules/Bluetooth/system",
         "packages/modules/Bluetooth/system/gd",
         "packages/modules/Bluetooth/system/vnd/include",
@@ -85,6 +86,9 @@ cc_library_static {
         "a2dp/a2dp_vendor_ldac.cc",
         "a2dp/a2dp_vendor_ldac_decoder.cc",
         "a2dp/a2dp_vendor_ldac_encoder.cc",
+        "a2dp/a2dp_vendor_opus.cc",
+        "a2dp/a2dp_vendor_opus_encoder.cc",
+        "a2dp/a2dp_vendor_opus_decoder.cc",
         "avct/avct_api.cc",
         "avct/avct_bcb_act.cc",
         "avct/avct_ccb.cc",
@@ -268,6 +272,7 @@ cc_test {
         "libbtdevice",
         "libg722codec",
         "liblc3",
+        "libopus",
         "libosi",
         "libudrv-uipc",
         "libbt-protos-lite",
diff --git a/system/stack/a2dp/a2dp_codec_config.cc b/system/stack/a2dp/a2dp_codec_config.cc
index 52e0f6fcb1969d42d1de0113a593bb68585ec906..b7b970fbe481eb4345b975401f32216aa51a6272 100644
--- a/system/stack/a2dp/a2dp_codec_config.cc
+++ b/system/stack/a2dp/a2dp_codec_config.cc
@@ -33,6 +33,7 @@
 #include "a2dp_vendor_aptx.h"
 #include "a2dp_vendor_aptx_hd.h"
 #include "a2dp_vendor_ldac.h"
+#include "a2dp_vendor_opus.h"
 #endif
 
 #include "bta/av/bta_av_int.h"
@@ -111,7 +112,7 @@ void A2dpCodecConfig::setDefaultCodecPriority() {
 A2dpCodecConfig* A2dpCodecConfig::createCodec(
     btav_a2dp_codec_index_t codec_index,
     btav_a2dp_codec_priority_t codec_priority) {
-  LOG_INFO("%s: codec %s", __func__, A2DP_CodecIndexStr(codec_index));
+  LOG_INFO("%s", A2DP_CodecIndexStr(codec_index));
 
   A2dpCodecConfig* codec_config = nullptr;
   switch (codec_index) {
@@ -140,6 +141,12 @@ A2dpCodecConfig* A2dpCodecConfig::createCodec(
     case BTAV_A2DP_CODEC_INDEX_SINK_LDAC:
       codec_config = new A2dpCodecConfigLdacSink(codec_priority);
       break;
+    case BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS:
+      codec_config = new A2dpCodecConfigOpusSource(codec_priority);
+      break;
+    case BTAV_A2DP_CODEC_INDEX_SINK_OPUS:
+      codec_config = new A2dpCodecConfigOpusSink(codec_priority);
+      break;
 #endif
     case BTAV_A2DP_CODEC_INDEX_MAX:
     default:
@@ -586,6 +593,9 @@ bool A2dpCodecs::init() {
       } else if (strcmp(tok, "ldac") == 0) {
         LOG_INFO("%s: LDAC offload supported", __func__);
         offload_codec_support[BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC] = true;
+      } else if (strcmp(tok, "opus") == 0) {
+        LOG_INFO("%s: Opus offload supported", __func__);
+        offload_codec_support[BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS] = true;
 #endif
       }
       tok = strtok_r(NULL, "-", &tmp_token);
diff --git a/system/stack/a2dp/a2dp_vendor.cc b/system/stack/a2dp/a2dp_vendor.cc
index 30dc2069f89c2bab501eb4af8278530419d615dd..cde70c1b59875df1abc75f642a036cd498eb6f8d 100644
--- a/system/stack/a2dp/a2dp_vendor.cc
+++ b/system/stack/a2dp/a2dp_vendor.cc
@@ -25,6 +25,7 @@
 #include "a2dp_vendor_aptx.h"
 #include "a2dp_vendor_aptx_hd.h"
 #include "a2dp_vendor_ldac.h"
+#include "a2dp_vendor_opus.h"
 #include "bt_target.h"
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
@@ -51,6 +52,11 @@ bool A2DP_IsVendorSourceCodecValid(const uint8_t* p_codec_info) {
     return A2DP_IsVendorSourceCodecValidLdac(p_codec_info);
   }
 
+  // Check for Opus
+  if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) {
+    return A2DP_IsVendorSourceCodecValidOpus(p_codec_info);
+  }
+
   // Add checks based on <vendor_id, codec_id>
 
   return false;
@@ -68,6 +74,11 @@ bool A2DP_IsVendorSinkCodecValid(const uint8_t* p_codec_info) {
     return A2DP_IsVendorSinkCodecValidLdac(p_codec_info);
   }
 
+  // Check for Opus
+  if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) {
+    return A2DP_IsVendorSinkCodecValidOpus(p_codec_info);
+  }
+
   return false;
 }
 
@@ -83,6 +94,11 @@ bool A2DP_IsVendorPeerSourceCodecValid(const uint8_t* p_codec_info) {
     return A2DP_IsVendorPeerSourceCodecValidLdac(p_codec_info);
   }
 
+  // Check for Opus
+  if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) {
+    return A2DP_IsVendorPeerSourceCodecValidOpus(p_codec_info);
+  }
+
   return false;
 }
 
@@ -107,6 +123,11 @@ bool A2DP_IsVendorPeerSinkCodecValid(const uint8_t* p_codec_info) {
     return A2DP_IsVendorPeerSinkCodecValidLdac(p_codec_info);
   }
 
+  // Check for Opus
+  if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) {
+    return A2DP_IsVendorPeerSinkCodecValidOpus(p_codec_info);
+  }
+
   // Add checks based on <vendor_id, codec_id>
 
   return false;
@@ -124,6 +145,11 @@ bool A2DP_IsVendorSinkCodecSupported(const uint8_t* p_codec_info) {
     return A2DP_IsVendorSinkCodecSupportedLdac(p_codec_info);
   }
 
+  // Check for Opus
+  if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) {
+    return A2DP_IsVendorSinkCodecSupportedOpus(p_codec_info);
+  }
+
   return false;
 }
 
@@ -139,6 +165,11 @@ bool A2DP_IsVendorPeerSourceCodecSupported(const uint8_t* p_codec_info) {
     return A2DP_IsPeerSourceCodecSupportedLdac(p_codec_info);
   }
 
+  // Check for Opus
+  if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) {
+    return A2DP_IsPeerSourceCodecSupportedOpus(p_codec_info);
+  }
+
   return false;
 }
 
@@ -185,6 +216,12 @@ bool A2DP_VendorUsesRtpHeader(bool content_protection_enabled,
                                         p_codec_info);
   }
 
+  // Check for Opus
+  if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) {
+    return A2DP_VendorUsesRtpHeaderOpus(content_protection_enabled,
+                                        p_codec_info);
+  }
+
   // Add checks based on <content_protection_enabled, vendor_id, codec_id>
 
   return true;
@@ -211,6 +248,11 @@ const char* A2DP_VendorCodecName(const uint8_t* p_codec_info) {
     return A2DP_VendorCodecNameLdac(p_codec_info);
   }
 
+  // Check for Opus
+  if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) {
+    return A2DP_VendorCodecNameOpus(p_codec_info);
+  }
+
   // Add checks based on <vendor_id, codec_id>
 
   return "UNKNOWN VENDOR CODEC";
@@ -250,6 +292,11 @@ bool A2DP_VendorCodecTypeEquals(const uint8_t* p_codec_info_a,
     return A2DP_VendorCodecTypeEqualsLdac(p_codec_info_a, p_codec_info_b);
   }
 
+  // Check for Opus
+  if (vendor_id_a == A2DP_OPUS_VENDOR_ID && codec_id_a == A2DP_OPUS_CODEC_ID) {
+    return A2DP_VendorCodecTypeEqualsOpus(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".
 
@@ -290,6 +337,11 @@ bool A2DP_VendorCodecEquals(const uint8_t* p_codec_info_a,
     return A2DP_VendorCodecEqualsLdac(p_codec_info_a, p_codec_info_b);
   }
 
+  // Check for Opus
+  if (vendor_id_a == A2DP_OPUS_VENDOR_ID && codec_id_a == A2DP_OPUS_CODEC_ID) {
+    return A2DP_VendorCodecEqualsOpus(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".
 
@@ -317,6 +369,11 @@ int A2DP_VendorGetBitRate(const uint8_t* p_codec_info) {
     return A2DP_VendorGetBitRateLdac(p_codec_info);
   }
 
+  // Check for Opus
+  if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) {
+    return A2DP_VendorGetBitRateOpus(p_codec_info);
+  }
+
   // Add checks based on <vendor_id, codec_id>
 
   return -1;
@@ -343,6 +400,11 @@ int A2DP_VendorGetTrackSampleRate(const uint8_t* p_codec_info) {
     return A2DP_VendorGetTrackSampleRateLdac(p_codec_info);
   }
 
+  // Check for Opus
+  if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) {
+    return A2DP_VendorGetTrackSampleRateOpus(p_codec_info);
+  }
+
   // Add checks based on <vendor_id, codec_id>
 
   return -1;
@@ -369,6 +431,11 @@ int A2DP_VendorGetTrackBitsPerSample(const uint8_t* p_codec_info) {
     return A2DP_VendorGetTrackBitsPerSampleLdac(p_codec_info);
   }
 
+  // Check for Opus
+  if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) {
+    return A2DP_VendorGetTrackBitsPerSampleOpus(p_codec_info);
+  }
+
   // Add checks based on <vendor_id, codec_id>
 
   return -1;
@@ -395,6 +462,11 @@ int A2DP_VendorGetTrackChannelCount(const uint8_t* p_codec_info) {
     return A2DP_VendorGetTrackChannelCountLdac(p_codec_info);
   }
 
+  // Check for Opus
+  if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) {
+    return A2DP_VendorGetTrackChannelCountOpus(p_codec_info);
+  }
+
   // Add checks based on <vendor_id, codec_id>
 
   return -1;
@@ -412,6 +484,11 @@ int A2DP_VendorGetSinkTrackChannelType(const uint8_t* p_codec_info) {
     return A2DP_VendorGetSinkTrackChannelTypeLdac(p_codec_info);
   }
 
+  // Check for Opus
+  if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) {
+    return A2DP_VendorGetSinkTrackChannelTypeOpus(p_codec_info);
+  }
+
   return -1;
 }
 
@@ -439,6 +516,11 @@ bool A2DP_VendorGetPacketTimestamp(const uint8_t* p_codec_info,
     return A2DP_VendorGetPacketTimestampLdac(p_codec_info, p_data, p_timestamp);
   }
 
+  // Check for Opus
+  if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) {
+    return A2DP_VendorGetPacketTimestampOpus(p_codec_info, p_data, p_timestamp);
+  }
+
   // Add checks based on <vendor_id, codec_id>
 
   return false;
@@ -469,6 +551,12 @@ bool A2DP_VendorBuildCodecHeader(const uint8_t* p_codec_info, BT_HDR* p_buf,
                                            frames_per_packet);
   }
 
+  // Check for Opus
+  if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) {
+    return A2DP_VendorBuildCodecHeaderOpus(p_codec_info, p_buf,
+                                           frames_per_packet);
+  }
+
   // Add checks based on <vendor_id, codec_id>
 
   return false;
@@ -496,6 +584,11 @@ const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterface(
     return A2DP_VendorGetEncoderInterfaceLdac(p_codec_info);
   }
 
+  // Check for Opus
+  if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) {
+    return A2DP_VendorGetEncoderInterfaceOpus(p_codec_info);
+  }
+
   // Add checks based on <vendor_id, codec_id>
 
   return NULL;
@@ -514,6 +607,11 @@ const tA2DP_DECODER_INTERFACE* A2DP_VendorGetDecoderInterface(
     return A2DP_VendorGetDecoderInterfaceLdac(p_codec_info);
   }
 
+  // Check for Opus
+  if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) {
+    return A2DP_VendorGetDecoderInterfaceOpus(p_codec_info);
+  }
+
   return NULL;
 }
 
@@ -538,6 +636,11 @@ bool A2DP_VendorAdjustCodec(uint8_t* p_codec_info) {
     return A2DP_VendorAdjustCodecLdac(p_codec_info);
   }
 
+  // Check for Opus
+  if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) {
+    return A2DP_VendorAdjustCodecOpus(p_codec_info);
+  }
+
   // Add checks based on <vendor_id, codec_id>
 
   return false;
@@ -565,6 +668,11 @@ btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndex(
     return A2DP_VendorSourceCodecIndexLdac(p_codec_info);
   }
 
+  // Check for Opus
+  if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) {
+    return A2DP_VendorSourceCodecIndexOpus(p_codec_info);
+  }
+
   // Add checks based on <vendor_id, codec_id>
 
   return BTAV_A2DP_CODEC_INDEX_MAX;
@@ -582,6 +690,11 @@ btav_a2dp_codec_index_t A2DP_VendorSinkCodecIndex(const uint8_t* p_codec_info) {
     return A2DP_VendorSinkCodecIndexLdac(p_codec_info);
   }
 
+  // Check for Opus
+  if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) {
+    return A2DP_VendorSinkCodecIndexOpus(p_codec_info);
+  }
+
   return BTAV_A2DP_CODEC_INDEX_MAX;
 }
 
@@ -604,11 +717,9 @@ const char* A2DP_VendorCodecIndexStr(btav_a2dp_codec_index_t codec_index) {
     case BTAV_A2DP_CODEC_INDEX_SOURCE_LC3:
       return "LC3 not implemented";
     case BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS:
-      // TODO(b/226441860): in-progress
-      return "Opus";
+      return A2DP_VendorCodecIndexStrOpus();
     case BTAV_A2DP_CODEC_INDEX_SINK_OPUS:
-      // TODO(b/226441860): in-progress
-      return "Opus SINK";
+      return A2DP_VendorCodecIndexStrOpusSink();
     // Add a switch statement for each vendor-specific codec
     case BTAV_A2DP_CODEC_INDEX_MAX:
       break;
@@ -637,9 +748,9 @@ bool A2DP_VendorInitCodecConfig(btav_a2dp_codec_index_t codec_index,
     case BTAV_A2DP_CODEC_INDEX_SOURCE_LC3:
       break;  // not implemented
     case BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS:
-      // TODO(b/226441860): in-progress
+      return A2DP_VendorInitCodecConfigOpus(p_cfg);
     case BTAV_A2DP_CODEC_INDEX_SINK_OPUS:
-      // TODO(b/226441860): in-progress
+      return A2DP_VendorInitCodecConfigOpusSink(p_cfg);
     // Add a switch statement for each vendor-specific codec
     case BTAV_A2DP_CODEC_INDEX_MAX:
       break;
@@ -669,6 +780,11 @@ std::string A2DP_VendorCodecInfoString(const uint8_t* p_codec_info) {
     return A2DP_VendorCodecInfoStringLdac(p_codec_info);
   }
 
+  // Check for Opus
+  if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) {
+    return A2DP_VendorCodecInfoStringOpus(p_codec_info);
+  }
+
   // Add checks based on <vendor_id, codec_id>
 
   return "Unsupported codec vendor_id: " + loghex(vendor_id) +
diff --git a/system/stack/a2dp/a2dp_vendor_opus.cc b/system/stack/a2dp/a2dp_vendor_opus.cc
new file mode 100644
index 0000000000000000000000000000000000000000..8390035b0a4642b1df74206180b9c29017a26913
--- /dev/null
+++ b/system/stack/a2dp/a2dp_vendor_opus.cc
@@ -0,0 +1,1332 @@
+/*
+ * Copyright 2021 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 Opus Codec Information
+ *  Element and Media Payload.
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "a2dp_vendor_opus"
+
+#include "a2dp_vendor_opus.h"
+
+#include <base/logging.h>
+#include <string.h>
+
+#include "a2dp_vendor.h"
+#include "a2dp_vendor_opus_decoder.h"
+#include "a2dp_vendor_opus_encoder.h"
+#include "bt_target.h"
+#include "bt_utils.h"
+#include "btif_av_co.h"
+#include "osi/include/log.h"
+#include "osi/include/osi.h"
+
+// data type for the Opus Codec Information Element */
+// NOTE: bits_per_sample and frameSize for Opus encoder initialization.
+typedef struct {
+  uint32_t vendorId;
+  uint16_t codecId;    /* Codec ID for Opus */
+  uint8_t sampleRate;  /* Sampling Frequency */
+  uint8_t channelMode; /* STEREO/DUAL/MONO */
+  btav_a2dp_codec_bits_per_sample_t bits_per_sample;
+  uint8_t future1; /* codec_specific_1 framesize */
+  uint8_t future2; /* codec_specific_2 */
+  uint8_t future3; /* codec_specific_3 */
+  uint8_t future4; /* codec_specific_4 */
+} tA2DP_OPUS_CIE;
+
+/* Opus Source codec capabilities */
+static const tA2DP_OPUS_CIE a2dp_opus_source_caps = {
+    A2DP_OPUS_VENDOR_ID,  // vendorId
+    A2DP_OPUS_CODEC_ID,   // codecId
+    // sampleRate
+    (A2DP_OPUS_SAMPLING_FREQ_48000),
+    // channelMode
+    (A2DP_OPUS_CHANNEL_MODE_STEREO),
+    // bits_per_sample
+    (BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16),
+    // future 1 frameSize
+    (A2DP_OPUS_20MS_FRAMESIZE),
+    // future 2
+    0x00,
+    // future 3
+    0x00,
+    // future 4
+    0x00};
+
+/* Opus Sink codec capabilities */
+static const tA2DP_OPUS_CIE a2dp_opus_sink_caps = {
+    A2DP_OPUS_VENDOR_ID,  // vendorId
+    A2DP_OPUS_CODEC_ID,   // codecId
+    // sampleRate
+    (A2DP_OPUS_SAMPLING_FREQ_48000),
+    // channelMode
+    (A2DP_OPUS_CHANNEL_MODE_STEREO),
+    // bits_per_sample
+    (BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16),
+    // future 1 frameSize
+    (A2DP_OPUS_20MS_FRAMESIZE),
+    // future 2
+    0x00,
+    // future 3
+    0x00,
+    // future 4
+    0x00};
+
+/* Default Opus codec configuration */
+static const tA2DP_OPUS_CIE a2dp_opus_default_config = {
+    A2DP_OPUS_VENDOR_ID,                 // vendorId
+    A2DP_OPUS_CODEC_ID,                  // codecId
+    A2DP_OPUS_SAMPLING_FREQ_48000,       // sampleRate
+    A2DP_OPUS_CHANNEL_MODE_STEREO,       // channelMode
+    BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16,  // bits_per_sample
+    A2DP_OPUS_20MS_FRAMESIZE,            // frameSize
+    0x00,                                // future 2
+    0x00,                                // future 3
+    0x00                                 // future 4
+};
+
+static const tA2DP_ENCODER_INTERFACE a2dp_encoder_interface_opus = {
+    a2dp_vendor_opus_encoder_init,
+    a2dp_vendor_opus_encoder_cleanup,
+    a2dp_vendor_opus_feeding_reset,
+    a2dp_vendor_opus_feeding_flush,
+    a2dp_vendor_opus_get_encoder_interval_ms,
+    a2dp_vendor_opus_get_effective_frame_size,
+    a2dp_vendor_opus_send_frames,
+    a2dp_vendor_opus_set_transmit_queue_length};
+
+static const tA2DP_DECODER_INTERFACE a2dp_decoder_interface_opus = {
+    a2dp_vendor_opus_decoder_init,          a2dp_vendor_opus_decoder_cleanup,
+    a2dp_vendor_opus_decoder_decode_packet, a2dp_vendor_opus_decoder_start,
+    a2dp_vendor_opus_decoder_suspend,       a2dp_vendor_opus_decoder_configure,
+};
+
+UNUSED_ATTR static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilityOpus(
+    const tA2DP_OPUS_CIE* p_cap, const uint8_t* p_codec_info,
+    bool is_peer_codec_info);
+
+// Builds the Opus 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 Opus 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_BuildInfoOpus(uint8_t media_type,
+                                       const tA2DP_OPUS_CIE* p_ie,
+                                       uint8_t* p_result) {
+  if (p_ie == NULL || p_result == NULL) {
+    LOG_ERROR("invalid information element");
+    return A2DP_INVALID_PARAMS;
+  }
+
+  *p_result++ = A2DP_OPUS_CODEC_LEN;
+  *p_result++ = (media_type << 4);
+  *p_result++ = A2DP_MEDIA_CT_NON_A2DP;
+
+  // Vendor ID and Codec ID
+  *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 = 0;
+  *p_result |= (uint8_t)(p_ie->channelMode) & A2DP_OPUS_CHANNEL_MODE_MASK;
+  if ((*p_result & A2DP_OPUS_CHANNEL_MODE_MASK) == 0) {
+    LOG_ERROR("channelmode 0x%X setting failed", (p_ie->channelMode));
+    return A2DP_INVALID_PARAMS;
+  }
+
+  *p_result |= ((uint8_t)(p_ie->future1) & A2DP_OPUS_FRAMESIZE_MASK);
+  if ((*p_result & A2DP_OPUS_FRAMESIZE_MASK) == 0) {
+    LOG_ERROR("frameSize 0x%X setting failed", (p_ie->future1));
+    return A2DP_INVALID_PARAMS;
+  }
+
+  *p_result |= ((uint8_t)(p_ie->sampleRate) & A2DP_OPUS_SAMPLING_FREQ_MASK);
+  if ((*p_result & A2DP_OPUS_SAMPLING_FREQ_MASK) == 0) {
+    LOG_ERROR("samplerate 0x%X setting failed", (p_ie->sampleRate));
+    return A2DP_INVALID_PARAMS;
+  }
+
+  p_result++;
+
+  return A2DP_SUCCESS;
+}
+
+// Parses the Opus 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_ParseInfoOpus(tA2DP_OPUS_CIE* p_ie,
+                                       const uint8_t* p_codec_info,
+                                       bool is_capability) {
+  uint8_t losc;
+  uint8_t media_type;
+  tA2DP_CODEC_TYPE codec_type;
+
+  if (p_ie == NULL || p_codec_info == NULL) {
+    LOG_ERROR("unable to parse information element");
+    return A2DP_INVALID_PARAMS;
+  }
+
+  // Check the codec capability length
+  losc = *p_codec_info++;
+  if (losc != A2DP_OPUS_CODEC_LEN) {
+    LOG_ERROR("invalid codec ie length %d", losc);
+    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) {
+    LOG_ERROR("invalid codec");
+    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 += 4;
+  p_ie->codecId =
+      (*p_codec_info & 0x00FF) | (*(p_codec_info + 1) << 8 & 0xFF00);
+  p_codec_info += 2;
+  if (p_ie->vendorId != A2DP_OPUS_VENDOR_ID ||
+      p_ie->codecId != A2DP_OPUS_CODEC_ID) {
+    LOG_ERROR("wrong vendor or codec id");
+    return A2DP_WRONG_CODEC;
+  }
+
+  p_ie->channelMode = *p_codec_info & A2DP_OPUS_CHANNEL_MODE_MASK;
+  p_ie->future1 = *p_codec_info & A2DP_OPUS_FRAMESIZE_MASK;
+  p_ie->sampleRate = *p_codec_info & A2DP_OPUS_SAMPLING_FREQ_MASK;
+  p_ie->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16;
+
+  if (is_capability) {
+    // NOTE: The checks here are very liberal. We should be using more
+    // pedantic checks specific to the SRC or SNK as specified in the spec.
+    if (A2DP_BitsSet(p_ie->sampleRate) == A2DP_SET_ZERO_BIT) {
+      LOG_ERROR("invalid sample rate 0x%X", p_ie->sampleRate);
+      return A2DP_BAD_SAMP_FREQ;
+    }
+    if (A2DP_BitsSet(p_ie->channelMode) == A2DP_SET_ZERO_BIT) {
+      LOG_ERROR("invalid channel mode");
+      return A2DP_BAD_CH_MODE;
+    }
+
+    return A2DP_SUCCESS;
+  }
+
+  if (A2DP_BitsSet(p_ie->sampleRate) != A2DP_SET_ONE_BIT) {
+    LOG_ERROR("invalid sampling frequency 0x%X", p_ie->sampleRate);
+    return A2DP_BAD_SAMP_FREQ;
+  }
+  if (A2DP_BitsSet(p_ie->channelMode) != A2DP_SET_ONE_BIT) {
+    LOG_ERROR("invalid channel mode.");
+    return A2DP_BAD_CH_MODE;
+  }
+
+  return A2DP_SUCCESS;
+}
+
+// Build the Opus Media Payload Header.
+// |p_dst| points to the location where the header should be written to.
+// If |frag| is true, the media payload frame is fragmented.
+// |start| is true for the first packet of a fragmented frame.
+// |last| is true for the last packet of a fragmented frame.
+// If |frag| is false, |num| is the number of number of frames in the packet,
+// otherwise is the number of remaining fragments (including this one).
+static void A2DP_BuildMediaPayloadHeaderOpus(uint8_t* p_dst, bool frag,
+                                             bool start, bool last,
+                                             uint8_t num) {
+  if (p_dst == NULL) return;
+
+  *p_dst = 0;
+  if (frag) *p_dst |= A2DP_OPUS_HDR_F_MSK;
+  if (start) *p_dst |= A2DP_OPUS_HDR_S_MSK;
+  if (last) *p_dst |= A2DP_OPUS_HDR_L_MSK;
+  *p_dst |= (A2DP_OPUS_HDR_NUM_MSK & num);
+}
+
+bool A2DP_IsVendorSourceCodecValidOpus(const uint8_t* p_codec_info) {
+  tA2DP_OPUS_CIE cfg_cie;
+
+  /* Use a liberal check when parsing the codec info */
+  return (A2DP_ParseInfoOpus(&cfg_cie, p_codec_info, false) == A2DP_SUCCESS) ||
+         (A2DP_ParseInfoOpus(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS);
+}
+
+bool A2DP_IsVendorSinkCodecValidOpus(const uint8_t* p_codec_info) {
+  tA2DP_OPUS_CIE cfg_cie;
+
+  /* Use a liberal check when parsing the codec info */
+  return (A2DP_ParseInfoOpus(&cfg_cie, p_codec_info, false) == A2DP_SUCCESS) ||
+         (A2DP_ParseInfoOpus(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS);
+}
+
+bool A2DP_IsVendorPeerSourceCodecValidOpus(const uint8_t* p_codec_info) {
+  tA2DP_OPUS_CIE cfg_cie;
+
+  /* Use a liberal check when parsing the codec info */
+  return (A2DP_ParseInfoOpus(&cfg_cie, p_codec_info, false) == A2DP_SUCCESS) ||
+         (A2DP_ParseInfoOpus(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS);
+}
+
+bool A2DP_IsVendorPeerSinkCodecValidOpus(const uint8_t* p_codec_info) {
+  tA2DP_OPUS_CIE cfg_cie;
+
+  /* Use a liberal check when parsing the codec info */
+  return (A2DP_ParseInfoOpus(&cfg_cie, p_codec_info, false) == A2DP_SUCCESS) ||
+         (A2DP_ParseInfoOpus(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS);
+}
+
+bool A2DP_IsVendorSinkCodecSupportedOpus(const uint8_t* p_codec_info) {
+  return A2DP_CodecInfoMatchesCapabilityOpus(&a2dp_opus_sink_caps, p_codec_info,
+                                             false) == A2DP_SUCCESS;
+}
+bool A2DP_IsPeerSourceCodecSupportedOpus(const uint8_t* p_codec_info) {
+  return A2DP_CodecInfoMatchesCapabilityOpus(&a2dp_opus_sink_caps, p_codec_info,
+                                             true) == A2DP_SUCCESS;
+}
+
+// Checks whether A2DP Opus codec configuration matches with a device's codec
+// capabilities. |p_cap| is the Opus 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_CodecInfoMatchesCapabilityOpus(
+    const tA2DP_OPUS_CIE* p_cap, const uint8_t* p_codec_info,
+    bool is_capability) {
+  tA2DP_STATUS status;
+  tA2DP_OPUS_CIE cfg_cie;
+
+  /* parse configuration */
+  status = A2DP_ParseInfoOpus(&cfg_cie, p_codec_info, is_capability);
+  if (status != A2DP_SUCCESS) {
+    LOG_ERROR("parsing failed %d", status);
+    return status;
+  }
+
+  /* verify that each parameter is in range */
+
+  LOG_VERBOSE("SAMPLING FREQ peer: 0x%x, capability 0x%x", cfg_cie.sampleRate,
+              p_cap->sampleRate);
+  LOG_VERBOSE("CH_MODE peer: 0x%x, capability 0x%x", cfg_cie.channelMode,
+              p_cap->channelMode);
+  LOG_VERBOSE("FRAMESIZE peer: 0x%x, capability 0x%x", cfg_cie.future1,
+              p_cap->future1);
+
+  /* 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;
+
+  /* frameSize */
+  if ((cfg_cie.future1 & p_cap->future1) == 0) return A2DP_NS_FRAMESIZE;
+
+  return A2DP_SUCCESS;
+}
+
+bool A2DP_VendorUsesRtpHeaderOpus(UNUSED_ATTR bool content_protection_enabled,
+                                  UNUSED_ATTR const uint8_t* p_codec_info) {
+  return true;
+}
+
+const char* A2DP_VendorCodecNameOpus(UNUSED_ATTR const uint8_t* p_codec_info) {
+  return "Opus";
+}
+
+bool A2DP_VendorCodecTypeEqualsOpus(const uint8_t* p_codec_info_a,
+                                    const uint8_t* p_codec_info_b) {
+  tA2DP_OPUS_CIE Opus_cie_a;
+  tA2DP_OPUS_CIE Opus_cie_b;
+
+  // Check whether the codec info contains valid data
+  tA2DP_STATUS a2dp_status =
+      A2DP_ParseInfoOpus(&Opus_cie_a, p_codec_info_a, true);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR("cannot decode codec information: %d", a2dp_status);
+    return false;
+  }
+  a2dp_status = A2DP_ParseInfoOpus(&Opus_cie_b, p_codec_info_b, true);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR("cannot decode codec information: %d", a2dp_status);
+    return false;
+  }
+
+  return true;
+}
+
+bool A2DP_VendorCodecEqualsOpus(const uint8_t* p_codec_info_a,
+                                const uint8_t* p_codec_info_b) {
+  tA2DP_OPUS_CIE Opus_cie_a;
+  tA2DP_OPUS_CIE Opus_cie_b;
+
+  // Check whether the codec info contains valid data
+  tA2DP_STATUS a2dp_status =
+      A2DP_ParseInfoOpus(&Opus_cie_a, p_codec_info_a, true);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR("cannot decode codec information: %d", a2dp_status);
+    return false;
+  }
+  a2dp_status = A2DP_ParseInfoOpus(&Opus_cie_b, p_codec_info_b, true);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR("cannot decode codec information: %d", a2dp_status);
+    return false;
+  }
+
+  return (Opus_cie_a.sampleRate == Opus_cie_b.sampleRate) &&
+         (Opus_cie_a.channelMode == Opus_cie_b.channelMode) &&
+         (Opus_cie_a.future1 == Opus_cie_b.future1);
+}
+
+int A2DP_VendorGetBitRateOpus(const uint8_t* p_codec_info) {
+  int channel_count = A2DP_VendorGetTrackChannelCountOpus(p_codec_info);
+  int framesize = A2DP_VendorGetFrameSizeOpus(p_codec_info);
+  int samplerate = A2DP_VendorGetTrackSampleRateOpus(p_codec_info);
+
+  // in milliseconds
+  switch ((framesize * 1000) / samplerate) {
+    case 20:
+      if (channel_count == 2) {
+        return 256000;
+      } else if (channel_count == 1) {
+        return 128000;
+      } else
+        return -1;
+    default:
+      return -1;
+  }
+}
+
+int A2DP_VendorGetTrackSampleRateOpus(const uint8_t* p_codec_info) {
+  tA2DP_OPUS_CIE Opus_cie;
+
+  // Check whether the codec info contains valid data
+  tA2DP_STATUS a2dp_status = A2DP_ParseInfoOpus(&Opus_cie, p_codec_info, false);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR("cannot decode codec information: %d", a2dp_status);
+    return -1;
+  }
+
+  switch (Opus_cie.sampleRate) {
+    case A2DP_OPUS_SAMPLING_FREQ_48000:
+      return 48000;
+  }
+
+  return -1;
+}
+
+int A2DP_VendorGetTrackBitsPerSampleOpus(const uint8_t* p_codec_info) {
+  tA2DP_OPUS_CIE Opus_cie;
+
+  // Check whether the codec info contains valid data
+  tA2DP_STATUS a2dp_status = A2DP_ParseInfoOpus(&Opus_cie, p_codec_info, false);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR("cannot decode codec information: %d", a2dp_status);
+    return -1;
+  }
+
+  switch (Opus_cie.bits_per_sample) {
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16:
+      return 16;
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24:
+      return 24;
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
+      return 32;
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE:
+    default:
+      LOG_ERROR("Invalid bit depth setting");
+      return -1;
+  }
+}
+
+int A2DP_VendorGetTrackChannelCountOpus(const uint8_t* p_codec_info) {
+  tA2DP_OPUS_CIE Opus_cie;
+
+  // Check whether the codec info contains valid data
+  tA2DP_STATUS a2dp_status = A2DP_ParseInfoOpus(&Opus_cie, p_codec_info, false);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR("cannot decode codec information: %d", a2dp_status);
+    return -1;
+  }
+
+  switch (Opus_cie.channelMode) {
+    case A2DP_OPUS_CHANNEL_MODE_MONO:
+      return 1;
+    case A2DP_OPUS_CHANNEL_MODE_STEREO:
+    case A2DP_OPUS_CHANNEL_MODE_DUAL_MONO:
+      return 2;
+    default:
+      LOG_ERROR("Invalid channel setting");
+  }
+
+  return -1;
+}
+
+int A2DP_VendorGetSinkTrackChannelTypeOpus(const uint8_t* p_codec_info) {
+  tA2DP_OPUS_CIE Opus_cie;
+
+  // Check whether the codec info contains valid data
+  tA2DP_STATUS a2dp_status = A2DP_ParseInfoOpus(&Opus_cie, p_codec_info, false);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR("cannot decode codec information: %d", a2dp_status);
+    return -1;
+  }
+
+  switch (Opus_cie.channelMode) {
+    case A2DP_OPUS_CHANNEL_MODE_MONO:
+      return 1;
+    case A2DP_OPUS_CHANNEL_MODE_STEREO:
+      return 2;
+  }
+
+  return -1;
+}
+
+int A2DP_VendorGetChannelModeCodeOpus(const uint8_t* p_codec_info) {
+  tA2DP_OPUS_CIE Opus_cie;
+
+  // Check whether the codec info contains valid data
+  tA2DP_STATUS a2dp_status = A2DP_ParseInfoOpus(&Opus_cie, p_codec_info, false);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR("cannot decode codec information: %d", a2dp_status);
+    return -1;
+  }
+
+  switch (Opus_cie.channelMode) {
+    case A2DP_OPUS_CHANNEL_MODE_MONO:
+    case A2DP_OPUS_CHANNEL_MODE_STEREO:
+      return Opus_cie.channelMode;
+    default:
+      break;
+  }
+
+  return -1;
+}
+
+int A2DP_VendorGetFrameSizeOpus(const uint8_t* p_codec_info) {
+  tA2DP_OPUS_CIE Opus_cie;
+
+  // Check whether the codec info contains valid data
+  tA2DP_STATUS a2dp_status = A2DP_ParseInfoOpus(&Opus_cie, p_codec_info, false);
+  if (a2dp_status != A2DP_SUCCESS) {
+    LOG_ERROR("cannot decode codec information: %d", a2dp_status);
+    return -1;
+  }
+  int samplerate = A2DP_VendorGetTrackSampleRateOpus(p_codec_info);
+
+  switch (Opus_cie.future1) {
+    case A2DP_OPUS_20MS_FRAMESIZE:
+      if (samplerate == 48000) {
+        return 960;
+      }
+  }
+
+  return -1;
+}
+
+bool A2DP_VendorGetPacketTimestampOpus(UNUSED_ATTR const uint8_t* p_codec_info,
+                                       const uint8_t* p_data,
+                                       uint32_t* p_timestamp) {
+  *p_timestamp = *(const uint32_t*)p_data;
+  return true;
+}
+
+bool A2DP_VendorBuildCodecHeaderOpus(UNUSED_ATTR const uint8_t* p_codec_info,
+                                     BT_HDR* p_buf,
+                                     uint16_t frames_per_packet) {
+  uint8_t* p;
+
+  p_buf->offset -= A2DP_OPUS_MPL_HDR_LEN;
+  p = (uint8_t*)(p_buf + 1) + p_buf->offset;
+  p_buf->len += A2DP_OPUS_MPL_HDR_LEN;
+
+  A2DP_BuildMediaPayloadHeaderOpus(p, false, false, false,
+                                   (uint8_t)frames_per_packet);
+
+  return true;
+}
+
+std::string A2DP_VendorCodecInfoStringOpus(const uint8_t* p_codec_info) {
+  std::stringstream res;
+  std::string field;
+  tA2DP_STATUS a2dp_status;
+  tA2DP_OPUS_CIE Opus_cie;
+
+  a2dp_status = A2DP_ParseInfoOpus(&Opus_cie, p_codec_info, true);
+  if (a2dp_status != A2DP_SUCCESS) {
+    res << "A2DP_ParseInfoOpus fail: " << loghex(a2dp_status);
+    return res.str();
+  }
+
+  res << "\tname: Opus\n";
+
+  // Sample frequency
+  field.clear();
+  AppendField(&field, (Opus_cie.sampleRate == 0), "NONE");
+  AppendField(&field, (Opus_cie.sampleRate & A2DP_OPUS_SAMPLING_FREQ_48000),
+              "48000");
+  res << "\tsamp_freq: " << field << " (" << loghex(Opus_cie.sampleRate)
+      << ")\n";
+
+  // Channel mode
+  field.clear();
+  AppendField(&field, (Opus_cie.channelMode == 0), "NONE");
+  AppendField(&field, (Opus_cie.channelMode & A2DP_OPUS_CHANNEL_MODE_MONO),
+              "Mono");
+  AppendField(&field, (Opus_cie.channelMode & A2DP_OPUS_CHANNEL_MODE_STEREO),
+              "Stereo");
+  res << "\tch_mode: " << field << " (" << loghex(Opus_cie.channelMode)
+      << ")\n";
+
+  // Framesize
+  field.clear();
+  AppendField(&field, (Opus_cie.future1 == 0), "NONE");
+  AppendField(&field, (Opus_cie.future1 & A2DP_OPUS_20MS_FRAMESIZE), "20ms");
+  AppendField(&field, (Opus_cie.future1 & A2DP_OPUS_10MS_FRAMESIZE), "10ms");
+  res << "\tframesize: " << field << " (" << loghex(Opus_cie.future1) << ")\n";
+
+  return res.str();
+}
+
+const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterfaceOpus(
+    const uint8_t* p_codec_info) {
+  if (!A2DP_IsVendorSourceCodecValidOpus(p_codec_info)) return NULL;
+
+  return &a2dp_encoder_interface_opus;
+}
+
+const tA2DP_DECODER_INTERFACE* A2DP_VendorGetDecoderInterfaceOpus(
+    const uint8_t* p_codec_info) {
+  if (!A2DP_IsVendorSinkCodecValidOpus(p_codec_info)) return NULL;
+
+  return &a2dp_decoder_interface_opus;
+}
+
+bool A2DP_VendorAdjustCodecOpus(uint8_t* p_codec_info) {
+  tA2DP_OPUS_CIE cfg_cie;
+
+  // Nothing to do: just verify the codec info is valid
+  if (A2DP_ParseInfoOpus(&cfg_cie, p_codec_info, true) != A2DP_SUCCESS)
+    return false;
+
+  return true;
+}
+
+btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndexOpus(
+    UNUSED_ATTR const uint8_t* p_codec_info) {
+  return BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS;
+}
+
+btav_a2dp_codec_index_t A2DP_VendorSinkCodecIndexOpus(
+    UNUSED_ATTR const uint8_t* p_codec_info) {
+  return BTAV_A2DP_CODEC_INDEX_SINK_OPUS;
+}
+
+const char* A2DP_VendorCodecIndexStrOpus(void) { return "Opus"; }
+
+const char* A2DP_VendorCodecIndexStrOpusSink(void) { return "Opus SINK"; }
+
+bool A2DP_VendorInitCodecConfigOpus(AvdtpSepConfig* p_cfg) {
+  if (A2DP_BuildInfoOpus(AVDT_MEDIA_TYPE_AUDIO, &a2dp_opus_source_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;
+}
+
+bool A2DP_VendorInitCodecConfigOpusSink(AvdtpSepConfig* p_cfg) {
+  return A2DP_BuildInfoOpus(AVDT_MEDIA_TYPE_AUDIO, &a2dp_opus_sink_caps,
+                            p_cfg->codec_info) == A2DP_SUCCESS;
+}
+
+UNUSED_ATTR static void build_codec_config(const tA2DP_OPUS_CIE& config_cie,
+                                           btav_a2dp_codec_config_t* result) {
+  if (config_cie.sampleRate & A2DP_OPUS_SAMPLING_FREQ_48000)
+    result->sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+
+  result->bits_per_sample = config_cie.bits_per_sample;
+
+  if (config_cie.channelMode & A2DP_OPUS_CHANNEL_MODE_MONO)
+    result->channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+  if (config_cie.channelMode & A2DP_OPUS_CHANNEL_MODE_STEREO) {
+    result->channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+  }
+
+  if (config_cie.future1 & A2DP_OPUS_20MS_FRAMESIZE)
+    result->codec_specific_1 |= BTAV_A2DP_CODEC_FRAME_SIZE_20MS;
+  if (config_cie.future1 & A2DP_OPUS_10MS_FRAMESIZE)
+    result->codec_specific_1 |= BTAV_A2DP_CODEC_FRAME_SIZE_10MS;
+}
+
+A2dpCodecConfigOpusSource::A2dpCodecConfigOpusSource(
+    btav_a2dp_codec_priority_t codec_priority)
+    : A2dpCodecConfigOpusBase(BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS,
+                              A2DP_VendorCodecIndexStrOpus(), codec_priority,
+                              true) {
+  // Compute the local capability
+  if (a2dp_opus_source_caps.sampleRate & A2DP_OPUS_SAMPLING_FREQ_48000) {
+    codec_local_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+  }
+  codec_local_capability_.bits_per_sample =
+      a2dp_opus_source_caps.bits_per_sample;
+  if (a2dp_opus_source_caps.channelMode & A2DP_OPUS_CHANNEL_MODE_MONO) {
+    codec_local_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+  }
+  if (a2dp_opus_source_caps.channelMode & A2DP_OPUS_CHANNEL_MODE_STEREO) {
+    codec_local_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+  }
+}
+
+A2dpCodecConfigOpusSource::~A2dpCodecConfigOpusSource() {}
+
+bool A2dpCodecConfigOpusSource::init() {
+  if (!isValid()) return false;
+
+  return true;
+}
+
+bool A2dpCodecConfigOpusSource::useRtpHeaderMarkerBit() const { return false; }
+
+//
+// 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_OPUS_CIE* p_result,
+                                    btav_a2dp_codec_config_t* p_codec_config) {
+  if (sampleRate & A2DP_OPUS_SAMPLING_FREQ_48000) {
+    p_result->sampleRate = A2DP_OPUS_SAMPLING_FREQ_48000;
+    p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+    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_OPUS_CIE* p_result, btav_a2dp_codec_config_t* p_codec_config) {
+  switch (p_codec_audio_config->sample_rate) {
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_48000:
+      if (sampleRate & A2DP_OPUS_SAMPLING_FREQ_48000) {
+        p_result->sampleRate = A2DP_OPUS_SAMPLING_FREQ_48000;
+        p_codec_config->sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+        return true;
+      }
+      break;
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_16000:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_24000:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_44100:
+    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 from |bits_per_sample|.
+// |bits_per_sample| 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_best_bits_per_sample(
+    btav_a2dp_codec_bits_per_sample_t bits_per_sample, tA2DP_OPUS_CIE* p_result,
+    btav_a2dp_codec_config_t* p_codec_config) {
+  if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32) {
+    p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32;
+    p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32;
+    return true;
+  }
+  if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24) {
+    p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24;
+    p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24;
+    return true;
+  }
+  if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16) {
+    p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16;
+    p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16;
+    return true;
+  }
+  return false;
+}
+
+//
+// Selects the audio bits per sample from |p_codec_audio_config|.
+// |bits_per_sample| 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_bits_per_sample(
+    const btav_a2dp_codec_config_t* p_codec_audio_config,
+    btav_a2dp_codec_bits_per_sample_t bits_per_sample, tA2DP_OPUS_CIE* p_result,
+    btav_a2dp_codec_config_t* p_codec_config) {
+  switch (p_codec_audio_config->bits_per_sample) {
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16:
+      if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16) {
+        p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16;
+        p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16;
+        return true;
+      }
+      break;
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24:
+      if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24) {
+        p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24;
+        p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24;
+        return true;
+      }
+      break;
+    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
+      if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32) {
+        p_codec_config->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32;
+        p_result->bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32;
+        return true;
+      }
+      break;
+    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_OPUS_CIE* p_result,
+                                     btav_a2dp_codec_config_t* p_codec_config) {
+  if (channelMode & A2DP_OPUS_CHANNEL_MODE_STEREO) {
+    p_result->channelMode = A2DP_OPUS_CHANNEL_MODE_STEREO;
+    p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+    return true;
+  }
+  if (channelMode & A2DP_OPUS_CHANNEL_MODE_MONO) {
+    p_result->channelMode = A2DP_OPUS_CHANNEL_MODE_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_OPUS_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_OPUS_CHANNEL_MODE_MONO) {
+        p_result->channelMode = A2DP_OPUS_CHANNEL_MODE_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_OPUS_CHANNEL_MODE_STEREO) {
+        p_result->channelMode = A2DP_OPUS_CHANNEL_MODE_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 A2dpCodecConfigOpusBase::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_OPUS_CIE peer_info_cie;
+  tA2DP_OPUS_CIE result_config_cie;
+  uint8_t channelMode;
+  uint8_t sampleRate;
+  uint8_t frameSize;
+  btav_a2dp_codec_bits_per_sample_t bits_per_sample;
+  const tA2DP_OPUS_CIE* p_a2dp_opus_caps =
+      (is_source_) ? &a2dp_opus_source_caps : &a2dp_opus_sink_caps;
+
+  btav_a2dp_codec_config_t device_codec_config_ = getCodecConfig();
+
+  LOG_INFO(
+      "AudioManager stream config %d sample rate %d bit depth %d channel "
+      "mode",
+      device_codec_config_.sample_rate, device_codec_config_.bits_per_sample,
+      device_codec_config_.channel_mode);
+
+  // 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_selectable_capability =
+      codec_selectable_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_ParseInfoOpus(&peer_info_cie, p_peer_codec_info, is_capability);
+  if (status != A2DP_SUCCESS) {
+    LOG_ERROR("can't parse peer's capabilities: error = %d", status);
+    goto fail;
+  }
+
+  //
+  // Build the preferred configuration
+  //
+  memset(&result_config_cie, 0, sizeof(result_config_cie));
+  result_config_cie.vendorId = p_a2dp_opus_caps->vendorId;
+  result_config_cie.codecId = p_a2dp_opus_caps->codecId;
+
+  //
+  // Select the sample frequency
+  //
+  sampleRate = p_a2dp_opus_caps->sampleRate & peer_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_48000:
+      if (sampleRate & A2DP_OPUS_SAMPLING_FREQ_48000) {
+        result_config_cie.sampleRate = A2DP_OPUS_SAMPLING_FREQ_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_44100:
+    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_16000:
+    case BTAV_A2DP_CODEC_SAMPLE_RATE_24000:
+    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 {
+    // Compute the selectable capability
+    if (sampleRate & A2DP_OPUS_SAMPLING_FREQ_48000) {
+      codec_selectable_capability_.sample_rate |=
+          BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+    }
+
+    if (codec_config_.sample_rate != BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) break;
+
+    // Compute the common capability
+    if (sampleRate & A2DP_OPUS_SAMPLING_FREQ_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_opus_default_config.sampleRate & peer_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(
+        "cannot match sample frequency: local caps = 0x%x "
+        "peer info = 0x%x",
+        p_a2dp_opus_caps->sampleRate, peer_info_cie.sampleRate);
+    goto fail;
+  }
+
+  //
+  // Select the bits per sample
+  //
+  // NOTE: this information is NOT included in the Opus A2DP codec description
+  // that is sent OTA.
+  bits_per_sample = p_a2dp_opus_caps->bits_per_sample;
+  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:
+      if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16) {
+        result_config_cie.bits_per_sample = codec_user_config_.bits_per_sample;
+        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:
+      if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24) {
+        result_config_cie.bits_per_sample = codec_user_config_.bits_per_sample;
+        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_32:
+      if (bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32) {
+        result_config_cie.bits_per_sample = codec_user_config_.bits_per_sample;
+        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_NONE:
+      result_config_cie.bits_per_sample = 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 {
+    // Compute the selectable capability
+    codec_selectable_capability_.bits_per_sample =
+        p_a2dp_opus_caps->bits_per_sample;
+
+    if (codec_config_.bits_per_sample != BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE)
+      break;
+
+    // Compute the common capability
+    codec_capability_.bits_per_sample = bits_per_sample;
+
+    // No user preference - try yhe codec audio config
+    if (select_audio_bits_per_sample(&codec_audio_config_,
+                                     p_a2dp_opus_caps->bits_per_sample,
+                                     &result_config_cie, &codec_config_)) {
+      break;
+    }
+
+    // No user preference - try the default config
+    if (select_best_bits_per_sample(a2dp_opus_default_config.bits_per_sample,
+                                    &result_config_cie, &codec_config_)) {
+      break;
+    }
+
+    // No user preference - use the best match
+    if (select_best_bits_per_sample(p_a2dp_opus_caps->bits_per_sample,
+                                    &result_config_cie, &codec_config_)) {
+      break;
+    }
+  } while (false);
+  if (codec_config_.bits_per_sample == BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) {
+    LOG_ERROR(
+        "cannot match bits per sample: default = 0x%x "
+        "user preference = 0x%x",
+        a2dp_opus_default_config.bits_per_sample,
+        codec_user_config_.bits_per_sample);
+    goto fail;
+  }
+
+  //
+  // Select the channel mode
+  //
+  channelMode = p_a2dp_opus_caps->channelMode & peer_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_OPUS_CHANNEL_MODE_MONO) {
+        result_config_cie.channelMode = A2DP_OPUS_CHANNEL_MODE_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_OPUS_CHANNEL_MODE_STEREO) {
+        result_config_cie.channelMode = A2DP_OPUS_CHANNEL_MODE_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 {
+    // Compute the selectable capability
+    if (channelMode & A2DP_OPUS_CHANNEL_MODE_MONO) {
+      codec_selectable_capability_.channel_mode |=
+          BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+    }
+    if (channelMode & A2DP_OPUS_CHANNEL_MODE_STEREO) {
+      codec_selectable_capability_.channel_mode |=
+          BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+    }
+
+    if (codec_config_.channel_mode != BTAV_A2DP_CODEC_CHANNEL_MODE_NONE) break;
+
+    // Compute the common capability
+    if (channelMode & A2DP_OPUS_CHANNEL_MODE_MONO)
+      codec_capability_.channel_mode |= BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+    if (channelMode & A2DP_OPUS_CHANNEL_MODE_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_opus_default_config.channelMode & peer_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(
+        "cannot match channel mode: local caps = 0x%x "
+        "peer info = 0x%x",
+        p_a2dp_opus_caps->channelMode, peer_info_cie.channelMode);
+    goto fail;
+  }
+
+  //
+  // Select the frame size
+  //
+  frameSize = p_a2dp_opus_caps->future1 & peer_info_cie.future1;
+  codec_config_.codec_specific_1 = BTAV_A2DP_CODEC_FRAME_SIZE_NONE;
+  switch (codec_user_config_.codec_specific_1) {
+    case BTAV_A2DP_CODEC_FRAME_SIZE_20MS:
+      if (frameSize & A2DP_OPUS_20MS_FRAMESIZE) {
+        result_config_cie.future1 = A2DP_OPUS_20MS_FRAMESIZE;
+        codec_capability_.codec_specific_1 =
+            codec_user_config_.codec_specific_1;
+        codec_config_.codec_specific_1 = codec_user_config_.codec_specific_1;
+      }
+      break;
+    case BTAV_A2DP_CODEC_FRAME_SIZE_10MS:
+      if (frameSize & A2DP_OPUS_10MS_FRAMESIZE) {
+        result_config_cie.future1 = A2DP_OPUS_10MS_FRAMESIZE;
+        codec_capability_.codec_specific_1 =
+            codec_user_config_.codec_specific_1;
+        codec_config_.codec_specific_1 = codec_user_config_.codec_specific_1;
+      }
+      break;
+    case BTAV_A2DP_CODEC_FRAME_SIZE_NONE:
+      codec_capability_.codec_specific_1 = BTAV_A2DP_CODEC_FRAME_SIZE_NONE;
+      codec_config_.codec_specific_1 = BTAV_A2DP_CODEC_FRAME_SIZE_NONE;
+      break;
+  }
+
+  // No user preference - set default value
+  codec_config_.codec_specific_1 = BTAV_A2DP_CODEC_FRAME_SIZE_20MS;
+  result_config_cie.future1 = A2DP_OPUS_20MS_FRAMESIZE;
+  result_config_cie.future3 = 0x00;
+
+  if (codec_config_.codec_specific_1 == BTAV_A2DP_CODEC_FRAME_SIZE_NONE) {
+    LOG_ERROR(
+        "cannot match frame size: local caps = 0x%x "
+        "peer info = 0x%x",
+        p_a2dp_opus_caps->future1, peer_info_cie.future1);
+    goto fail;
+  }
+
+  if (A2DP_BuildInfoOpus(AVDT_MEDIA_TYPE_AUDIO, &result_config_cie,
+                         p_result_codec_config) != A2DP_SUCCESS) {
+    LOG_ERROR("failed to BuildInfoOpus for result_config_cie");
+    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, and the
+  // result codec config.
+  if (is_capability) {
+    status = A2DP_BuildInfoOpus(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie,
+                                ota_codec_peer_capability_);
+  } else {
+    status = A2DP_BuildInfoOpus(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie,
+                                ota_codec_peer_config_);
+  }
+  CHECK(status == A2DP_SUCCESS);
+
+  status = A2DP_BuildInfoOpus(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_selectable_capability_ = saved_codec_selectable_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;
+}
+
+bool A2dpCodecConfigOpusBase::setPeerCodecCapabilities(
+    const uint8_t* p_peer_codec_capabilities) {
+  std::lock_guard<std::recursive_mutex> lock(codec_mutex_);
+  tA2DP_OPUS_CIE peer_info_cie;
+  uint8_t channelMode;
+  uint8_t sampleRate;
+  const tA2DP_OPUS_CIE* p_a2dp_opus_caps =
+      (is_source_) ? &a2dp_opus_source_caps : &a2dp_opus_sink_caps;
+
+  // Save the internal state
+  btav_a2dp_codec_config_t saved_codec_selectable_capability =
+      codec_selectable_capability_;
+  uint8_t saved_ota_codec_peer_capability[AVDT_CODEC_SIZE];
+  memcpy(saved_ota_codec_peer_capability, ota_codec_peer_capability_,
+         sizeof(ota_codec_peer_capability_));
+
+  tA2DP_STATUS status =
+      A2DP_ParseInfoOpus(&peer_info_cie, p_peer_codec_capabilities, true);
+  if (status != A2DP_SUCCESS) {
+    LOG_ERROR("can't parse peer's capabilities: error = %d", status);
+    goto fail;
+  }
+
+  // Compute the selectable capability - sample rate
+  sampleRate = p_a2dp_opus_caps->sampleRate & peer_info_cie.sampleRate;
+  if (sampleRate & A2DP_OPUS_SAMPLING_FREQ_48000) {
+    codec_selectable_capability_.sample_rate |=
+        BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
+  }
+
+  // Compute the selectable capability - bits per sample
+  codec_selectable_capability_.bits_per_sample =
+      p_a2dp_opus_caps->bits_per_sample;
+
+  // Compute the selectable capability - channel mode
+  channelMode = p_a2dp_opus_caps->channelMode & peer_info_cie.channelMode;
+  if (channelMode & A2DP_OPUS_CHANNEL_MODE_MONO) {
+    codec_selectable_capability_.channel_mode |=
+        BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
+  }
+  if (channelMode & A2DP_OPUS_CHANNEL_MODE_STEREO) {
+    codec_selectable_capability_.channel_mode |=
+        BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
+  }
+
+  LOG_INFO("BuildInfoOpus for peer info cie for ota caps");
+  status = A2DP_BuildInfoOpus(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie,
+                              ota_codec_peer_capability_);
+  CHECK(status == A2DP_SUCCESS);
+  return true;
+
+fail:
+  // Restore the internal state
+  codec_selectable_capability_ = saved_codec_selectable_capability;
+  memcpy(ota_codec_peer_capability_, saved_ota_codec_peer_capability,
+         sizeof(ota_codec_peer_capability_));
+  return false;
+}
+
+A2dpCodecConfigOpusSink::A2dpCodecConfigOpusSink(
+    btav_a2dp_codec_priority_t codec_priority)
+    : A2dpCodecConfigOpusBase(BTAV_A2DP_CODEC_INDEX_SINK_OPUS,
+                              A2DP_VendorCodecIndexStrOpusSink(),
+                              codec_priority, false) {}
+
+A2dpCodecConfigOpusSink::~A2dpCodecConfigOpusSink() {}
+
+bool A2dpCodecConfigOpusSink::init() {
+  if (!isValid()) return false;
+
+  return true;
+}
+
+bool A2dpCodecConfigOpusSink::useRtpHeaderMarkerBit() const { return false; }
+
+bool A2dpCodecConfigOpusSink::updateEncoderUserConfig(
+    UNUSED_ATTR const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+    UNUSED_ATTR bool* p_restart_input, UNUSED_ATTR bool* p_restart_output,
+    UNUSED_ATTR bool* p_config_updated) {
+  return false;
+}
diff --git a/system/stack/a2dp/a2dp_vendor_opus_decoder.cc b/system/stack/a2dp/a2dp_vendor_opus_decoder.cc
new file mode 100644
index 0000000000000000000000000000000000000000..20867b5a808d236f10c798005820409b6b934ab6
--- /dev/null
+++ b/system/stack/a2dp/a2dp_vendor_opus_decoder.cc
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2021 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_opus_decoder"
+
+#include "a2dp_vendor_opus_decoder.h"
+
+#include <base/logging.h>
+#include <opus.h>
+
+#include "a2dp_vendor_opus.h"
+#include "osi/include/allocator.h"
+#include "osi/include/log.h"
+
+typedef struct {
+  OpusDecoder* opus_handle = nullptr;
+  bool has_opus_handle;
+  int16_t* decode_buf = nullptr;
+  decoded_data_callback_t decode_callback;
+} tA2DP_OPUS_DECODER_CB;
+
+static tA2DP_OPUS_DECODER_CB a2dp_opus_decoder_cb;
+
+void a2dp_vendor_opus_decoder_cleanup(void) {
+  if (a2dp_opus_decoder_cb.has_opus_handle) {
+    osi_free(a2dp_opus_decoder_cb.opus_handle);
+
+    if (a2dp_opus_decoder_cb.decode_buf != nullptr) {
+      memset(a2dp_opus_decoder_cb.decode_buf, 0,
+             A2DP_OPUS_DECODE_BUFFER_LENGTH);
+      osi_free(a2dp_opus_decoder_cb.decode_buf);
+      a2dp_opus_decoder_cb.decode_buf = nullptr;
+    }
+    a2dp_opus_decoder_cb.has_opus_handle = false;
+  }
+
+  return;
+}
+
+bool a2dp_vendor_opus_decoder_init(decoded_data_callback_t decode_callback) {
+  a2dp_vendor_opus_decoder_cleanup();
+
+  int32_t err_val = OPUS_OK;
+  int32_t size = 0;
+
+  size = opus_decoder_get_size(A2DP_OPUS_CODEC_OUTPUT_CHS);
+  a2dp_opus_decoder_cb.opus_handle =
+      static_cast<OpusDecoder*>(osi_malloc(size));
+  if (a2dp_opus_decoder_cb.opus_handle == nullptr) {
+    LOG_ERROR("failed to allocate opus decoder handle");
+    return false;
+  }
+  err_val = opus_decoder_init(a2dp_opus_decoder_cb.opus_handle,
+                              A2DP_OPUS_CODEC_DEFAULT_SAMPLERATE,
+                              A2DP_OPUS_CODEC_OUTPUT_CHS);
+  if (err_val == OPUS_OK) {
+    a2dp_opus_decoder_cb.has_opus_handle = true;
+
+    a2dp_opus_decoder_cb.decode_buf =
+        static_cast<int16_t*>(osi_malloc(A2DP_OPUS_DECODE_BUFFER_LENGTH));
+
+    memset(a2dp_opus_decoder_cb.decode_buf, 0, A2DP_OPUS_DECODE_BUFFER_LENGTH);
+
+    a2dp_opus_decoder_cb.decode_callback = decode_callback;
+    LOG_INFO("decoder init success");
+    return true;
+  } else {
+    LOG_ERROR("failed to initialize Opus Decoder");
+    a2dp_opus_decoder_cb.has_opus_handle = false;
+    return false;
+  }
+
+  return false;
+}
+
+void a2dp_vendor_opus_decoder_configure(const uint8_t* p_codec_info) { return; }
+
+bool a2dp_vendor_opus_decoder_decode_packet(BT_HDR* p_buf) {
+  uint32_t frameSize;
+  uint32_t numChannels;
+  uint32_t numFrames;
+  int32_t ret_val = 0;
+  uint32_t frameLen = 0;
+
+  if (p_buf == nullptr) {
+    LOG_ERROR("Dropping packet with nullptr");
+    return false;
+  }
+
+  auto* pBuffer =
+      reinterpret_cast<unsigned char*>(p_buf->data + p_buf->offset + 1);
+  int32_t bufferSize = p_buf->len - 1;
+
+  numChannels = opus_packet_get_nb_channels(pBuffer);
+  numFrames = opus_packet_get_nb_frames(pBuffer, bufferSize);
+  frameSize = opus_packet_get_samples_per_frame(
+      pBuffer, A2DP_OPUS_CODEC_DEFAULT_SAMPLERATE);
+  frameLen = opus_packet_get_nb_samples(pBuffer, bufferSize,
+                                        A2DP_OPUS_CODEC_DEFAULT_SAMPLERATE);
+  uint32_t num_frames = pBuffer[0] & 0xf;
+
+  LOG_ERROR("numframes %d framesize %d framelen %d bufferSize %d", num_frames,
+            frameSize, frameLen, bufferSize);
+  LOG_ERROR("numChannels %d numFrames %d offset %d", numChannels, numFrames,
+            p_buf->offset);
+
+  for (uint32_t frame = 0; frame < numFrames; ++frame) {
+    {
+      numChannels = opus_packet_get_nb_channels(pBuffer);
+
+      ret_val = opus_decode(a2dp_opus_decoder_cb.opus_handle,
+                            reinterpret_cast<unsigned char*>(pBuffer),
+                            bufferSize, a2dp_opus_decoder_cb.decode_buf,
+                            A2DP_OPUS_DECODE_BUFFER_LENGTH, 0 /* flags */);
+
+      if (ret_val < OPUS_OK) {
+        LOG_ERROR("Opus DecodeFrame failed %d, applying concealment", ret_val);
+        ret_val = opus_decode(a2dp_opus_decoder_cb.opus_handle, NULL, 0,
+                              a2dp_opus_decoder_cb.decode_buf,
+                              A2DP_OPUS_DECODE_BUFFER_LENGTH, 0 /* flags */);
+      }
+
+      size_t frame_len =
+          ret_val * numChannels * sizeof(a2dp_opus_decoder_cb.decode_buf[0]);
+      a2dp_opus_decoder_cb.decode_callback(
+          reinterpret_cast<uint8_t*>(a2dp_opus_decoder_cb.decode_buf),
+          frame_len);
+    }
+  }
+  return true;
+}
+
+void a2dp_vendor_opus_decoder_start(void) { return; }
+
+void a2dp_vendor_opus_decoder_suspend(void) {
+  int32_t err_val = 0;
+
+  if (a2dp_opus_decoder_cb.has_opus_handle) {
+    err_val =
+        opus_decoder_ctl(a2dp_opus_decoder_cb.opus_handle, OPUS_RESET_STATE);
+    if (err_val != OPUS_OK) {
+      LOG_ERROR("failed to reset decoder");
+    }
+  }
+  return;
+}
diff --git a/system/stack/a2dp/a2dp_vendor_opus_encoder.cc b/system/stack/a2dp/a2dp_vendor_opus_encoder.cc
new file mode 100644
index 0000000000000000000000000000000000000000..ded13e4ae9c260f29fbeb0fe5d5f08350d4ec066
--- /dev/null
+++ b/system/stack/a2dp/a2dp_vendor_opus_encoder.cc
@@ -0,0 +1,532 @@
+/*
+ * Copyright 2021 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_opus_encoder"
+#define ATRACE_TAG ATRACE_TAG_AUDIO
+
+#include "a2dp_vendor_opus_encoder.h"
+
+#ifndef OS_GENERIC
+#include <cutils/trace.h>
+#endif
+#include <dlfcn.h>
+#include <inttypes.h>
+#include <opus.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "a2dp_vendor.h"
+#include "a2dp_vendor_opus.h"
+#include "common/time_util.h"
+#include "osi/include/allocator.h"
+#include "osi/include/log.h"
+#include "osi/include/osi.h"
+#include "stack/include/bt_hdr.h"
+
+typedef struct {
+  uint32_t sample_rate;
+  uint16_t bitrate;
+  uint16_t framesize;
+  uint8_t channel_mode;
+  uint8_t bits_per_sample;
+  uint8_t quality_mode_index;
+  int pcm_wlength;
+  uint8_t pcm_fmt;
+} tA2DP_OPUS_ENCODER_PARAMS;
+
+typedef struct {
+  float counter;
+  uint32_t bytes_per_tick;
+  uint64_t last_frame_us;
+} tA2DP_OPUS_FEEDING_STATE;
+
+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_opus_encoder_stats_t;
+
+typedef struct {
+  a2dp_source_read_callback_t read_callback;
+  a2dp_source_enqueue_callback_t enqueue_callback;
+  uint16_t TxAaMtuSize;
+  size_t TxQueueLength;
+
+  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
+
+  OpusEncoder* opus_handle;
+  bool has_opus_handle;  // True if opus_handle is valid
+
+  tA2DP_FEEDING_PARAMS feeding_params;
+  tA2DP_OPUS_ENCODER_PARAMS opus_encoder_params;
+  tA2DP_OPUS_FEEDING_STATE opus_feeding_state;
+
+  a2dp_opus_encoder_stats_t stats;
+} tA2DP_OPUS_ENCODER_CB;
+
+static tA2DP_OPUS_ENCODER_CB a2dp_opus_encoder_cb;
+
+static bool a2dp_vendor_opus_encoder_update(uint16_t peer_mtu,
+                                            A2dpCodecConfig* a2dp_codec_config,
+                                            bool* p_restart_input,
+                                            bool* p_restart_output,
+                                            bool* p_config_updated);
+static void a2dp_opus_get_num_frame_iteration(uint8_t* num_of_iterations,
+                                              uint8_t* num_of_frames,
+                                              uint64_t timestamp_us);
+static void a2dp_opus_encode_frames(uint8_t nb_frame);
+static bool a2dp_opus_read_feeding(uint8_t* read_buffer, uint32_t* bytes_read);
+
+void a2dp_vendor_opus_encoder_cleanup(void) {
+  if (a2dp_opus_encoder_cb.has_opus_handle) {
+    osi_free(a2dp_opus_encoder_cb.opus_handle);
+    a2dp_opus_encoder_cb.has_opus_handle = false;
+    a2dp_opus_encoder_cb.opus_handle = nullptr;
+  }
+  memset(&a2dp_opus_encoder_cb, 0, sizeof(a2dp_opus_encoder_cb));
+
+  a2dp_opus_encoder_cb.stats.session_start_us =
+      bluetooth::common::time_get_os_boottime_us();
+
+  a2dp_opus_encoder_cb.timestamp = 0;
+
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+  a2dp_opus_encoder_cb.use_SCMS_T = true;
+#else
+  a2dp_opus_encoder_cb.use_SCMS_T = false;
+#endif
+  return;
+}
+
+void a2dp_vendor_opus_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) {
+  uint32_t error_val;
+
+  a2dp_vendor_opus_encoder_cleanup();
+
+  a2dp_opus_encoder_cb.read_callback = read_callback;
+  a2dp_opus_encoder_cb.enqueue_callback = enqueue_callback;
+  a2dp_opus_encoder_cb.is_peer_edr = p_peer_params->is_peer_edr;
+  a2dp_opus_encoder_cb.peer_supports_3mbps = p_peer_params->peer_supports_3mbps;
+  a2dp_opus_encoder_cb.peer_mtu = p_peer_params->peer_mtu;
+
+  // 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;
+
+  uint32_t size = opus_encoder_get_size(A2DP_OPUS_CODEC_OUTPUT_CHS);
+  a2dp_opus_encoder_cb.opus_handle =
+      static_cast<OpusEncoder*>(osi_malloc(size));
+  if (a2dp_opus_encoder_cb.opus_handle == nullptr) {
+    LOG_ERROR("failed to allocate opus encoder handle");
+    return;
+  }
+
+  error_val = opus_encoder_init(
+      a2dp_opus_encoder_cb.opus_handle, A2DP_OPUS_CODEC_DEFAULT_SAMPLERATE,
+      A2DP_OPUS_CODEC_OUTPUT_CHS, OPUS_APPLICATION_AUDIO);
+
+  if (error_val != OPUS_OK) {
+    LOG_ERROR(
+        "failed to init opus encoder (handle size %d, sampling rate %d, "
+        "output chs %d, error %d)",
+        size, A2DP_OPUS_CODEC_DEFAULT_SAMPLERATE, A2DP_OPUS_CODEC_OUTPUT_CHS,
+        error_val);
+    osi_free(a2dp_opus_encoder_cb.opus_handle);
+    return;
+  } else {
+    a2dp_opus_encoder_cb.has_opus_handle = true;
+  }
+
+  a2dp_vendor_opus_encoder_update(a2dp_opus_encoder_cb.peer_mtu,
+                                  a2dp_codec_config, &restart_input,
+                                  &restart_output, &config_updated);
+
+  return;
+}
+
+bool A2dpCodecConfigOpusSource::updateEncoderUserConfig(
+    const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params, bool* p_restart_input,
+    bool* p_restart_output, bool* p_config_updated) {
+  if (a2dp_opus_encoder_cb.peer_mtu == 0) {
+    LOG_ERROR(
+        "Cannot update the codec encoder for %s: "
+        "invalid peer MTU",
+        name().c_str());
+    return false;
+  }
+
+  return a2dp_vendor_opus_encoder_update(a2dp_opus_encoder_cb.peer_mtu, this,
+                                         p_restart_input, p_restart_output,
+                                         p_config_updated);
+}
+
+static bool a2dp_vendor_opus_encoder_update(uint16_t peer_mtu,
+                                            A2dpCodecConfig* a2dp_codec_config,
+                                            bool* p_restart_input,
+                                            bool* p_restart_output,
+                                            bool* p_config_updated) {
+  tA2DP_OPUS_ENCODER_PARAMS* p_encoder_params =
+      &a2dp_opus_encoder_cb.opus_encoder_params;
+  uint8_t codec_info[AVDT_CODEC_SIZE];
+  uint32_t error = 0;
+
+  *p_restart_input = false;
+  *p_restart_output = false;
+  *p_config_updated = false;
+
+  if (!a2dp_opus_encoder_cb.has_opus_handle ||
+      a2dp_opus_encoder_cb.opus_handle == NULL) {
+    LOG_ERROR("Cannot get Opus encoder handle");
+    return false;
+  }
+  CHECK(a2dp_opus_encoder_cb.opus_handle != nullptr);
+
+  if (!a2dp_codec_config->copyOutOtaCodecConfig(codec_info)) {
+    LOG_ERROR(
+        "Cannot update the codec encoder for %s: "
+        "invalid codec config",
+        a2dp_codec_config->name().c_str());
+    return false;
+  }
+  const uint8_t* p_codec_info = codec_info;
+  btav_a2dp_codec_config_t codec_config = a2dp_codec_config->getCodecConfig();
+
+  // The feeding parameters
+  tA2DP_FEEDING_PARAMS* p_feeding_params = &a2dp_opus_encoder_cb.feeding_params;
+  p_feeding_params->sample_rate =
+      A2DP_VendorGetTrackSampleRateOpus(p_codec_info);
+  p_feeding_params->bits_per_sample =
+      a2dp_codec_config->getAudioBitsPerSample();
+  p_feeding_params->channel_count =
+      A2DP_VendorGetTrackChannelCountOpus(p_codec_info);
+  LOG_INFO("sample_rate=%u bits_per_sample=%u channel_count=%u",
+           p_feeding_params->sample_rate, p_feeding_params->bits_per_sample,
+           p_feeding_params->channel_count);
+  a2dp_vendor_opus_feeding_reset();
+
+  // The codec parameters
+  p_encoder_params->sample_rate =
+      a2dp_opus_encoder_cb.feeding_params.sample_rate;
+  p_encoder_params->channel_mode =
+      A2DP_VendorGetChannelModeCodeOpus(p_codec_info);
+  p_encoder_params->framesize = A2DP_VendorGetFrameSizeOpus(p_codec_info);
+  p_encoder_params->bitrate = A2DP_VendorGetBitRateOpus(p_codec_info);
+
+  uint16_t mtu_size =
+      BT_DEFAULT_BUFFER_SIZE - A2DP_OPUS_OFFSET - sizeof(BT_HDR);
+  if (mtu_size < peer_mtu) {
+    a2dp_opus_encoder_cb.TxAaMtuSize = mtu_size;
+  } else {
+    a2dp_opus_encoder_cb.TxAaMtuSize = peer_mtu;
+  }
+
+  // Set the bitrate quality mode index
+  if (codec_config.codec_specific_3 != 0) {
+    p_encoder_params->quality_mode_index = codec_config.codec_specific_3 % 10;
+    LOG_INFO("setting bitrate quality mode to %d",
+             p_encoder_params->quality_mode_index);
+  } else {
+    p_encoder_params->quality_mode_index = 5;
+    LOG_INFO("setting bitrate quality mode to default %d",
+             p_encoder_params->quality_mode_index);
+  }
+
+  error = opus_encoder_ctl(
+      a2dp_opus_encoder_cb.opus_handle,
+      OPUS_SET_COMPLEXITY(p_encoder_params->quality_mode_index));
+
+  if (error != OPUS_OK) {
+    LOG_ERROR("failed to set encoder bitrate quality setting");
+    return false;
+  }
+
+  p_encoder_params->pcm_wlength =
+      a2dp_opus_encoder_cb.feeding_params.bits_per_sample >> 3;
+
+  LOG_INFO("setting bitrate to %d", p_encoder_params->bitrate);
+  error = opus_encoder_ctl(a2dp_opus_encoder_cb.opus_handle,
+                           OPUS_SET_BITRATE(p_encoder_params->bitrate));
+
+  if (error != OPUS_OK) {
+    LOG_ERROR("failed to set encoder bitrate");
+    return false;
+  }
+
+  // Set the Audio format from pcm_wlength
+  if (p_encoder_params->pcm_wlength == 2)
+    p_encoder_params->pcm_fmt = 16;
+  else if (p_encoder_params->pcm_wlength == 3)
+    p_encoder_params->pcm_fmt = 24;
+  else if (p_encoder_params->pcm_wlength == 4)
+    p_encoder_params->pcm_fmt = 32;
+
+  return true;
+}
+
+void a2dp_vendor_opus_feeding_reset(void) {
+  memset(&a2dp_opus_encoder_cb.opus_feeding_state, 0,
+         sizeof(a2dp_opus_encoder_cb.opus_feeding_state));
+
+  a2dp_opus_encoder_cb.opus_feeding_state.bytes_per_tick =
+      (a2dp_opus_encoder_cb.feeding_params.sample_rate *
+       a2dp_opus_encoder_cb.feeding_params.bits_per_sample / 8 *
+       a2dp_opus_encoder_cb.feeding_params.channel_count *
+       a2dp_vendor_opus_get_encoder_interval_ms()) /
+      1000;
+
+  return;
+}
+
+void a2dp_vendor_opus_feeding_flush(void) {
+  a2dp_opus_encoder_cb.opus_feeding_state.counter = 0.0f;
+
+  return;
+}
+
+uint64_t a2dp_vendor_opus_get_encoder_interval_ms(void) {
+  return ((a2dp_opus_encoder_cb.opus_encoder_params.framesize * 1000) /
+          a2dp_opus_encoder_cb.opus_encoder_params.sample_rate);
+}
+
+void a2dp_vendor_opus_send_frames(uint64_t timestamp_us) {
+  uint8_t nb_frame = 0;
+  uint8_t nb_iterations = 0;
+
+  a2dp_opus_get_num_frame_iteration(&nb_iterations, &nb_frame, timestamp_us);
+  if (nb_frame == 0) return;
+
+  for (uint8_t counter = 0; counter < nb_iterations; counter++) {
+    // Transcode frame and enqueue
+    a2dp_opus_encode_frames(nb_frame);
+  }
+
+  return;
+}
+
+// Obtains the number of frames to send and number of iterations
+// to be used. |num_of_iterations| and |num_of_frames| parameters
+// are used as output param for returning the respective values.
+static void a2dp_opus_get_num_frame_iteration(uint8_t* num_of_iterations,
+                                              uint8_t* num_of_frames,
+                                              uint64_t timestamp_us) {
+  uint32_t result = 0;
+  uint8_t nof = 0;
+  uint8_t noi = 1;
+
+  uint32_t pcm_bytes_per_frame =
+      a2dp_opus_encoder_cb.opus_encoder_params.framesize *
+      a2dp_opus_encoder_cb.feeding_params.channel_count *
+      a2dp_opus_encoder_cb.feeding_params.bits_per_sample / 8;
+
+  uint32_t us_this_tick = a2dp_vendor_opus_get_encoder_interval_ms() * 1000;
+  uint64_t now_us = timestamp_us;
+  if (a2dp_opus_encoder_cb.opus_feeding_state.last_frame_us != 0)
+    us_this_tick =
+        (now_us - a2dp_opus_encoder_cb.opus_feeding_state.last_frame_us);
+  a2dp_opus_encoder_cb.opus_feeding_state.last_frame_us = now_us;
+
+  a2dp_opus_encoder_cb.opus_feeding_state.counter +=
+      (float)a2dp_opus_encoder_cb.opus_feeding_state.bytes_per_tick *
+      us_this_tick / (a2dp_vendor_opus_get_encoder_interval_ms() * 1000);
+
+  result =
+      a2dp_opus_encoder_cb.opus_feeding_state.counter / pcm_bytes_per_frame;
+  a2dp_opus_encoder_cb.opus_feeding_state.counter -=
+      result * pcm_bytes_per_frame;
+  nof = result;
+
+  *num_of_frames = nof;
+  *num_of_iterations = noi;
+}
+
+static void a2dp_opus_encode_frames(uint8_t nb_frame) {
+  tA2DP_OPUS_ENCODER_PARAMS* p_encoder_params =
+      &a2dp_opus_encoder_cb.opus_encoder_params;
+  unsigned char* packet;
+  uint8_t remain_nb_frame = nb_frame;
+  uint16_t opus_frame_size = p_encoder_params->framesize;
+  uint8_t read_buffer[p_encoder_params->framesize *
+                      p_encoder_params->pcm_wlength *
+                      p_encoder_params->channel_mode];
+
+  int32_t out_frames = 0;
+  uint32_t written = 0;
+
+  uint32_t bytes_read = 0;
+  while (nb_frame) {
+    BT_HDR* p_buf = (BT_HDR*)osi_malloc(BT_DEFAULT_BUFFER_SIZE);
+    p_buf->offset = A2DP_OPUS_OFFSET;
+    p_buf->len = 0;
+    p_buf->layer_specific = 0;
+    a2dp_opus_encoder_cb.stats.media_read_total_expected_packets++;
+
+    do {
+      //
+      // Read the PCM data and encode it
+      //
+      uint32_t temp_bytes_read = 0;
+      if (a2dp_opus_read_feeding(read_buffer, &temp_bytes_read)) {
+        bytes_read += temp_bytes_read;
+        packet = (unsigned char*)(p_buf + 1) + p_buf->offset + p_buf->len;
+
+        if (a2dp_opus_encoder_cb.opus_handle == NULL) {
+          LOG_ERROR("invalid OPUS handle");
+          a2dp_opus_encoder_cb.stats.media_read_total_dropped_packets++;
+          osi_free(p_buf);
+          return;
+        }
+
+        written =
+            opus_encode(a2dp_opus_encoder_cb.opus_handle,
+                        (const opus_int16*)&read_buffer[0], opus_frame_size,
+                        packet, (BT_DEFAULT_BUFFER_SIZE - p_buf->offset));
+
+        if (written <= 0) {
+          LOG_ERROR("OPUS encoding error");
+          a2dp_opus_encoder_cb.stats.media_read_total_dropped_packets++;
+          osi_free(p_buf);
+          return;
+        } else {
+          out_frames++;
+        }
+        p_buf->len += written;
+        nb_frame--;
+        p_buf->layer_specific += out_frames;  // added a frame to the buffer
+      } else {
+        LOG_WARN("Opus src buffer underflow %d", nb_frame);
+        a2dp_opus_encoder_cb.opus_feeding_state.counter +=
+            nb_frame * opus_frame_size *
+            a2dp_opus_encoder_cb.feeding_params.channel_count *
+            a2dp_opus_encoder_cb.feeding_params.bits_per_sample / 8;
+
+        // no more pcm to read
+        nb_frame = 0;
+      }
+    } while ((written == 0) && nb_frame);
+
+    if (p_buf->len) {
+      /*
+       * Timestamp of the media packet header represent the TS of the
+       * first frame, i.e. the timestamp before including this frame.
+       */
+      *((uint32_t*)(p_buf + 1)) = a2dp_opus_encoder_cb.timestamp;
+
+      a2dp_opus_encoder_cb.timestamp += p_buf->layer_specific * opus_frame_size;
+
+      uint8_t done_nb_frame = remain_nb_frame - nb_frame;
+      remain_nb_frame = nb_frame;
+
+      if (!a2dp_opus_encoder_cb.enqueue_callback(p_buf, done_nb_frame,
+                                                 bytes_read))
+        return;
+    } else {
+      a2dp_opus_encoder_cb.stats.media_read_total_dropped_packets++;
+      osi_free(p_buf);
+    }
+  }
+}
+
+static bool a2dp_opus_read_feeding(uint8_t* read_buffer, uint32_t* bytes_read) {
+  uint32_t read_size = a2dp_opus_encoder_cb.opus_encoder_params.framesize *
+                       a2dp_opus_encoder_cb.feeding_params.channel_count *
+                       a2dp_opus_encoder_cb.feeding_params.bits_per_sample / 8;
+
+  a2dp_opus_encoder_cb.stats.media_read_total_expected_reads_count++;
+  a2dp_opus_encoder_cb.stats.media_read_total_expected_read_bytes += read_size;
+
+  /* Read Data from UIPC channel */
+  uint32_t nb_byte_read =
+      a2dp_opus_encoder_cb.read_callback(read_buffer, read_size);
+  a2dp_opus_encoder_cb.stats.media_read_total_actual_read_bytes += nb_byte_read;
+
+  if (nb_byte_read < read_size) {
+    if (nb_byte_read == 0) return false;
+
+    /* Fill the unfilled part of the read buffer with silence (0) */
+    memset(((uint8_t*)read_buffer) + nb_byte_read, 0, read_size - nb_byte_read);
+    nb_byte_read = read_size;
+  }
+  a2dp_opus_encoder_cb.stats.media_read_total_actual_reads_count++;
+
+  *bytes_read = nb_byte_read;
+  return true;
+}
+
+void a2dp_vendor_opus_set_transmit_queue_length(size_t transmit_queue_length) {
+  a2dp_opus_encoder_cb.TxQueueLength = transmit_queue_length;
+
+  return;
+}
+
+uint64_t A2dpCodecConfigOpusSource::encoderIntervalMs() const {
+  return a2dp_vendor_opus_get_encoder_interval_ms();
+}
+
+int a2dp_vendor_opus_get_effective_frame_size() {
+  return a2dp_opus_encoder_cb.TxAaMtuSize;
+}
+
+void A2dpCodecConfigOpusSource::debug_codec_dump(int fd) {
+  a2dp_opus_encoder_stats_t* stats = &a2dp_opus_encoder_cb.stats;
+  tA2DP_OPUS_ENCODER_PARAMS* p_encoder_params =
+      &a2dp_opus_encoder_cb.opus_encoder_params;
+
+  A2dpCodecConfig::debug_codec_dump(fd);
+
+  dprintf(fd,
+          "  Packet counts (expected/dropped)                        : %zu / "
+          "%zu\n",
+          stats->media_read_total_expected_packets,
+          stats->media_read_total_dropped_packets);
+
+  dprintf(fd,
+          "  PCM read counts (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);
+
+  dprintf(fd,
+          "  OPUS transmission bitrate (Kbps)                        : %d\n",
+          p_encoder_params->bitrate);
+
+  dprintf(fd,
+          "  OPUS saved transmit queue length                        : %zu\n",
+          a2dp_opus_encoder_cb.TxQueueLength);
+
+  return;
+}
diff --git a/system/stack/include/a2dp_error_codes.h b/system/stack/include/a2dp_error_codes.h
index 0aa7105b7acd20c4275cbed319f351eb4dfb3632..ae5e26acf5f2502fd1027eccfe7372a91b8fed4d 100644
--- a/system/stack/include/a2dp_error_codes.h
+++ b/system/stack/include/a2dp_error_codes.h
@@ -128,6 +128,9 @@
  */
 #define A2DP_BAD_CP_FORMAT 0xE1
 
+/* Invalid framesize */
+#define A2DP_NS_FRAMESIZE 0xE2
+
 typedef uint8_t tA2DP_STATUS;
 
 #endif  // A2DP_ERROR_CODES_H
diff --git a/system/stack/include/a2dp_vendor_opus.h b/system/stack/include/a2dp_vendor_opus.h
new file mode 100644
index 0000000000000000000000000000000000000000..08a0b7b660f2ede8478d9e9aed7d540099be9df0
--- /dev/null
+++ b/system/stack/include/a2dp_vendor_opus.h
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2021 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 Opus
+//
+
+#ifndef A2DP_VENDOR_OPUS_H
+#define A2DP_VENDOR_OPUS_H
+
+#include "a2dp_codec_api.h"
+#include "a2dp_vendor_opus_constants.h"
+#include "avdt_api.h"
+
+class A2dpCodecConfigOpusBase : public A2dpCodecConfig {
+ protected:
+  A2dpCodecConfigOpusBase(btav_a2dp_codec_index_t codec_index,
+                          const std::string& name,
+                          btav_a2dp_codec_priority_t codec_priority,
+                          bool is_source)
+      : A2dpCodecConfig(codec_index, name, codec_priority),
+        is_source_(is_source) {}
+  bool setCodecConfig(const uint8_t* p_peer_codec_info, bool is_capability,
+                      uint8_t* p_result_codec_config) override;
+  bool setPeerCodecCapabilities(
+      const uint8_t* p_peer_codec_capabilities) override;
+
+ private:
+  bool is_source_;  // True if local is Source
+};
+
+class A2dpCodecConfigOpusSource : public A2dpCodecConfigOpusBase {
+ public:
+  A2dpCodecConfigOpusSource(btav_a2dp_codec_priority_t codec_priority);
+  virtual ~A2dpCodecConfigOpusSource();
+
+  bool init() override;
+  uint64_t encoderIntervalMs() const;
+
+ private:
+  bool useRtpHeaderMarkerBit() const override;
+  bool updateEncoderUserConfig(
+      const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+      bool* p_restart_input, bool* p_restart_output, bool* p_config_updated);
+  void debug_codec_dump(int fd) override;
+};
+
+class A2dpCodecConfigOpusSink : public A2dpCodecConfigOpusBase {
+ public:
+  A2dpCodecConfigOpusSink(btav_a2dp_codec_priority_t codec_priority);
+  virtual ~A2dpCodecConfigOpusSink();
+
+  bool init() override;
+  uint64_t encoderIntervalMs() const;
+
+ private:
+  bool useRtpHeaderMarkerBit() const override;
+  bool updateEncoderUserConfig(
+      const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
+      bool* p_restart_input, bool* p_restart_output, bool* p_config_updated);
+};
+
+// Checks whether the codec capabilities contain a valid A2DP Opus Source
+// codec.
+// NOTE: only codecs that are implemented are considered valid.
+// Returns true if |p_codec_info| contains information about a valid Opus
+// codec, otherwise false.
+bool A2DP_IsVendorSourceCodecValidOpus(const uint8_t* p_codec_info);
+
+// Checks whether the codec capabilities contain a valid A2DP Opus Sink
+// codec.
+// NOTE: only codecs that are implemented are considered valid.
+// Returns true if |p_codec_info| contains information about a valid Opus
+// codec, otherwise false.
+bool A2DP_IsVendorSinkCodecValidOpus(const uint8_t* p_codec_info);
+
+// Checks whether the codec capabilities contain a valid peer A2DP Opus Sink
+// codec.
+// NOTE: only codecs that are implemented are considered valid.
+// Returns true if |p_codec_info| contains information about a valid Opus
+// codec, otherwise false.
+bool A2DP_IsVendorPeerSinkCodecValidOpus(const uint8_t* p_codec_info);
+
+// Checks whether the codec capabilities contain a valid peer A2DP Opus Source
+// codec.
+// NOTE: only codecs that are implemented are considered valid.
+// Returns true if |p_codec_info| contains information about a valid Opus
+// codec, otherwise false.
+bool A2DP_IsVendorPeerSourceCodecValidOpus(const uint8_t* p_codec_info);
+
+// Checks whether A2DP Opus Sink codec is supported.
+// |p_codec_info| contains information about the codec capabilities.
+// Returns true if the A2DP Opus Sink codec is supported, otherwise false.
+bool A2DP_IsVendorSinkCodecSupportedOpus(const uint8_t* p_codec_info);
+
+// Checks whether an A2DP Opus Source codec for a peer Source device is
+// supported.
+// |p_codec_info| contains information about the codec capabilities of the
+// peer device.
+// Returns true if the A2DP Opus Source codec for a peer Source device is
+// supported, otherwise false.
+bool A2DP_IsPeerSourceCodecSupportedOpus(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_VendorUsesRtpHeaderOpus(bool content_protection_enabled,
+                                  const uint8_t* p_codec_info);
+
+// Gets the A2DP Opus codec name for a given |p_codec_info|.
+const char* A2DP_VendorCodecNameOpus(const uint8_t* p_codec_info);
+
+// Checks whether two A2DP Opus 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_VendorCodecTypeEqualsOpus(const uint8_t* p_codec_info_a,
+                                    const uint8_t* p_codec_info_b);
+
+// Checks whether two A2DP Opus 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 Opus, the return value is false.
+bool A2DP_VendorCodecEqualsOpus(const uint8_t* p_codec_info_a,
+                                const uint8_t* p_codec_info_b);
+
+// Gets the track sample rate value for the A2DP Opus codec.
+// |p_codec_info| is a pointer to the Opus codec_info to decode.
+// Returns the track sample rate on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetTrackSampleRateOpus(const uint8_t* p_codec_info);
+
+// Gets the track bits per sample value for the A2DP Opus codec.
+// |p_codec_info| is a pointer to the Opus codec_info to decode.
+// Returns the track bits per sample on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetTrackBitsPerSampleOpus(const uint8_t* p_codec_info);
+
+// Gets the track bitrate value for the A2DP Opus codec.
+// |p_codec_info| is a pointer to the Opus codec_info to decode.
+// Returns the track sample rate on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetBitRateOpus(const uint8_t* p_codec_info);
+
+// Gets the channel count for the A2DP Opus codec.
+// |p_codec_info| is a pointer to the Opus codec_info to decode.
+// Returns the channel count on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetTrackChannelCountOpus(const uint8_t* p_codec_info);
+
+// Gets the channel type for the A2DP Opus codec.
+// 1 for mono, or 3 for dual channel/stereo.
+// |p_codec_info| is a pointer to the Opus codec_info to decode.
+// Returns the channel count on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetSinkTrackChannelTypeOpus(const uint8_t* p_codec_info);
+
+// Gets the channel mode code for the A2DP Opus codec.
+// The actual value is codec-specific - see |A2DP_OPUS_CHANNEL_MODE_*|.
+// |p_codec_info| is a pointer to the Opus codec_info to decode.
+// Returns the channel mode code on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetChannelModeCodeOpus(const uint8_t* p_codec_info);
+
+// Gets the framesize value (in ms) for the A2DP Opus codec.
+// |p_codec_info| is a pointer to the Opus codec_info to decode.
+// Returns the framesize on success, or -1 if |p_codec_info|
+// contains invalid codec information.
+int A2DP_VendorGetFrameSizeOpus(const uint8_t* p_codec_info);
+
+// Gets the A2DP Opus 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_VendorGetPacketTimestampOpus(const uint8_t* p_codec_info,
+                                       const uint8_t* p_data,
+                                       uint32_t* p_timestamp);
+
+// Builds A2DP Opus 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_VendorBuildCodecHeaderOpus(const uint8_t* p_codec_info, BT_HDR* p_buf,
+                                     uint16_t frames_per_packet);
+
+// Decodes A2DP Opus codec info into a human readable string.
+// |p_codec_info| is a pointer to the Opus codec_info to decode.
+// Returns a string describing the codec information.
+std::string A2DP_VendorCodecInfoStringOpus(const uint8_t* p_codec_info);
+
+// Gets the A2DP Opus 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 Opus encoder interface if the |p_codec_info| is valid and
+// supported, otherwise NULL.
+const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterfaceOpus(
+    const uint8_t* p_codec_info);
+
+// Gets the current A2DP Opus decoder interface that can be used to decode
+// received A2DP packets - see |tA2DP_DECODER_INTERFACE|.
+// |p_codec_info| contains the codec information.
+// Returns the A2DP Opus decoder interface if the |p_codec_info| is valid and
+// supported, otherwise NULL.
+const tA2DP_DECODER_INTERFACE* A2DP_VendorGetDecoderInterfaceOpus(
+    const uint8_t* p_codec_info);
+
+// Adjusts the A2DP Opus 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_VendorAdjustCodecOpus(uint8_t* p_codec_info);
+
+// Gets the A2DP Opus 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_VendorSourceCodecIndexOpus(
+    const uint8_t* p_codec_info);
+
+// Gets the A2DP Opus Sink 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_VendorSinkCodecIndexOpus(
+    const uint8_t* p_codec_info);
+
+// Gets the A2DP Opus Source codec name.
+const char* A2DP_VendorCodecIndexStrOpus(void);
+
+// Gets the A2DP Opus Sink codec name.
+const char* A2DP_VendorCodecIndexStrOpusSink(void);
+
+// Initializes A2DP Opus Source codec information into |AvdtpSepConfig|
+// configuration entry pointed by |p_cfg|.
+bool A2DP_VendorInitCodecConfigOpus(AvdtpSepConfig* p_cfg);
+
+// Initializes A2DP Opus Sink codec information into |AvdtpSepConfig|
+// configuration entry pointed by |p_cfg|.
+bool A2DP_VendorInitCodecConfigOpusSink(AvdtpSepConfig* p_cfg);
+
+#endif  // A2DP_VENDOR_OPUS_H
diff --git a/system/stack/include/a2dp_vendor_opus_constants.h b/system/stack/include/a2dp_vendor_opus_constants.h
new file mode 100644
index 0000000000000000000000000000000000000000..272b04c48d3dea7a62ac3b30e48d22864774e5ef
--- /dev/null
+++ b/system/stack/include/a2dp_vendor_opus_constants.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2021 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 Opus codec
+//
+
+#ifndef A2DP_VENDOR_OPUS_CONSTANTS_H
+#define A2DP_VENDOR_OPUS_CONSTANTS_H
+
+#define A2DP_OPUS_CODEC_LEN 9
+
+#define A2DP_OPUS_CODEC_OUTPUT_CHS 2
+#define A2DP_OPUS_CODEC_DEFAULT_SAMPLERATE 48000
+#define A2DP_OPUS_CODEC_DEFAULT_FRAMESIZE 960
+#define A2DP_OPUS_DECODE_BUFFER_LENGTH \
+  (A2DP_OPUS_CODEC_OUTPUT_CHS * A2DP_OPUS_CODEC_DEFAULT_FRAMESIZE * 4)
+
+// [Octet 0-3] Vendor ID
+#define A2DP_OPUS_VENDOR_ID 0x000000E0
+// [Octet 4-5] Vendor Specific Codec ID
+#define A2DP_OPUS_CODEC_ID 0x0001
+// [Octet 6], [Bits 0,1,2] Channel Mode
+#define A2DP_OPUS_CHANNEL_MODE_MASK 0x07
+#define A2DP_OPUS_CHANNEL_MODE_MONO 0x01
+#define A2DP_OPUS_CHANNEL_MODE_STEREO 0x02
+#define A2DP_OPUS_CHANNEL_MODE_DUAL_MONO 0x04
+// [Octet 6], [Bits 3,4] Future 2, FrameSize
+#define A2DP_OPUS_FRAMESIZE_MASK 0x18
+#define A2DP_OPUS_10MS_FRAMESIZE 0x08
+#define A2DP_OPUS_20MS_FRAMESIZE 0x10
+// [Octet 6], [Bits 5] Sampling Frequency
+#define A2DP_OPUS_SAMPLING_FREQ_MASK 0x80
+#define A2DP_OPUS_SAMPLING_FREQ_48000 0x80
+// [Octet 6], [Bits 6,7] Reserved
+#define A2DP_OPUS_FUTURE_3 0x40
+#define A2DP_OPUS_FUTURE_4 0x80
+
+// Length of the Opus Media Payload header
+#define A2DP_OPUS_MPL_HDR_LEN 1
+
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+#define A2DP_OPUS_OFFSET (AVDT_MEDIA_OFFSET + A2DP_OPUS_MPL_HDR_LEN + 1)
+#else
+#define A2DP_OPUS_OFFSET (AVDT_MEDIA_OFFSET + A2DP_OPUS_MPL_HDR_LEN)
+#endif
+
+#define A2DP_OPUS_HDR_F_MSK 0x80
+#define A2DP_OPUS_HDR_S_MSK 0x40
+#define A2DP_OPUS_HDR_L_MSK 0x20
+#define A2DP_OPUS_HDR_NUM_MSK 0x0F
+
+#endif
diff --git a/system/stack/include/a2dp_vendor_opus_decoder.h b/system/stack/include/a2dp_vendor_opus_decoder.h
new file mode 100644
index 0000000000000000000000000000000000000000..67b5bf706f3b2e22641589cdf7731f668474f045
--- /dev/null
+++ b/system/stack/include/a2dp_vendor_opus_decoder.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2021 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 Opus Decoder
+//
+
+#ifndef A2DP_VENDOR_OPUS_DECODER_H
+#define A2DP_VENDOR_OPUS_DECODER_H
+
+#include "a2dp_codec_api.h"
+
+// Initialize the A2DP Opus decoder.
+bool a2dp_vendor_opus_decoder_init(decoded_data_callback_t decode_callback);
+
+// Cleanup the A2DP Opus decoder.
+void a2dp_vendor_opus_decoder_cleanup(void);
+
+// Decodes |p_buf|. Calls |decode_callback| passed into
+// |a2dp_vendor_opus_decoder_init| if decoded frames are available.
+bool a2dp_vendor_opus_decoder_decode_packet(BT_HDR* p_buf);
+
+// Start the A2DP Opus decoder.
+void a2dp_vendor_opus_decoder_start(void);
+
+// Suspend the A2DP Opus decoder.
+void a2dp_vendor_opus_decoder_suspend(void);
+
+// A2DP Opus decoder configuration.
+void a2dp_vendor_opus_decoder_configure(const uint8_t* p_codec_info);
+
+#endif  // A2DP_VENDOR_OPUS_DECODER_H
diff --git a/system/stack/include/a2dp_vendor_opus_encoder.h b/system/stack/include/a2dp_vendor_opus_encoder.h
new file mode 100644
index 0000000000000000000000000000000000000000..0f88265f1f94dfe8799d73c5d5c9a89ce55882a1
--- /dev/null
+++ b/system/stack/include/a2dp_vendor_opus_encoder.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2021 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 Opus Encoder
+//
+
+#ifndef A2DP_VENDOR_OPUS_ENCODER_H
+#define A2DP_VENDOR_OPUS_ENCODER_H
+
+#include "a2dp_codec_api.h"
+
+// Initialize the A2DP Opus 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_opus_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 Opus encoder.
+void a2dp_vendor_opus_encoder_cleanup(void);
+
+// Reset the feeding for the A2DP Opus encoder.
+void a2dp_vendor_opus_feeding_reset(void);
+
+// Flush the feeding for the A2DP Opus encoder.
+void a2dp_vendor_opus_feeding_flush(void);
+
+// Get the A2DP Opus encoder interval (in milliseconds).
+uint64_t a2dp_vendor_opus_get_encoder_interval_ms(void);
+
+// Prepare and send A2DP Opus encoded frames.
+// |timestamp_us| is the current timestamp (in microseconds).
+void a2dp_vendor_opus_send_frames(uint64_t timestamp_us);
+
+// Set transmit queue length for the A2DP Opus (Dynamic Bit Rate) mechanism.
+void a2dp_vendor_opus_set_transmit_queue_length(size_t transmit_queue_length);
+
+// Get the A2DP Opus encoded maximum frame size
+int a2dp_vendor_opus_get_effective_frame_size();
+
+#endif  // A2DP_VENDOR_OPUS_ENCODER_H
diff --git a/system/stack/test/fuzzers/Android.bp b/system/stack/test/fuzzers/Android.bp
index 54812c0b29871168005544ffc92df6501f661b7f..376b5ad5927c252f1ddf506c3389e154b2ad73e5 100644
--- a/system/stack/test/fuzzers/Android.bp
+++ b/system/stack/test/fuzzers/Android.bp
@@ -35,6 +35,7 @@ cc_defaults {
         "libbtdevice",
         "libg722codec",
         "liblc3",
+        "libopus",
         "libosi",
         "libudrv-uipc",
         "libbt-protos-lite",
diff --git a/system/stack/test/stack_a2dp_test.cc b/system/stack/test/stack_a2dp_test.cc
index fe7692a8a5f414e4b4fed7eb62ef41fac7bb38f3..84744211185a76de4f6b8a89c5065f3a9baa2dac 100644
--- a/system/stack/test/stack_a2dp_test.cc
+++ b/system/stack/test/stack_a2dp_test.cc
@@ -27,6 +27,7 @@
 #include "stack/include/a2dp_codec_api.h"
 #include "stack/include/a2dp_sbc.h"
 #include "stack/include/a2dp_vendor.h"
+#include "stack/include/a2dp_vendor_opus_constants.h"
 #include "stack/include/bt_hdr.h"
 
 namespace {
@@ -187,6 +188,47 @@ const uint8_t codec_info_aac_sink_capability[AVDT_CODEC_SIZE] = {
     9                                  // Fake
 };
 
+const uint8_t codec_info_opus[AVDT_CODEC_SIZE] = {
+    A2DP_OPUS_CODEC_LEN,         // Length
+    AVDT_MEDIA_TYPE_AUDIO << 4,  // Media Type
+    A2DP_MEDIA_CT_NON_A2DP,      // Media Codec Type Vendor
+    (A2DP_OPUS_VENDOR_ID & 0x000000FF),
+    (A2DP_OPUS_VENDOR_ID & 0x0000FF00) >> 8,
+    (A2DP_OPUS_VENDOR_ID & 0x00FF0000) >> 16,
+    (A2DP_OPUS_VENDOR_ID & 0xFF000000) >> 24,
+    (A2DP_OPUS_CODEC_ID & 0x00FF),
+    (A2DP_OPUS_CODEC_ID & 0xFF00) >> 8,
+    A2DP_OPUS_CHANNEL_MODE_STEREO | A2DP_OPUS_20MS_FRAMESIZE |
+        A2DP_OPUS_SAMPLING_FREQ_48000};
+
+const uint8_t codec_info_opus_capability[AVDT_CODEC_SIZE] = {
+    A2DP_OPUS_CODEC_LEN,         // Length
+    AVDT_MEDIA_TYPE_AUDIO << 4,  // Media Type
+    A2DP_MEDIA_CT_NON_A2DP,      // Media Codec Type Vendor
+    (A2DP_OPUS_VENDOR_ID & 0x000000FF),
+    (A2DP_OPUS_VENDOR_ID & 0x0000FF00) >> 8,
+    (A2DP_OPUS_VENDOR_ID & 0x00FF0000) >> 16,
+    (A2DP_OPUS_VENDOR_ID & 0xFF000000) >> 24,
+    (A2DP_OPUS_CODEC_ID & 0x00FF),
+    (A2DP_OPUS_CODEC_ID & 0xFF00) >> 8,
+    A2DP_OPUS_CHANNEL_MODE_MONO | A2DP_OPUS_CHANNEL_MODE_STEREO |
+        A2DP_OPUS_10MS_FRAMESIZE | A2DP_OPUS_20MS_FRAMESIZE |
+        A2DP_OPUS_SAMPLING_FREQ_48000};
+
+const uint8_t codec_info_opus_sink_capability[AVDT_CODEC_SIZE] = {
+    A2DP_OPUS_CODEC_LEN,         // Length
+    AVDT_MEDIA_TYPE_AUDIO << 4,  // Media Type
+    A2DP_MEDIA_CT_NON_A2DP,      // Media Codec Type Vendor
+    (A2DP_OPUS_VENDOR_ID & 0x000000FF),
+    (A2DP_OPUS_VENDOR_ID & 0x0000FF00) >> 8,
+    (A2DP_OPUS_VENDOR_ID & 0x00FF0000) >> 16,
+    (A2DP_OPUS_VENDOR_ID & 0xFF000000) >> 24,
+    (A2DP_OPUS_CODEC_ID & 0x00FF),
+    (A2DP_OPUS_CODEC_ID & 0xFF00) >> 8,
+    A2DP_OPUS_CHANNEL_MODE_MONO | A2DP_OPUS_CHANNEL_MODE_STEREO |
+        A2DP_OPUS_10MS_FRAMESIZE | A2DP_OPUS_20MS_FRAMESIZE |
+        A2DP_OPUS_SAMPLING_FREQ_48000};
+
 const uint8_t codec_info_non_a2dp[AVDT_CODEC_SIZE] = {
     8,              // Length
     0,              // Media Type: AVDT_MEDIA_TYPE_AUDIO
@@ -265,10 +307,11 @@ class StackA2dpTest : public ::testing::Test {
           supported = has_shared_library(LDAC_DECODER_LIB_NAME);
           break;
         case BTAV_A2DP_CODEC_INDEX_SOURCE_LC3:
+          break;
         case BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS:
-          // TODO(b/226441860): in-progress
         case BTAV_A2DP_CODEC_INDEX_SINK_OPUS:
-          // TODO(b/226441860): in-progress
+          supported = true;
+          break;
         case BTAV_A2DP_CODEC_INDEX_MAX:
           // Needed to avoid using "default:" case so we can capture when
           // a new codec is added, and it can be included here.
@@ -386,6 +429,32 @@ TEST_F(StackA2dpTest, test_a2dp_is_codec_valid_aac) {
   EXPECT_FALSE(A2DP_IsPeerSinkCodecValid(codec_info_aac_invalid));
 }
 
+TEST_F(StackA2dpTest, test_a2dp_is_codec_valid_opus) {
+  ASSERT_TRUE(A2DP_IsVendorSourceCodecValid(codec_info_opus));
+  ASSERT_TRUE(A2DP_IsVendorSourceCodecValid(codec_info_opus_capability));
+  ASSERT_TRUE(A2DP_IsVendorPeerSourceCodecValid(codec_info_opus));
+  ASSERT_TRUE(A2DP_IsVendorPeerSourceCodecValid(codec_info_opus_capability));
+
+  ASSERT_TRUE(A2DP_IsVendorSinkCodecValid(codec_info_opus_sink_capability));
+  ASSERT_TRUE(A2DP_IsVendorPeerSinkCodecValid(codec_info_opus_sink_capability));
+
+  // Test with invalid Opus configuration
+  uint8_t codec_info_opus_invalid[AVDT_CODEC_SIZE];
+  memcpy(codec_info_opus_invalid, codec_info_opus, sizeof(codec_info_opus));
+  codec_info_opus_invalid[0] = 0;  // Corrupt the Length field
+  ASSERT_FALSE(A2DP_IsVendorSourceCodecValid(codec_info_opus_invalid));
+  ASSERT_FALSE(A2DP_IsVendorSinkCodecValid(codec_info_opus_invalid));
+  ASSERT_FALSE(A2DP_IsVendorPeerSourceCodecValid(codec_info_opus_invalid));
+  ASSERT_FALSE(A2DP_IsVendorPeerSinkCodecValid(codec_info_opus_invalid));
+
+  memcpy(codec_info_opus_invalid, codec_info_opus, sizeof(codec_info_opus));
+  codec_info_opus_invalid[1] = 0xff;  // Corrupt the Media Type field
+  ASSERT_FALSE(A2DP_IsVendorSourceCodecValid(codec_info_opus_invalid));
+  ASSERT_FALSE(A2DP_IsVendorSinkCodecValid(codec_info_opus_invalid));
+  ASSERT_FALSE(A2DP_IsVendorPeerSourceCodecValid(codec_info_opus_invalid));
+  ASSERT_FALSE(A2DP_IsVendorPeerSinkCodecValid(codec_info_opus_invalid));
+}
+
 TEST_F(StackA2dpTest, test_a2dp_get_codec_type) {
   tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(codec_info_sbc);
   EXPECT_EQ(codec_type, A2DP_MEDIA_CT_SBC);
@@ -393,6 +462,9 @@ TEST_F(StackA2dpTest, test_a2dp_get_codec_type) {
   codec_type = A2DP_GetCodecType(codec_info_aac);
   EXPECT_EQ(codec_type, A2DP_MEDIA_CT_AAC);
 
+  codec_type = A2DP_GetCodecType(codec_info_opus);
+  ASSERT_EQ(codec_type, A2DP_MEDIA_CT_NON_A2DP);
+
   codec_type = A2DP_GetCodecType(codec_info_non_a2dp);
   EXPECT_EQ(codec_type, A2DP_MEDIA_CT_NON_A2DP);
 }
@@ -443,6 +515,9 @@ TEST_F(StackA2dpTest, test_a2dp_uses_rtp_header) {
   EXPECT_TRUE(A2DP_UsesRtpHeader(true, codec_info_aac));
   EXPECT_TRUE(A2DP_UsesRtpHeader(false, codec_info_aac));
 
+  ASSERT_TRUE(A2DP_VendorUsesRtpHeader(true, codec_info_opus));
+  ASSERT_TRUE(A2DP_VendorUsesRtpHeader(false, codec_info_opus));
+
   EXPECT_TRUE(A2DP_UsesRtpHeader(true, codec_info_non_a2dp));
   EXPECT_TRUE(A2DP_UsesRtpHeader(false, codec_info_non_a2dp));
 }
@@ -473,6 +548,9 @@ TEST_F(StackA2dpTest, test_a2dp_codec_name) {
   EXPECT_STREQ(A2DP_CodecName(codec_info_aac), "AAC");
   EXPECT_STREQ(A2DP_CodecName(codec_info_aac_capability), "AAC");
   EXPECT_STREQ(A2DP_CodecName(codec_info_aac_sink_capability), "AAC");
+  ASSERT_STREQ(A2DP_CodecName(codec_info_opus), "Opus");
+  ASSERT_STREQ(A2DP_CodecName(codec_info_opus_capability), "Opus");
+  ASSERT_STREQ(A2DP_CodecName(codec_info_opus_sink_capability), "Opus");
   EXPECT_STREQ(A2DP_CodecName(codec_info_non_a2dp), "UNKNOWN VENDOR CODEC");
 
   // Test all unknown codecs
@@ -496,11 +574,19 @@ TEST_F(StackA2dpTest, test_a2dp_codec_type_equals) {
   EXPECT_TRUE(A2DP_CodecTypeEquals(codec_info_sbc, codec_info_sbc_capability));
   EXPECT_TRUE(
       A2DP_CodecTypeEquals(codec_info_sbc, codec_info_sbc_sink_capability));
+
   EXPECT_TRUE(A2DP_CodecTypeEquals(codec_info_aac, codec_info_aac_capability));
   EXPECT_TRUE(
       A2DP_CodecTypeEquals(codec_info_aac, codec_info_aac_sink_capability));
+
+  ASSERT_TRUE(
+      A2DP_VendorCodecTypeEquals(codec_info_opus, codec_info_opus_capability));
+  ASSERT_TRUE(A2DP_VendorCodecTypeEquals(codec_info_opus,
+                                         codec_info_opus_sink_capability));
+
   EXPECT_TRUE(
       A2DP_CodecTypeEquals(codec_info_non_a2dp, codec_info_non_a2dp_fake));
+
   EXPECT_FALSE(A2DP_CodecTypeEquals(codec_info_sbc, codec_info_non_a2dp));
   EXPECT_FALSE(A2DP_CodecTypeEquals(codec_info_aac, codec_info_non_a2dp));
   EXPECT_FALSE(A2DP_CodecTypeEquals(codec_info_sbc, codec_info_aac));
@@ -509,6 +595,7 @@ TEST_F(StackA2dpTest, test_a2dp_codec_type_equals) {
 TEST_F(StackA2dpTest, test_a2dp_codec_equals) {
   uint8_t codec_info_sbc_test[AVDT_CODEC_SIZE];
   uint8_t codec_info_aac_test[AVDT_CODEC_SIZE];
+  uint8_t codec_info_opus_test[AVDT_CODEC_SIZE];
   uint8_t codec_info_non_a2dp_test[AVDT_CODEC_SIZE];
 
   // Test two identical SBC codecs
@@ -521,6 +608,11 @@ TEST_F(StackA2dpTest, test_a2dp_codec_equals) {
   memcpy(codec_info_aac_test, codec_info_aac, sizeof(codec_info_aac));
   EXPECT_TRUE(A2DP_CodecEquals(codec_info_aac, codec_info_aac_test));
 
+  // Test two identical Opus codecs
+  memset(codec_info_opus_test, 0xAB, sizeof(codec_info_opus_test));
+  memcpy(codec_info_opus_test, codec_info_opus, sizeof(codec_info_opus));
+  ASSERT_TRUE(A2DP_VendorCodecEquals(codec_info_opus, codec_info_opus_test));
+
   // Test two identical non-A2DP codecs that are not recognized
   memset(codec_info_non_a2dp_test, 0xAB, sizeof(codec_info_non_a2dp_test));
   memcpy(codec_info_non_a2dp_test, codec_info_non_a2dp,
@@ -529,7 +621,8 @@ TEST_F(StackA2dpTest, test_a2dp_codec_equals) {
 
   // Test two codecs that have different types
   EXPECT_FALSE(A2DP_CodecEquals(codec_info_sbc, codec_info_non_a2dp));
-  EXPECT_FALSE(A2DP_CodecEquals(codec_info_sbc, codec_info_aac));
+  ASSERT_FALSE(A2DP_CodecEquals(codec_info_sbc, codec_info_aac));
+  ASSERT_FALSE(A2DP_CodecEquals(codec_info_sbc, codec_info_opus));
 
   // Test two SBC codecs that are slightly different
   memset(codec_info_sbc_test, 0xAB, sizeof(codec_info_sbc_test));
@@ -567,12 +660,14 @@ TEST_F(StackA2dpTest, test_a2dp_codec_equals) {
 TEST_F(StackA2dpTest, test_a2dp_get_track_sample_rate) {
   EXPECT_EQ(A2DP_GetTrackSampleRate(codec_info_sbc), 44100);
   EXPECT_EQ(A2DP_GetTrackSampleRate(codec_info_aac), 44100);
+  ASSERT_EQ(A2DP_VendorGetTrackSampleRate(codec_info_opus), 48000);
   EXPECT_EQ(A2DP_GetTrackSampleRate(codec_info_non_a2dp), -1);
 }
 
 TEST_F(StackA2dpTest, test_a2dp_get_track_channel_count) {
   EXPECT_EQ(A2DP_GetTrackChannelCount(codec_info_sbc), 2);
   EXPECT_EQ(A2DP_GetTrackChannelCount(codec_info_aac), 2);
+  ASSERT_EQ(A2DP_VendorGetTrackChannelCount(codec_info_opus), 2);
   EXPECT_EQ(A2DP_GetTrackChannelCount(codec_info_non_a2dp), -1);
 }
 
@@ -625,6 +720,7 @@ TEST_F(StackA2dpTest, test_a2dp_get_max_bitpool_sbc) {
 TEST_F(StackA2dpTest, test_a2dp_get_sink_track_channel_type) {
   EXPECT_EQ(A2DP_GetSinkTrackChannelType(codec_info_sbc), 3);
   EXPECT_EQ(A2DP_GetSinkTrackChannelType(codec_info_aac), 3);
+  ASSERT_EQ(A2DP_VendorGetSinkTrackChannelType(codec_info_opus), 2);
   EXPECT_EQ(A2DP_GetSinkTrackChannelType(codec_info_non_a2dp), -1);
 }
 
@@ -669,6 +765,13 @@ TEST_F(StackA2dpTest, test_a2dp_get_packet_timestamp) {
   EXPECT_TRUE(A2DP_GetPacketTimestamp(codec_info_aac, a2dp_data, &timestamp));
   EXPECT_EQ(timestamp, static_cast<uint32_t>(0x12345678));
 
+  memset(a2dp_data, 0xAB, sizeof(a2dp_data));
+  *p_ts = 0x12345678;
+  timestamp = 0xFFFFFFFF;
+  ASSERT_TRUE(
+      A2DP_VendorGetPacketTimestamp(codec_info_opus, a2dp_data, &timestamp));
+  ASSERT_EQ(timestamp, static_cast<uint32_t>(0x12345678));
+
   memset(a2dp_data, 0xAB, sizeof(a2dp_data));
   *p_ts = 0x12345678;
   timestamp = 0xFFFFFFFF;
@@ -761,6 +864,12 @@ TEST_F(StackA2dpTest, test_a2dp_source_codec_index) {
             BTAV_A2DP_CODEC_INDEX_SOURCE_AAC);
   EXPECT_EQ(A2DP_SourceCodecIndex(codec_info_aac_sink_capability),
             BTAV_A2DP_CODEC_INDEX_SOURCE_AAC);
+  ASSERT_EQ(A2DP_VendorSourceCodecIndex(codec_info_opus),
+            BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS);
+  ASSERT_EQ(A2DP_VendorSourceCodecIndex(codec_info_opus_capability),
+            BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS);
+  ASSERT_EQ(A2DP_VendorSourceCodecIndex(codec_info_opus_sink_capability),
+            BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS);
   EXPECT_EQ(A2DP_SourceCodecIndex(codec_info_non_a2dp),
             BTAV_A2DP_CODEC_INDEX_MAX);
 }
@@ -779,6 +888,12 @@ TEST_F(StackA2dpTest, test_a2dp_sink_codec_index) {
             BTAV_A2DP_CODEC_INDEX_SINK_AAC);
   EXPECT_EQ(A2DP_SinkCodecIndex(codec_info_aac_sink_capability),
             BTAV_A2DP_CODEC_INDEX_SINK_AAC);
+  ASSERT_EQ(A2DP_VendorSinkCodecIndex(codec_info_opus),
+            BTAV_A2DP_CODEC_INDEX_SINK_OPUS);
+  ASSERT_EQ(A2DP_VendorSinkCodecIndex(codec_info_opus_capability),
+            BTAV_A2DP_CODEC_INDEX_SINK_OPUS);
+  ASSERT_EQ(A2DP_VendorSinkCodecIndex(codec_info_opus_sink_capability),
+            BTAV_A2DP_CODEC_INDEX_SINK_OPUS);
   EXPECT_EQ(A2DP_SinkCodecIndex(codec_info_non_a2dp),
             BTAV_A2DP_CODEC_INDEX_MAX);
 }
@@ -788,6 +903,10 @@ TEST_F(StackA2dpTest, test_a2dp_codec_index_str) {
   EXPECT_STREQ(A2DP_CodecIndexStr(BTAV_A2DP_CODEC_INDEX_SOURCE_SBC), "SBC");
   EXPECT_STREQ(A2DP_CodecIndexStr(BTAV_A2DP_CODEC_INDEX_SINK_SBC), "SBC SINK");
   EXPECT_STREQ(A2DP_CodecIndexStr(BTAV_A2DP_CODEC_INDEX_SOURCE_AAC), "AAC");
+  ASSERT_STREQ(A2DP_VendorCodecIndexStr(BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS),
+               "Opus");
+  ASSERT_STREQ(A2DP_VendorCodecIndexStr(BTAV_A2DP_CODEC_INDEX_SINK_OPUS),
+               "Opus SINK");
 
   // Test that the unknown codec string has not changed
   EXPECT_STREQ(A2DP_CodecIndexStr(BTAV_A2DP_CODEC_INDEX_MAX),
diff --git a/system/test/headless/Android.bp b/system/test/headless/Android.bp
index 92c6302ccf89993d4caa5ec5508edd1cce9b5b0e..5e009487acf3b7d248eb7b62dde2ad7381f75c06 100644
--- a/system/test/headless/Android.bp
+++ b/system/test/headless/Android.bp
@@ -66,6 +66,7 @@ cc_test {
         "libFraunhoferAAC",
         "libg722codec",
         "liblc3",
+        "libopus",
         "libosi",
         "libprotobuf-cpp-lite",
         "libudrv-uipc",
diff --git a/system/test/suite/Android.bp b/system/test/suite/Android.bp
index c39b1eb204820c0698b4d1cde5845cbdec633c7f..30f6f20c0bce72878b314eca0e3f2421d3aeaf2a 100644
--- a/system/test/suite/Android.bp
+++ b/system/test/suite/Android.bp
@@ -90,6 +90,7 @@ cc_defaults {
         "libg722codec",
         "libgmock",
         "liblc3",
+        "libopus",
         "libosi",
         "libstatslog_bt",
         "libc++fs",