1use bitflags::bitflags;
6use bt_obex::ObexError;
7use fidl_fuchsia_bluetooth_map as fidl_bt_map;
8use objects::ObexObjectError;
9use packet_encoding::{codable_as_bitmask, decodable_enum};
10use std::fmt;
11use std::str::FromStr;
12
13pub mod packets;
14
15use thiserror::Error;
16
17pub const NOTIFICATION_STATUS_TAG_ID: u8 = 0x0E;
19pub const MAP_SUPPORTED_FEATURES_TAG_ID: u8 = 0x29;
20
21#[non_exhaustive]
23#[derive(Error, Debug)]
24pub enum Error {
25 #[error("Obex error: {:?}", .0)]
26 Obex(#[from] ObexError),
27
28 #[error("Invalid message type")]
29 InvalidMessageType,
30
31 #[error("Service record item is missing or invalid: {:?}", .0)]
32 InvalidSdp(ServiceRecordItem),
33
34 #[error("Service is not GOEP interoperable")]
35 NotGoepInteroperable,
36
37 #[error("Invalid parameters")]
38 InvalidParameters,
39
40 #[error("MAS instance does not exist: (id {:?})", .0)]
41 MasInstanceDoesNotExist(u8),
42
43 #[error("Feature is not supported by the remote peer")]
44 NotSupported,
45
46 #[error("Invalid Message Notification Service state")]
47 InvalidMnsState,
48
49 #[error("Not connected to any Message Access Service instances")]
50 MasUnavailable,
51
52 #[error(transparent)]
53 Other(#[from] anyhow::Error),
54}
55
56impl Error {
57 pub fn other(error_msg: impl std::fmt::Debug) -> Self {
58 Error::Other(anyhow::format_err!("{error_msg:?}"))
59 }
60}
61
62impl From<&Error> for fidl_bt_map::Error {
63 fn from(value: &Error) -> Self {
64 match value {
65 &Error::Obex(_) => fidl_bt_map::Error::Unavailable,
66 &Error::InvalidMessageType => fidl_bt_map::Error::Unknown,
67 &Error::InvalidSdp(_) => fidl_bt_map::Error::Unavailable,
68 &Error::NotGoepInteroperable => fidl_bt_map::Error::Unavailable,
69 &Error::InvalidParameters => fidl_bt_map::Error::BadRequest,
70 &Error::MasInstanceDoesNotExist(_) => fidl_bt_map::Error::NotFound,
71 &Error::NotSupported => fidl_bt_map::Error::NotSupported,
72 &Error::InvalidMnsState => fidl_bt_map::Error::BadRequest,
73 &Error::MasUnavailable => fidl_bt_map::Error::Unavailable,
74 &Error::Other(_) => fidl_bt_map::Error::Unknown,
75 }
76 }
77}
78
79impl From<Error> for fidl_bt_map::Error {
80 fn from(value: Error) -> Self {
81 (&value).into()
82 }
83}
84
85#[derive(Debug)]
87pub enum ServiceRecordItem {
88 MasServiceClassId,
89 ObexProtocolDescriptor,
90 MapProfileDescriptor,
91 MasInstanceId,
92 SupportedMessageTypes,
93 MapSupportedFeatures,
94 ServiceName,
95}
96
97bitflags! {
98 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
102 pub struct MapSupportedFeatures: u32 {
103 const NOTIFICATION_REGISTRATION = 0x00000001;
104 const NOTIFICATION = 0x00000002;
105 const BROWSING = 0x00000004;
106 const UPLOADING = 0x00000008;
107 const DELETE = 0x00000010;
108 const INSTANCE_INFORMATION = 0x00000020;
109 const EXTENDED_EVENT_REPORT_1_1 = 0x00000040;
110 const EVENT_REPORT_VERSION_1_2 = 0x00000080;
111 const MESSAGE_FORMAT_VERSION_1_1 = 0x00000100;
112 const MESSAGES_LISTING_FORMAT_VERSION_1_1 = 0x00000200;
113 const PERSISTENT_MESSAGE_HANDLES = 0x00000400;
114 const DATABASE_IDENTIFIER = 0x00000800;
115 const FOLDER_VERSION_COUNTER = 0x00001000;
116 const CONVERSATION_VERSION_COUNTER = 0x00002000;
117 const PARTICIPANT_PRESENCE_CHANGE_NOTIFICATION = 0x00004000;
118 const PARTICIPANT_CHAT_STATE_CHANGE_NOTIFICATION = 0x00008000;
119 const PBAP_CONTACT_CROSS_REFERENCE = 0x00010000;
120 const NOTIFICATION_FILTERING = 0x00020000;
121 const UTC_OFFSET_TIMESTAMP_FORMAT = 0x00040000;
122 const MAPSUPPORTEDFEATURES_IN_CONNECT_REQUEST = 0x00080000;
125 const CONVERSATION_LISTING = 0x00100000;
126 const OWNER_STATUS = 0x00200000;
127 const MESSAGE_FORWARDING = 0x00400000;
128
129 const SUPPORTS_NOTIFICATION = Self::NOTIFICATION.bits() | Self::NOTIFICATION_REGISTRATION.bits();
130 }
131}
132
133decodable_enum! {
134 pub enum MessageType<u8, Error, InvalidMessageType> {
135 Email = 0x1,
136 SmsGsm = 0x2,
137 SmsCdma = 0x4,
138 Mms = 0x8,
139 Im = 0x10,
140 }
142}
143
144impl MessageType {
145 const MESSAGE_TYPE_EMAIL: &'static str = "EMAIL";
146 const MESSAGE_TYPE_SMS_GSM: &'static str = "SMS_GSM";
147 const MESSAGE_TYPE_SMS_CDMA: &'static str = "SMS_CDMA";
148 const MESSAGE_TYPE_MMS: &'static str = "MMS";
149 const MESSAGE_TYPE_IM: &'static str = "IM";
150}
151
152impl fmt::Display for MessageType {
153 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154 let val = match self {
155 Self::Email => Self::MESSAGE_TYPE_EMAIL,
156 Self::SmsGsm => Self::MESSAGE_TYPE_SMS_GSM,
157 Self::SmsCdma => Self::MESSAGE_TYPE_SMS_CDMA,
158 Self::Mms => Self::MESSAGE_TYPE_MMS,
159 Self::Im => Self::MESSAGE_TYPE_IM,
160 };
161 write!(f, "{}", val)
162 }
163}
164
165impl FromStr for MessageType {
166 type Err = ObexObjectError;
167 fn from_str(src: &str) -> Result<Self, Self::Err> {
168 match src {
169 Self::MESSAGE_TYPE_EMAIL => Ok(Self::Email),
170 Self::MESSAGE_TYPE_SMS_GSM => Ok(Self::SmsGsm),
171 Self::MESSAGE_TYPE_SMS_CDMA => Ok(Self::SmsCdma),
172 Self::MESSAGE_TYPE_MMS => Ok(Self::Mms),
173 Self::MESSAGE_TYPE_IM => Ok(Self::Im),
174 v => Err(ObexObjectError::invalid_data(v)),
175 }
176 }
177}
178
179impl From<MessageType> for fidl_bt_map::MessageType {
180 fn from(value: MessageType) -> Self {
181 match value {
182 MessageType::Email => fidl_bt_map::MessageType::EMAIL,
183 MessageType::SmsGsm => fidl_bt_map::MessageType::SMS_GSM,
184 MessageType::SmsCdma => fidl_bt_map::MessageType::SMS_CDMA,
185 MessageType::Mms => fidl_bt_map::MessageType::MMS,
186 MessageType::Im => fidl_bt_map::MessageType::IM,
187 }
188 }
189}
190
191codable_as_bitmask!(MessageType, u8, Error, InvalidMessageType);
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196 use std::collections::HashSet;
197
198 #[test]
199 fn message_type() {
200 let email_and_im = 0x11;
201
202 let types_: HashSet<MessageType> = MessageType::from_bits(email_and_im)
203 .collect::<Result<HashSet<_>, _>>()
204 .expect("should not fail");
205
206 assert_eq!(2, types_.len());
207
208 let expected = [MessageType::Email, MessageType::Im].into_iter().collect();
209
210 assert_eq!(types_, expected);
211
212 let all = MessageType::VARIANTS;
213 let value = MessageType::to_bits(all.iter()).expect("should work");
214 assert_eq!(0x1F, value);
215 }
216
217 #[test]
218 fn map_supported_features() {
219 const NOTIFICATION_REG: u32 = 0x1;
220 assert_eq!(
221 MapSupportedFeatures::from_bits_truncate(NOTIFICATION_REG),
222 MapSupportedFeatures::NOTIFICATION_REGISTRATION
223 );
224
225 const NOTIFICATION_REG_AND_OWNER_STATUS: u32 = 0x200001;
226 let features = MapSupportedFeatures::from_bits_truncate(NOTIFICATION_REG_AND_OWNER_STATUS);
227 assert!(features.contains(MapSupportedFeatures::NOTIFICATION_REGISTRATION));
228 assert!(features.contains(MapSupportedFeatures::OWNER_STATUS));
229 assert_eq!(
230 features,
231 MapSupportedFeatures::NOTIFICATION_REGISTRATION | MapSupportedFeatures::OWNER_STATUS
232 );
233 }
234
235 #[test]
236 fn supports_notification() {
237 let features = MapSupportedFeatures::from_bits_truncate(0x3);
239 assert!(features.contains(MapSupportedFeatures::SUPPORTS_NOTIFICATION));
240
241 let features = MapSupportedFeatures::NOTIFICATION;
242 assert!(!features.contains(MapSupportedFeatures::SUPPORTS_NOTIFICATION));
243
244 let features = MapSupportedFeatures::NOTIFICATION_REGISTRATION;
245 assert!(!features.contains(MapSupportedFeatures::SUPPORTS_NOTIFICATION));
246
247 let features = MapSupportedFeatures::MESSAGE_FORWARDING;
248 assert!(!features.contains(MapSupportedFeatures::SUPPORTS_NOTIFICATION));
249 }
250}