• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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