1 /*
2 * Copyright (c) 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 "AudioInterruptZone"
17 #endif
18
19 #include "audio_interrupt_zone.h"
20 #include "audio_interrupt_service.h"
21
22 namespace OHOS {
23 namespace AudioStandard {
24 static constexpr uid_t UID_AUDIO = 1041;
25
AudioInterruptZoneManager()26 AudioInterruptZoneManager::AudioInterruptZoneManager()
27 {}
28
~AudioInterruptZoneManager()29 AudioInterruptZoneManager::~AudioInterruptZoneManager()
30 {
31 service_ = nullptr;
32 }
33
InitService(AudioInterruptService * service)34 void AudioInterruptZoneManager::InitService(AudioInterruptService *service)
35 {
36 service_ = service;
37 }
38
GetAudioFocusInfoList(const int32_t zoneId,AudioFocusList & focusInfoList)39 int32_t AudioInterruptZoneManager::GetAudioFocusInfoList(const int32_t zoneId,
40 AudioFocusList &focusInfoList)
41 {
42 CHECK_AND_RETURN_RET_LOG(service_ != nullptr, ERR_INVALID_PARAM, "interrupt service is nullptr");
43 auto itZone = service_->zonesMap_.find(zoneId);
44 focusInfoList = {};
45
46 CHECK_AND_RETURN_RET_LOG(itZone != service_->zonesMap_.end(), SUCCESS, "zone %{public}d is not found", zoneId);
47 CHECK_AND_RETURN_RET_LOG(itZone->second != nullptr, SUCCESS, "zone %{public}d focusInfoList is null", zoneId);
48
49 focusInfoList = itZone->second->audioFocusInfoList;
50 return SUCCESS;
51 }
52
GetAudioFocusInfoList(const int32_t zoneId,const std::string & deviceTag,AudioFocusList & focusInfoList)53 int32_t AudioInterruptZoneManager::GetAudioFocusInfoList(const int32_t zoneId,
54 const std::string &deviceTag, AudioFocusList &focusInfoList)
55 {
56 CHECK_AND_RETURN_RET_LOG(service_ != nullptr, ERR_INVALID_PARAM, "interrupt service is nullptr");
57 auto itZone = service_->zonesMap_.find(zoneId);
58 focusInfoList = {};
59
60 CHECK_AND_RETURN_RET_LOG(itZone != service_->zonesMap_.end(), SUCCESS, "zone %{public}d is not found", zoneId);
61 CHECK_AND_RETURN_RET_LOG(itZone->second != nullptr, SUCCESS, "zone %{public}d focusInfoList is null", zoneId);
62
63 for (const auto &focus : itZone->second->audioFocusInfoList) {
64 CHECK_AND_CONTINUE(focus.first.deviceTag == deviceTag);
65 focusInfoList.emplace_back(focus);
66 }
67 return SUCCESS;
68 }
69
CreateAudioInterruptZone(const int32_t zoneId,AudioZoneFocusStrategy focusStrategy,bool checkPermission)70 int32_t AudioInterruptZoneManager::CreateAudioInterruptZone(const int32_t zoneId,
71 AudioZoneFocusStrategy focusStrategy, bool checkPermission)
72 {
73 CHECK_AND_RETURN_RET_LOG(service_ != nullptr, ERR_INVALID_PARAM, "interrupt service is nullptr");
74 CHECK_AND_RETURN_RET_LOG(zoneId >= 0, ERR_INVALID_PARAM, "zone id is invalid");
75 if (checkPermission) {
76 CHECK_AND_RETURN_RET_LOG(CheckAudioInterruptZonePermission(), ERR_INVALID_PARAM,
77 "audio zone permission deny");
78 }
79
80 auto &tempMap = service_->zonesMap_;
81 CHECK_AND_RETURN_RET_LOG(tempMap.find(zoneId) == tempMap.end() || tempMap[zoneId] == nullptr,
82 ERR_INVALID_PARAM, "zone %{public}d already exist", zoneId);
83
84 std::shared_ptr<AudioInterruptZone> zone = std::make_shared<AudioInterruptZone>();
85 CHECK_AND_RETURN_RET_LOG(zone != nullptr, ERROR, "zone %{public}d create interrupt failed", zoneId);
86
87 zone->zoneId = zoneId;
88 zone->focusStrategy = focusStrategy;
89 tempMap[zoneId] = zone;
90 return SUCCESS;
91 }
92
ReleaseAudioInterruptZone(const int32_t zoneId,GetZoneIdFunc func)93 int32_t AudioInterruptZoneManager::ReleaseAudioInterruptZone(const int32_t zoneId, GetZoneIdFunc func)
94 {
95 CHECK_AND_RETURN_RET_LOG(service_ != nullptr, ERR_INVALID_PARAM, "interrupt service is nullptr");
96 CHECK_AND_RETURN_RET_LOG(func != nullptr, ERR_INVALID_PARAM, "zone id is invalid");
97 CHECK_AND_RETURN_RET_LOG(CheckAudioInterruptZonePermission(), ERR_INVALID_PARAM,
98 "audio zone permission deny");
99
100 auto &tempMap = service_->zonesMap_;
101 CHECK_AND_RETURN_RET_LOG(tempMap.find(zoneId) != tempMap.end() && tempMap[zoneId] != nullptr,
102 ERR_INVALID_PARAM, "zone %{public}d not exist", zoneId);
103 CHECK_AND_RETURN_RET_LOG(zoneId != AudioInterruptService::ZONEID_DEFAULT,
104 ERR_INVALID_PARAM, "zone %{public}d is default zone", zoneId);
105
106 bool updateScene = false;
107 auto &releaseZone = tempMap[zoneId];
108 for (auto it = releaseZone->audioFocusInfoList.begin(); it != releaseZone->audioFocusInfoList.end(); it++) {
109 if ((it->second != ACTIVE && it->second != DUCK) ||
110 (it->first.streamUsage == STREAM_USAGE_UNKNOWN ||
111 it->first.streamUsage == STREAM_USAGE_MEDIA ||
112 it->first.streamUsage == STREAM_USAGE_MOVIE)) {
113 ForceStopAudioFocusInZone(zoneId, it->first);
114 } else {
115 int32_t destZoneId = func(it->first.uid, it->first.deviceTag, "", it->first.streamUsage);
116 service_->ActivateAudioInterruptInternal(zoneId, it->first, false, updateScene);
117 }
118 }
119
120 tempMap.erase(zoneId);
121 return SUCCESS;
122 }
123
ForceStopAudioFocusInZone(int32_t zoneId,const AudioInterrupt & interrupt)124 void AudioInterruptZoneManager::ForceStopAudioFocusInZone(int32_t zoneId, const AudioInterrupt &interrupt)
125 {
126 AUDIO_DEBUG_LOG("force stop interrupt %{public}d,%{public}d,%{public}d of zone %{public}d",
127 interrupt.uid, interrupt.pid, interrupt.streamId, zoneId);
128
129 CHECK_AND_RETURN_LOG(service_->sessionService_ != nullptr, "session service is nullptr");
130 InterruptEventInternal interruptEvent {INTERRUPT_TYPE_BEGIN, INTERRUPT_FORCE, INTERRUPT_HINT_STOP, 1.0f};
131 if (service_->handler_ != nullptr) {
132 service_->handler_->SendInterruptEventWithStreamIdCallback(interruptEvent, interrupt.streamId);
133 }
134
135 auto audioSession = service_->sessionService_->GetAudioSessionByPid(interrupt.pid);
136 CHECK_AND_RETURN_LOG(audioSession != nullptr, "audio session is nullptr");
137 audioSession->RemoveStreamInfo(interrupt.streamId);
138 }
139
MigrateAudioInterruptZone(const int32_t zoneId,GetZoneIdFunc func)140 int32_t AudioInterruptZoneManager::MigrateAudioInterruptZone(const int32_t zoneId, GetZoneIdFunc func)
141 {
142 CHECK_AND_RETURN_RET_LOG(service_ != nullptr, ERR_INVALID_PARAM, "interrupt service is nullptr");
143 CHECK_AND_RETURN_RET_LOG(func != nullptr, ERR_INVALID_PARAM, "zone id is invalid");
144 auto &tempMap = service_->zonesMap_;
145 CHECK_AND_RETURN_RET_LOG(tempMap.find(zoneId) != tempMap.end() && tempMap[zoneId] != nullptr,
146 ERR_INVALID_PARAM, "zone %{public}d not exist", zoneId);
147
148 auto &focusInfoList = tempMap[zoneId]->audioFocusInfoList;
149 AUDIO_INFO_LOG("migrate interrupt size %{public}zu from zone %{public}d", focusInfoList.size(), zoneId);
150 bool isMigrate = false;
151 bool updateScene = false;
152 for (auto itFocus = focusInfoList.begin(); itFocus != focusInfoList.end();) {
153 int32_t toZoneId = func(itFocus->first.uid, itFocus->first.deviceTag, "", itFocus->first.streamUsage);
154 if (toZoneId == zoneId) {
155 ++itFocus;
156 continue;
157 }
158 if (itFocus->second == ACTIVE || itFocus->second == DUCK) {
159 service_->ActivateAudioInterruptInternal(toZoneId, itFocus->first, false, updateScene);
160 } else {
161 ForceStopAudioFocusInZone(zoneId, itFocus->first);
162 }
163 focusInfoList.erase(itFocus++);
164 isMigrate = true;
165 }
166 CHECK_AND_RETURN_RET_LOG(isMigrate, SUCCESS, "no interrupt need migrate");
167
168 ForceStopAllAudioFocusInZone(tempMap[zoneId]);
169 if (tempMap[zoneId]->audioFocusInfoList.size() > 0) {
170 service_->ResumeAudioFocusList(zoneId, false);
171 }
172 return SUCCESS;
173 }
174
ForceStopAllAudioFocusInZone(std::shared_ptr<AudioInterruptZone> & zone)175 void AudioInterruptZoneManager::ForceStopAllAudioFocusInZone(std::shared_ptr<AudioInterruptZone> &zone)
176 {
177 for (auto it = zone->audioFocusInfoList.begin(); it != zone->audioFocusInfoList.end();) {
178 if (it->second == ACTIVE || it->second == DUCK) {
179 it++;
180 continue;
181 }
182 ForceStopAudioFocusInZone(zone->zoneId, it->first);
183 zone->audioFocusInfoList.erase(it++);
184 }
185 }
186
InjectInterruptToAudioZone(const int32_t zoneId,const AudioFocusList & interrupts)187 int32_t AudioInterruptZoneManager::InjectInterruptToAudioZone(const int32_t zoneId,
188 const AudioFocusList &interrupts)
189 {
190 CHECK_AND_RETURN_RET_LOG(service_ != nullptr, ERR_INVALID_PARAM, "interrupt service is nullptr");
191 CHECK_AND_RETURN_RET_LOG(CheckAudioInterruptZonePermission(), ERR_INVALID_PARAM,
192 "audio zone permission deny");
193
194 auto &tempMap = service_->zonesMap_;
195 CHECK_AND_RETURN_RET_LOG(tempMap.find(zoneId) != tempMap.end() && tempMap[zoneId] != nullptr,
196 ERR_INVALID_PARAM, "zone %{public}d not exist", zoneId);
197
198 AUDIO_INFO_LOG("inject interrupt size %{public}zu to zone %{public}d", interrupts.size(), zoneId);
199 auto oldFocusList = tempMap[zoneId]->audioFocusInfoList;
200 AudioFocusList newFocusList = interrupts;
201 for (auto itOld = oldFocusList.begin(); itOld != oldFocusList.end(); itOld++) {
202 auto isPresent = [itOld](const std::pair<AudioInterrupt, AudioFocuState> &item) {
203 return item.first.streamId == itOld->first.streamId;
204 };
205 auto itNew = std::find_if(newFocusList.begin(), newFocusList.end(), isPresent);
206 if (itNew == newFocusList.end()) {
207 ForceStopAudioFocusInZone(zoneId, itOld->first);
208 } else {
209 AUDIO_INFO_LOG("try to refresh interrupt %{public}d,%{public}d,%{public}d"
210 "state from %{public}d to %{public}d",
211 itOld->first.uid, itOld->first.pid, itOld->first.streamId,
212 itOld->second, itNew->second);
213 if (itNew->second != itOld->second) {
214 bool removeFocusInfo = false;
215 service_->SendInterruptEvent(itOld->second, itNew->second, itOld, removeFocusInfo);
216 }
217 newFocusList.erase(itNew);
218 }
219 }
220
221 if (newFocusList.size() > 0) {
222 AUDIO_WARNING_LOG("has unexplained new focus for zone %{public}d", zoneId);
223 }
224
225 tempMap[zoneId]->audioFocusInfoList = interrupts;
226 return SUCCESS;
227 }
228
InjectInterruptToAudioZone(const int32_t zoneId,const std::string & deviceTag,const AudioFocusList & interrupts)229 int32_t AudioInterruptZoneManager::InjectInterruptToAudioZone(const int32_t zoneId,
230 const std::string &deviceTag, const AudioFocusList &interrupts)
231 {
232 CHECK_AND_RETURN_RET_LOG(service_ != nullptr, ERR_INVALID_PARAM, "interrupt service is nullptr");
233 CHECK_AND_RETURN_RET_LOG(CheckAudioInterruptZonePermission(), ERR_INVALID_PARAM,
234 "audio zone permission deny");
235
236 auto &tempMap = service_->zonesMap_;
237 CHECK_AND_RETURN_RET_LOG(tempMap.find(zoneId) != tempMap.end() && tempMap[zoneId] != nullptr,
238 ERR_INVALID_PARAM, "zone %{public}d not exist", zoneId);
239 CHECK_AND_RETURN_RET_LOG(!deviceTag.empty(), ERR_INVALID_PARAM,
240 "device tag is invalid for zone %{public}d", zoneId);
241
242 AUDIO_INFO_LOG("inject interrupt size %{public}zu with device tag %{public}s to zone %{public}d",
243 interrupts.size(), deviceTag.c_str(), zoneId);
244 AudioFocusList newFocusList = interrupts;
245 AudioFocusList activeFocusList;
246 AudioFocusIterator oldDeviceList = QueryAudioFocusFromZone(zoneId, deviceTag);
247
248 for (auto &itNew : newFocusList) {
249 auto isPresent = [itNew, deviceTag](const std::list<std::pair<AudioInterrupt,
250 AudioFocuState>>::iterator &iter) {
251 return iter->first.streamId == itNew.first.streamId && iter->first.deviceTag == deviceTag;
252 };
253 auto itOld = std::find_if(oldDeviceList.begin(), oldDeviceList.end(), isPresent);
254 if (itOld == oldDeviceList.end()) {
255 AUDIO_DEBUG_LOG("record new interrupt %{public}d", itNew.first.streamId);
256 itNew.first.deviceTag = deviceTag;
257 activeFocusList.emplace_back(itNew);
258 } else {
259 if ((*itOld)->second != itNew.second) {
260 AUDIO_DEBUG_LOG("ref interrupt %{public}d state from %{public}d to %{public}d",
261 itNew.first.streamId, (*itOld)->second, itNew.second);
262 (*itOld)->second = itNew.second;
263 }
264 oldDeviceList.erase(itOld);
265 }
266 }
267
268 AUDIO_DEBUG_LOG("focus list size is %{public}zu for zone %{public}d before remove",
269 tempMap[zoneId]->audioFocusInfoList.size(), zoneId);
270 RemoveAudioZoneInterrupts(zoneId, oldDeviceList);
271 TryActiveAudioFocusForZone(zoneId, activeFocusList);
272 return SUCCESS;
273 }
274
QueryAudioFocusFromZone(int32_t zoneId,const std::string & deviceTag)275 AudioFocusIterator AudioInterruptZoneManager::QueryAudioFocusFromZone(int32_t zoneId,
276 const std::string &deviceTag)
277 {
278 AudioFocusIterator deviceList;
279 CHECK_AND_RETURN_RET_LOG(service_ != nullptr, deviceList, "service is nullptr");
280 auto &focusInfoList = service_->zonesMap_[zoneId]->audioFocusInfoList;
281 for (auto it = focusInfoList.begin(); it != focusInfoList.end(); it++) {
282 CHECK_AND_CONTINUE(it->first.deviceTag == deviceTag);
283 deviceList.emplace_back(it);
284 }
285 return deviceList;
286 }
287
RemoveAudioZoneInterrupts(int32_t zoneId,const AudioFocusIterator & focus)288 void AudioInterruptZoneManager::RemoveAudioZoneInterrupts(int32_t zoneId, const AudioFocusIterator &focus)
289 {
290 CHECK_AND_RETURN_LOG(service_->sessionService_ != nullptr, "session service is nullptr");
291 for (auto &it : focus) {
292 auto audioSession = service_->sessionService_->GetAudioSessionByPid(it->first.pid);
293 if (audioSession != nullptr) {
294 audioSession->RemoveStreamInfo(it->first.streamId);
295 }
296 AUDIO_DEBUG_LOG("remove interrupt %{public}d from zone %{public}d",
297 it->first.streamId, zoneId);
298 service_->zonesMap_[zoneId]->audioFocusInfoList.erase(it);
299 }
300 }
301
TryActiveAudioFocusForZone(int32_t zoneId,AudioFocusList & activeFocusList)302 void AudioInterruptZoneManager::TryActiveAudioFocusForZone(int32_t zoneId, AudioFocusList &activeFocusList)
303 {
304 CHECK_AND_RETURN_LOG(service_ != nullptr, "service is nullptr");
305 AUDIO_DEBUG_LOG("focus list size is %{public}zu for zone %{public}d before active",
306 service_->zonesMap_[zoneId]->audioFocusInfoList.size(), zoneId);
307 if (activeFocusList.size() > 0) {
308 for (auto itActive : activeFocusList) {
309 AUDIO_DEBUG_LOG("active new interrupt %{public}d", itActive.first.streamId);
310 bool updateScene = false;
311 service_->ActivateAudioInterruptInternal(zoneId, itActive.first, false, updateScene);
312 }
313 } else {
314 TryResumeAudioFocusForZone(zoneId);
315 }
316 AUDIO_DEBUG_LOG("focus list size is %{public}zu for zone %{public}d after active",
317 service_->zonesMap_[zoneId]->audioFocusInfoList.size(), zoneId);
318 }
319
TryResumeAudioFocusForZone(int32_t zoneId)320 void AudioInterruptZoneManager::TryResumeAudioFocusForZone(int32_t zoneId)
321 {
322 CHECK_AND_RETURN_LOG(service_ != nullptr, "service is nullptr");
323 AUDIO_DEBUG_LOG("try resume audio focus list for zone %{public}d", zoneId);
324 auto &focusList = service_->zonesMap_[zoneId]->audioFocusInfoList;
325 CHECK_AND_RETURN_LOG(focusList.size() > 0, "focus list is empty");
326
327 for (auto it = focusList.begin(); it != focusList.end(); ++it) {
328 CHECK_AND_RETURN(it->second != ACTIVE);
329 }
330 service_->ResumeAudioFocusList(zoneId, false);
331 }
332
FindZoneByPid(int32_t pid)333 int32_t AudioInterruptZoneManager::FindZoneByPid(int32_t pid)
334 {
335 CHECK_AND_RETURN_RET_LOG(service_ != nullptr, AudioInterruptService::ZONEID_DEFAULT, "service is nullptr");
336 for (const auto &zone : service_->zonesMap_) {
337 CHECK_AND_CONTINUE(zone.second != nullptr);
338
339 for (const auto &it : zone.second->audioFocusInfoList) {
340 CHECK_AND_CONTINUE(it.first.pid == pid);
341 return zone.first;
342 }
343 }
344 AUDIO_WARNING_LOG("pid %{public}d not in audio zone, use default", pid);
345 return AudioInterruptService::ZONEID_DEFAULT;
346 }
347
CheckAudioInterruptZonePermission()348 bool AudioInterruptZoneManager::CheckAudioInterruptZonePermission()
349 {
350 AUDIO_INFO_LOG("audiointerruptzone permission: %{public}d", PermissionUtil::VerifySystemPermission());
351 return PermissionUtil::VerifySystemPermission();
352 }
353 } // namespace AudioStandard
354 } // namespace OHOS
355