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