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