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