blob: 7a894431690a134a69710f76f57fbb12ef48c250 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use anyhow::{format_err, Error};
use fidl_fuchsia_bluetooth::{Appearance, Uuid as FidlUuid};
use fidl_fuchsia_bluetooth_avdtp_test::PeerControllerProxy;
use fidl_fuchsia_bluetooth_avrcp::{
AvcPanelCommand, BatteryStatus, CustomAttributeValue, CustomPlayerApplicationSetting,
Equalizer, PlayStatus, PlaybackStatus, PlayerApplicationSettingAttributeId,
PlayerApplicationSettings, RepeatStatusMode, ScanMode, ShuffleMode,
};
use fidl_fuchsia_bluetooth_gatt2::{
AttributePermissions, Characteristic, Descriptor, ReadByTypeResult, SecurityRequirements,
ServiceHandle, ServiceInfo, ServiceKind,
};
use fidl_fuchsia_bluetooth_le::{
AdvertisingData, AdvertisingModeHint, AdvertisingParameters, ConnectionOptions, Filter,
ManufacturerData, ServiceData,
};
use fidl_fuchsia_bluetooth_sys::Peer;
use fuchsia_bluetooth::types::Uuid;
use num_derive::FromPrimitive;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use std::str::FromStr;
use crate::common_utils::common::macros::parse_arg;
/// Handling different sessions.
/// Key is a generic id that is generated by the tool that is associated with a remote peer.
/// Value is the controller associated with the remote peer.
pub type PeerFactoryMap = HashMap<String, PeerControllerProxy>;
/// BleScan result type
/// TODO(https://fxbug.dev/42168627): Add support for RemoteDevices when clone() is implemented
#[derive(Serialize, Clone, Debug)]
pub struct BleScanResponse {
pub id: String,
pub name: String,
pub connectable: bool,
}
impl BleScanResponse {
pub fn new(id: String, name: String, connectable: bool) -> BleScanResponse {
BleScanResponse { id, name, connectable }
}
}
/// BleAdvertise result type (only uuid)
/// TODO(https://fxbug.dev/42168627): Add support for AdvertisingData when clone() is implemented
#[derive(Serialize, Clone, Debug)]
pub struct BleAdvertiseResponse {
pub name: Option<String>,
}
impl BleAdvertiseResponse {
pub fn new(name: Option<String>) -> BleAdvertiseResponse {
BleAdvertiseResponse { name }
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct SecurityRequirementsContainer {
pub encryption_required: bool,
pub authentication_required: bool,
pub authorization_required: bool,
}
impl SecurityRequirementsContainer {
pub fn new(info: Option<SecurityRequirements>) -> SecurityRequirementsContainer {
match info {
Some(sec) => SecurityRequirementsContainer {
encryption_required: sec.encryption_required.unwrap_or(false),
authentication_required: sec.authentication_required.unwrap_or(false),
authorization_required: sec.authorization_required.unwrap_or(false),
},
None => SecurityRequirementsContainer::default(),
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct AttributePermissionsContainer {
pub read: SecurityRequirementsContainer,
pub write: SecurityRequirementsContainer,
pub update: SecurityRequirementsContainer,
}
impl AttributePermissionsContainer {
pub fn new(info: Option<AttributePermissions>) -> Result<AttributePermissionsContainer, Error> {
match info {
Some(perm) => Ok(AttributePermissionsContainer {
read: SecurityRequirementsContainer::new(perm.read),
write: SecurityRequirementsContainer::new(perm.write),
update: SecurityRequirementsContainer::new(perm.update),
}),
None => return Err(format_err!("Unable to get information of AttributePermissions.")),
}
}
}
// Discover Characteristic response to hold characteristic info
// as Characteristics are not serializable.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct GattcDiscoverDescriptorResponse {
pub id: u64,
pub permissions: Option<AttributePermissionsContainer>,
pub uuid_type: String,
}
impl GattcDiscoverDescriptorResponse {
pub fn new(info: Vec<Descriptor>) -> Vec<GattcDiscoverDescriptorResponse> {
let mut res = Vec::new();
for v in info {
let copy = GattcDiscoverDescriptorResponse {
id: v.handle.unwrap().value,
permissions: match AttributePermissionsContainer::new(v.permissions) {
Ok(n) => Some(n),
Err(_) => None,
},
uuid_type: Uuid::from(v.type_.unwrap()).to_string(),
};
res.push(copy)
}
res
}
}
// Discover Characteristic response to hold characteristic info
// as Characteristics are not serializable.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct GattcDiscoverCharacteristicResponse {
pub id: u64,
pub properties: u32,
pub permissions: Option<AttributePermissionsContainer>,
pub uuid_type: String,
pub descriptors: Vec<GattcDiscoverDescriptorResponse>,
}
impl GattcDiscoverCharacteristicResponse {
pub fn new(info: Vec<Characteristic>) -> Vec<GattcDiscoverCharacteristicResponse> {
let mut res = Vec::new();
for v in info {
let copy = GattcDiscoverCharacteristicResponse {
id: v.handle.unwrap().value,
properties: v.properties.unwrap().bits().into(),
permissions: match AttributePermissionsContainer::new(v.permissions) {
Ok(n) => Some(n),
Err(_) => None,
},
uuid_type: Uuid::from(v.type_.unwrap()).to_string(),
descriptors: {
match v.descriptors {
Some(d) => GattcDiscoverDescriptorResponse::new(d),
None => Vec::new(),
}
},
};
res.push(copy)
}
res
}
}
/// BleConnectPeripheral response (aka ServiceInfo)
/// TODO(https://fxbug.dev/42168627): Add support for ServiceInfo when clone(), serialize(), derived
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct BleConnectPeripheralResponse {
pub id: u64,
pub primary: bool,
pub uuid_type: String,
}
impl BleConnectPeripheralResponse {
pub fn new(info: Vec<ServiceInfo>) -> Vec<BleConnectPeripheralResponse> {
let mut res = Vec::new();
for v in info {
let copy = BleConnectPeripheralResponse {
id: v.handle.unwrap().value,
primary: v.kind.unwrap() == ServiceKind::Primary,
uuid_type: Uuid::from(v.type_.unwrap()).to_string(),
};
res.push(copy)
}
res
}
}
#[derive(Clone, Debug, Serialize)]
pub struct SerializablePeer {
pub address: Option<[u8; 6]>,
pub appearance: Option<u32>,
pub device_class: Option<u32>,
pub id: Option<String>,
pub name: Option<String>,
pub connected: Option<bool>,
pub bonded: Option<bool>,
pub rssi: Option<i8>,
pub services: Option<Vec<[u8; 16]>>,
pub technology: Option<u32>,
pub tx_power: Option<i8>,
}
impl From<&Peer> for SerializablePeer {
fn from(peer: &Peer) -> Self {
let services = match &peer.services {
Some(s) => {
let mut service_list = Vec::new();
for item in s {
service_list.push(item.value);
}
Some(service_list)
}
None => None,
};
SerializablePeer {
address: peer.address.map(|a| a.bytes),
appearance: peer.appearance.map(|a| a.into_primitive() as u32),
device_class: peer.device_class.map(|d| d.value),
id: peer.id.map(|i| i.value.to_string()),
name: peer.name.clone(),
connected: peer.connected,
bonded: peer.bonded,
rssi: peer.rssi,
services: services,
technology: peer.technology.map(|t| t as u32),
tx_power: peer.tx_power,
}
}
}
#[derive(Clone, Debug, Serialize)]
pub struct SerializableReadByTypeResult {
pub id: Option<u64>,
pub value: Option<Vec<u8>>,
}
impl SerializableReadByTypeResult {
pub fn new(result: ReadByTypeResult) -> Option<Self> {
if result.error.is_some() {
return None;
}
let id = result.handle.unwrap().value;
let value = result.value.unwrap().value.unwrap();
Some(SerializableReadByTypeResult { id: Some(id), value: Some(value) })
}
}
#[derive(Clone, Debug, Serialize, Eq, Copy)]
pub struct CustomPlayStatus {
pub song_length: Option<u32>,
pub song_position: Option<u32>,
pub playback_status: Option<u8>,
}
impl CustomPlayStatus {
pub fn new(status: &PlayStatus) -> Self {
let playback_status = match status.playback_status {
Some(p) => Some(p as u8),
None => None,
};
CustomPlayStatus {
song_length: status.song_length,
song_position: status.song_position,
playback_status: playback_status,
}
}
}
impl From<CustomPlayStatus> for PlayStatus {
fn from(status: CustomPlayStatus) -> Self {
let playback_status = match status.playback_status {
Some(0) => Some(PlaybackStatus::Stopped),
Some(1) => Some(PlaybackStatus::Playing),
Some(2) => Some(PlaybackStatus::Paused),
Some(3) => Some(PlaybackStatus::FwdSeek),
Some(4) => Some(PlaybackStatus::RevSeek),
Some(255) => Some(PlaybackStatus::Error),
None => None,
_ => panic!("Unknown playback status!"),
};
PlayStatus {
song_length: status.song_length,
song_position: status.song_position,
playback_status: playback_status,
..Default::default()
}
}
}
impl From<PlayStatus> for CustomPlayStatus {
fn from(status: PlayStatus) -> Self {
CustomPlayStatus {
song_length: status.song_length,
song_position: status.song_position,
playback_status: match status.playback_status {
Some(p) => Some(p as u8),
None => None,
},
}
}
}
impl PartialEq for CustomPlayStatus {
fn eq(&self, other: &CustomPlayStatus) -> bool {
self.song_length == other.song_length
&& self.song_position == other.song_position
&& self.playback_status == other.playback_status
}
}
#[derive(Copy, Clone, Debug, FromPrimitive, Serialize, Deserialize)]
#[repr(u8)]
pub enum CustomAvcPanelCommand {
Select = 0,
Up = 1,
Down = 2,
Left = 3,
Right = 4,
RootMenu = 9,
ContentsMenu = 11,
FavoriteMenu = 12,
Exit = 13,
OnDemandMenu = 14,
AppsMenu = 15,
Key0 = 32,
Key1 = 33,
Key2 = 34,
Key3 = 35,
Key4 = 36,
Key5 = 37,
Key6 = 38,
Key7 = 39,
Key8 = 40,
Key9 = 41,
Dot = 42,
Enter = 43,
ChannelUp = 48,
ChannelDown = 49,
ChannelPrevious = 50,
InputSelect = 52,
Info = 53,
Help = 54,
PageUp = 55,
PageDown = 56,
Lock = 58,
Power = 64,
VolumeUp = 65,
VolumeDown = 66,
Mute = 67,
Play = 68,
Stop = 69,
Pause = 70,
Record = 71,
Rewind = 72,
FastForward = 73,
Eject = 74,
Forward = 75,
Backward = 76,
List = 77,
F1 = 113,
F2 = 114,
F3 = 115,
F4 = 116,
F5 = 117,
F6 = 118,
F7 = 119,
F8 = 120,
F9 = 121,
Red = 122,
Green = 123,
Blue = 124,
Yellow = 125,
}
impl From<CustomAvcPanelCommand> for AvcPanelCommand {
fn from(command: CustomAvcPanelCommand) -> Self {
match command {
CustomAvcPanelCommand::Select => AvcPanelCommand::Select,
CustomAvcPanelCommand::Up => AvcPanelCommand::Up,
CustomAvcPanelCommand::Down => AvcPanelCommand::Down,
CustomAvcPanelCommand::Left => AvcPanelCommand::Left,
CustomAvcPanelCommand::Right => AvcPanelCommand::Right,
CustomAvcPanelCommand::RootMenu => AvcPanelCommand::RootMenu,
CustomAvcPanelCommand::ContentsMenu => AvcPanelCommand::ContentsMenu,
CustomAvcPanelCommand::FavoriteMenu => AvcPanelCommand::FavoriteMenu,
CustomAvcPanelCommand::Exit => AvcPanelCommand::Exit,
CustomAvcPanelCommand::OnDemandMenu => AvcPanelCommand::OnDemandMenu,
CustomAvcPanelCommand::AppsMenu => AvcPanelCommand::AppsMenu,
CustomAvcPanelCommand::Key0 => AvcPanelCommand::Key0,
CustomAvcPanelCommand::Key1 => AvcPanelCommand::Key1,
CustomAvcPanelCommand::Key2 => AvcPanelCommand::Key2,
CustomAvcPanelCommand::Key3 => AvcPanelCommand::Key3,
CustomAvcPanelCommand::Key4 => AvcPanelCommand::Key4,
CustomAvcPanelCommand::Key5 => AvcPanelCommand::Key5,
CustomAvcPanelCommand::Key6 => AvcPanelCommand::Key6,
CustomAvcPanelCommand::Key7 => AvcPanelCommand::Key7,
CustomAvcPanelCommand::Key8 => AvcPanelCommand::Key8,
CustomAvcPanelCommand::Key9 => AvcPanelCommand::Key9,
CustomAvcPanelCommand::Dot => AvcPanelCommand::Dot,
CustomAvcPanelCommand::Enter => AvcPanelCommand::Enter,
CustomAvcPanelCommand::ChannelUp => AvcPanelCommand::ChannelUp,
CustomAvcPanelCommand::ChannelDown => AvcPanelCommand::ChannelDown,
CustomAvcPanelCommand::ChannelPrevious => AvcPanelCommand::ChannelPrevious,
CustomAvcPanelCommand::InputSelect => AvcPanelCommand::InputSelect,
CustomAvcPanelCommand::Info => AvcPanelCommand::Info,
CustomAvcPanelCommand::Help => AvcPanelCommand::Help,
CustomAvcPanelCommand::PageUp => AvcPanelCommand::PageUp,
CustomAvcPanelCommand::PageDown => AvcPanelCommand::PageDown,
CustomAvcPanelCommand::Lock => AvcPanelCommand::Lock,
CustomAvcPanelCommand::Power => AvcPanelCommand::Power,
CustomAvcPanelCommand::VolumeUp => AvcPanelCommand::VolumeUp,
CustomAvcPanelCommand::VolumeDown => AvcPanelCommand::VolumeDown,
CustomAvcPanelCommand::Mute => AvcPanelCommand::Mute,
CustomAvcPanelCommand::Play => AvcPanelCommand::Play,
CustomAvcPanelCommand::Stop => AvcPanelCommand::Stop,
CustomAvcPanelCommand::Pause => AvcPanelCommand::Pause,
CustomAvcPanelCommand::Record => AvcPanelCommand::Record,
CustomAvcPanelCommand::Rewind => AvcPanelCommand::Rewind,
CustomAvcPanelCommand::FastForward => AvcPanelCommand::FastForward,
CustomAvcPanelCommand::Eject => AvcPanelCommand::Eject,
CustomAvcPanelCommand::Forward => AvcPanelCommand::Forward,
CustomAvcPanelCommand::Backward => AvcPanelCommand::Backward,
CustomAvcPanelCommand::List => AvcPanelCommand::List,
CustomAvcPanelCommand::F1 => AvcPanelCommand::F1,
CustomAvcPanelCommand::F2 => AvcPanelCommand::F2,
CustomAvcPanelCommand::F3 => AvcPanelCommand::F3,
CustomAvcPanelCommand::F4 => AvcPanelCommand::F4,
CustomAvcPanelCommand::F5 => AvcPanelCommand::F5,
CustomAvcPanelCommand::F6 => AvcPanelCommand::F6,
CustomAvcPanelCommand::F7 => AvcPanelCommand::F7,
CustomAvcPanelCommand::F8 => AvcPanelCommand::F8,
CustomAvcPanelCommand::F9 => AvcPanelCommand::F9,
CustomAvcPanelCommand::Red => AvcPanelCommand::Red,
CustomAvcPanelCommand::Green => AvcPanelCommand::Green,
CustomAvcPanelCommand::Blue => AvcPanelCommand::Blue,
CustomAvcPanelCommand::Yellow => AvcPanelCommand::Yellow,
}
}
}
impl From<String> for CustomAvcPanelCommand {
fn from(command: String) -> Self {
match command.as_str() {
"Select" => CustomAvcPanelCommand::Select,
"Up" => CustomAvcPanelCommand::Up,
"Down" => CustomAvcPanelCommand::Down,
"Left" => CustomAvcPanelCommand::Left,
"Right" => CustomAvcPanelCommand::Right,
"RootMenu" => CustomAvcPanelCommand::RootMenu,
"ContentsMenu" => CustomAvcPanelCommand::ContentsMenu,
"FavoriteMenu" => CustomAvcPanelCommand::FavoriteMenu,
"Exit" => CustomAvcPanelCommand::Exit,
"OnDemandMenu" => CustomAvcPanelCommand::OnDemandMenu,
"AppsMenu" => CustomAvcPanelCommand::AppsMenu,
"Key0" => CustomAvcPanelCommand::Key0,
"Key1" => CustomAvcPanelCommand::Key1,
"Key2" => CustomAvcPanelCommand::Key2,
"Key3" => CustomAvcPanelCommand::Key3,
"Key4" => CustomAvcPanelCommand::Key4,
"Key5" => CustomAvcPanelCommand::Key5,
"Key6" => CustomAvcPanelCommand::Key6,
"Key7" => CustomAvcPanelCommand::Key7,
"Key8" => CustomAvcPanelCommand::Key8,
"Key9" => CustomAvcPanelCommand::Key9,
"Dot" => CustomAvcPanelCommand::Dot,
"Enter" => CustomAvcPanelCommand::Enter,
"ChannelUp" => CustomAvcPanelCommand::ChannelUp,
"ChannelDown" => CustomAvcPanelCommand::ChannelDown,
"ChannelPrevious" => CustomAvcPanelCommand::ChannelPrevious,
"InputSelect" => CustomAvcPanelCommand::InputSelect,
"Info" => CustomAvcPanelCommand::Info,
"Help" => CustomAvcPanelCommand::Help,
"PageUp" => CustomAvcPanelCommand::PageUp,
"PageDown" => CustomAvcPanelCommand::PageDown,
"Lock" => CustomAvcPanelCommand::Lock,
"Power" => CustomAvcPanelCommand::Power,
"VolumeUp" => CustomAvcPanelCommand::VolumeUp,
"VolumeDown" => CustomAvcPanelCommand::VolumeDown,
"Mute" => CustomAvcPanelCommand::Mute,
"Play" => CustomAvcPanelCommand::Play,
"Stop" => CustomAvcPanelCommand::Stop,
"Pause" => CustomAvcPanelCommand::Pause,
"Record" => CustomAvcPanelCommand::Record,
"Rewind" => CustomAvcPanelCommand::Rewind,
"FastForward" => CustomAvcPanelCommand::FastForward,
"Eject" => CustomAvcPanelCommand::Eject,
"Forward" => CustomAvcPanelCommand::Forward,
"Backward" => CustomAvcPanelCommand::Backward,
"List" => CustomAvcPanelCommand::List,
"F1" => CustomAvcPanelCommand::F1,
"F2" => CustomAvcPanelCommand::F2,
"F3" => CustomAvcPanelCommand::F3,
"F4" => CustomAvcPanelCommand::F4,
"F5" => CustomAvcPanelCommand::F5,
"F6" => CustomAvcPanelCommand::F6,
"F7" => CustomAvcPanelCommand::F7,
"F8" => CustomAvcPanelCommand::F8,
"F9" => CustomAvcPanelCommand::F9,
"Red" => CustomAvcPanelCommand::Red,
"Green" => CustomAvcPanelCommand::Green,
"Blue" => CustomAvcPanelCommand::Blue,
"Yellow" => CustomAvcPanelCommand::Yellow,
_invalid => panic!("Invalid CustomAvcPanelCommand command:{:?}", _invalid),
}
}
}
#[derive(Deserialize)]
pub struct AbsoluteVolumeCommand {
pub absolute_volume: u8,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct CustomPlayerApplicationSettingsAttributeIds {
pub attribute_ids: Option<Vec<u8>>,
}
impl CustomPlayerApplicationSettingsAttributeIds {
pub fn to_vec(&self) -> Vec<PlayerApplicationSettingAttributeId> {
match &self.attribute_ids {
Some(vec) => vec
.into_iter()
.map(|u8| match u8 {
1 => PlayerApplicationSettingAttributeId::Equalizer,
2 => PlayerApplicationSettingAttributeId::RepeatStatusMode,
3 => PlayerApplicationSettingAttributeId::ShuffleMode,
4 => PlayerApplicationSettingAttributeId::ScanMode,
invalid => panic!(
"Invalid value for PlayerApplicationSettingAttributeId {:?}",
invalid
),
})
.collect(),
None => Vec::new(),
}
}
}
#[derive(Clone, Debug, Serialize)]
pub enum CustomPlayerApplicationSettingsAttributeId {
Equalizer = 1,
RepeatStatusMode = 2,
ShuffleMode = 3,
ScanMode = 4,
}
impl From<u8> for CustomPlayerApplicationSettingsAttributeId {
fn from(attribute_id: u8) -> CustomPlayerApplicationSettingsAttributeId {
match attribute_id {
1 => CustomPlayerApplicationSettingsAttributeId::Equalizer,
2 => CustomPlayerApplicationSettingsAttributeId::RepeatStatusMode,
3 => CustomPlayerApplicationSettingsAttributeId::ShuffleMode,
4 => CustomPlayerApplicationSettingsAttributeId::ScanMode,
_ => panic!("Invalid attribute id: {:?}", attribute_id),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct CustomPlayerApplicationSettings {
pub equalizer: Option<CustomEqualizer>,
pub repeat_status_mode: Option<CustomRepeatStatusMode>,
pub shuffle_mode: Option<CustomShuffleMode>,
pub scan_mode: Option<CustomScanMode>,
pub custom_settings: Option<Vec<CustomCustomPlayerApplicationSetting>>,
}
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct CustomCustomPlayerApplicationSetting {
pub attribute_id: Option<u8>,
pub attribute_name: Option<String>,
pub possible_values: Option<Vec<CustomCustomAttributeValue>>,
pub current_value: Option<u8>,
}
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct CustomCustomAttributeValue {
pub description: String,
pub value: u8,
}
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Copy)]
pub enum CustomEqualizer {
Off = 1,
On = 2,
}
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Copy)]
pub enum CustomRepeatStatusMode {
Off = 1,
SingleTrackRepeat = 2,
AllTrackRepeat = 3,
GroupRepeat = 4,
}
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Copy)]
pub enum CustomShuffleMode {
Off = 1,
AllTrackShuffle = 2,
GroupShuffle = 3,
}
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Copy)]
pub enum CustomScanMode {
Off = 1,
AllTrackScan = 2,
GroupScan = 3,
}
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Copy)]
pub enum CustomBatteryStatus {
Normal = 0,
Warning = 1,
Critical = 2,
External = 3,
FullCharge = 4,
Reserved = 5,
}
impl From<PlayerApplicationSettings> for CustomPlayerApplicationSettings {
fn from(settings: PlayerApplicationSettings) -> CustomPlayerApplicationSettings {
CustomPlayerApplicationSettings {
equalizer: match settings.equalizer {
Some(equalizer) => match equalizer {
Equalizer::Off => Some(CustomEqualizer::Off),
Equalizer::On => Some(CustomEqualizer::On),
},
None => None,
},
repeat_status_mode: match settings.repeat_status_mode {
Some(repeat_status_mode) => match repeat_status_mode {
RepeatStatusMode::Off => Some(CustomRepeatStatusMode::Off),
RepeatStatusMode::SingleTrackRepeat => {
Some(CustomRepeatStatusMode::SingleTrackRepeat)
}
RepeatStatusMode::AllTrackRepeat => {
Some(CustomRepeatStatusMode::AllTrackRepeat)
}
RepeatStatusMode::GroupRepeat => Some(CustomRepeatStatusMode::GroupRepeat),
},
None => None,
},
shuffle_mode: match settings.shuffle_mode {
Some(shuffle_mode) => match shuffle_mode {
ShuffleMode::Off => Some(CustomShuffleMode::Off),
ShuffleMode::AllTrackShuffle => Some(CustomShuffleMode::AllTrackShuffle),
ShuffleMode::GroupShuffle => Some(CustomShuffleMode::GroupShuffle),
},
None => None,
},
scan_mode: match settings.scan_mode {
Some(scan_mode) => match scan_mode {
ScanMode::Off => Some(CustomScanMode::Off),
ScanMode::AllTrackScan => Some(CustomScanMode::AllTrackScan),
ScanMode::GroupScan => Some(CustomScanMode::GroupScan),
},
None => None,
},
custom_settings: match settings.custom_settings {
Some(custom_settings_vec) => Some(
custom_settings_vec
.into_iter()
.map(|custom_settings| CustomCustomPlayerApplicationSetting {
attribute_id: custom_settings.attribute_id,
attribute_name: custom_settings.attribute_name,
possible_values: match custom_settings.possible_values {
Some(possible_values) => Some(
possible_values
.into_iter()
.map(|possible_value| possible_value.into())
.collect(),
),
None => None,
},
current_value: custom_settings.current_value,
})
.collect(),
),
None => None,
},
}
}
}
impl From<CustomPlayerApplicationSettings> for PlayerApplicationSettings {
fn from(settings: CustomPlayerApplicationSettings) -> PlayerApplicationSettings {
PlayerApplicationSettings {
equalizer: match settings.equalizer {
Some(equalizer) => match equalizer {
CustomEqualizer::Off => Some(Equalizer::Off),
CustomEqualizer::On => Some(Equalizer::On),
},
None => None,
},
repeat_status_mode: match settings.repeat_status_mode {
Some(repeat_status_mode) => match repeat_status_mode {
CustomRepeatStatusMode::Off => Some(RepeatStatusMode::Off),
CustomRepeatStatusMode::SingleTrackRepeat => {
Some(RepeatStatusMode::SingleTrackRepeat)
}
CustomRepeatStatusMode::AllTrackRepeat => {
Some(RepeatStatusMode::AllTrackRepeat)
}
CustomRepeatStatusMode::GroupRepeat => Some(RepeatStatusMode::GroupRepeat),
},
None => None,
},
shuffle_mode: match settings.shuffle_mode {
Some(shuffle_mode) => match shuffle_mode {
CustomShuffleMode::Off => Some(ShuffleMode::Off),
CustomShuffleMode::AllTrackShuffle => Some(ShuffleMode::AllTrackShuffle),
CustomShuffleMode::GroupShuffle => Some(ShuffleMode::GroupShuffle),
},
None => None,
},
scan_mode: match settings.scan_mode {
Some(scan_mode) => match scan_mode {
CustomScanMode::Off => Some(ScanMode::Off),
CustomScanMode::AllTrackScan => Some(ScanMode::AllTrackScan),
CustomScanMode::GroupScan => Some(ScanMode::GroupScan),
},
None => None,
},
custom_settings: match settings.custom_settings {
Some(custom_settings_vec) => Some(
custom_settings_vec
.into_iter()
.map(|custom_settings| CustomPlayerApplicationSetting {
attribute_id: custom_settings.attribute_id,
attribute_name: custom_settings.attribute_name,
possible_values: match custom_settings.possible_values {
Some(possible_values) => Some(
possible_values
.into_iter()
.map(|possible_value| possible_value.into())
.collect(),
),
None => None,
},
current_value: custom_settings.current_value,
..Default::default()
})
.collect(),
),
None => None,
},
..Default::default()
}
}
}
impl From<CustomCustomAttributeValue> for CustomAttributeValue {
fn from(attribute_value: CustomCustomAttributeValue) -> CustomAttributeValue {
CustomAttributeValue {
description: attribute_value.description,
value: attribute_value.value,
}
}
}
impl From<CustomAttributeValue> for CustomCustomAttributeValue {
fn from(attribute_value: CustomAttributeValue) -> CustomCustomAttributeValue {
CustomCustomAttributeValue {
description: attribute_value.description,
value: attribute_value.value,
}
}
}
impl From<BatteryStatus> for CustomBatteryStatus {
fn from(status: BatteryStatus) -> Self {
match status {
BatteryStatus::Normal => CustomBatteryStatus::Normal,
BatteryStatus::Warning => CustomBatteryStatus::Warning,
BatteryStatus::Critical => CustomBatteryStatus::Critical,
BatteryStatus::External => CustomBatteryStatus::External,
BatteryStatus::FullCharge => CustomBatteryStatus::FullCharge,
BatteryStatus::Reserved => CustomBatteryStatus::Reserved,
}
}
}
impl From<CustomBatteryStatus> for BatteryStatus {
fn from(status: CustomBatteryStatus) -> Self {
match status {
CustomBatteryStatus::Normal => BatteryStatus::Normal,
CustomBatteryStatus::Warning => BatteryStatus::Warning,
CustomBatteryStatus::Critical => BatteryStatus::Critical,
CustomBatteryStatus::External => BatteryStatus::External,
CustomBatteryStatus::FullCharge => BatteryStatus::FullCharge,
CustomBatteryStatus::Reserved => BatteryStatus::Reserved,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CustomNotificationsFilter {
pub notifications: u32,
pub position_change_interval: Option<u32>,
}
pub struct FacadeArg {
value: Value,
}
impl FacadeArg {
pub fn new(value: Value) -> Self {
FacadeArg { value }
}
}
/// Deserializes a serde json object according to the following schema
///
/// {
/// 'advertising_data': {
/// 'name': Some(String),
/// 'appearance': Some(u64),
/// 'service_data': {
/// 'uuid': ['fb', '34', '9b', '5f', '80', '00', '00', '80', '00', '10', '00', '00', '01', '18', '00', '00'].
/// 'data': "1"
/// },
/// 'service_uuids': [
/// ['fb', '34', '9b', '5f', '80', '00', '00', '80', '00', '10', '00', '00', '01', '18', '00', '00'].
/// ['fb', '34', '9b', '5f', '80', '00', '00', '80', '00', '10', '00', '00', '00', '18', '00', '00']
/// ],
/// 'manufacturer_data': {
/// 'id': 10,
/// 'data'
/// },
/// 'uris': Some(['telnet://192.0.2.16:80/']),
/// 'tx_power_level': Some(1),
///
/// }
///
/// }
///
/// Note: A human readable uuid is represented as a list of bytes:
/// Example Human Readable UUID: '00001801-0000-1000-8000-00805f9b34fb'
/// Actual input:
/// ['fb', '34', '9b', '5f', '80', '00', '00', '80', '00', '10', '00', '00', '01', '18', '00', '00']
impl TryInto<AdvertisingData> for FacadeArg {
type Error = Error;
fn try_into(self) -> Result<AdvertisingData, Self::Error> {
/// Parse json input UUID and return Bluetooth UUID format.
///
/// # Arguments
/// * `json_uuid`: The JSON UUID in the form of a list of bytes.
fn parse_uuid(json_uuid: &Value) -> Result<FidlUuid, Error> {
let mut byte_list = vec![];
for byte_string in json_uuid.as_array().unwrap() {
let raw_value = match i64::from_str_radix(byte_string.as_str().unwrap(), 16) {
Ok(v) => v as u8,
Err(e) => bail!("Failed to convert raw value with: {:?}", e),
};
byte_list.push(raw_value);
}
Ok(FidlUuid {
value: byte_list.as_slice().try_into().expect("Failed to set UUID value."),
})
}
/// Parse a list of service uuids from a json input list.
///
/// # Arguments
/// * `json_service_uuids`: The JSON UUIDs in the form of lists of list of bytes.
fn parse_service_uuids(json_service_uuids: &Vec<Value>) -> Result<Vec<FidlUuid>, Error> {
let mut uuid_list = Vec::new();
for raw_uuid_list in json_service_uuids {
uuid_list.push(parse_uuid(raw_uuid_list)?);
}
Ok(uuid_list)
}
/// Parse the json input service data into a list of ServiceData
///
/// # Arguments
/// * `json_service_data`: The JSON representation of ServiceData to parse.
fn parse_service_data(json_service_data: &Vec<Value>) -> Result<Vec<ServiceData>, Error> {
let mut manufacturer_data_list = Vec::new();
for raw_service_data in json_service_data {
let uuid = match raw_service_data.get("uuid") {
Some(v) => parse_uuid(v)?,
None => bail!("Missing Service data info 'uuid'."),
};
let data = match raw_service_data.get("data") {
Some(d) => d.to_string().into_bytes(),
None => bail!("Missing Service data info 'data'."),
};
manufacturer_data_list.push(ServiceData { uuid, data });
}
Ok(manufacturer_data_list)
}
/// Parse the json input manufacturer data into a list of ManufacturerData
///
/// # Arguments
/// * `json_manufacturer_data`: The JSON representation of ManufacturerData to parse.
fn parse_manufacturer_data(
json_manufacturer_data: &Vec<Value>,
) -> Result<Vec<ManufacturerData>, Error> {
let mut manufacturer_data_list = Vec::new();
for raw_manufacturer_data in json_manufacturer_data {
let company_id = match raw_manufacturer_data.get("id") {
Some(v) => match v.as_u64() {
Some(c) => c as u16,
None => bail!("Company id not a valid value."),
},
None => bail!("Missing Manufacturer info 'id'."),
};
let data = match raw_manufacturer_data.get("data") {
Some(d) => d.to_string().into_bytes(),
None => bail!("Missing Manufacturer info 'data'."),
};
manufacturer_data_list.push(ManufacturerData { company_id, data });
}
Ok(manufacturer_data_list)
}
let name: Option<String> = self.value["name"].as_str().map(String::from);
let service_uuids = match self.value.get("service_uuids") {
Some(v) => {
if v.is_null() {
None
} else {
match v.clone().as_array() {
Some(list) => Some(parse_service_uuids(list)?),
None => bail!("Attribute 'service_uuids' is not a parseable list."),
}
}
}
None => None,
};
let appearance = match self.value.get("appearance") {
Some(v) => {
if v.is_null() {
None
} else {
match v.as_u64() {
Some(c) => Appearance::from_primitive(c as u16),
None => None,
}
}
}
None => bail!("Value 'appearance' missing."),
};
let include_tx_power_level =
self.value.get("tx_power_level").and_then(|v| Some(!v.is_null()));
let service_data = match self.value.get("service_data") {
Some(raw) => {
if raw.is_null() {
None
} else {
match raw.as_array() {
Some(list) => Some(parse_service_data(list)?),
None => None,
}
}
}
None => bail!("Value 'service_data' missing."),
};
let manufacturer_data = match self.value.get("manufacturer_data") {
Some(raw) => {
if raw.is_null() {
None
} else {
match raw.as_array() {
Some(list) => Some(parse_manufacturer_data(list)?),
None => None,
}
}
}
None => bail!("Value 'manufacturer_data' missing."),
};
let uris = match self.value.get("uris") {
Some(raw) => {
if raw.is_null() {
None
} else {
match raw.as_array() {
Some(list) => {
let mut uri_list = Vec::new();
for item in list {
match item.as_str() {
Some(i) => uri_list.push(String::from(i)),
None => bail!("Expected URI string"),
}
}
Some(uri_list)
}
None => None,
}
}
}
None => bail!("Value 'uris' missing."),
};
Ok(AdvertisingData {
name,
appearance,
service_uuids,
service_data,
manufacturer_data,
uris: uris,
include_tx_power_level,
..Default::default()
})
}
}
impl TryInto<AdvertisingParameters> for FacadeArg {
type Error = Error;
fn try_into(self) -> Result<AdvertisingParameters, Self::Error> {
let advertising_data = match self.value.get("advertising_data") {
Some(adr) => Some(FacadeArg::new(adr.clone()).try_into()?),
None => bail!("Value 'advertising_data' missing."),
};
let scan_response = match self.value.get("scan_response") {
Some(scn) => {
if scn.is_null() {
None
} else {
Some(FacadeArg::new(scn.clone()).try_into()?)
}
}
None => None,
};
let conn_raw = self.value.get("connectable").ok_or(format_err!("Connectable missing"))?;
let connectable: bool = conn_raw.as_bool().unwrap_or(false);
let conn_opts = if connectable {
Some(ConnectionOptions {
bondable_mode: Some(true),
service_filter: None,
..Default::default()
})
} else {
None
};
Ok(AdvertisingParameters {
data: advertising_data,
scan_response: scan_response,
mode_hint: Some(AdvertisingModeHint::VeryFast),
connection_options: conn_opts,
..Default::default()
})
}
}
impl TryInto<Filter> for FacadeArg {
type Error = Error;
fn try_into(self) -> Result<Filter, Self::Error> {
let value = self.value.get("filter").ok_or(format_err!("Scan filter missing."))?.clone();
let name = value["name_substring"].as_str().map(String::from);
// For now, no scan profile, so default to empty Filter
Ok(Filter { name, ..Default::default() })
}
}
impl TryInto<ServiceInfo> for FacadeArg {
type Error = Error;
fn try_into(self) -> Result<ServiceInfo, Self::Error> {
let value = self.value.clone();
let id = parse_arg!(value, as_u64, "id")?;
let handle = ServiceHandle { value: id };
let primary = parse_arg!(value, as_bool, "primary")?;
let kind = if primary { ServiceKind::Primary } else { ServiceKind::Secondary };
let type_ = parse_arg!(value, as_str, "type")?.to_string();
let type_: FidlUuid =
Uuid::from_str(&type_).map_err(|_| format_err!("Invalid type"))?.into();
Ok(ServiceInfo {
handle: Some(handle),
kind: Some(kind),
type_: Some(type_),
// TODO(https://fxbug.dev/42169516): Add support for GATT characteristics and includes
..Default::default()
})
}
}