bt_map/
lib.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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
17// Tag IDs are listed in MAP v1.4.2 Section 6.3.1.
18pub const NOTIFICATION_STATUS_TAG_ID: u8 = 0x0E;
19pub const MAP_SUPPORTED_FEATURES_TAG_ID: u8 = 0x29;
20
21/// Errors that occur during the use of the MAP library.
22#[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/// Service record item expected from MAP related SDP.
86#[derive(Debug)]
87pub enum ServiceRecordItem {
88    MasServiceClassId,
89    ObexProtocolDescriptor,
90    MapProfileDescriptor,
91    MasInstanceId,
92    SupportedMessageTypes,
93    MapSupportedFeatures,
94    ServiceName,
95}
96
97bitflags! {
98    /// See MAP v1.4.2 section 7.1 SDP Interoperability Requirements.
99    /// According to MAP v1.4.2 section 6.3.1, the features are
100    /// represented in big-endian byte ordering.
101    #[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        // Below feature bits are only available for Message Access
123        // Services and not for Message Notification Services.
124        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        // Bits 5-7 are RFU.
141    }
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        // Has notification (bit 0) and registration (bit 1).
238        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}