1 /*
2 * Copyright (c) 2024-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #ifndef LOG_TAG
16 #define LOG_TAG "AudioUsbManager"
17 #endif
18
19 #include "audio_usb_manager.h"
20
21 #include <sstream>
22 #include <dirent.h>
23 #include <fstream>
24
25 #include "common_event_manager.h"
26 #include "common_event_support.h"
27 #include "usb_srv_client.h"
28
29 #include "audio_core_service.h"
30
31 namespace OHOS {
32 namespace AudioStandard {
33
34 using namespace USB;
35
ReadTextFile(const string & file)36 static string ReadTextFile(const string &file)
37 {
38 string ret;
39 ifstream fin;
40 fin.open(file.c_str(), ios::binary | ios::in);
41 if (fin) {
42 int val;
43 while ((val = fin.get()) != EOF) {
44 ret.push_back(static_cast<char>(val));
45 }
46 fin.close();
47 }
48 return ret;
49 }
50
FillSoundCard(const string & path,SoundCard & card)51 static void FillSoundCard(const string &path, SoundCard &card)
52 {
53 DIR *dir = opendir(path.c_str());
54 CHECK_AND_RETURN_RET(dir != nullptr,);
55 struct dirent *tmp;
56 while ((tmp = readdir(dir)) != nullptr) {
57 string file(tmp->d_name);
58 if (file == "usbbus") {
59 card.usbBus_ = ReadTextFile(path + "/" + file);
60 continue;
61 } else if (file.find("pcm", 0) == 0) {
62 if (file.back() == 'c') {
63 card.isCapturer_ = true;
64 } else if (file.back() == 'p') {
65 card.isPlayer_ = true;
66 }
67 }
68 }
69 closedir(dir);
70 }
71
Trim(const string & str)72 static string Trim(const string &str)
73 {
74 static const set<char> WHITE_SPACE{' ', '\r', '\n', '\t'};
75 size_t pos = 0;
76 size_t end = str.length();
77 for (; pos < end; pos++) {
78 if (WHITE_SPACE.find(str[pos]) == WHITE_SPACE.end()) {
79 break;
80 }
81 }
82 for (; end > pos; end--) {
83 if (WHITE_SPACE.find(str[end - 1]) == WHITE_SPACE.end()) {
84 break;
85 }
86 }
87 return str.substr(pos, end - pos);
88 }
89
GetUsbSoundCards()90 static vector<SoundCard> GetUsbSoundCards()
91 {
92 const string baseDir{"/proc/asound"};
93 const string card{"card"};
94 vector<SoundCard> soundCards;
95 DIR *dir = opendir(baseDir.c_str());
96 CHECK_AND_RETURN_RET(dir != nullptr, soundCards);
97 struct dirent *tmp;
98 int cardNum;
99 while ((tmp = readdir(dir)) != nullptr) {
100 string file(tmp->d_name);
101 if (file.length() <= card.length() || !(file.find(card, 0) == 0)) {continue;}
102 string cardNumStr = file.substr(card.length());
103 if (!StrToInt(cardNumStr, cardNum)) {continue;}
104 SoundCard card = {.cardNum_ = static_cast<uint32_t>(cardNum)};
105 FillSoundCard(baseDir + "/" + file, card);
106 if (card.usbBus_.empty()) {continue;}
107 soundCards.push_back(card);
108 }
109 closedir(dir);
110 return soundCards;
111 }
112
GetDeviceAddr(const uint32_t cardNum)113 static string GetDeviceAddr(const uint32_t cardNum)
114 {
115 ostringstream oss;
116 oss << "card=" << cardNum << ";device=0";
117 return oss.str();
118 }
119
GetUsbAddr(const SoundCard & card)120 static UsbAddr GetUsbAddr(const SoundCard &card)
121 {
122 size_t pos = card.usbBus_.find('/');
123 CHECK_AND_RETURN_RET_LOG(pos != string::npos, {}, "Error Parameter: card.usbbus");
124 int busNum;
125 int devAddr;
126 string busNumStr = Trim(card.usbBus_.substr(0, pos));
127 string devAddrStr = Trim(card.usbBus_.substr(pos + 1));
128 CHECK_AND_RETURN_RET_LOG(StrToInt(busNumStr, busNum) && StrToInt(devAddrStr, devAddr), {}, "StrToInt ERROR");
129 return {static_cast<uint8_t>(busNum), static_cast<uint8_t>(devAddr)};
130 }
131
IsAudioDevice(USB::UsbDevice & usbDevice)132 static bool IsAudioDevice(USB::UsbDevice &usbDevice)
133 {
134 for (auto &usbConfig : usbDevice.GetConfigs()) {
135 for (auto &usbInterface : usbConfig.GetInterfaces()) {
136 if (usbInterface.GetClass() == 1 && usbInterface.GetSubClass() == 1) {
137 return true;
138 }
139 }
140 }
141 return false;
142 }
143
SubscribeCommonEvent()144 static shared_ptr<AudioUsbManager::EventSubscriber> SubscribeCommonEvent()
145 {
146 EventFwk::MatchingSkills matchingSkills;
147 matchingSkills.AddEvent(EventFwk::CommonEventSupport::COMMON_EVENT_USB_DEVICE_ATTACHED);
148 matchingSkills.AddEvent(EventFwk::CommonEventSupport::COMMON_EVENT_USB_DEVICE_DETACHED);
149 EventFwk::CommonEventSubscribeInfo subscribeInfo(matchingSkills);
150 subscribeInfo.SetThreadMode(EventFwk::CommonEventSubscribeInfo::COMMON);
151 auto subscriber = make_shared<AudioUsbManager::EventSubscriber>(subscribeInfo);
152 auto ret = EventFwk::CommonEventManager::NewSubscribeCommonEvent(subscriber);
153 CHECK_AND_RETURN_RET_LOG(ret == ERR_OK, nullptr, "NewSubscribeCommonEvent Failed. ret=%{public}d", ret);
154 return subscriber;
155 }
156
NotSameSoundCard(const UsbAudioDevice & dev1,const UsbAudioDevice & dev2)157 static bool NotSameSoundCard(const UsbAudioDevice &dev1, const UsbAudioDevice &dev2)
158 {
159 return dev1.cardNum_ != dev2.cardNum_ || dev1.isCapturer_ != dev2.isCapturer_ || dev1.isPlayer_ != dev2.isPlayer_;
160 }
161
EncUsbAddr(const string & src)162 string EncUsbAddr(const string &src)
163 {
164 const string head("card=");
165 auto pos = src.find(';', head.length());
166 CHECK_AND_RETURN_RET_LOG(pos != string::npos, "", "Illegal usb address");
167 auto num = src.substr(head.length(), pos - head.length());
168 return string("c**") + num + "**";
169 }
170
GetInstance()171 AudioUsbManager &AudioUsbManager::GetInstance()
172 {
173 static AudioUsbManager sManager;
174 return sManager;
175 }
176
Init(std::shared_ptr<IDeviceStatusObserver> observer)177 void AudioUsbManager::Init(std::shared_ptr<IDeviceStatusObserver> observer)
178 {
179 lock_guard<mutex> lock(mutex_);
180 if (!initialized_) {
181 #ifdef DETECT_SOUNDBOX
182 AUDIO_INFO_LOG("Entry. DETECT_SOUNDBOX=true");
183 #else
184 AUDIO_INFO_LOG("Entry. DETECT_SOUNDBOX=false");
185 #endif
186 observer_ = observer;
187 RefreshUsbAudioDevices();
188 initialized_ = true;
189 }
190 }
191
Deinit()192 void AudioUsbManager::Deinit()
193 {
194 lock_guard<mutex> lock(mutex_);
195 if (initialized_) {
196 if (eventSubscriber_) {
197 EventFwk::CommonEventManager::NewUnSubscribeCommonEvent(eventSubscriber_);
198 eventSubscriber_.reset();
199 }
200 audioDevices_.clear();
201 soundCardMap_.clear();
202 initialized_ = false;
203 }
204 }
205
RefreshUsbAudioDevices()206 void AudioUsbManager::RefreshUsbAudioDevices()
207 {
208 vector<UsbAudioDevice> devices;
209 auto ret = GetUsbAudioDevices(devices);
210 CHECK_AND_RETURN_LOG(ret == SUCCESS, "GetUsbAudioDevices Failed. ret=%{public}d", ret);
211 vector<UsbAudioDevice> toAdd;
212 for (auto &device : devices) {
213 auto it = find_if(audioDevices_.cbegin(), audioDevices_.cend(), [&device](auto &item) {
214 return device.usbAddr_ == item.usbAddr_ && device.name_ == item.name_;
215 });
216 if (it == audioDevices_.cend()) {
217 toAdd.push_back(device);
218 }
219 }
220 CHECK_AND_RETURN_RET(!toAdd.empty(),);
221 soundCardMap_ = GetUsbSoundCardMap();
222 for (auto &device : toAdd) {
223 if (!FillUsbAudioDevice(device)) { continue; }
224 audioDevices_.push_back(device);
225 NotifyDevice(device, true);
226 }
227 }
228
SubscribeEvent()229 void AudioUsbManager::SubscribeEvent()
230 {
231 AUDIO_INFO_LOG("Entry");
232 CHECK_AND_RETURN_LOG(eventSubscriber_ == nullptr, "feventSubscriber_ already exists");
233 eventSubscriber_ = SubscribeCommonEvent();
234 lock_guard<mutex> lock(mutex_);
235 RefreshUsbAudioDevices();
236 }
237
NotifyDevice(const UsbAudioDevice & device,const bool isConnected)238 void AudioUsbManager::NotifyDevice(const UsbAudioDevice &device, const bool isConnected)
239 {
240 DeviceType devType = DeviceType::DEVICE_TYPE_USB_HEADSET;
241 string macAddress = GetDeviceAddr(device.cardNum_);
242 AudioStreamInfo streamInfo{};
243 string deviceName = device.name_ + "-" + to_string(device.cardNum_);
244 if (device.isPlayer_) {
245 AUDIO_INFO_LOG("Usb out, devType=%{public}d, isConnected=%{public}d, "
246 "macAddress=%{public}s, deviceName=%{public}s, role=%{public}d", devType, isConnected,
247 EncUsbAddr(macAddress).c_str(), deviceName.c_str(), DeviceRole::OUTPUT_DEVICE);
248 CHECK_AND_RETURN_LOG(observer_ != nullptr, "observer is null");
249 observer_->OnDeviceStatusUpdated(devType, isConnected, macAddress,
250 deviceName, streamInfo, OUTPUT_DEVICE, device.isCapturer_);
251 }
252 if (device.isCapturer_) {
253 AUDIO_INFO_LOG("Usb in, devType=%{public}d, isConnected=%{public}d, "
254 "macAddress=%{public}s, deviceName=%{public}s, role=%{public}d", devType, isConnected,
255 EncUsbAddr(macAddress).c_str(), deviceName.c_str(), DeviceRole::INPUT_DEVICE);
256 CHECK_AND_RETURN_LOG(observer_ != nullptr, "observer is null");
257 observer_->OnDeviceStatusUpdated(devType, isConnected, macAddress,
258 deviceName, streamInfo, INPUT_DEVICE, device.isPlayer_);
259 }
260 }
261
GetUsbSoundCardMap()262 map<UsbAddr, SoundCard> AudioUsbManager::GetUsbSoundCardMap()
263 {
264 map<UsbAddr, SoundCard> cardMap;
265 auto cardList = GetUsbSoundCards();
266 for (auto &card : cardList) {
267 cardMap[GetUsbAddr(card)] = card;
268 }
269 return cardMap;
270 }
271
GetUsbAudioDevices(vector<UsbAudioDevice> & result)272 int32_t AudioUsbManager::GetUsbAudioDevices(vector<UsbAudioDevice> &result)
273 {
274 vector<USB::UsbDevice> deviceList;
275 auto ret = UsbSrvClient::GetInstance().GetDevices(deviceList);
276 CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret,
277 "GetDevices failed. ret=%{public}d. size=%{public}zu", ret, deviceList.size());
278 for (auto &usbDevice : deviceList) {
279 if (IsAudioDevice(usbDevice)) {
280 result.push_back({
281 {usbDevice.GetBusNum(), usbDevice.GetDevAddr()},
282 usbDevice.GetProductName(),
283 });
284 }
285 }
286 return SUCCESS;
287 }
288
OnReceiveEvent(const EventFwk::CommonEventData & data)289 void AudioUsbManager::EventSubscriber::OnReceiveEvent(const EventFwk::CommonEventData &data)
290 {
291 string action = data.GetWant().GetAction();
292 AUDIO_INFO_LOG("OnReceiveEvent Entry. action=%{public}s", action.c_str());
293 bool isAttach{false};
294 if (action == EventFwk::CommonEventSupport::COMMON_EVENT_USB_DEVICE_ATTACHED) {
295 isAttach = true;
296 } else if (action == EventFwk::CommonEventSupport::COMMON_EVENT_USB_DEVICE_DETACHED) {
297 isAttach = false;
298 } else {
299 return;
300 }
301 string devStr = data.GetData();
302 CHECK_AND_RETURN_LOG(!devStr.empty(), "Error: data.GetData() returns empty");
303 auto *devJson = cJSON_Parse(devStr.c_str());
304 CHECK_AND_RETURN_LOG(devJson, "Create devJson error");
305 USB::UsbDevice usbDevice(devJson);
306 cJSON_Delete(devJson);
307 if (!IsAudioDevice(usbDevice)) {
308 return;
309 }
310 UsbAudioDevice device = {
311 {usbDevice.GetBusNum(), usbDevice.GetDevAddr()},
312 usbDevice.GetProductName()
313 };
314 AudioUsbManager::GetInstance().HandleAudioDeviceEvent(make_pair(device, isAttach));
315 }
316
HandleAudioDeviceEvent(pair<UsbAudioDevice,bool> && p)317 void AudioUsbManager::HandleAudioDeviceEvent(pair<UsbAudioDevice, bool> &&p)
318 {
319 AUDIO_INFO_LOG("Entry. deviceName=%{public}s, busNum=%{public}d, devAddr=%{public}d, isAttach=%{public}d",
320 p.first.name_.c_str(), p.first.usbAddr_.busNum_, p.first.usbAddr_.devAddr_, p.second);
321 lock_guard<mutex> lock(mutex_);
322 auto it = find(audioDevices_.begin(), audioDevices_.end(), p.first);
323 if (p.second) {
324 soundCardMap_ = GetUsbSoundCardMap();
325 CHECK_AND_RETURN_LOG(FillUsbAudioDevice(p.first), "Error: FillUsbAudioDevice Failed");
326 UpdateDevice(p.first, it);
327 NotifyDevice(p.first, true);
328 } else {
329 CHECK_AND_RETURN_LOG(it != audioDevices_.end(), "Detached Device does not exist");
330 NotifyDevice(*it, false);
331 audioDevices_.erase(it);
332 }
333 }
334
FillUsbAudioDevice(UsbAudioDevice & device)335 bool AudioUsbManager::FillUsbAudioDevice(UsbAudioDevice &device)
336 {
337 auto it = soundCardMap_.find(device.usbAddr_);
338 CHECK_AND_RETURN_RET_LOG(it != soundCardMap_.end(), false,
339 "Error: No sound card matches usb device[%{public}s]", device.name_.c_str());
340 auto &card = it->second;
341 CHECK_AND_RETURN_RET_LOG(card.isPlayer_ || card.isCapturer_, false,
342 "Error: Sound card[%{public}d] is not player and not capturer", card.cardNum_);
343 device.cardNum_ = card.cardNum_;
344 device.isCapturer_ = card.isCapturer_;
345 device.isPlayer_ = card.isPlayer_;
346 return true;
347 }
348
UpdateDevice(const UsbAudioDevice & dev,std::__wrap_iter<UsbAudioDevice * > & it)349 void AudioUsbManager::UpdateDevice(const UsbAudioDevice &dev, std::__wrap_iter<UsbAudioDevice *> &it)
350 {
351 if (it != audioDevices_.end()) {
352 if (NotSameSoundCard(dev, *it)) {
353 NotifyDevice(*it, false);
354 }
355 *it = dev;
356 } else {
357 audioDevices_.push_back(dev);
358 }
359 }
360 } // namespace AudioStandard
361 } // namespace OHOS