• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025-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 
16 #include "audio_resource_service.h"
17 
18 #include <memory>
19 #include <cmath>
20 
21 #include "concurrent_task_client.h"
22 #include "rtg_interface.h"
23 #include "audio_errors.h"
24 #include "audio_utils.h"
25 #include "audio_workgroup_callback_proxy.h"
26 #include "audio_workgroup_callback.h"
27 #include "audio_schedule.h"
28 
29 namespace OHOS {
30 namespace AudioStandard {
31 namespace {
32     static constexpr int32_t AUDIO_MAX_PROCESS = 2;
33     static constexpr int32_t AUDIO_MAX_GRP_PER_PROCESS = 4;
34     static constexpr int32_t AUDIO_MAX_RT_THREADS = 4;
35 }
36 
GetInstance()37 AudioResourceService *AudioResourceService::GetInstance()
38 {
39     static AudioResourceService audioResource;
40     return &audioResource;
41 }
42 
AudioResourceService()43 AudioResourceService::AudioResourceService()
44 {
45 }
46 
~AudioResourceService()47 AudioResourceService::~AudioResourceService()
48 {
49 }
50 
IsValidPid(int32_t pid)51 static bool IsValidPid(int32_t pid)
52 {
53     return pid > 0;
54 }
55 
AudioWorkgroupCheck(int32_t pid)56 int32_t AudioResourceService::AudioWorkgroupCheck(int32_t pid)
57 {
58     bool inGroup = IsProcessInWorkgroup(pid);
59     std::lock_guard<std::mutex> lock(workgroupLock_);
60     if (inGroup) {
61         if (audioWorkgroupMap_[pid].groups.size() >= AUDIO_MAX_GRP_PER_PROCESS) {
62             AUDIO_INFO_LOG("[WorkgroupInServer] pid=%{public}d more than 4 groups is not allowed\n", pid);
63             return ERR_NOT_SUPPORTED;
64         }
65     } else {
66         uint32_t normalPidCount = 0;
67         for (const auto& [key, process] : audioWorkgroupMap_) {
68             if (!process.hasSystemPermission) {
69                 normalPidCount++;
70             }
71         }
72         if (normalPidCount >= AUDIO_MAX_PROCESS) {
73             AUDIO_INFO_LOG("[WorkgroupInServer] more than %{public}d processes is not allowed\n", AUDIO_MAX_PROCESS);
74             return ERR_NOT_SUPPORTED;
75         }
76     }
77     return SUCCESS;
78 }
79 
CreateAudioWorkgroup(int32_t pid,const sptr<IRemoteObject> & object)80 int32_t AudioResourceService::CreateAudioWorkgroup(int32_t pid, const sptr<IRemoteObject> &object)
81 {
82     CHECK_AND_RETURN_RET_LOG(pid > 0, ERR_INVALID_PARAM, "CreateAudioWorkgroup for pid < 0");
83 
84     if (!object) {
85         AUDIO_ERR_LOG("[AudioResourceService] object is nullptr!");
86         return ERR_OPERATION_FAILED;
87     }
88 
89     int ret = AudioWorkgroupCheck(pid);
90     CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ERR_NOT_SUPPORTED, "[WorkgroupInServer]:"
91         "1, Maximum 2 processes can create deadline workgroup."
92         "2, Maximum 4 workgroups can be created per process.");
93 
94     ConcurrentTask::IntervalReply reply;
95     OHOS::ConcurrentTask::ConcurrentTaskClient::GetInstance().SetAudioDeadline(
96         ConcurrentTask::AUDIO_DDL_CREATE_GRP, -1, -1, reply);
97     if (reply.rtgId != -1) {
98         std::lock_guard<std::mutex> lock(workgroupLock_);
99         auto workgroup = std::make_shared<AudioWorkgroup>(reply.rtgId);
100         audioWorkgroupMap_[pid].groups[reply.rtgId] = workgroup;
101 
102         sptr<AudioWorkgroupDeathRecipient> deathRecipient = new AudioWorkgroupDeathRecipient();
103         deathRecipient->SetNotifyCb([this, workgroup, object]() {
104             this->OnWorkgroupRemoteDied(workgroup, object);
105         });
106         object->AddDeathRecipient(deathRecipient);
107         deathRecipientMap_[workgroup] = std::make_pair(object, deathRecipient);
108         RegisterAudioWorkgroupMonitor(pid, reply.rtgId, object);
109         DumpAudioWorkgroupMap();
110     }
111 
112     Trace trace("[WorkgroupInServer] CreateAudioWorkgroup pid:" + std::to_string(pid) +
113         " groupId:" + std::to_string(reply.rtgId));
114 
115     return reply.rtgId;
116 }
117 
ReleaseAudioWorkgroup(int32_t pid,int32_t workgroupId)118 int32_t AudioResourceService::ReleaseAudioWorkgroup(int32_t pid, int32_t workgroupId)
119 {
120     if (!IsValidPid(pid)) {
121         AUDIO_ERR_LOG("[AudioResourceService] ReleaseAudioWorkgroup failed, err pid:%{public}d", pid);
122         return ERR_OPERATION_FAILED;
123     }
124 
125     Trace trace("[WorkgroupInServer] WorkgroupInServer pid:" + std::to_string(pid) +
126         " workgroupId:" + std::to_string(workgroupId));
127 
128     AudioWorkgroup *group = GetAudioWorkgroupPtr(pid, workgroupId);
129     CHECK_AND_RETURN_RET_LOG(group != nullptr, ERR_INVALID_PARAM, "AudioWorkgroup operated is not exsit");
130     ConcurrentTask::IntervalReply reply;
131     OHOS::ConcurrentTask::ConcurrentTaskClient::GetInstance().SetAudioDeadline(
132         ConcurrentTask::AUDIO_DDL_DESTROY_GRP, -1, workgroupId, reply);
133     if (reply.paramA != 0) {
134         AUDIO_ERR_LOG("[WorkgroupInServer] ReleaseAudioWorkgroup failed, workgroupId:%{public}d", workgroupId);
135         return ERR_OPERATION_FAILED;
136     }
137 
138     std::lock_guard<std::mutex> lock(workgroupLock_);
139     auto workgroupPtr = audioWorkgroupMap_[pid].groups[workgroupId];
140     if (workgroupPtr) {
141         auto deathIt = deathRecipientMap_.find(workgroupPtr);
142         if (deathIt != deathRecipientMap_.end()) {
143             ReleaseWorkgroupDeathRecipient(workgroupPtr, deathIt->second.first);
144         }
145     }
146 
147     audioWorkgroupMap_[pid].groups.erase(workgroupId);
148     if (audioWorkgroupMap_[pid].groups.size() == 0) {
149         audioWorkgroupMap_.erase(pid);
150     }
151     DumpAudioWorkgroupMap();
152     return SUCCESS;
153 }
154 
AddThreadToGroup(int32_t pid,int32_t workgroupId,int32_t tokenId)155 int32_t AudioResourceService::AddThreadToGroup(int32_t pid, int32_t workgroupId, int32_t tokenId)
156 {
157     if (pid == tokenId) {
158         AUDIO_ERR_LOG("[WorkgroupInServer] main thread pid=%{public}d is not allowed to be added", pid);
159         return ERR_OPERATION_FAILED;
160     }
161     AudioWorkgroup *group = GetAudioWorkgroupPtr(pid, workgroupId);
162     CHECK_AND_RETURN_RET_LOG(group != nullptr, ERR_INVALID_PARAM, "AudioWorkgroup operated is not exsit");
163 
164     if (GetThreadsNumPerProcess(pid) >= AUDIO_MAX_RT_THREADS) {
165         AUDIO_ERR_LOG("error: Maximum 4 threads can be added per process");
166         return ERR_NOT_SUPPORTED;
167     }
168 
169     int32_t ret = group->AddThread(tokenId);
170     return ret;
171 }
172 
RemoveThreadFromGroup(int32_t pid,int32_t workgroupId,int32_t tokenId)173 int32_t AudioResourceService::RemoveThreadFromGroup(int32_t pid, int32_t workgroupId, int32_t tokenId)
174 {
175     AudioWorkgroup *group = GetAudioWorkgroupPtr(pid, workgroupId);
176     CHECK_AND_RETURN_RET_LOG(group != nullptr, ERR_INVALID_PARAM, "AudioWorkgroup operated is not exsit");
177     int32_t ret = group->RemoveThread(tokenId);
178     return ret;
179 }
180 
StartGroup(int32_t pid,int32_t workgroupId,uint64_t startTime,uint64_t deadlineTime)181 int32_t AudioResourceService::StartGroup(int32_t pid, int32_t workgroupId, uint64_t startTime, uint64_t deadlineTime)
182 {
183     AudioWorkgroup *group = GetAudioWorkgroupPtr(pid, workgroupId);
184     CHECK_AND_RETURN_RET_LOG(group != nullptr, ERR_INVALID_PARAM, "AudioWorkgroup operated is not exsit");
185     int32_t ret = group->Start(startTime, deadlineTime);
186     return ret;
187 }
188 
StopGroup(int32_t pid,int32_t workgroupId)189 int32_t AudioResourceService::StopGroup(int32_t pid, int32_t workgroupId)
190 {
191     AudioWorkgroup *group = GetAudioWorkgroupPtr(pid, workgroupId);
192     CHECK_AND_RETURN_RET_LOG(group != nullptr, ERR_INVALID_PARAM, "AudioWorkgroup operated is not exsit");
193     int32_t ret = group->Stop();
194     return ret;
195 }
196 
GetAudioWorkgroupPtr(int32_t pid,int32_t workgroupId)197 AudioWorkgroup *AudioResourceService::GetAudioWorkgroupPtr(int32_t pid, int32_t workgroupId)
198 {
199     std::lock_guard<std::mutex> lock(workgroupLock_);
200     std::shared_ptr<AudioWorkgroup> group_ptr = audioWorkgroupMap_[pid].groups[workgroupId];
201     if (!group_ptr) {
202         AUDIO_ERR_LOG("[WorkgroupInServer] get AudioWorkgroup ptr failed\n");
203         return nullptr;
204     }
205     return group_ptr.get();
206 }
207 
AudioWorkgroupDeathRecipient()208 AudioResourceService::AudioWorkgroupDeathRecipient::AudioWorkgroupDeathRecipient()
209 {
210     AUDIO_ERR_LOG("[WorkgroupInServer] AudioWorkgroupDeathRecipient ctor");
211 }
212 
OnRemoteDied(const wptr<IRemoteObject> & remote)213 void AudioResourceService::AudioWorkgroupDeathRecipient::OnRemoteDied(const wptr<IRemoteObject> &remote)
214 {
215     if (diedCb_ != nullptr) {
216         diedCb_();
217     }
218 }
219 
SetNotifyCb(NotifyCbFunc func)220 void AudioResourceService::AudioWorkgroupDeathRecipient::SetNotifyCb(NotifyCbFunc func)
221 {
222     diedCb_ = func;
223 }
224 
OnWorkgroupRemoteDied(const std::shared_ptr<AudioWorkgroup> & workgroup,const sptr<IRemoteObject> & remoteObj)225 void AudioResourceService::OnWorkgroupRemoteDied(const std::shared_ptr<AudioWorkgroup> &workgroup,
226     const sptr<IRemoteObject> &remoteObj)
227 {
228     std::lock_guard<std::mutex> lock(workgroupLock_);
229     ReleaseWorkgroupDeathRecipient(workgroup, remoteObj);
230 
231     std::vector<int> pidsToDelete;
232     for (auto& [pid, process] : audioWorkgroupMap_) {
233         bool isGroupsCleared = false;
234         for (auto it = process.groups.begin(); it != process.groups.end();) {
235             if (it->second == workgroup) {
236                 it = process.groups.erase(it);
237                 isGroupsCleared = true;
238             } else {
239                 ++it;
240             }
241         }
242         if (isGroupsCleared && process.groups.empty()) {
243             pidsToDelete.push_back(pid);
244         }
245     }
246     for (int32_t pid : pidsToDelete) {
247         AUDIO_INFO_LOG("[WorkgroupInServer] All workgroups for pid:%{public}d released", pid);
248         audioWorkgroupMap_.erase(pid);
249     }
250     DumpAudioWorkgroupMap();
251 }
252 
ReleaseWorkgroupDeathRecipient(const std::shared_ptr<AudioWorkgroup> & workgroup,const sptr<IRemoteObject> & remoteObj)253 void AudioResourceService::ReleaseWorkgroupDeathRecipient(const std::shared_ptr<AudioWorkgroup> &workgroup,
254     const sptr<IRemoteObject> &remoteObj)
255 {
256     auto it = deathRecipientMap_.find(workgroup);
257     if (it != deathRecipientMap_.end()) {
258         if (it->second.first == remoteObj) {
259             remoteObj->RemoveDeathRecipient(it->second.second);
260             deathRecipientMap_.erase(it);
261         }
262     }
263 }
264 
GetThreadsNumPerProcess(int32_t pid)265 int32_t AudioResourceService::GetThreadsNumPerProcess(int32_t pid)
266 {
267     uint32_t count = 0;
268     std::lock_guard<std::mutex> lock(workgroupLock_);
269     for (const auto &[id, group]: audioWorkgroupMap_[pid].groups) {
270         AUDIO_INFO_LOG("[WorkgroupInServer] pid=%{public}d groupID=%{public}d\n", pid, id);
271         if (group != nullptr) {
272             count += group->GetThreadsNums();
273         }
274     }
275     AUDIO_INFO_LOG("[WorkgroupInServer] pid=%{public}d total threads=%{public}d\n", pid, count);
276     return count;
277 }
278 
IsProcessInWorkgroup(int32_t pid)279 bool AudioResourceService::IsProcessInWorkgroup(int32_t pid)
280 {
281     std::lock_guard<std::mutex> lock(workgroupLock_);
282     return audioWorkgroupMap_.find(pid) != audioWorkgroupMap_.end();
283 }
284 
IsProcessHasSystemPermission(int32_t pid)285 bool AudioResourceService::IsProcessHasSystemPermission(int32_t pid)
286 {
287     std::lock_guard<std::mutex> lock(workgroupLock_);
288     return audioWorkgroupMap_[pid].hasSystemPermission;
289 }
290 
291 
RegisterAudioWorkgroupMonitor(int32_t pid,int32_t groupId,const sptr<IRemoteObject> & object)292 int32_t AudioResourceService::RegisterAudioWorkgroupMonitor(int32_t pid, int32_t groupId,
293     const sptr<IRemoteObject> &object)
294 {
295     uint32_t ret = SUCCESS;
296 
297     // system permission HAP no need manage
298     audioWorkgroupMap_[pid].hasSystemPermission = PermissionUtil::VerifySystemPermission();
299     if (audioWorkgroupMap_[pid].hasSystemPermission) {
300         return SUCCESS;
301     }
302 
303     sptr<IAudioWorkgroupCallback > listener = iface_cast<IAudioWorkgroupCallback >(object);
304 
305     CHECK_AND_RETURN_RET_LOG(listener != nullptr, ERR_INVALID_PARAM, "AudioServer: listener obj cast failed");
306 
307     std::shared_ptr<AudioWorkgroupCallbackForMonitor> callback = std::make_shared<AudioWorkgroupCallback>(listener);
308     CHECK_AND_RETURN_RET_LOG(callback != nullptr, ERR_INVALID_PARAM, "failed to  create callback obj");
309 
310     for (auto &[id, group]: audioWorkgroupMap_[pid].groups) {
311         if (id == groupId) {
312             group->callback = callback;
313             AUDIO_INFO_LOG("[WorkgroupInServer] pid[%{public}d] groudId[%{public}d] registered", pid, id);
314         }
315     }
316     return ret;
317 }
318 
WorkgroupRendererMonitor(int32_t pid,const bool isAllowed)319 void AudioResourceService::WorkgroupRendererMonitor(int32_t pid, const bool isAllowed)
320 {
321     // Even though the caller has checked once, here checks is still allowed
322     if (!IsProcessInWorkgroup(pid)) {
323         return;
324     }
325 
326     std::lock_guard<std::mutex> lock(workgroupLock_);
327     if (isAllowed == audioWorkgroupMap_[pid].permission) {
328         return;
329     }
330     audioWorkgroupMap_[pid].permission = isAllowed;
331 
332     struct AudioWorkgroupChangeInfo info = {
333         .pid = pid,
334         .startAllowed = audioWorkgroupMap_[pid].permission,
335     };
336 
337     for (const auto &[id, group]: audioWorkgroupMap_[pid].groups) {
338         const auto &callback = group->callback;
339         if (callback == nullptr) {
340             break;
341         }
342         info.groupId = id;
343         AUDIO_INFO_LOG("[WorkgroupInServer] pid:%{public}d, groupId:%{public}d startAllowed:%{public}d",
344             info.pid, info.groupId, info.startAllowed);
345         callback->OnWorkgroupChange(info);
346     }
347 }
348 
DumpAudioWorkgroupMap()349 void AudioResourceService::DumpAudioWorkgroupMap()
350 {
351     for (const auto& [key, process] : audioWorkgroupMap_) {
352         const auto& groups = process.groups;
353         for (const auto& [groupKey, audioWorkgroup] : groups) {
354             if (audioWorkgroup != nullptr) {
355                 AUDIO_INFO_LOG("[WorkgroupInServer] pid:%{public}d, group:%{public}d, "
356                     "permission:%{public}d, hasSystemPermission:%{public}d, callback:%{public}s",
357                     key, groupKey, process.permission, process.hasSystemPermission,
358                     ((audioWorkgroup->callback != nullptr) ? "registered" : "no register"));
359             }
360         }
361     }
362 }
363 
GetProcessesOfAudioWorkgroup()364 std::vector<int32_t> AudioResourceService::GetProcessesOfAudioWorkgroup()
365 {
366     std::vector<int32_t> keys;
367     std::lock_guard<std::mutex> lock(workgroupLock_);
368     for (const auto& pair : audioWorkgroupMap_) {
369         keys.push_back(pair.first);
370     }
371     return keys;
372 }
373 
ImproveAudioWorkgroupPrio(int32_t pid,const std::unordered_map<int32_t,bool> & threads)374 int32_t AudioResourceService::ImproveAudioWorkgroupPrio(int32_t pid,
375     const std::unordered_map<int32_t, bool> &threads)
376 {
377     for (const auto &tid : threads) {
378         AUDIO_INFO_LOG("[WorkgroupInServer]set pid:%{public}d tid:%{public}d to qos_level7", pid, tid.first);
379         ScheduleReportData(pid, tid.first, "audio_server");
380     }
381     return AUDIO_OK;
382 }
383 
RestoreAudioWorkgroupPrio(int32_t pid,const std::unordered_map<int32_t,int32_t> & threads)384 int32_t AudioResourceService::RestoreAudioWorkgroupPrio(int32_t pid,
385     const std::unordered_map<int32_t, int32_t> &threads)
386 {
387     for (const auto &tid : threads) {
388         AUDIO_INFO_LOG("[WorkgroupInServer]set pid:%{public}d tid:%{public}d to qos%{public}d",
389             pid, tid.first, tid.second);
390         ScheduleReportDataWithQosLevel(pid, tid.first, "audio_server", tid.second);
391     }
392     return AUDIO_OK;
393 }
394 } // namespce AudioStandard
395 } // namespace OHOS
396