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