1 /*
2 * Copyright 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "avrcp_sdp_records.h"
18
19 #include <bluetooth/log.h>
20
21 #include "bta/sys/bta_sys.h"
22 #include "stack/include/avrc_api.h"
23 #include "stack/include/bt_types.h"
24 #include "stack/include/bt_uuid16.h"
25 #include "stack/include/sdp_api.h"
26
27 using namespace bluetooth::legacy::stack::sdp;
28
29 namespace bluetooth::avrcp {
30
AddRecord(const AvrcpSdpRecord & sdp_record_reference,uint16_t & request_id,const bool add_sys_uid)31 uint16_t AvrcSdpRecordHelper::AddRecord(const AvrcpSdpRecord& sdp_record_reference,
32 uint16_t& request_id, const bool add_sys_uid) {
33 uint16_t result = AVRC_FAIL;
34 if (!sdp_record_request_map_.contains(request_id)) {
35 request_id = ++request_id_counter_;
36 log::debug("Generated request id: {}", request_id);
37 }
38 sdp_record_request_map_.insert({request_id_counter_, sdp_record_reference});
39 AvrcpSdpRecord merged_sdp_records;
40 MergeSdpRecords(merged_sdp_records);
41 if (sdp_record_handle_ == RECORD_NOT_ASSIGNED) {
42 log::debug("Adding a new record for {} with uuid 0x{:x} and categories as 0x{:x}",
43 merged_sdp_records.service_name, merged_sdp_records.service_uuid,
44 merged_sdp_records.categories);
45 sdp_record_handle_ = get_legacy_stack_sdp_api()->handle.SDP_CreateRecord();
46 if (add_sys_uid) {
47 bta_sys_add_uuid(merged_sdp_records.service_uuid);
48 }
49 result =
50 AVRC_AddRecord(merged_sdp_records.service_uuid, merged_sdp_records.service_name.c_str(),
51 merged_sdp_records.provider_name.c_str(), merged_sdp_records.categories,
52 sdp_record_handle_, merged_sdp_records.browse_supported,
53 merged_sdp_records.profile_version, merged_sdp_records.cover_art_psm);
54 } else {
55 // SDP record is already present. Update the existing SDP record with the
56 // new supported categories.
57 result = UpdateRecord(merged_sdp_records.categories);
58 }
59
60 return result;
61 }
62
UpdateRecord(const uint16_t new_categories)63 uint16_t AvrcSdpRecordHelper::UpdateRecord(const uint16_t new_categories) {
64 log::debug("Categories set to 0x{:x}", new_categories);
65 uint8_t temp[sizeof(uint16_t)], *p;
66 p = temp;
67 UINT16_TO_BE_STREAM(p, new_categories);
68 return get_legacy_stack_sdp_api()->handle.SDP_AddAttribute(
69 sdp_record_handle_, ATTR_ID_SUPPORTED_FEATURES, UINT_DESC_TYPE, sizeof(temp),
70 (uint8_t*)temp)
71 ? AVRC_SUCCESS
72 : AVRC_FAIL;
73 }
74
RemoveRecord(const uint16_t request_id)75 uint16_t AvrcSdpRecordHelper::RemoveRecord(const uint16_t request_id) {
76 if (!sdp_record_request_map_.contains(request_id)) {
77 log::warn("Trying to remove request id: {} that doesn't exist", request_id);
78 return AVRC_FAIL;
79 }
80 const auto& sdp_record_request_pair = sdp_record_request_map_.find(request_id);
81 const auto service_uuid = sdp_record_request_pair->second.service_uuid;
82 sdp_record_request_map_.erase(request_id);
83 AvrcpSdpRecord merged_sdp_records;
84 MergeSdpRecords(merged_sdp_records);
85 const uint16_t categories = merged_sdp_records.categories;
86 log::info("Categories after removing the request_id {} : 0x{:x} for service uuid 0x{:x}",
87 request_id, categories, service_uuid);
88 if (sdp_record_handle_ != RECORD_NOT_ASSIGNED) {
89 if (categories) {
90 uint8_t temp[sizeof(uint16_t)], *p;
91 p = temp;
92 UINT16_TO_BE_STREAM(p, categories);
93 return get_legacy_stack_sdp_api()->handle.SDP_AddAttribute(
94 sdp_record_handle_, ATTR_ID_SUPPORTED_FEATURES, UINT_DESC_TYPE, sizeof(temp),
95 (uint8_t*)temp)
96 ? AVRC_SUCCESS
97 : AVRC_FAIL;
98 } else {
99 log::info("Removing the record for service uuid 0x{:x}", service_uuid);
100 bta_sys_remove_uuid(service_uuid);
101 auto result = AVRC_RemoveRecord(sdp_record_handle_);
102 sdp_record_handle_ = RECORD_NOT_ASSIGNED;
103 return result;
104 }
105 }
106 // Nothing to remove.
107 return AVRC_SUCCESS;
108 }
109
MergeSdpRecords(AvrcpSdpRecord & merged_sdp_record_reference)110 bool AvrcSdpRecordHelper::MergeSdpRecords(AvrcpSdpRecord& merged_sdp_record_reference) {
111 if (sdp_record_request_map_.empty()) {
112 return false;
113 }
114 int i = 0;
115 merged_sdp_record_reference = sdp_record_request_map_.begin()->second;
116 for (const auto& sdp_record_request_pair : sdp_record_request_map_) {
117 if (i++ == 0) {
118 continue;
119 }
120 const auto& sdp_record_value = sdp_record_request_pair.second;
121 merged_sdp_record_reference.AddToExistingCategories(sdp_record_value.categories);
122 // Register the highest profile version.
123 if (sdp_record_value.profile_version > merged_sdp_record_reference.profile_version) {
124 merged_sdp_record_reference.profile_version = sdp_record_value.profile_version;
125 }
126 if (sdp_record_value.cover_art_psm != 0) {
127 merged_sdp_record_reference.cover_art_psm = sdp_record_value.cover_art_psm;
128 }
129 // Enable browse supported if any of the requests had browsing enabled.
130 merged_sdp_record_reference.browse_supported |= sdp_record_value.browse_supported;
131 }
132 return true;
133 }
EnableCovertArt(uint16_t cover_art_psm,uint16_t request_id)134 uint16_t TargetAvrcSdpRecordHelper::EnableCovertArt(uint16_t cover_art_psm, uint16_t request_id) {
135 log::debug("Adding cover art support for request id {}", request_id);
136 AVRC_RemoveRecord(sdp_record_handle_);
137 sdp_record_handle_ = RECORD_NOT_ASSIGNED;
138 if (sdp_record_request_map_.contains(request_id)) {
139 auto& sdp_record_reference = sdp_record_request_map_.find(request_id)->second;
140 sdp_record_reference.cover_art_psm = cover_art_psm;
141 sdp_record_reference.AddToExistingCategories(AVRC_SUPF_TG_PLAYER_COVER_ART);
142 return AddRecord(sdp_record_reference, request_id, false);
143 }
144 return AVRC_FAIL;
145 }
146
DisableCovertArt(uint16_t request_id)147 uint16_t TargetAvrcSdpRecordHelper::DisableCovertArt(uint16_t request_id) {
148 log::debug("Disabling cover art support for request id {}", request_id);
149 AVRC_RemoveRecord(sdp_record_handle_);
150 sdp_record_handle_ = RECORD_NOT_ASSIGNED;
151 if (sdp_record_request_map_.contains(request_id)) {
152 auto& sdp_record_reference = sdp_record_request_map_.find(request_id)->second;
153 sdp_record_reference.cover_art_psm = 0;
154 sdp_record_reference.RemoveCategory(AVRC_SUPF_TG_PLAYER_COVER_ART);
155 return AddRecord(sdp_record_reference, request_id, false);
156 }
157 return AVRC_FAIL;
158 }
159
UpdateRecord(const uint16_t new_categories)160 uint16_t ControlAvrcSdpRecordHelper::UpdateRecord(const uint16_t new_categories) {
161 bool result = AvrcSdpRecordHelper::UpdateRecord(new_categories) ? AVRC_SUCCESS : AVRC_FAIL;
162 AvrcpSdpRecord merged_sdp_records;
163 const bool is_request_available = MergeSdpRecords(merged_sdp_records);
164 if (is_request_available) {
165 if (merged_sdp_records.profile_version > AVRC_REV_1_3) {
166 uint16_t class_list[2], count = 1;
167 class_list[0] = merged_sdp_records.service_uuid;
168 if (merged_sdp_records.service_uuid == UUID_SERVCLASS_AV_REMOTE_CONTROL) {
169 class_list[1] = UUID_SERVCLASS_AV_REM_CTRL_CONTROL;
170 count = 2;
171 }
172 result &= get_legacy_stack_sdp_api()->handle.SDP_AddServiceClassIdList(sdp_record_handle_,
173 count, class_list);
174 }
175 result &= get_legacy_stack_sdp_api()->handle.SDP_AddProfileDescriptorList(
176 sdp_record_handle_, merged_sdp_records.service_uuid,
177 merged_sdp_records.profile_version);
178 }
179 return result ? AVRC_SUCCESS : AVRC_FAIL;
180 }
181
EnableCovertArt(uint16_t,const uint16_t)182 uint16_t ControlAvrcSdpRecordHelper::EnableCovertArt(uint16_t /*cover_art_psm*/,
183 const uint16_t /*request_id*/) {
184 log::warn(
185 "Enabling cover art support dynamically is not supported for service "
186 "UUID {:x}",
187 UUID_SERVCLASS_AV_REM_CTRL_CONTROL);
188 return AVRC_FAIL;
189 }
190
DisableCovertArt(const uint16_t)191 uint16_t ControlAvrcSdpRecordHelper::DisableCovertArt(const uint16_t /*request_id*/) {
192 log::warn(
193 "Disabling cover art support dynamically is not supported for service "
194 "UUID {:x}",
195 UUID_SERVCLASS_AV_REM_CTRL_CONTROL);
196 return AVRC_FAIL;
197 }
198
199 } // namespace bluetooth::avrcp
200