• 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     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