diff --git a/system/btif/src/bluetooth.cc b/system/btif/src/bluetooth.cc
index cce99c2248c6c07bab57fa8160367fb57095bbad..d6ba57592990d29742249b337d2611228ad897ec 100644
--- a/system/btif/src/bluetooth.cc
+++ b/system/btif/src/bluetooth.cc
@@ -87,6 +87,7 @@
 #include "common/metrics.h"
 #include "common/os_utils.h"
 #include "device/include/device_iot_config.h"
+#include "device/include/esco_parameters.h"
 #include "device/include/interop.h"
 #include "device/include/interop_config.h"
 #include "include/check.h"
@@ -489,6 +490,10 @@ static bool get_swb_supported() {
   return hfp_hal_interface::get_swb_supported();
 }
 
+static bool is_coding_format_supported(esco_coding_format_t coding_format) {
+  return hfp_hal_interface::is_coding_format_supported(coding_format);
+}
+
 bool is_common_criteria_mode() {
   return is_bluetooth_uid() && common_criteria_mode;
 }
@@ -1205,6 +1210,7 @@ EXPORT_SYMBOL bt_interface_t bluetoothInterface = {
         set_event_filter_connection_setup_all_devices,
     .get_wbs_supported = get_wbs_supported,
     .get_swb_supported = get_swb_supported,
+    .is_coding_format_supported = is_coding_format_supported,
     .metadata_changed = metadata_changed,
     .interop_match_addr = interop_match_addr,
     .interop_match_name = interop_match_name,
diff --git a/system/gd/rust/linux/client/src/dbus_iface.rs b/system/gd/rust/linux/client/src/dbus_iface.rs
index dabeb6d9af4c03dad528ea08e29036dfbc6f7ebe..fb392a3795952b79ac27398373a04b7259bd9da2 100644
--- a/system/gd/rust/linux/client/src/dbus_iface.rs
+++ b/system/gd/rust/linux/client/src/dbus_iface.rs
@@ -10,7 +10,7 @@ use bt_topshim::profiles::a2dp::{
 };
 use bt_topshim::profiles::avrcp::PlayerMetadata;
 use bt_topshim::profiles::gatt::{AdvertisingStatus, GattStatus, LePhy};
-use bt_topshim::profiles::hfp::HfpCodecCapability;
+use bt_topshim::profiles::hfp::{EscoCodingFormat, HfpCodecBitId, HfpCodecFormat};
 use bt_topshim::profiles::hid_host::BthhReportType;
 use bt_topshim::profiles::sdp::{
     BtSdpDipRecord, BtSdpHeaderOverlay, BtSdpMasRecord, BtSdpMnsRecord, BtSdpMpsRecord,
@@ -435,13 +435,16 @@ impl_dbus_arg_from_into!(A2dpCodecSampleRate, i32);
 impl_dbus_arg_from_into!(A2dpCodecBitsPerSample, i32);
 impl_dbus_arg_from_into!(A2dpCodecChannelMode, i32);
 
-impl_dbus_arg_from_into!(HfpCodecCapability, i32);
+impl_dbus_arg_from_into!(EscoCodingFormat, u8);
+impl_dbus_arg_from_into!(HfpCodecBitId, i32);
+impl_dbus_arg_from_into!(HfpCodecFormat, i32);
+
 #[dbus_propmap(BluetoothAudioDevice)]
 pub struct BluetoothAudioDeviceDBus {
     address: String,
     name: String,
     a2dp_caps: Vec<A2dpCodecConfig>,
-    hfp_cap: HfpCodecCapability,
+    hfp_cap: HfpCodecFormat,
     absolute_volume: bool,
 }
 
@@ -1001,6 +1004,11 @@ impl IBluetooth for BluetoothDBus {
     fn get_supported_roles(&self) -> Vec<BtAdapterRole> {
         dbus_generated!()
     }
+
+    #[dbus_method("IsCodingFormatSupported")]
+    fn is_coding_format_supported(&self, coding_format: EscoCodingFormat) -> bool {
+        dbus_generated!()
+    }
 }
 
 pub(crate) struct BluetoothQALegacyDBus {
@@ -2656,7 +2664,7 @@ impl IBluetoothMedia for BluetoothMediaDBus {
         &mut self,
         address: String,
         sco_offload: bool,
-        disabled_codecs: HfpCodecCapability,
+        disabled_codecs: HfpCodecBitId,
     ) -> bool {
         dbus_generated!()
     }
diff --git a/system/gd/rust/linux/service/src/iface_bluetooth.rs b/system/gd/rust/linux/service/src/iface_bluetooth.rs
index c84e076b74acd27991b3e081fb09a92b6f0ce0ff..d1783e38cd6f92470712c7ed6c779f021ed07011 100644
--- a/system/gd/rust/linux/service/src/iface_bluetooth.rs
+++ b/system/gd/rust/linux/service/src/iface_bluetooth.rs
@@ -5,6 +5,8 @@ use bt_topshim::btif::{
 use bt_topshim::profiles::socket::SocketType;
 use bt_topshim::profiles::ProfileConnectionState;
 
+use bt_topshim::profiles::hfp::EscoCodingFormat;
+
 use bt_topshim::profiles::hid_host::BthhReportType;
 
 use bt_topshim::profiles::sdp::{
@@ -424,6 +426,7 @@ impl DBusArg for BtSdpRecord {
 }
 
 impl_dbus_arg_enum!(BtDiscMode);
+impl_dbus_arg_from_into!(EscoCodingFormat, u8);
 
 #[allow(dead_code)]
 struct IBluetoothDBus {}
@@ -712,6 +715,11 @@ impl IBluetooth for IBluetoothDBus {
     fn get_supported_roles(&self) -> Vec<BtAdapterRole> {
         dbus_generated!()
     }
+
+    #[dbus_method("IsCodingFormatSupported", DBusLog::Disable)]
+    fn is_coding_format_supported(&self, coding_format: EscoCodingFormat) -> bool {
+        dbus_generated!()
+    }
 }
 
 impl_dbus_arg_enum!(SocketType);
diff --git a/system/gd/rust/linux/service/src/iface_bluetooth_media.rs b/system/gd/rust/linux/service/src/iface_bluetooth_media.rs
index cb575bd22afc958fa40c84bb6c610cc97d1de153..0dc4b47c4364448339f426e456b3a569cbe35973 100644
--- a/system/gd/rust/linux/service/src/iface_bluetooth_media.rs
+++ b/system/gd/rust/linux/service/src/iface_bluetooth_media.rs
@@ -3,7 +3,7 @@ use bt_topshim::profiles::a2dp::{
     A2dpCodecSampleRate, PresentationPosition,
 };
 use bt_topshim::profiles::avrcp::PlayerMetadata;
-use bt_topshim::profiles::hfp::HfpCodecCapability;
+use bt_topshim::profiles::hfp::{HfpCodecBitId, HfpCodecFormat};
 use btstack::bluetooth_media::{BluetoothAudioDevice, IBluetoothMedia, IBluetoothMediaCallback};
 use btstack::RPCProxy;
 
@@ -43,11 +43,12 @@ pub struct BluetoothAudioDeviceDBus {
     address: String,
     name: String,
     a2dp_caps: Vec<A2dpCodecConfig>,
-    hfp_cap: HfpCodecCapability,
+    hfp_cap: HfpCodecFormat,
     absolute_volume: bool,
 }
 
-impl_dbus_arg_from_into!(HfpCodecCapability, i32);
+impl_dbus_arg_from_into!(HfpCodecBitId, i32);
+impl_dbus_arg_from_into!(HfpCodecFormat, i32);
 impl_dbus_arg_enum!(A2dpCodecIndex);
 impl_dbus_arg_from_into!(A2dpCodecSampleRate, i32);
 impl_dbus_arg_from_into!(A2dpCodecBitsPerSample, i32);
@@ -254,7 +255,7 @@ impl IBluetoothMedia for IBluetoothMediaDBus {
         &mut self,
         address: String,
         sco_offload: bool,
-        disabled_codecs: HfpCodecCapability,
+        disabled_codecs: HfpCodecBitId,
     ) -> bool {
         dbus_generated!()
     }
diff --git a/system/gd/rust/linux/stack/src/bluetooth.rs b/system/gd/rust/linux/stack/src/bluetooth.rs
index 2a655c4496e85b3cfd08b80a1ab0e78ad5327578..2e0cf877c40670809cc7040b59be7ccc686618ba 100644
--- a/system/gd/rust/linux/stack/src/bluetooth.rs
+++ b/system/gd/rust/linux/stack/src/bluetooth.rs
@@ -10,6 +10,7 @@ use bt_topshim::btif::{
 use bt_topshim::{
     controller, metrics,
     profiles::gatt::GattStatus,
+    profiles::hfp::EscoCodingFormat,
     profiles::hid_host::{
         BthhConnectionState, BthhHidInfo, BthhProtocolMode, BthhReportType, BthhStatus,
         HHCallbacks, HHCallbacksDispatcher, HidHost,
@@ -251,6 +252,9 @@ pub trait IBluetooth {
 
     /// Returns a list of all the roles that are supported.
     fn get_supported_roles(&self) -> Vec<BtAdapterRole>;
+
+    /// Returns whether the coding format is supported.
+    fn is_coding_format_supported(&self, coding_format: EscoCodingFormat) -> bool;
 }
 
 /// Adapter API for Bluetooth qualification and verification.
@@ -2797,6 +2801,10 @@ impl IBluetooth for Bluetooth {
 
         roles
     }
+
+    fn is_coding_format_supported(&self, coding_format: EscoCodingFormat) -> bool {
+        self.intf.lock().unwrap().is_coding_format_supported(coding_format as u8)
+    }
 }
 
 impl BtifSdpCallbacks for Bluetooth {
diff --git a/system/gd/rust/linux/stack/src/bluetooth_media.rs b/system/gd/rust/linux/stack/src/bluetooth_media.rs
index e96c882f0612b339d8711f4b54f8d759ff455d3a..4c93aa4dc67c2daf161f765999b47e3c68cd5a27 100644
--- a/system/gd/rust/linux/stack/src/bluetooth_media.rs
+++ b/system/gd/rust/linux/stack/src/bluetooth_media.rs
@@ -14,9 +14,9 @@ use bt_topshim::profiles::avrcp::{
 };
 use bt_topshim::profiles::hfp::interop_insert_call_when_sco_start;
 use bt_topshim::profiles::hfp::{
-    BthfAudioState, BthfConnectionState, CallHoldCommand, CallInfo, CallSource, CallState, Hfp,
-    HfpCallbacks, HfpCallbacksDispatcher, HfpCodecCapability, HfpCodecId, PhoneState,
-    TelephonyDeviceStatus,
+    BthfAudioState, BthfConnectionState, CallHoldCommand, CallInfo, CallSource, CallState,
+    EscoCodingFormat, Hfp, HfpCallbacks, HfpCallbacksDispatcher, HfpCodecBitId, HfpCodecFormat,
+    HfpCodecId, PhoneState, TelephonyDeviceStatus,
 };
 use bt_topshim::profiles::ProfileConnectionState;
 use bt_topshim::{metrics, topstack};
@@ -129,7 +129,7 @@ pub trait IBluetoothMedia {
         &mut self,
         address: String,
         sco_offload: bool,
-        disabled_codecs: HfpCodecCapability,
+        disabled_codecs: HfpCodecBitId,
     ) -> bool;
     fn stop_sco_call(&mut self, address: String);
 
@@ -247,7 +247,7 @@ pub struct BluetoothAudioDevice {
     pub address: String,
     pub name: String,
     pub a2dp_caps: Vec<A2dpCodecConfig>,
-    pub hfp_cap: HfpCodecCapability,
+    pub hfp_cap: HfpCodecFormat,
     pub absolute_volume: bool,
 }
 
@@ -256,7 +256,7 @@ impl BluetoothAudioDevice {
         address: String,
         name: String,
         a2dp_caps: Vec<A2dpCodecConfig>,
-        hfp_cap: HfpCodecCapability,
+        hfp_cap: HfpCodecFormat,
         absolute_volume: bool,
     ) -> BluetoothAudioDevice {
         BluetoothAudioDevice { address, name, a2dp_caps, hfp_cap, absolute_volume }
@@ -304,7 +304,7 @@ pub struct BluetoothMedia {
     hfp_states: HashMap<RawAddress, BthfConnectionState>,
     hfp_audio_state: HashMap<RawAddress, BthfAudioState>,
     a2dp_caps: HashMap<RawAddress, Vec<A2dpCodecConfig>>,
-    hfp_cap: HashMap<RawAddress, HfpCodecCapability>,
+    hfp_cap: HashMap<RawAddress, HfpCodecFormat>,
     fallback_tasks: Arc<Mutex<HashMap<RawAddress, Option<(JoinHandle<()>, Instant)>>>>,
     absolute_volume: bool,
     uinput: UInput,
@@ -711,7 +711,7 @@ impl BluetoothMedia {
                         // The device may not support codec-negotiation,
                         // in which case we shall assume it supports CVSD at this point.
                         if !self.hfp_cap.contains_key(&addr) {
-                            self.hfp_cap.insert(addr, HfpCodecCapability::CVSD);
+                            self.hfp_cap.insert(addr, HfpCodecFormat::CVSD);
                         }
                         self.add_connected_profile(addr, uuid::Profile::Hfp);
 
@@ -719,11 +719,7 @@ impl BluetoothMedia {
                         // This is only used for Bluetooth HFP qualification.
                         if self.mps_qualification_enabled && self.phone_state.num_active > 0 {
                             debug!("[{}]: Connect SCO due to active call.", DisplayAddress(&addr));
-                            self.start_sco_call_impl(
-                                addr.to_string(),
-                                false,
-                                HfpCodecCapability::NONE,
-                            );
+                            self.start_sco_call_impl(addr.to_string(), false, HfpCodecBitId::NONE);
                         }
 
                         self.uhid_create(addr);
@@ -910,32 +906,55 @@ impl BluetoothMedia {
                     .set_battery_info(self.battery_provider_id, battery_set);
             }
             HfpCallbacks::WbsCapsUpdate(wbs_supported, addr) => {
+                let is_transparent_coding_format_supported = match &self.adapter {
+                    Some(adapter) => adapter
+                        .lock()
+                        .unwrap()
+                        .is_coding_format_supported(EscoCodingFormat::TRANSPARENT),
+                    _ => false,
+                };
+
+                let is_msbc_coding_format_supported = match &self.adapter {
+                    Some(adapter) => {
+                        adapter.lock().unwrap().is_coding_format_supported(EscoCodingFormat::MSBC)
+                    }
+                    _ => false,
+                };
+
+                let mut codec_diff = HfpCodecFormat::NONE;
+                if is_transparent_coding_format_supported {
+                    codec_diff |= HfpCodecFormat::MSBC_TRANSPARENT;
+                }
+                if is_msbc_coding_format_supported {
+                    codec_diff |= HfpCodecFormat::MSBC;
+                }
+
                 if let Some(cur_hfp_cap) = self.hfp_cap.get_mut(&addr) {
                     if wbs_supported {
-                        *cur_hfp_cap |= HfpCodecCapability::MSBC;
-                    } else if (*cur_hfp_cap & HfpCodecCapability::MSBC) == HfpCodecCapability::MSBC
-                    {
-                        *cur_hfp_cap ^= HfpCodecCapability::MSBC;
+                        *cur_hfp_cap |= codec_diff;
+                    } else {
+                        *cur_hfp_cap &= !codec_diff;
                     }
                 } else {
                     let new_hfp_cap = match wbs_supported {
-                        true => HfpCodecCapability::CVSD | HfpCodecCapability::MSBC,
-                        false => HfpCodecCapability::CVSD,
+                        true => HfpCodecFormat::CVSD | codec_diff,
+                        false => HfpCodecFormat::CVSD,
                     };
                     self.hfp_cap.insert(addr, new_hfp_cap);
                 }
             }
             HfpCallbacks::SwbCapsUpdate(swb_supported, addr) => {
+                // LC3 can be propagated to this point only if adapter supports transparent mode.
                 if let Some(cur_hfp_cap) = self.hfp_cap.get_mut(&addr) {
                     if swb_supported {
-                        *cur_hfp_cap |= HfpCodecCapability::LC3;
-                    } else if (*cur_hfp_cap & HfpCodecCapability::LC3) == HfpCodecCapability::LC3 {
-                        *cur_hfp_cap ^= HfpCodecCapability::LC3;
+                        *cur_hfp_cap |= HfpCodecFormat::LC3_TRANSPARENT;
+                    } else {
+                        *cur_hfp_cap &= !HfpCodecFormat::LC3_TRANSPARENT;
                     }
                 } else {
                     let new_hfp_cap = match swb_supported {
-                        true => HfpCodecCapability::CVSD | HfpCodecCapability::LC3,
-                        false => HfpCodecCapability::CVSD,
+                        true => HfpCodecFormat::CVSD | HfpCodecFormat::LC3_TRANSPARENT,
+                        false => HfpCodecFormat::CVSD,
                     };
                     self.hfp_cap.insert(addr, new_hfp_cap);
                 }
@@ -994,7 +1013,7 @@ impl BluetoothMedia {
 
                 if self.mps_qualification_enabled {
                     debug!("[{}]: Start SCO call due to ATA", DisplayAddress(&addr));
-                    self.start_sco_call_impl(addr.to_string(), false, HfpCodecCapability::NONE);
+                    self.start_sco_call_impl(addr.to_string(), false, HfpCodecBitId::NONE);
                 }
                 self.uhid_send_input_report(&addr);
             }
@@ -1599,7 +1618,7 @@ impl BluetoothMedia {
                     addr.to_string(),
                     name.clone(),
                     cur_a2dp_caps.unwrap_or(&Vec::new()).to_vec(),
-                    *cur_hfp_cap.unwrap_or(&HfpCodecCapability::NONE),
+                    *cur_hfp_cap.unwrap_or(&HfpCodecFormat::NONE),
                     absolute_volume,
                 );
 
@@ -1799,7 +1818,7 @@ impl BluetoothMedia {
         &mut self,
         address: String,
         sco_offload: bool,
-        disabled_codecs: HfpCodecCapability,
+        disabled_codecs: HfpCodecBitId,
     ) -> bool {
         match (|| -> Result<(), &str> {
             let addr = RawAddress::from_string(address.clone())
@@ -2756,7 +2775,7 @@ impl IBluetoothMedia for BluetoothMedia {
         &mut self,
         address: String,
         sco_offload: bool,
-        disabled_codecs: HfpCodecCapability,
+        disabled_codecs: HfpCodecBitId,
     ) -> bool {
         self.start_sco_call_impl(address, sco_offload, disabled_codecs)
     }
@@ -2791,16 +2810,33 @@ impl IBluetoothMedia for BluetoothMedia {
 
         match self.hfp_audio_state.get(&addr) {
             Some(BthfAudioState::Connected) => match self.hfp_cap.get(&addr) {
-                Some(caps) if (*caps & HfpCodecCapability::LC3) == HfpCodecCapability::LC3 => 4,
-                Some(caps) if (*caps & HfpCodecCapability::MSBC) == HfpCodecCapability::MSBC => 2,
-                Some(caps) if (*caps & HfpCodecCapability::CVSD) == HfpCodecCapability::CVSD => 1,
+                Some(caps)
+                    if (*caps & HfpCodecFormat::LC3_TRANSPARENT)
+                        == HfpCodecFormat::LC3_TRANSPARENT =>
+                {
+                    HfpCodecBitId::LC3
+                }
+                Some(caps) if (*caps & HfpCodecFormat::MSBC) == HfpCodecFormat::MSBC => {
+                    HfpCodecBitId::MSBC
+                }
+                Some(caps)
+                    if (*caps & HfpCodecFormat::MSBC_TRANSPARENT)
+                        == HfpCodecFormat::MSBC_TRANSPARENT =>
+                {
+                    HfpCodecBitId::MSBC
+                }
+                Some(caps) if (*caps & HfpCodecFormat::CVSD) == HfpCodecFormat::CVSD => {
+                    HfpCodecBitId::CVSD
+                }
                 _ => {
                     warn!("hfp_cap not found, fallback to CVSD.");
-                    1
+                    HfpCodecBitId::CVSD
                 }
             },
-            _ => 0,
+            _ => HfpCodecBitId::NONE,
         }
+        .try_into()
+        .unwrap()
     }
 
     fn get_presentation_position(&mut self) -> PresentationPosition {
@@ -3029,7 +3065,7 @@ impl IBluetoothTelephony for BluetoothMedia {
                 }
             }) {
                 info!("Start SCO call due to call answered");
-                self.start_sco_call_impl(addr.to_string(), false, HfpCodecCapability::NONE);
+                self.start_sco_call_impl(addr.to_string(), false, HfpCodecBitId::NONE);
             }
         }
 
@@ -3090,7 +3126,7 @@ impl IBluetoothTelephony for BluetoothMedia {
     }
 
     fn audio_connect(&mut self, address: String) -> bool {
-        self.start_sco_call_impl(address, false, HfpCodecCapability::NONE)
+        self.start_sco_call_impl(address, false, HfpCodecBitId::NONE)
     }
 
     fn audio_disconnect(&mut self, address: String) {
diff --git a/system/gd/rust/topshim/src/btif.rs b/system/gd/rust/topshim/src/btif.rs
index 6ac58ee379f7cb5f46e7f877bad7a9d0c6144aed..5620932cda112fee58a1bf77afce4a9b5e100ce5 100644
--- a/system/gd/rust/topshim/src/btif.rs
+++ b/system/gd/rust/topshim/src/btif.rs
@@ -1327,6 +1327,10 @@ impl BluetoothInterface {
         ccall!(self, get_swb_supported)
     }
 
+    pub fn is_coding_format_supported(&self, coding_format: u8) -> bool {
+        ccall!(self, is_coding_format_supported, coding_format)
+    }
+
     pub fn le_rand(&self) -> i32 {
         ccall!(self, le_rand)
     }
diff --git a/system/gd/rust/topshim/src/profiles/hfp.rs b/system/gd/rust/topshim/src/profiles/hfp.rs
index 3017986aa125cba6092954b0c6834ea0f024bebb..4ea72e959e7fc4ce0c1122f6a3e0164b400e3786 100644
--- a/system/gd/rust/topshim/src/profiles/hfp.rs
+++ b/system/gd/rust/topshim/src/profiles/hfp.rs
@@ -11,6 +11,7 @@ use topshim_macros::{cb_variant, profile_enabled_or};
 use log::warn;
 
 #[derive(Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd, Clone)]
+#[repr(u8)]
 pub enum HfpCodecId {
     NONE = 0x00,
     CVSD = 0x01,
@@ -18,6 +19,32 @@ pub enum HfpCodecId {
     LC3 = 0x03,
 }
 
+#[derive(Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd, Clone)]
+#[repr(u8)]
+pub enum EscoCodingFormat {
+    ULAW = 0x00,
+    ALAW = 0x01,
+    CVSD = 0x02,
+    TRANSPARENT = 0x03,
+    LINEAR = 0x04,
+    MSBC = 0x05,
+    LC3 = 0x06,
+    G729A = 0x07,
+    VENDOR = 0xff,
+}
+
+impl From<u8> for EscoCodingFormat {
+    fn from(item: u8) -> Self {
+        EscoCodingFormat::from_u8(item).unwrap()
+    }
+}
+
+impl From<EscoCodingFormat> for u8 {
+    fn from(item: EscoCodingFormat) -> Self {
+        item as u8
+    }
+}
+
 #[derive(Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd, Clone)]
 #[repr(u32)]
 pub enum BthfConnectionState {
@@ -49,24 +76,58 @@ impl From<u32> for BthfAudioState {
     }
 }
 
+// This is used for codec-negotiation related methods that do not
+// concern with the coding format. Do not confuse this with |HfpCodecFormat|.
+bitflags! {
+    #[derive(Default)]
+    pub struct HfpCodecBitId: i32 {
+        const NONE = 0b000;
+        const CVSD = 0b001;
+        const MSBC = 0b010;
+        const LC3 =  0b100;
+    }
+}
+
+impl TryInto<u8> for HfpCodecBitId {
+    type Error = ();
+    fn try_into(self) -> Result<u8, Self::Error> {
+        Ok(self.bits().try_into().unwrap())
+    }
+}
+
+impl TryInto<i32> for HfpCodecBitId {
+    type Error = ();
+    fn try_into(self) -> Result<i32, Self::Error> {
+        Ok(self.bits())
+    }
+}
+
+impl TryFrom<i32> for HfpCodecBitId {
+    type Error = ();
+    fn try_from(val: i32) -> Result<Self, Self::Error> {
+        Self::from_bits(val).ok_or(())
+    }
+}
+
 bitflags! {
     #[derive(Default)]
-    pub struct HfpCodecCapability: i32 {
-        const NONE = 0b00;
-        const CVSD = 0b01;
-        const MSBC = 0b10;
-        const LC3 = 0b100;
+    pub struct HfpCodecFormat: i32 {
+        const NONE =             0b0000;
+        const CVSD =             0b0001;
+        const MSBC_TRANSPARENT = 0b0010;
+        const MSBC =             0b0100;
+        const LC3_TRANSPARENT =  0b1000;
     }
 }
 
-impl TryInto<i32> for HfpCodecCapability {
+impl TryInto<i32> for HfpCodecFormat {
     type Error = ();
     fn try_into(self) -> Result<i32, Self::Error> {
         Ok(self.bits())
     }
 }
 
-impl TryFrom<i32> for HfpCodecCapability {
+impl TryFrom<i32> for HfpCodecFormat {
     type Error = ();
     fn try_from(val: i32) -> Result<Self, Self::Error> {
         Self::from_bits(val).ok_or(())
diff --git a/system/include/hardware/bluetooth.h b/system/include/hardware/bluetooth.h
index 57c59645db25c7c7f7578de9f809da19acb2f390..aabe594abce63da0b54304f7889fe459478f64ad 100644
--- a/system/include/hardware/bluetooth.h
+++ b/system/include/hardware/bluetooth.h
@@ -942,6 +942,13 @@ typedef struct {
    */
   bool (*get_swb_supported)();
 
+  /**
+   *
+   * Is the specified coding format supported by the adapter
+   *
+   */
+  bool (*is_coding_format_supported)(uint8_t coding_format);
+
   /**
    * Data passed from BluetoothDevice.metadata_changed
    *
diff --git a/system/stack/btm/btm_sco_hfp_hal.cc b/system/stack/btm/btm_sco_hfp_hal.cc
index 9e2496ebd0e45dd16695146e2effc0d68450162a..e10a92148d9df752b2d05a8bd5d6e02d8d6ac105 100644
--- a/system/stack/btm/btm_sco_hfp_hal.cc
+++ b/system/stack/btm/btm_sco_hfp_hal.cc
@@ -48,6 +48,11 @@ void init() {
   cached_codecs.emplace_back(msbc);
 }
 
+// This is not used in Android.
+bool is_coding_format_supported(esco_coding_format_t coding_format) {
+  return true;
+}
+
 // Android statically compiles WBS support.
 bool get_wbs_supported() { return !DISABLE_WBS; }
 
diff --git a/system/stack/btm/btm_sco_hfp_hal.h b/system/stack/btm/btm_sco_hfp_hal.h
index 741aa6853f7a3331c50410d7472da2168d4e81c8..624f387bc43f0cefae2b04dffd9954d3622d8fd1 100644
--- a/system/stack/btm/btm_sco_hfp_hal.h
+++ b/system/stack/btm/btm_sco_hfp_hal.h
@@ -69,6 +69,9 @@ constexpr inline int esco_coding_to_codec(esco_coding_format_t esco_coding) {
 // Initialize the SCO HFP HAL module
 void init();
 
+// Check if specified coding format is supported by the adapter.
+bool is_coding_format_supported(esco_coding_format_t coding_format);
+
 // Check if wideband speech is supported on local device.
 bool get_wbs_supported();
 
diff --git a/system/stack/btm/btm_sco_hfp_hal_linux.cc b/system/stack/btm/btm_sco_hfp_hal_linux.cc
index 236b6c00de36d1150df0eb604b7cf989193dc0d0..655b41b25b32db4f139fe54dae7099e9d47bc870 100644
--- a/system/stack/btm/btm_sco_hfp_hal_linux.cc
+++ b/system/stack/btm/btm_sco_hfp_hal_linux.cc
@@ -319,25 +319,37 @@ void init() {
   close(fd);
 }
 
-// Check if wideband speech is supported on local device
-bool get_wbs_supported() {
+// Check if the specified coding format is supported by the adapter.
+bool is_coding_format_supported(esco_coding_format_t coding_format) {
+  if (coding_format != ESCO_CODING_FORMAT_TRANSPNT &&
+      coding_format != ESCO_CODING_FORMAT_MSBC) {
+    log::warn("Unsupported coding format to query: {}", coding_format);
+    return false;
+  }
+
   for (cached_codec_info c : cached_codecs) {
-    if (c.inner.codec == MSBC || c.inner.codec == MSBC_TRANSPARENT) {
+    if (c.inner.codec == MSBC_TRANSPARENT &&
+        coding_format == ESCO_CODING_FORMAT_TRANSPNT) {
+      return true;
+    }
+    if (c.inner.codec == MSBC && coding_format == ESCO_CODING_FORMAT_MSBC) {
       return true;
     }
   }
+
   return false;
 }
 
+// Check if wideband speech is supported on local device
+bool get_wbs_supported() {
+  return is_coding_format_supported(ESCO_CODING_FORMAT_TRANSPNT) ||
+         is_coding_format_supported(ESCO_CODING_FORMAT_MSBC);
+}
+
 // Check if super-wideband speech is supported on local device
 bool get_swb_supported() {
-  for (cached_codec_info c : cached_codecs) {
-    // SWB runs on the same path as MSBC non-offload.
-    if (c.inner.codec == MSBC_TRANSPARENT) {
-      return true;
-    }
-  }
-  return false;
+  // We only support SWB via transparent mode.
+  return is_coding_format_supported(ESCO_CODING_FORMAT_TRANSPNT);
 }
 
 // Checks the supported codecs
diff --git a/system/test/mock/mock_stack_btm_sco_hfp_hal.cc b/system/test/mock/mock_stack_btm_sco_hfp_hal.cc
index 96d18afe24a738b0131889bd4aac74c522f2c9fb..1083a3601afaccfb2f3bf209ccfc0ce8755b8322 100644
--- a/system/test/mock/mock_stack_btm_sco_hfp_hal.cc
+++ b/system/test/mock/mock_stack_btm_sco_hfp_hal.cc
@@ -47,6 +47,7 @@ struct init init;
 struct notify_sco_connection_change notify_sco_connection_change;
 struct set_codec_datapath set_codec_datapath;
 struct update_esco_parameters update_esco_parameters;
+struct is_coding_format_supported is_coding_format_supported;
 
 }  // namespace stack_btm_sco_hfp_hal
 }  // namespace mock
@@ -64,6 +65,7 @@ bool get_offload_supported::return_value = false;
 int get_packet_size::return_value = 0;
 bool get_wbs_supported::return_value = false;
 bool get_swb_supported::return_value = false;
+bool is_coding_format_supported::return_value = false;
 
 }  // namespace stack_btm_sco_hfp_hal
 }  // namespace mock
@@ -100,6 +102,11 @@ bool get_swb_supported() {
   inc_func_call_count(__func__);
   return test::mock::stack_btm_sco_hfp_hal::get_swb_supported();
 }
+bool is_coding_format_supported(esco_coding_format_t coding_format) {
+  inc_func_call_count(__func__);
+  return test::mock::stack_btm_sco_hfp_hal::is_coding_format_supported(
+      coding_format);
+}
 void init() {
   inc_func_call_count(__func__);
   test::mock::stack_btm_sco_hfp_hal::init();
diff --git a/system/test/mock/mock_stack_btm_sco_hfp_hal.h b/system/test/mock/mock_stack_btm_sco_hfp_hal.h
index fdabd2b150240e8dc69d50341c657659ef3d8955..9474d4c9c62d43b86878e70b7d7f8ed50c957a14 100644
--- a/system/test/mock/mock_stack_btm_sco_hfp_hal.h
+++ b/system/test/mock/mock_stack_btm_sco_hfp_hal.h
@@ -114,6 +114,19 @@ struct get_swb_supported {
 };
 extern struct get_swb_supported get_swb_supported;
 
+// Name: is_coding_format_supported
+// Params: esco_coding_format_t coding_format
+// Return: bool
+struct is_coding_format_supported {
+  static bool return_value;
+  std::function<bool(esco_coding_format_t coding_format)> body{
+      [](esco_coding_format_t /* coding_format */) { return return_value; }};
+  bool operator()(esco_coding_format_t coding_format) {
+    return body(coding_format);
+  };
+};
+extern struct is_coding_format_supported is_coding_format_supported;
+
 // Name: init
 // Params:
 // Return: void