[bt-bass][bt-common] Update Metadata for LtValue

Update usage in BASS library as well.

Expand possible ratings to match the actual values allowed
and add tests.

Test: updated tests, cargo test in bt-bass and bt-common
Change-Id: Ib4737b37ea4d66dda8000bad2dd85b0008984879
Reviewed-on: https://bluetooth-review.git.corp.google.com/c/bluetooth/+/1420
Reviewed-by: Dayeong Lee <dayeonglee@google.com>
diff --git a/rust/bt-bass/.gitignore b/rust/bt-bass/.gitignore
index ada8be9..75e03aa 100644
--- a/rust/bt-bass/.gitignore
+++ b/rust/bt-bass/.gitignore
@@ -11,4 +11,7 @@
 **/*.rs.bk
 
 # MSVC Windows builds of rustc generate these, which store debugging information
-*.pdb
\ No newline at end of file
+*.pdb
+
+# Vim swap files
+*.swp
diff --git a/rust/bt-bass/src/types.rs b/rust/bt-bass/src/types.rs
index 2844069..842b7be 100644
--- a/rust/bt-bass/src/types.rs
+++ b/rust/bt-bass/src/types.rs
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+use bt_common::core::ltv::LtValue;
 use bt_common::core::{AdvertisingSetId, PaInterval};
 use bt_common::generic_audio::metadata_ltv::*;
 use bt_common::packet_encoding::{Decodable, Encodable, Error as PacketError};
@@ -12,7 +13,8 @@
 const PA_SYNC_BYTE_SIZE: usize = 1;
 const SOURCE_ID_BYTE_SIZE: usize = 1;
 
-/// 16-bit UUID value for the characteristics offered by the Broadcast Audio Scan Service.
+/// 16-bit UUID value for the characteristics offered by the Broadcast Audio
+/// Scan Service.
 pub const BROADCAST_AUDIO_SCAN_CONTROL_POINT_UUID: Uuid = Uuid::from_u16(0x2BC7);
 pub const BROADCAST_RECEIVE_STATE_UUID: Uuid = Uuid::from_u16(0x2BC8);
 
@@ -71,7 +73,8 @@
         if buf.len() < self.encoded_len() {
             return Err(PacketError::BufferTooSmall);
         }
-        // Since 3-byte value is being fit into u32, we ignore the most significant byte.
+        // Since 3-byte value is being fit into u32, we ignore the most significant
+        // byte.
         buf[0..3].copy_from_slice(&self.0.to_le_bytes()[0..3]);
         Ok(())
     }
@@ -83,7 +86,6 @@
 
 /// Broadcast Audio Scan Control Point characteristic opcode as defined in
 /// Broadcast Audio Scan Service spec v1.0 Section 3.1.
-#[repr(u8)]
 #[derive(Debug, PartialEq)]
 pub enum ControlPointOpcode {
     RemoteScanStopped = 0x00,
@@ -115,16 +117,18 @@
     }
 }
 
-/// Trait for objects that represent a Broadcast Audio Scan Control Point characteristic.
-/// When written by a client, the Broadcast Audio Scan Control Point characteristic is defined as an
-/// 8-bit enumerated value, known as the opcode, followed by zero or more parameter octets.
-/// The opcode represents the operation that would be performed in the Broadcast Audio Scan Service server.
-/// See BASS spec v1.0 Section 3.1 for details.
+/// Trait for objects that represent a Broadcast Audio Scan Control Point
+/// characteristic. When written by a client, the Broadcast Audio Scan Control
+/// Point characteristic is defined as an 8-bit enumerated value, known as the
+/// opcode, followed by zero or more parameter octets. The opcode represents the
+/// operation that would be performed in the Broadcast Audio Scan Service
+/// server. See BASS spec v1.0 Section 3.1 for details.
 pub trait ControlPointOperation {
     // Returns the expected opcode for this operation.
     fn opcode() -> ControlPointOpcode;
 
-    // Given the raw encoded value of the opcode, verifies it and returns the equivalent ControlPointOpcode object.
+    // Given the raw encoded value of the opcode, verifies it and returns the
+    // equivalent ControlPointOpcode object.
     fn check_opcode(raw_value: u8) -> Result<ControlPointOpcode, PacketError> {
         let expected = Self::opcode();
         let got = ControlPointOpcode::try_from(raw_value)?;
@@ -571,7 +575,7 @@
     // 0xFFFFFFFF: means No preference if used in BroadcastAudioScanControlPoint,
     //             Failed to sync if used in ReceiveState.
     bis_sync_bitfield: u32,
-    metadata: Metadata,
+    metadata: Vec<Metadata>,
 }
 
 impl BigSubgroup {
@@ -588,7 +592,7 @@
         }
     }
 
-    pub fn with_metadata(self, metadata: Metadata) -> Self {
+    pub fn with_metadata(self, metadata: Vec<Metadata>) -> Self {
         Self { bis_sync_bitfield: self.bis_sync_bitfield, metadata }
     }
 }
@@ -602,22 +606,20 @@
         }
         let bis_sync = u32::from_le_bytes(buf[0..4].try_into().unwrap());
         let metadata_len = buf[4] as usize;
-        let mut metadata = Vec::new();
 
         let mut start_idx = 5;
         if buf.len() < start_idx + metadata_len {
             return Err(PacketError::UnexpectedDataLength);
         }
 
-        while start_idx < 5 + metadata_len {
-            let (data, len) = Metadatum::decode(&buf[start_idx..])
-                .map_err(|e| PacketError::InvalidParameter(format!("{e}")))?;
-            metadata.push(data);
-            start_idx += len;
-        }
+        let (results_metadata, consumed_len) =
+            Metadata::decode_all(&buf[start_idx..start_idx + metadata_len]);
+        start_idx += consumed_len;
         if start_idx != 5 + metadata_len {
             return Err(PacketError::UnexpectedDataLength);
         }
+        // Ignore any undecodable metadata types
+        let metadata = results_metadata.into_iter().filter_map(Result::ok).collect();
         Ok((BigSubgroup { bis_sync_bitfield: bis_sync, metadata }, start_idx))
     }
 }
@@ -680,10 +682,10 @@
 
 /// Broadcast Receive State characteristic as defined in
 /// Broadcast Audio Scan Service spec v1.0 Section 3.2.
-/// The Broadcast Receive State characteristic is used by the server to expose information
-/// about a Broadcast Source. If the server has not written a Source_ID value to the
-/// Broadcast Receive State characteristic, the Broadcast Recieve State characteristic value
-/// shall be empty.
+/// The Broadcast Receive State characteristic is used by the server to expose
+/// information about a Broadcast Source. If the server has not written a
+/// Source_ID value to the Broadcast Receive State characteristic, the Broadcast
+/// Recieve State characteristic value shall be empty.
 #[derive(Debug, PartialEq)]
 pub enum BroadcastReceiveState {
     Empty,
@@ -863,7 +865,8 @@
     }
 
     fn encoded_len(&self) -> core::primitive::usize {
-        // Length including Source_ID, Source_Address_Type, Source_Address, Source_Adv_SID, Broadcast_ID, PA_Sync_State, BIG_Encryption, Bad_Code,
+        // Length including Source_ID, Source_Address_Type, Source_Address,
+        // Source_Adv_SID, Broadcast_ID, PA_Sync_State, BIG_Encryption, Bad_Code,
         // Num_Subgroups and subgroup-related params.
         SOURCE_ID_BYTE_SIZE
             + AddressType::BYTE_SIZE
@@ -902,7 +905,8 @@
     }
 }
 
-/// Represents BIG_Encryption and Bad_Code params from BASS spec v.1.0 Table 3.9.
+/// Represents BIG_Encryption and Bad_Code params from BASS spec v.1.0 Table
+/// 3.9.
 #[derive(Clone, Copy, Debug, PartialEq)]
 pub enum EncryptionStatus {
     NotEncrypted,
@@ -979,6 +983,8 @@
 mod tests {
     use super::*;
 
+    use bt_common::generic_audio::ContextType;
+
     #[test]
     fn broadcast_id() {
         let id = BroadcastId(0x000A0B0C);
@@ -1141,8 +1147,8 @@
     fn add_source_with_subgroups() {
         // Encoding operation with subgroups.
         let subgroups = vec![BigSubgroup::new(None).with_metadata(vec![
-            Metadatum::PreferredAudioContexts(ContextType::MEDIA | ContextType::GAME), // encoded_len = 4
-            Metadatum::ProgramInfo("test".to_string()), // encoded_len = 6
+            Metadata::PreferredAudioContexts(vec![ContextType::Media, ContextType::Game]), // encoded_len = 4
+            Metadata::ProgramInfo("test".to_string()), // encoded_len = 6
         ])];
         let op = AddSourceOperation::new(
             AddressType::Random,
@@ -1192,9 +1198,9 @@
     fn modify_source_with_subgroups() {
         // Encoding operation with subgroups.
         let subgroups = vec![
-            BigSubgroup::new(None).with_metadata(vec![Metadatum::ParentalRating(Rating::AllAge)]), // encoded_len = 8
+            BigSubgroup::new(None).with_metadata(vec![Metadata::ParentalRating(Rating::all_age())]), /* encoded_len = 8 */
             BigSubgroup::new(Some(0x000000FE))
-                .with_metadata(vec![Metadatum::BroadcastAudioImmediateRenderingFlag]), // encoded_len = 7
+                .with_metadata(vec![Metadata::BroadcastAudioImmediateRenderingFlag]), /* encoded_len = 7 */
         ];
         let op = ModifySourceOperation::new(
             0x0B,
@@ -1309,7 +1315,7 @@
             big_encryption: EncryptionStatus::NotEncrypted,
             subgroups: vec![
                 BigSubgroup::new(None)
-                    .with_metadata(vec![Metadatum::ParentalRating(Rating::AllAge)]), // encoded_len = 8
+                    .with_metadata(vec![Metadata::ParentalRating(Rating::AllAge)]), /* encoded_len = 8 */
             ],
         });
         assert_eq!(state.encoded_len(), 23);
diff --git a/rust/bt-common/Cargo.toml b/rust/bt-common/Cargo.toml
index 7f8ebe3..874319b 100644
--- a/rust/bt-common/Cargo.toml
+++ b/rust/bt-common/Cargo.toml
@@ -5,7 +5,6 @@
 license = "BSD-2-Clause"
 
 [dependencies]
-bitflags = "2.4.0"
 thiserror = "1.0"
 lazy_static = "1"
 
diff --git a/rust/bt-common/src/company_id.rs b/rust/bt-common/src/company_id.rs
index 78addec..811ef2a 100644
--- a/rust/bt-common/src/company_id.rs
+++ b/rust/bt-common/src/company_id.rs
@@ -9,6 +9,12 @@
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct CompanyId(u16);
 
+impl CompanyId {
+    pub fn to_le_bytes(&self) -> [u8; 2] {
+        self.0.to_le_bytes()
+    }
+}
+
 impl From<u16> for CompanyId {
     fn from(value: u16) -> Self {
         Self(value)
diff --git a/rust/bt-common/src/generic_audio/metadata_ltv.rs b/rust/bt-common/src/generic_audio/metadata_ltv.rs
index 3630f4e..8056537 100644
--- a/rust/bt-common/src/generic_audio/metadata_ltv.rs
+++ b/rust/bt-common/src/generic_audio/metadata_ltv.rs
@@ -2,342 +2,245 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use bitflags::bitflags;
+use crate::core::ltv::LtValue;
+use crate::packet_encoding::Error as PacketError;
+use crate::{decodable_enum, CompanyId};
 
-use crate::packet_encoding::{Decodable, Encodable, Error as PacketError};
+use crate::generic_audio::ContextType;
 
-pub type Metadata = Vec<Metadatum>;
-
-#[derive(Debug, PartialEq)]
-#[repr(u8)]
-pub enum Type {
-    PreferredAudioContexts = 0x01,
-    StreamingAudioContexts = 0x02,
-    ProgramInfo = 0x03,
-    Language = 0x04,
-    CCIDList = 0x05,
-    ParentalRating = 0x06,
-    ProgramInfoURI = 0x07,
-    ExtendedMetadata = 0xfe,
-    VendorSpecific = 0xff,
-    AudioActiveState = 0x08,
-    BroadcastAudioImmediateRenderingFlag = 0x09,
-}
-
-impl Type {
-    fn length_check(&self, provided: usize) -> Result<(), PacketError> {
-        if self.total_size_range().contains(&provided) {
-            Ok(())
-        } else {
-            Err(PacketError::UnexpectedDataLength)
-        }
-    }
-
-    // Total size range including all Length, Type, and Value parameters.
-    fn total_size_range(&self) -> std::ops::Range<usize> {
-        match self {
-            Type::PreferredAudioContexts | Type::StreamingAudioContexts => 4..5,
-            Type::ProgramInfo | Type::CCIDList | Type::ProgramInfoURI => 2..usize::MAX,
-            Type::Language => 5..6,
-            Type::ParentalRating => 3..4,
-            Type::ExtendedMetadata | Type::VendorSpecific => 4..256,
-            Type::AudioActiveState => 3..4,
-            Type::BroadcastAudioImmediateRenderingFlag => 2..3,
-        }
+decodable_enum! {
+    pub enum MetadataType<u8, PacketError, OutOfRange> {
+        PreferredAudioContexts = 0x01,
+        StreamingAudioContexts = 0x02,
+        ProgramInfo = 0x03,
+        Language = 0x04,
+        CCIDList = 0x05,
+        ParentalRating = 0x06,
+        ProgramInfoURI = 0x07,
+        AudioActiveState = 0x08,
+        BroadcastAudioImmediateRenderingFlag = 0x09,
+        ExtendedMetadata = 0xfe,
+        VendorSpecific = 0xff,
     }
 }
 
-impl TryFrom<u8> for Type {
-    type Error = PacketError;
-
-    fn try_from(value: u8) -> Result<Self, Self::Error> {
-        let t = match value {
-            0x01 => Type::PreferredAudioContexts,
-            0x02 => Type::StreamingAudioContexts,
-            0x03 => Type::ProgramInfo,
-            0x04 => Type::Language,
-            0x05 => Type::CCIDList,
-            0x06 => Type::ParentalRating,
-            0x07 => Type::ProgramInfoURI,
-            0xFE => Type::ExtendedMetadata,
-            0xFF => Type::VendorSpecific,
-            0x08 => Type::AudioActiveState,
-            0x09 => Type::BroadcastAudioImmediateRenderingFlag,
-            _ => return Err(PacketError::OutOfRange),
-        };
-        Ok(t)
-    }
-}
-
-#[derive(Debug, PartialEq)]
-pub enum Metadatum {
-    PreferredAudioContexts(ContextType),
-    StreamingAudioContexts(ContextType),
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum Metadata {
+    PreferredAudioContexts(Vec<ContextType>),
+    StreamingAudioContexts(Vec<ContextType>),
     ProgramInfo(String),
     /// 3 byte, lower case language code as defined in ISO 639-­3
-    LanguageCode(String),
+    Language(String),
     CCIDList(Vec<u8>),
     ParentalRating(Rating),
     ProgramInfoURI(String),
     ExtendedMetadata {
         type_: u16,
-        metadata: Vec<u8>,
+        data: Vec<u8>,
     },
     VendorSpecific {
-        company_id: u16,
-        metadata: Vec<u8>,
+        company_id: CompanyId,
+        data: Vec<u8>,
     },
     AudioActiveState(bool),
     // Flag for Broadcast Audio Immediate Rendering.
     BroadcastAudioImmediateRenderingFlag,
 }
 
-impl Metadatum {
-    const LENGTH_BYTE_SIZE: usize = 1;
-    const TYPE_BYTE_SIZE: usize = 1;
-    const MIN_PACKET_SIZE: usize = Self::LENGTH_BYTE_SIZE + Self::TYPE_BYTE_SIZE;
+impl LtValue for Metadata {
+    type Type = MetadataType;
 
-    // Size of this metadatum's Value parameter. Does not include the Length or Type
-    // size.
-    pub fn value_encoded_len(&self) -> usize {
-        match self {
-            Metadatum::PreferredAudioContexts(_) => 2,
-            Metadatum::StreamingAudioContexts(_) => 2,
-            Metadatum::ProgramInfo(value) => value.len(),
-            Metadatum::LanguageCode(_) => 3,
-            Metadatum::CCIDList(ccids) => ccids.len(),
-            Metadatum::ParentalRating(_) => 1,
-            Metadatum::ProgramInfoURI(uri) => uri.len(),
-            Metadatum::ExtendedMetadata { type_: _, metadata } => 2 + metadata.len(),
-            Metadatum::VendorSpecific { company_id: _, metadata } => 2 + metadata.len(),
-            Metadatum::AudioActiveState(_) => 1,
-            Metadatum::BroadcastAudioImmediateRenderingFlag => 0,
+    const NAME: &'static str = "Metadata";
+
+    fn type_from_octet(x: u8) -> Option<Self::Type> {
+        x.try_into().ok()
+    }
+
+    fn length_range_from_type(ty: Self::Type) -> std::ops::RangeInclusive<u8> {
+        match ty {
+            MetadataType::PreferredAudioContexts | MetadataType::StreamingAudioContexts => 3..=3,
+            MetadataType::ProgramInfo | MetadataType::CCIDList | MetadataType::ProgramInfoURI => {
+                1..=u8::MAX
+            }
+            MetadataType::Language => 4..=4,
+            MetadataType::ParentalRating => 2..=2,
+            MetadataType::ExtendedMetadata | MetadataType::VendorSpecific => 3..=u8::MAX,
+            MetadataType::AudioActiveState => 2..=2,
+            MetadataType::BroadcastAudioImmediateRenderingFlag => 1..=1,
         }
     }
 
-    pub fn type_(&self) -> Type {
+    fn into_type(&self) -> Self::Type {
         match self {
-            Metadatum::PreferredAudioContexts(_) => Type::PreferredAudioContexts,
-            Metadatum::StreamingAudioContexts(_) => Type::StreamingAudioContexts,
-            Metadatum::ProgramInfo(_) => Type::ProgramInfo,
-            Metadatum::LanguageCode(_) => Type::Language,
-            Metadatum::CCIDList(_) => Type::CCIDList,
-            Metadatum::ParentalRating(_) => Type::ParentalRating,
-            Metadatum::ProgramInfoURI(_) => Type::ProgramInfoURI,
-            Metadatum::ExtendedMetadata { .. } => Type::ExtendedMetadata,
-            Metadatum::VendorSpecific { .. } => Type::VendorSpecific,
-            Metadatum::AudioActiveState(_) => Type::AudioActiveState,
-            Metadatum::BroadcastAudioImmediateRenderingFlag => {
-                Type::BroadcastAudioImmediateRenderingFlag
+            Metadata::PreferredAudioContexts(_) => MetadataType::PreferredAudioContexts,
+            Metadata::StreamingAudioContexts(_) => MetadataType::StreamingAudioContexts,
+            Metadata::ProgramInfo(_) => MetadataType::ProgramInfo,
+            Metadata::Language(_) => MetadataType::Language,
+            Metadata::CCIDList(_) => MetadataType::CCIDList,
+            Metadata::ParentalRating(_) => MetadataType::ParentalRating,
+            Metadata::ProgramInfoURI(_) => MetadataType::ProgramInfoURI,
+            Metadata::ExtendedMetadata { .. } => MetadataType::ExtendedMetadata,
+            Metadata::VendorSpecific { .. } => MetadataType::VendorSpecific,
+            Metadata::AudioActiveState(_) => MetadataType::AudioActiveState,
+            Metadata::BroadcastAudioImmediateRenderingFlag => {
+                MetadataType::BroadcastAudioImmediateRenderingFlag
             }
         }
     }
-}
 
-impl Decodable for Metadatum {
-    type Error = PacketError;
+    fn value_encoded_len(&self) -> u8 {
+        match self {
+            Metadata::PreferredAudioContexts(_) => 2,
+            Metadata::StreamingAudioContexts(_) => 2,
+            Metadata::ProgramInfo(value) => value.len() as u8,
+            Metadata::Language(_) => 3,
+            Metadata::CCIDList(ccids) => ccids.len() as u8,
+            Metadata::ParentalRating(_) => 1,
+            Metadata::ProgramInfoURI(uri) => uri.len() as u8,
+            Metadata::ExtendedMetadata { type_: _, data } => 2 + data.len() as u8,
+            Metadata::VendorSpecific { company_id: _, data } => 2 + data.len() as u8,
+            Metadata::AudioActiveState(_) => 1,
+            Metadata::BroadcastAudioImmediateRenderingFlag => 0,
+        }
+    }
 
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
-        if buf.len() < Self::MIN_PACKET_SIZE {
-            return Err(PacketError::UnexpectedDataLength);
-        }
-        // Total length of the buffer including all Length, Type, and Value parameters.
-        let total_len = 1 + buf[0] as usize;
-        if buf.len() < total_len {
-            return Err(PacketError::UnexpectedDataLength);
-        }
-        let type_ = Type::try_from(buf[1])?;
-        let _ = type_.length_check(total_len)?;
-        let m: Metadatum = match type_ {
-            Type::PreferredAudioContexts | Type::StreamingAudioContexts => {
+    fn decode_value(ty: &Self::Type, buf: &[u8]) -> Result<Self, crate::packet_encoding::Error> {
+        let m: Metadata = match ty {
+            MetadataType::PreferredAudioContexts | MetadataType::StreamingAudioContexts => {
                 let context_type =
-                    ContextType::try_from(u16::from_le_bytes(buf[2..4].try_into().unwrap()))?;
-                if type_ == Type::PreferredAudioContexts {
+                    ContextType::from_bits(u16::from_le_bytes([buf[0], buf[1]])).collect();
+                if ty == &MetadataType::PreferredAudioContexts {
                     Self::PreferredAudioContexts(context_type)
                 } else {
                     Self::StreamingAudioContexts(context_type)
                 }
             }
-            Type::ProgramInfo | Type::ProgramInfoURI => {
-                let value = String::from_utf8(buf[2..total_len].to_vec())
+            MetadataType::ProgramInfo | MetadataType::ProgramInfoURI => {
+                let value = String::from_utf8(buf[..].to_vec())
                     .map_err(|e| PacketError::InvalidParameter(format!("{e}")))?;
-                if type_ == Type::ProgramInfo {
+                if ty == &MetadataType::ProgramInfo {
                     Self::ProgramInfo(value)
                 } else {
                     Self::ProgramInfoURI(value)
                 }
             }
-            Type::Language => {
-                let code = String::from_utf8(buf[2..total_len].to_vec())
+            MetadataType::Language => {
+                let code = String::from_utf8(buf[..].to_vec())
                     .map_err(|e| PacketError::InvalidParameter(format!("{e}")))?;
-                Self::LanguageCode(code)
+                Self::Language(code)
             }
-            Type::CCIDList => Self::CCIDList(buf[2..total_len].to_vec()),
-            Type::ParentalRating => Self::ParentalRating(Rating::decode(&buf[2..3])?.0),
-            Type::ExtendedMetadata | Type::VendorSpecific => {
-                let type_or_id = u16::from_le_bytes(buf[2..4].try_into().unwrap());
-                let data = if buf.len() >= 5 { buf[4..total_len].to_vec() } else { vec![] };
-                if type_ == Type::ExtendedMetadata {
-                    Self::ExtendedMetadata { type_: type_or_id, metadata: data }
+            MetadataType::CCIDList => Self::CCIDList(buf[..].to_vec()),
+            MetadataType::ParentalRating => Self::ParentalRating(buf[0].into()),
+            MetadataType::ExtendedMetadata | MetadataType::VendorSpecific => {
+                let type_or_id = u16::from_le_bytes(buf[0..2].try_into().unwrap());
+                let data = if buf.len() >= 2 { buf[2..].to_vec() } else { vec![] };
+                if ty == &MetadataType::ExtendedMetadata {
+                    Self::ExtendedMetadata { type_: type_or_id, data }
                 } else {
-                    Self::VendorSpecific { company_id: type_or_id, metadata: data }
+                    Self::VendorSpecific { company_id: type_or_id.into(), data }
                 }
             }
-            Type::AudioActiveState => {
-                if buf[2] > 1 {
-                    return Err(PacketError::UnexpectedDataLength);
-                }
-                Self::AudioActiveState(buf[2] != 0)
-            }
-            Type::BroadcastAudioImmediateRenderingFlag => {
+            MetadataType::AudioActiveState => Self::AudioActiveState(buf[0] != 0),
+            MetadataType::BroadcastAudioImmediateRenderingFlag => {
                 Self::BroadcastAudioImmediateRenderingFlag
             }
         };
-        return Ok((m, total_len));
+        Ok(m)
     }
-}
 
-impl Encodable for Metadatum {
-    type Error = PacketError;
-
-    /// Encodes the object into a provided buffer in a Metadata LTV structure.
-    /// Bytes are in LSO.
-    // See Assigned Numbers section 6.12.6 for details about Metadata LTV
-    // structures.
-    fn encode(&self, buf: &mut [u8]) -> core::result::Result<(), Self::Error> {
-        if buf.len() < self.encoded_len() {
-            return Err(PacketError::BufferTooSmall);
-        }
-
-        // Length parameter value excludes the length of Length parameter itself (which
-        // is 1 byte).
-        buf[0] = (self.encoded_len() - 1)
-            .try_into()
-            .map_err(|_| PacketError::InvalidParameter("Metadata too big".to_string()))?;
-        buf[1] = self.type_() as u8;
-
+    fn encode_value(&self, buf: &mut [u8]) -> Result<(), crate::packet_encoding::Error> {
         match self {
             Self::PreferredAudioContexts(type_) | Self::StreamingAudioContexts(type_) => {
-                buf[2..4].copy_from_slice(&type_.bits().to_le_bytes())
+                [buf[0], buf[1]] = ContextType::to_bits(type_.iter()).to_le_bytes();
             }
             Self::ProgramInfo(value) | Self::ProgramInfoURI(value) => {
-                buf[2..2 + value.len()].copy_from_slice(value.as_bytes())
+                buf.copy_from_slice(value.as_bytes())
             }
-            Self::LanguageCode(value) if value.len() != 3 => {
+            Self::Language(value) if value.len() != 3 => {
                 return Err(PacketError::InvalidParameter(format!("{self}")));
             }
-            Self::LanguageCode(value) => buf[2..5].copy_from_slice(&value.as_bytes()[..3]),
-            Self::CCIDList(value) => buf[2..2 + value.len()].copy_from_slice(&value.as_slice()),
-            Self::ParentalRating(value) => buf[2] = value.into(),
-            Self::ExtendedMetadata { type_, metadata } => {
-                buf[2..4].copy_from_slice(&type_.to_le_bytes());
-                buf[4..4 + metadata.len()].copy_from_slice(&metadata.as_slice());
+            Self::Language(value) => buf.copy_from_slice(&value.as_bytes()[..3]),
+            Self::CCIDList(value) => buf.copy_from_slice(&value.as_slice()),
+            Self::ParentalRating(value) => buf[0] = value.into(),
+            Self::ExtendedMetadata { type_, data } => {
+                buf[0..2].copy_from_slice(&type_.to_le_bytes());
+                buf[2..].copy_from_slice(&data.as_slice());
             }
-            Self::VendorSpecific { company_id, metadata } => {
-                buf[2..4].copy_from_slice(&company_id.to_le_bytes());
-                buf[4..4 + metadata.len()].copy_from_slice(&metadata.as_slice());
+            Self::VendorSpecific { company_id, data } => {
+                [buf[0], buf[1]] = company_id.to_le_bytes();
+                buf[2..].copy_from_slice(&data.as_slice());
             }
-            Self::AudioActiveState(value) => buf[2] = *value as u8,
+            Self::AudioActiveState(value) => buf[0] = *value as u8,
             Self::BroadcastAudioImmediateRenderingFlag => {}
         }
         Ok(())
     }
-
-    fn encoded_len(&self) -> core::primitive::usize {
-        Self::MIN_PACKET_SIZE + self.value_encoded_len()
-    }
 }
 
-impl std::fmt::Display for Metadatum {
+impl std::fmt::Display for Metadata {
     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
         match self {
             // TODO(b/308483171): Consider using the Display for the inner fields instead of Debug.
-            Metadatum::PreferredAudioContexts(v) => write!(f, "Preferred Audio Contexts: {v:?}"),
-            Metadatum::StreamingAudioContexts(v) => write!(f, "Streaming Audio Contexts: {v:?}"),
-            Metadatum::ProgramInfo(v) => write!(f, "Progaam Info: {v:?}"),
-            Metadatum::LanguageCode(v) => write!(f, "Language: {v:?}"),
-            Metadatum::CCIDList(v) => write!(f, "CCID List: {v:?}"),
-            Metadatum::ParentalRating(v) => write!(f, "Parental Rating: {v:?}"),
-            Metadatum::ProgramInfoURI(v) => write!(f, "Program Info URI: {v:?}"),
-            Metadatum::ExtendedMetadata { type_, metadata } => {
-                write!(f, "Extended Metadata: type(0x{type_:02x}) metadata({metadata:?})")
+            Metadata::PreferredAudioContexts(v) => write!(f, "Preferred Audio Contexts: {v:?}"),
+            Metadata::StreamingAudioContexts(v) => write!(f, "Streaming Audio Contexts: {v:?}"),
+            Metadata::ProgramInfo(v) => write!(f, "Progaam Info: {v:?}"),
+            Metadata::Language(v) => write!(f, "Language: {v:?}"),
+            Metadata::CCIDList(v) => write!(f, "CCID List: {v:?}"),
+            Metadata::ParentalRating(v) => write!(f, "Parental Rating: {v:?}"),
+            Metadata::ProgramInfoURI(v) => write!(f, "Program Info URI: {v:?}"),
+            Metadata::ExtendedMetadata { type_, data } => {
+                write!(f, "Extended Metadata: type(0x{type_:02x}) data({data:?})")
             }
-            Metadatum::VendorSpecific { company_id, metadata } => {
-                write!(f, "Vendor Specific: company(0x{company_id:02x}) metadata({metadata:?})")
+            Metadata::VendorSpecific { company_id, data } => {
+                write!(f, "Vendor Specific: {company_id} data({data:?})")
             }
-            Metadatum::AudioActiveState(v) => write!(f, "Audio Active State: {v}"),
-            Metadatum::BroadcastAudioImmediateRenderingFlag => {
+            Metadata::AudioActiveState(v) => write!(f, "Audio Active State: {v}"),
+            Metadata::BroadcastAudioImmediateRenderingFlag => {
                 write!(f, "Broadcast Audio Immediate Rendering Flag")
             }
         }
     }
 }
 
-bitflags! {
-    #[derive(Clone, Copy, Debug, PartialEq)]
-    pub struct ContextType: u16 {
-        // 0x0000 value is prohibited.
-        const UNSPECIFIED      = 0x0001;
-        const CONVERSATIONAL   = 0x0002;
-        const MEDIA            = 0x0004;
-        const GAME             = 0x0008;
-        const INSTRUCTIONAL    = 0x0010;
-        const VOICE_ASSISTANTS = 0x0020;
-        const LIVE             = 0x0040;
-        const SOUND_EFFECTS    = 0x0080;
-        const NOTIFICATIONS    = 0x0100;
-        const RINGTONE         = 0x0200;
-        const ALERT            = 0x0400;
-        const EMERGENCY_ALARM  = 0x0800;
-    }
-}
-
-impl TryFrom<u16> for ContextType {
-    type Error = PacketError;
-
-    fn try_from(value: u16) -> Result<Self, Self::Error> {
-        if u16::count_ones(value) == 0 {
-            return Err(PacketError::InvalidParameter(format!("ContextType 0x0000 is prohibited")));
-        }
-        Ok(ContextType::from_bits_truncate(value))
-    }
-}
-
 /// Represents recommended minimum age of the viewer.
 /// The numbering scheme aligns with Annex F of EN 300 707 v1.2.1
 /// published by ETSI.
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub enum Rating {
     NoRating,
     AllAge,
     // Recommended for listeners of age x years, where x is the
     // value of u8 that's greater than or equal to 5.
-    Age(u8),
+    Age(u16),
 }
 
 impl Rating {
     // Minimum age that can be recommended.
-    const MIN_RECOMMENDED_AGE: u8 = 5;
+    const MIN_RECOMMENDED_AGE: u16 = 5;
 
     // When recommending for listeners of age Y years,
     // Subtract 3 from the recommended age to get the encoded value.
     // E.g., to indicate recommended age of 8 years or older, encode 5.
-    const AGE_OFFSET: u8 = 3;
+    const AGE_OFFSET: u16 = 3;
 
-    const fn no_rating() -> Self {
+    pub const fn no_rating() -> Self {
         Self::NoRating
     }
 
-    const fn all_age() -> Self {
+    pub const fn all_age() -> Self {
         Self::AllAge
     }
 
-    pub fn min_recommended(age: u8) -> Result<Self, PacketError> {
+    pub fn min_recommended(age: u16) -> Result<Self, PacketError> {
         if age < Self::MIN_RECOMMENDED_AGE {
             return Err(PacketError::InvalidParameter(format!(
                 "minimum recommended age must be at least 5. Got {age}"
             )));
         }
+        // We can represent up to 255 + 3 (age 258) using this encoding.
+        if age > u8::MAX as u16 + Self::AGE_OFFSET {
+            return Err(PacketError::OutOfRange);
+        }
         Ok(Rating::Age(age))
     }
 }
@@ -347,27 +250,21 @@
         match value {
             Rating::NoRating => 0x00,
             Rating::AllAge => 0x01,
-            Rating::Age(a) => a - Rating::AGE_OFFSET,
+            Rating::Age(a) => (a - Rating::AGE_OFFSET) as u8,
         }
     }
 }
 
-impl Decodable for Rating {
-    type Error = PacketError;
-
-    fn decode(buf: &[u8]) -> core::result::Result<(Self, usize), Self::Error> {
-        if buf.len() == 0 {
-            return Err(PacketError::UnexpectedDataLength);
-        }
-        let rating = match buf[0] {
+impl From<u8> for Rating {
+    fn from(value: u8) -> Self {
+        match value {
             0x00 => Rating::NoRating,
             0x01 => Rating::AllAge,
             value => {
-                let age = value.checked_add(Self::AGE_OFFSET).ok_or(PacketError::OutOfRange)?;
+                let age = value as u16 + Self::AGE_OFFSET;
                 Rating::min_recommended(age).unwrap()
             }
-        };
-        Ok((rating, 1))
+        }
     }
 }
 
@@ -375,10 +272,12 @@
 mod tests {
     use super::*;
 
+    use crate::packet_encoding::{Decodable, Encodable};
+
     #[test]
     fn metadataum_preferred_audio_contexts() {
         // Encoding.
-        let test = Metadatum::PreferredAudioContexts(ContextType::CONVERSATIONAL);
+        let test = Metadata::PreferredAudioContexts(vec![ContextType::Conversational]);
         assert_eq!(test.encoded_len(), 4);
         let mut buf = vec![0; test.encoded_len()];
         let _ = test.encode(&mut buf[..]).expect("should not fail");
@@ -387,7 +286,7 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = Metadatum::decode(&buf).expect("should succeed");
+        let decoded = Metadata::decode(&buf).expect("should succeed");
         assert_eq!(decoded.0, test);
         assert_eq!(decoded.1, 4);
     }
@@ -395,7 +294,8 @@
     #[test]
     fn metadatum_streaming_audio_contexts() {
         // Encoding.
-        let test = Metadatum::StreamingAudioContexts(ContextType::RINGTONE | ContextType::ALERT);
+        let test =
+            Metadata::StreamingAudioContexts(vec![ContextType::Ringtone, ContextType::Alerts]);
         assert_eq!(test.encoded_len(), 4);
         let mut buf = vec![0; test.encoded_len()];
         let _ = test.encode(&mut buf[..]).expect("should not fail");
@@ -404,7 +304,7 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = Metadatum::decode(&buf).expect("should succeed");
+        let decoded = Metadata::decode(&buf).expect("should succeed");
         assert_eq!(decoded.0, test);
         assert_eq!(decoded.1, 4);
     }
@@ -412,7 +312,7 @@
     #[test]
     fn metadatum_program_info() {
         // Encoding.
-        let test = Metadatum::ProgramInfo("a".to_string());
+        let test = Metadata::ProgramInfo("a".to_string());
         assert_eq!(test.encoded_len(), 3);
         let mut buf = vec![0; test.encoded_len()];
         let _ = test.encode(&mut buf[..]).expect("should not fail");
@@ -421,7 +321,7 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = Metadatum::decode(&buf).expect("should succeed");
+        let decoded = Metadata::decode(&buf).expect("should succeed");
         assert_eq!(decoded.0, test);
         assert_eq!(decoded.1, 3);
     }
@@ -429,7 +329,7 @@
     #[test]
     fn metadatum_language_code() {
         // Encoding.
-        let test = Metadatum::LanguageCode("eng".to_string());
+        let test = Metadata::Language("eng".to_string());
         assert_eq!(test.encoded_len(), 5);
         let mut buf = vec![0; test.encoded_len()];
         let _ = test.encode(&mut buf[..]).expect("should not fail");
@@ -438,7 +338,7 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = Metadatum::decode(&buf).expect("should succeed");
+        let decoded = Metadata::decode(&buf).expect("should succeed");
         assert_eq!(decoded.0, test);
         assert_eq!(decoded.1, 5);
     }
@@ -446,7 +346,7 @@
     #[test]
     fn metadatum_ccid_list() {
         // Encoding.
-        let test = Metadatum::CCIDList(vec![0x01, 0x02]);
+        let test = Metadata::CCIDList(vec![0x01, 0x02]);
         assert_eq!(test.encoded_len(), 4);
         let mut buf = vec![0; test.encoded_len()];
         let _ = test.encode(&mut buf[..]).expect("should not fail");
@@ -455,7 +355,7 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = Metadatum::decode(&buf).expect("should succeed");
+        let decoded = Metadata::decode(&buf).expect("should succeed");
         assert_eq!(decoded.0, test);
         assert_eq!(decoded.1, 4);
     }
@@ -463,7 +363,7 @@
     #[test]
     fn metadatum_parental_rating() {
         // Encding.
-        let test = Metadatum::ParentalRating(Rating::Age(8));
+        let test = Metadata::ParentalRating(Rating::Age(8));
         assert_eq!(test.encoded_len(), 0x03);
         let mut buf = vec![0; test.encoded_len()];
         let _ = test.encode(&mut buf[..]).expect("should not fail");
@@ -472,7 +372,7 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = Metadatum::decode(&buf).expect("should succeed");
+        let decoded = Metadata::decode(&buf).expect("should succeed");
         assert_eq!(decoded.0, test);
         assert_eq!(decoded.1, 3);
     }
@@ -480,7 +380,7 @@
     #[test]
     fn metadatum_vendor_specific() {
         // Encoding.
-        let test = Metadatum::VendorSpecific { company_id: 0x00E0, metadata: vec![0x01, 0x02] };
+        let test = Metadata::VendorSpecific { company_id: 0x00E0.into(), data: vec![0x01, 0x02] };
         assert_eq!(test.encoded_len(), 0x06);
         let mut buf = vec![0; test.encoded_len()];
         let _ = test.encode(&mut buf[..]).expect("should not fail");
@@ -489,7 +389,7 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = Metadatum::decode(&buf).expect("should succeed");
+        let decoded = Metadata::decode(&buf).expect("should succeed");
         assert_eq!(decoded.0, test);
         assert_eq!(decoded.1, 6);
     }
@@ -497,7 +397,7 @@
     #[test]
     fn metadatum_audio_active_state() {
         // Encoding.
-        let test = Metadatum::AudioActiveState(true);
+        let test = Metadata::AudioActiveState(true);
         assert_eq!(test.encoded_len(), 0x03);
         let mut buf = vec![0; test.encoded_len()];
         let _ = test.encode(&mut buf[..]).expect("should not fail");
@@ -506,7 +406,7 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = Metadatum::decode(&buf).expect("should succeed");
+        let decoded = Metadata::decode(&buf).expect("should succeed");
         assert_eq!(decoded.0, test);
         assert_eq!(decoded.1, 3);
     }
@@ -514,7 +414,7 @@
     #[test]
     fn metadatum_broadcast_audio_immediate_rendering() {
         // Encoding.
-        let test = Metadatum::BroadcastAudioImmediateRenderingFlag;
+        let test = Metadata::BroadcastAudioImmediateRenderingFlag;
         assert_eq!(test.encoded_len(), 0x02);
         let mut buf = vec![0; test.encoded_len()];
         let _ = test.encode(&mut buf[..]).expect("should not fail");
@@ -523,7 +423,7 @@
         assert_eq!(buf, bytes);
 
         // Decoding.
-        let decoded = Metadatum::decode(&buf).expect("should succeed");
+        let decoded = Metadata::decode(&buf).expect("should succeed");
         assert_eq!(decoded.0, test);
         assert_eq!(decoded.1, 2);
     }
@@ -531,21 +431,21 @@
     #[test]
     fn invalid_metadataum() {
         // Language code must be 3-lettered.
-        let test = Metadatum::LanguageCode("illegal".to_string());
+        let test = Metadata::Language("illegal".to_string());
         let mut buf = vec![0; test.encoded_len()];
         let _ = test.encode(&mut buf[..]).expect_err("should fail");
 
         // Not enough length for Length and Type for decoding.
         let buf = vec![0x03];
-        let _ = Metadatum::decode(&buf).expect_err("should fail");
+        let _ = Metadata::decode(&buf).expect_err("should fail");
 
         // Not enough length for Value field for decoding.
         let buf = vec![0x02, 0x01, 0x02];
-        let _ = Metadatum::decode(&buf).expect_err("should fail");
+        let _ = Metadata::decode(&buf).expect_err("should fail");
 
         // Buffer length does not match Length value for decoding.
         let buf = vec![0x03, 0x03, 0x61];
-        let _ = Metadatum::decode(&buf).expect_err("should fail");
+        let _ = Metadata::decode(&buf).expect_err("should fail");
     }
 
     #[test]
@@ -561,26 +461,25 @@
 
     #[test]
     fn decode_rating() {
-        let res = Rating::decode(&[0]).expect("should not fail");
-        assert_eq!(res.0, Rating::NoRating);
-        assert_eq!(res.1, 1);
+        let res = Rating::from(0);
+        assert_eq!(res, Rating::NoRating);
 
-        let res = Rating::decode(&[1]).expect("should not fail");
-        assert_eq!(res.0, Rating::AllAge);
-        assert_eq!(res.1, 1);
+        let res = Rating::from(1);
+        assert_eq!(res, Rating::AllAge);
 
-        let res = Rating::decode(&[2]).expect("should not fail");
-        assert_eq!(res.0, Rating::Age(5));
-        assert_eq!(res.1, 1);
+        let res = Rating::from(2);
+        assert_eq!(res, Rating::Age(5));
 
-        let res = Rating::decode(&[10]).expect("should not fail");
-        assert_eq!(res.0, Rating::Age(13));
-        assert_eq!(res.1, 1);
+        let res = Rating::from(255);
+        assert_eq!(res, Rating::Age(258));
+
+        let res = Rating::min_recommended(258).unwrap();
+        assert_eq!(255u8, (&res).into());
     }
 
     #[test]
     fn invalid_rating() {
         let _ = Rating::min_recommended(4).expect_err("should have failed");
-        let _ = Rating::decode(&[255]).expect_err("should fail");
+        let _ = Rating::min_recommended(350).expect_err("can't recommend for elves");
     }
 }
diff --git a/rust/bt-common/src/packet_encoding.rs b/rust/bt-common/src/packet_encoding.rs
index 3667ee1..8778fb2 100644
--- a/rust/bt-common/src/packet_encoding.rs
+++ b/rust/bt-common/src/packet_encoding.rs
@@ -71,8 +71,8 @@
             }
         }
 
-        impl ::core::convert::From<&$name> for $raw_type {
-            fn from(v: &$name) -> $raw_type {
+        impl ::core::convert::From<$name> for $raw_type {
+            fn from(v: $name) -> $raw_type {
                 match v {
                     $($name::$variant => $val),*,
                 }
@@ -104,7 +104,7 @@
             }
 
             pub fn to_bits<'a>(it: impl Iterator<Item = &'a $type>) -> $raw_type {
-                it.fold(0, |acc, item| acc | Into::<$raw_type>::into(item))
+                it.fold(0, |acc, item| acc | Into::<$raw_type>::into(*item))
             }
         }
     };