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