/****************************************************************************** * * Copyright 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ #include "storage_helper.h" #include #include "client_parser.h" #include "common/strings.h" #include "le_audio_types.h" #include "os/log.h" #include "stack/include/bt_types.h" using bluetooth::le_audio::types::hdl_pair; namespace bluetooth::le_audio { static constexpr uint8_t LEAUDIO_PACS_STORAGE_CURRENT_LAYOUT_MAGIC = 0x00; static constexpr uint8_t LEAUDIO_ASE_STORAGE_CURRENT_LAYOUT_MAGIC = 0x00; static constexpr uint8_t LEAUDIO_HANDLES_STORAGE_CURRENT_LAYOUT_MAGIC = 0x00; static constexpr uint8_t LEAUDIO_CODEC_ID_SZ = 5; static constexpr size_t LEAUDIO_STORAGE_MAGIC_SZ = sizeof(uint8_t) /* magic is always uint8_t */; static constexpr size_t LEAUDIO_STORAGE_HEADER_WITH_ENTRIES_SZ = LEAUDIO_STORAGE_MAGIC_SZ + sizeof(uint8_t); /* num_of_entries */ static constexpr size_t LEAUDIO_PACS_ENTRY_HDR_SZ = sizeof(uint16_t) /*handle*/ + sizeof(uint16_t) /*ccc handle*/ + sizeof(uint8_t) /* number of pack records in single characteristic */; static constexpr size_t LEAUDIO_PACS_ENTRY_SZ = sizeof(uint8_t) /* size of single pac record */ + LEAUDIO_CODEC_ID_SZ /*codec id*/ + sizeof(uint8_t) /*codec capabilities len*/ + sizeof(uint8_t) /*metadata len*/; static constexpr size_t LEAUDIO_ASES_ENTRY_SZ = sizeof(uint16_t) /*handle*/ + sizeof(uint16_t) /*ccc handle*/ + sizeof(uint8_t) /*direction*/ + sizeof(uint8_t) /*ase id*/; static constexpr size_t LEAUDIO_STORAGE_HANDLES_ENTRIES_SZ = LEAUDIO_STORAGE_MAGIC_SZ + sizeof(uint16_t) /*control point handle*/ + sizeof(uint16_t) /*ccc handle*/ + sizeof(uint16_t) /*sink audio location handle*/ + sizeof(uint16_t) /*ccc handle*/ + sizeof(uint16_t) /*source audio location handle*/ + sizeof(uint16_t) /*ccc handle*/ + sizeof(uint16_t) /*supported context type handle*/ + sizeof(uint16_t) /*ccc handle*/ + sizeof(uint16_t) /*available context type handle*/ + sizeof(uint16_t) /*ccc handle*/ + sizeof(uint16_t) /* tmas handle */; bool serializePacs( const bluetooth::le_audio::types::PublishedAudioCapabilities& pacs, std::vector& out) { auto num_of_pacs = pacs.size(); if (num_of_pacs == 0 || (num_of_pacs > std::numeric_limits::max())) { log::warn("No pacs available"); return false; } /* Calculate the total size */ auto pac_bin_size = LEAUDIO_STORAGE_HEADER_WITH_ENTRIES_SZ; for (auto pac_tuple : pacs) { auto& pac_recs = std::get<1>(pac_tuple); pac_bin_size += LEAUDIO_PACS_ENTRY_HDR_SZ; for (const auto& pac : pac_recs) { pac_bin_size += LEAUDIO_PACS_ENTRY_SZ; pac_bin_size += pac.metadata.size(); pac_bin_size += pac.codec_spec_caps_raw.size(); } } out.resize(pac_bin_size); auto* ptr = out.data(); /* header */ UINT8_TO_STREAM(ptr, LEAUDIO_PACS_STORAGE_CURRENT_LAYOUT_MAGIC); UINT8_TO_STREAM(ptr, num_of_pacs); /* pacs entries */ for (auto pac_tuple : pacs) { auto& pac_recs = std::get<1>(pac_tuple); uint16_t handle = std::get<0>(pac_tuple).val_hdl; uint16_t ccc_handle = std::get<0>(pac_tuple).ccc_hdl; UINT16_TO_STREAM(ptr, handle); UINT16_TO_STREAM(ptr, ccc_handle); UINT8_TO_STREAM(ptr, pac_recs.size()); log::verbose("Handle: 0x{:04x}, ccc handle: 0x{:04x}, pac count: {}", handle, ccc_handle, static_cast(pac_recs.size())); for (const auto& pac : pac_recs) { /* Pac len */ auto pac_len = LEAUDIO_PACS_ENTRY_SZ + pac.codec_spec_caps_raw.size() + pac.metadata.size(); log::verbose("Pac size {}", static_cast(pac_len)); UINT8_TO_STREAM(ptr, pac_len - 1 /* Minus size */); /* Codec ID*/ UINT8_TO_STREAM(ptr, pac.codec_id.coding_format); UINT16_TO_STREAM(ptr, pac.codec_id.vendor_company_id); UINT16_TO_STREAM(ptr, pac.codec_id.vendor_codec_id); /* Codec caps */ log::verbose("Codec capability size {}", static_cast(pac.codec_spec_caps_raw.size())); UINT8_TO_STREAM(ptr, pac.codec_spec_caps_raw.size()); if (pac.codec_spec_caps_raw.size() > 0) { ARRAY_TO_STREAM(ptr, pac.codec_spec_caps_raw.data(), static_cast(pac.codec_spec_caps_raw.size())); } /* Metadata */ log::verbose("Metadata size {}", static_cast(pac.metadata.size())); UINT8_TO_STREAM(ptr, pac.metadata.size()); if (pac.metadata.size() > 0) { ARRAY_TO_STREAM(ptr, pac.metadata.data(), (int)pac.metadata.size()); } } } return true; } bool SerializeSinkPacs(const bluetooth::le_audio::LeAudioDevice* leAudioDevice, std::vector& out) { if (leAudioDevice == nullptr) { log::warn("Skipping unknown device"); return false; } log::verbose("Device {}, num of PAC characteristics: {}", leAudioDevice->address_, static_cast(leAudioDevice->snk_pacs_.size())); return serializePacs(leAudioDevice->snk_pacs_, out); } bool SerializeSourcePacs( const bluetooth::le_audio::LeAudioDevice* leAudioDevice, std::vector& out) { if (leAudioDevice == nullptr) { log::warn("Skipping unknown device"); return false; } log::verbose("Device {}, num of PAC characteristics: {}", leAudioDevice->address_, static_cast(leAudioDevice->src_pacs_.size())); return serializePacs(leAudioDevice->src_pacs_, out); } bool deserializePacs(LeAudioDevice* leAudioDevice, types::PublishedAudioCapabilities& pacs_db, const std::vector& in) { if (in.size() < LEAUDIO_STORAGE_HEADER_WITH_ENTRIES_SZ + LEAUDIO_PACS_ENTRY_SZ) { log::warn("There is not single PACS stored"); return false; } auto* ptr = in.data(); uint8_t magic; STREAM_TO_UINT8(magic, ptr); if (magic != LEAUDIO_PACS_STORAGE_CURRENT_LAYOUT_MAGIC) { log::error("Invalid magic ({}!={}) for device {}", magic, LEAUDIO_PACS_STORAGE_CURRENT_LAYOUT_MAGIC, leAudioDevice->address_); return false; } uint8_t num_of_pacs_chars; STREAM_TO_UINT8(num_of_pacs_chars, ptr); if (in.size() < LEAUDIO_STORAGE_HEADER_WITH_ENTRIES_SZ + (num_of_pacs_chars * LEAUDIO_PACS_ENTRY_SZ)) { log::error("Invalid persistent storage data for device {}", leAudioDevice->address_); return false; } /* pacs entries */ while (num_of_pacs_chars--) { struct hdl_pair hdl_pair; uint8_t pac_count; STREAM_TO_UINT16(hdl_pair.val_hdl, ptr); STREAM_TO_UINT16(hdl_pair.ccc_hdl, ptr); STREAM_TO_UINT8(pac_count, ptr); log::verbose("Handle: 0x{:04x}, ccc handle: 0x{:04x}, pac_count: {}", hdl_pair.val_hdl, hdl_pair.ccc_hdl, pac_count); pacs_db.push_back(std::make_tuple( hdl_pair, std::vector())); auto hdl = hdl_pair.val_hdl; auto pac_tuple_iter = std::find_if( pacs_db.begin(), pacs_db.end(), [&hdl](auto& pac_ent) { return std::get<0>(pac_ent).val_hdl == hdl; }); std::vector pac_recs; while (pac_count--) { uint8_t pac_len; STREAM_TO_UINT8(pac_len, ptr); log::verbose("Pac len {}", pac_len); if (client_parser::pacs::ParseSinglePac(pac_recs, pac_len, ptr) < 0) { log::error("Cannot parse stored PACs (impossible)"); return false; } ptr += pac_len; } leAudioDevice->RegisterPACs(&std::get<1>(*pac_tuple_iter), &pac_recs); } return true; } bool DeserializeSinkPacs(bluetooth::le_audio::LeAudioDevice* leAudioDevice, const std::vector& in) { log::verbose(""); if (leAudioDevice == nullptr) { log::warn("Skipping unknown device"); return false; } return deserializePacs(leAudioDevice, leAudioDevice->snk_pacs_, in); } bool DeserializeSourcePacs(bluetooth::le_audio::LeAudioDevice* leAudioDevice, const std::vector& in) { log::verbose(""); if (leAudioDevice == nullptr) { log::warn("Skipping unknown device"); return false; } return deserializePacs(leAudioDevice, leAudioDevice->src_pacs_, in); } bool SerializeAses(const bluetooth::le_audio::LeAudioDevice* leAudioDevice, std::vector& out) { if (leAudioDevice == nullptr) { log::warn("Skipping unknown device"); return false; } auto num_of_ases = leAudioDevice->ases_.size(); log::debug("device: {}, number of ases {}", leAudioDevice->address_, static_cast(num_of_ases)); if (num_of_ases == 0 || (num_of_ases > std::numeric_limits::max())) { log::warn("No ases available for device {}", leAudioDevice->address_); return false; } /* Calculate the total size */ auto ases_bin_size = LEAUDIO_STORAGE_HEADER_WITH_ENTRIES_SZ + num_of_ases * LEAUDIO_ASES_ENTRY_SZ; out.resize(ases_bin_size); auto* ptr = out.data(); /* header */ UINT8_TO_STREAM(ptr, LEAUDIO_ASE_STORAGE_CURRENT_LAYOUT_MAGIC); UINT8_TO_STREAM(ptr, num_of_ases); /* pacs entries */ for (const auto& ase : leAudioDevice->ases_) { log::verbose( "Storing ASE ID: {}, direction {}, handle 0x{:04x}, ccc_handle " "0x{:04x}", ase.id, ase.direction == bluetooth::le_audio::types::kLeAudioDirectionSink ? "sink " : "source", ase.hdls.val_hdl, ase.hdls.ccc_hdl); UINT16_TO_STREAM(ptr, ase.hdls.val_hdl); UINT16_TO_STREAM(ptr, ase.hdls.ccc_hdl); UINT8_TO_STREAM(ptr, ase.id); UINT8_TO_STREAM(ptr, ase.direction); } return true; } bool DeserializeAses(bluetooth::le_audio::LeAudioDevice* leAudioDevice, const std::vector& in) { if (leAudioDevice == nullptr) { log::warn("Skipping unknown device"); return false; } if (in.size() < LEAUDIO_STORAGE_HEADER_WITH_ENTRIES_SZ + LEAUDIO_ASES_ENTRY_SZ) { log::warn("There is not single ASE stored for device {}", leAudioDevice->address_); return false; } auto* ptr = in.data(); uint8_t magic; STREAM_TO_UINT8(magic, ptr); if (magic != LEAUDIO_ASE_STORAGE_CURRENT_LAYOUT_MAGIC) { log::error("Invalid magic ({}!={}", magic, LEAUDIO_PACS_STORAGE_CURRENT_LAYOUT_MAGIC); return false; } uint8_t num_of_ases; STREAM_TO_UINT8(num_of_ases, ptr); if (in.size() < LEAUDIO_STORAGE_HEADER_WITH_ENTRIES_SZ + (num_of_ases * LEAUDIO_ASES_ENTRY_SZ)) { log::error("Invalid persistent storage data for device {}", leAudioDevice->address_); return false; } log::debug("Loading {} Ases for device {}", num_of_ases, leAudioDevice->address_); /* sets entries */ while (num_of_ases--) { uint16_t handle; uint16_t ccc_handle; uint8_t direction; uint8_t ase_id; STREAM_TO_UINT16(handle, ptr); STREAM_TO_UINT16(ccc_handle, ptr); STREAM_TO_UINT8(ase_id, ptr); STREAM_TO_UINT8(direction, ptr); leAudioDevice->ases_.emplace_back(handle, ccc_handle, direction, ase_id); log::verbose( "Loading ASE ID: {}, direction {}, handle 0x{:04x}, ccc_handle " "0x{:04x}", ase_id, direction == bluetooth::le_audio::types::kLeAudioDirectionSink ? "sink " : "source", handle, ccc_handle); } return true; } bool SerializeHandles(const LeAudioDevice* leAudioDevice, std::vector& out) { if (leAudioDevice == nullptr) { log::warn("Skipping unknown device"); return false; } /* Calculate the total size */ out.resize(LEAUDIO_STORAGE_HANDLES_ENTRIES_SZ); auto* ptr = out.data(); /* header */ UINT8_TO_STREAM(ptr, LEAUDIO_HANDLES_STORAGE_CURRENT_LAYOUT_MAGIC); if (leAudioDevice->ctp_hdls_.val_hdl == 0 || leAudioDevice->ctp_hdls_.ccc_hdl == 0) { log::warn("Invalid control point handles for device {}", leAudioDevice->address_); return false; } UINT16_TO_STREAM(ptr, leAudioDevice->ctp_hdls_.val_hdl); UINT16_TO_STREAM(ptr, leAudioDevice->ctp_hdls_.ccc_hdl); UINT16_TO_STREAM(ptr, leAudioDevice->snk_audio_locations_hdls_.val_hdl); UINT16_TO_STREAM(ptr, leAudioDevice->snk_audio_locations_hdls_.ccc_hdl); UINT16_TO_STREAM(ptr, leAudioDevice->src_audio_locations_hdls_.val_hdl); UINT16_TO_STREAM(ptr, leAudioDevice->src_audio_locations_hdls_.ccc_hdl); UINT16_TO_STREAM(ptr, leAudioDevice->audio_supp_cont_hdls_.val_hdl); UINT16_TO_STREAM(ptr, leAudioDevice->audio_supp_cont_hdls_.ccc_hdl); UINT16_TO_STREAM(ptr, leAudioDevice->audio_avail_hdls_.val_hdl); UINT16_TO_STREAM(ptr, leAudioDevice->audio_avail_hdls_.ccc_hdl); UINT16_TO_STREAM(ptr, leAudioDevice->tmap_role_hdl_); return true; } bool DeserializeHandles(LeAudioDevice* leAudioDevice, const std::vector& in) { if (leAudioDevice == nullptr) { log::warn("Skipping unknown device"); return false; } if (in.size() != LEAUDIO_STORAGE_HANDLES_ENTRIES_SZ) { log::warn("There is not single ASE stored for device {}", leAudioDevice->address_); return false; } auto* ptr = in.data(); uint8_t magic; STREAM_TO_UINT8(magic, ptr); if (magic != LEAUDIO_HANDLES_STORAGE_CURRENT_LAYOUT_MAGIC) { log::error("Invalid magic ({}!={}) for device {}", magic, LEAUDIO_PACS_STORAGE_CURRENT_LAYOUT_MAGIC, leAudioDevice->address_); return false; } STREAM_TO_UINT16(leAudioDevice->ctp_hdls_.val_hdl, ptr); STREAM_TO_UINT16(leAudioDevice->ctp_hdls_.ccc_hdl, ptr); log::verbose("ctp.val_hdl: 0x{:04x}, ctp.ccc_hdl: 0x{:04x}", leAudioDevice->ctp_hdls_.val_hdl, leAudioDevice->ctp_hdls_.ccc_hdl); STREAM_TO_UINT16(leAudioDevice->snk_audio_locations_hdls_.val_hdl, ptr); STREAM_TO_UINT16(leAudioDevice->snk_audio_locations_hdls_.ccc_hdl, ptr); log::verbose( "snk_audio_locations_hdls_.val_hdl: " "0x{:04x},snk_audio_locations_hdls_.ccc_hdl: 0x{:04x}", leAudioDevice->snk_audio_locations_hdls_.val_hdl, leAudioDevice->snk_audio_locations_hdls_.ccc_hdl); STREAM_TO_UINT16(leAudioDevice->src_audio_locations_hdls_.val_hdl, ptr); STREAM_TO_UINT16(leAudioDevice->src_audio_locations_hdls_.ccc_hdl, ptr); log::verbose( "src_audio_locations_hdls_.val_hdl: " "0x{:04x},src_audio_locations_hdls_.ccc_hdl: 0x{:04x}", leAudioDevice->src_audio_locations_hdls_.val_hdl, leAudioDevice->src_audio_locations_hdls_.ccc_hdl); STREAM_TO_UINT16(leAudioDevice->audio_supp_cont_hdls_.val_hdl, ptr); STREAM_TO_UINT16(leAudioDevice->audio_supp_cont_hdls_.ccc_hdl, ptr); log::verbose( "audio_supp_cont_hdls_.val_hdl: 0x{:04x},audio_supp_cont_hdls_.ccc_hdl: " "0x{:04x}", leAudioDevice->audio_supp_cont_hdls_.val_hdl, leAudioDevice->audio_supp_cont_hdls_.ccc_hdl); STREAM_TO_UINT16(leAudioDevice->audio_avail_hdls_.val_hdl, ptr); STREAM_TO_UINT16(leAudioDevice->audio_avail_hdls_.ccc_hdl, ptr); log::verbose( "audio_avail_hdls_.val_hdl: 0x{:04x},audio_avail_hdls_.ccc_hdl: 0x{:04x}", leAudioDevice->audio_avail_hdls_.val_hdl, leAudioDevice->audio_avail_hdls_.ccc_hdl); STREAM_TO_UINT16(leAudioDevice->tmap_role_hdl_, ptr); log::verbose("tmap_role_hdl_: 0x{:04x}", leAudioDevice->tmap_role_hdl_); leAudioDevice->known_service_handles_ = true; return true; } } // namespace bluetooth::le_audio