1 /*
2  * Copyright 2022 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 "content_control_id_keeper.h"
18 
19 #include <bluetooth/log.h>
20 
21 #include <algorithm>
22 #include <cstdint>
23 #include <map>
24 #include <memory>
25 #include <vector>
26 
27 #include "common/strings.h"
28 #include "le_audio_types.h"
29 
30 namespace {
31 
32 using bluetooth::common::ToString;
33 using bluetooth::le_audio::types::LeAudioContextType;
34 
35 }  // namespace
36 
37 namespace bluetooth::le_audio {
38 struct ccid_keeper {
39 public:
ccid_keeperbluetooth::le_audio::ccid_keeper40   ccid_keeper() {}
41 
~ccid_keeperbluetooth::le_audio::ccid_keeper42   ~ccid_keeper() {}
43 
SetCcidbluetooth::le_audio::ccid_keeper44   void SetCcid(types::LeAudioContextType context_type, int ccid) {
45     if (context_type >= LeAudioContextType::RFU) {
46       log::error("Unknownd context type {}", ToString(context_type));
47       return;
48     }
49 
50     log::debug("Ccid: {}, context type {}", ccid, ToString(context_type));
51     ccids_.insert_or_assign(context_type, ccid);
52   }
53 
SetCcidbluetooth::le_audio::ccid_keeper54   void SetCcid(const types::AudioContexts& contexts, int ccid) {
55     if (contexts.none()) {
56       RemoveCcid(ccid);
57       return;
58     }
59 
60     for (auto ctx : types::kLeAudioContextAllTypesArray) {
61       if (contexts.test(ctx)) {
62         SetCcid(ctx, ccid);
63       }
64     }
65   }
66 
RemoveCcidbluetooth::le_audio::ccid_keeper67   void RemoveCcid(int ccid) {
68     log::debug("Ccid: {}", ccid);
69 
70     auto iter = ccids_.begin();
71     while (iter != ccids_.end()) {
72       if (iter->second == ccid) {
73         iter = ccids_.erase(iter);
74       } else {
75         ++iter;
76       }
77     }
78   }
79 
GetCcidbluetooth::le_audio::ccid_keeper80   int GetCcid(types::LeAudioContextType context_type) const {
81     if (context_type >= LeAudioContextType::RFU) {
82       log::error("Unknownd context type {}", ToString(context_type));
83       return -1;
84     }
85 
86     if (ccids_.count(context_type) == 0) {
87       log::debug("No CCID for context {}", ToString(context_type));
88       return -1;
89     }
90 
91     return ccids_.at(context_type);
92   }
93 
94 private:
95   /* Ccid informations */
96   std::map<LeAudioContextType /* context */, int /*ccid */> ccids_;
97 };
98 
99 struct ContentControlIdKeeper::impl {
implbluetooth::le_audio::ContentControlIdKeeper::impl100   impl(const ContentControlIdKeeper& ccid_keeper) : ccid_keeper_(ccid_keeper) {}
101 
Startbluetooth::le_audio::ContentControlIdKeeper::impl102   void Start() {
103     log::assert_that(ccid_keeper_impl_ == nullptr, "assert failed: ccid_keeper_impl_ == nullptr");
104     ccid_keeper_impl_ = std::make_unique<ccid_keeper>();
105   }
106 
Stopbluetooth::le_audio::ContentControlIdKeeper::impl107   void Stop() {
108     log::assert_that(ccid_keeper_impl_ != nullptr, "assert failed: ccid_keeper_impl_ != nullptr");
109     ccid_keeper_impl_.reset();
110   }
111 
IsRunningbluetooth::le_audio::ContentControlIdKeeper::impl112   bool IsRunning() { return ccid_keeper_impl_ ? true : false; }
113 
114   const ContentControlIdKeeper& ccid_keeper_;
115   std::unique_ptr<ccid_keeper> ccid_keeper_impl_;
116 };
117 
ContentControlIdKeeper()118 ContentControlIdKeeper::ContentControlIdKeeper() : pimpl_(std::make_unique<impl>(*this)) {}
119 
Start()120 void ContentControlIdKeeper::Start() {
121   if (!pimpl_->IsRunning()) {
122     pimpl_->Start();
123   }
124 }
125 
Stop()126 void ContentControlIdKeeper::Stop() {
127   if (pimpl_->IsRunning()) {
128     pimpl_->Stop();
129   }
130 }
131 
GetCcid(types::LeAudioContextType context_type) const132 int ContentControlIdKeeper::GetCcid(types::LeAudioContextType context_type) const {
133   if (!pimpl_->IsRunning()) {
134     return -1;
135   }
136 
137   return pimpl_->ccid_keeper_impl_->GetCcid(context_type);
138 }
139 
SetCcid(types::LeAudioContextType context_type,int ccid)140 void ContentControlIdKeeper::SetCcid(types::LeAudioContextType context_type, int ccid) {
141   if (pimpl_->IsRunning()) {
142     if (context_type == types::LeAudioContextType::UNINITIALIZED) {
143       pimpl_->ccid_keeper_impl_->RemoveCcid(ccid);
144     } else {
145       pimpl_->ccid_keeper_impl_->SetCcid(context_type, ccid);
146     }
147   }
148 }
149 
SetCcid(const types::AudioContexts & contexts,int ccid)150 void ContentControlIdKeeper::SetCcid(const types::AudioContexts& contexts, int ccid) {
151   if (pimpl_->IsRunning()) {
152     pimpl_->ccid_keeper_impl_->SetCcid(contexts, ccid);
153   }
154 }
155 
GetAllCcids(const types::AudioContexts & contexts) const156 std::vector<uint8_t> ContentControlIdKeeper::GetAllCcids(
157         const types::AudioContexts& contexts) const {
158   std::vector<uint8_t> ccid_vec;
159   for (LeAudioContextType context : types::kLeAudioContextAllTypesArray) {
160     if (!contexts.test(context)) {
161       continue;
162     }
163     auto ccid = GetCcid(context);
164     if (ccid != -1) {
165       // Remove duplicates in case more than one context maps to the same CCID
166       if (std::find(ccid_vec.begin(), ccid_vec.end(), ccid) == ccid_vec.end()) {
167         ccid_vec.push_back(static_cast<uint8_t>(ccid));
168       }
169     }
170   }
171 
172   return ccid_vec;
173 }
174 
175 }  // namespace bluetooth::le_audio
176