1 /*
2 * Copyright (c) 2021-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 #ifndef ST_PULSEAUDIO_AUDIO_SERVICE_ADAPTER_IMPL_H
17 #define ST_PULSEAUDIO_AUDIO_SERVICE_ADAPTER_IMPL_H
18 #ifndef LOG_TAG
19 #define LOG_TAG "PulseAudioServiceAdapterImpl"
20 #endif
21
22 #include "pulse_audio_service_adapter_impl.h"
23
24 #include <sstream>
25 #include <unistd.h>
26 #include <thread>
27 #include <set>
28 #include <unordered_map>
29
30 #include "hisysevent.h"
31 #include "media_monitor_manager.h"
32 #include "event_bean.h"
33
34 #include "audio_errors.h"
35 #include "audio_pulseaudio_log.h"
36 #include "audio_utils.h"
37 #include "pa_adapter_tools.h"
38 #include "audio_schedule.h"
39
40 using namespace std;
41
42 namespace OHOS {
43 namespace AudioStandard {
44 static unique_ptr<AudioServiceAdapterCallback> g_audioServiceAdapterCallback;
45 SafeMap<uint32_t, uint32_t> PulseAudioServiceAdapterImpl::sourceIndexSessionIDMap;
46
47 static const int32_t PA_SERVICE_IMPL_TIMEOUT = 10; // 10s is better
48 static const int32_t PA_CORE_MODULE_INDEX = 2; // 0: unix stub module, 1: suspend on idle module, 2:Speak module
49 static const unordered_map<std::string, AudioStreamType> STREAM_TYPE_STRING_ENUM_MAP = {
50 {"voice_call", STREAM_VOICE_CALL},
51 {"voice_call_assistant", STREAM_VOICE_CALL_ASSISTANT},
52 {"music", STREAM_MUSIC},
53 {"ring", STREAM_RING},
54 {"media", STREAM_MEDIA},
55 {"voice_assistant", STREAM_VOICE_ASSISTANT},
56 {"system", STREAM_SYSTEM},
57 {"alarm", STREAM_ALARM},
58 {"notification", STREAM_NOTIFICATION},
59 {"bluetooth_sco", STREAM_BLUETOOTH_SCO},
60 {"enforced_audible", STREAM_ENFORCED_AUDIBLE},
61 {"dtmf", STREAM_DTMF},
62 {"tts", STREAM_TTS},
63 {"accessibility", STREAM_ACCESSIBILITY},
64 {"recording", STREAM_RECORDING},
65 {"movie", STREAM_MOVIE},
66 {"game", STREAM_GAME},
67 {"speech", STREAM_SPEECH},
68 {"system_enforced", STREAM_SYSTEM_ENFORCED},
69 {"ultrasonic", STREAM_ULTRASONIC},
70 {"wakeup", STREAM_WAKEUP},
71 {"voice_message", STREAM_VOICE_MESSAGE},
72 {"navigation", STREAM_NAVIGATION},
73 {"camcorder", STREAM_CAMCORDER}
74 };
75
76 PulseAudioServiceAdapterImpl::~PulseAudioServiceAdapterImpl() = default;
77
PulseAudioServiceAdapterImpl(unique_ptr<AudioServiceAdapterCallback> & cb)78 PulseAudioServiceAdapterImpl::PulseAudioServiceAdapterImpl(unique_ptr<AudioServiceAdapterCallback> &cb)
79 {
80 g_audioServiceAdapterCallback = move(cb);
81 }
82
Connect()83 bool PulseAudioServiceAdapterImpl::Connect()
84 {
85 mMainLoop = pa_threaded_mainloop_new();
86 CHECK_AND_RETURN_RET_LOG(mMainLoop, false, "MainLoop creation failed");
87 pa_threaded_mainloop_set_name(mMainLoop, "OS_AudioML");
88 if (pa_threaded_mainloop_start(mMainLoop) < 0) {
89 AUDIO_ERR_LOG("Failed to start mainloop");
90 pa_threaded_mainloop_free(mMainLoop);
91 return false;
92 }
93
94 PaLockGuard palock(mMainLoop);
95 Trace trace("PulseAudioServiceAdapterImpl::Connect");
96
97 while (true) {
98 pa_context_state_t state;
99
100 if (mContext != nullptr) {
101 state = pa_context_get_state(mContext);
102 if (state == PA_CONTEXT_READY) {
103 break;
104 }
105 // if pulseaudio is ready, retry connect to pulseaudio. before retry wait for sometime. reduce sleep later
106 usleep(PA_CONNECT_RETRY_SLEEP_IN_MICRO_SECONDS);
107 }
108
109 bool result = ConnectToPulseAudio();
110 if (!result || !PA_CONTEXT_IS_GOOD(pa_context_get_state(mContext))) {
111 continue;
112 }
113
114 AUDIO_DEBUG_LOG("pa context not ready... wait");
115
116 // Wait for the context to be ready
117 AudioXCollie audioXCollie("PulseAudioServiceAdapterImpl::Connect", PA_SERVICE_IMPL_TIMEOUT,
118 [](void *) {
119 AUDIO_ERR_LOG("Connect timeout");
120 }, nullptr, AUDIO_XCOLLIE_FLAG_LOG | AUDIO_XCOLLIE_FLAG_RECOVERY);
121 pa_threaded_mainloop_wait(mMainLoop);
122 }
123
124 return true;
125 }
126
ConnectToPulseAudio()127 bool PulseAudioServiceAdapterImpl::ConnectToPulseAudio()
128 {
129 if (mContext != nullptr) {
130 AUDIO_DEBUG_LOG("context is not null, disconnect first!");
131 pa_context_disconnect(mContext);
132 pa_context_set_state_callback(mContext, nullptr, nullptr);
133 pa_context_set_subscribe_callback(mContext, nullptr, nullptr);
134 pa_context_unref(mContext);
135 mContext = nullptr;
136 }
137 pa_proplist *proplist = pa_proplist_new();
138 if (proplist == nullptr) {
139 AUDIO_ERR_LOG("Connect to pulseAudio and new proplist return nullptr!");
140 return false;
141 }
142 pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "PulseAudio Service");
143 pa_proplist_sets(proplist, PA_PROP_APPLICATION_ID, "com.ohos.pulseaudio.service");
144 mContext = pa_context_new_with_proplist(pa_threaded_mainloop_get_api(mMainLoop), nullptr, proplist);
145 pa_proplist_free(proplist);
146
147 CHECK_AND_RETURN_RET_LOG(mContext != nullptr, false, "creating pa context failed");
148
149 pa_context_set_state_callback(mContext, PulseAudioServiceAdapterImpl::PaContextStateCb, this);
150 if (pa_context_connect(mContext, nullptr, PA_CONTEXT_NOFAIL, nullptr) < 0) {
151 if (pa_context_errno(mContext) == PA_ERR_INVALID) {
152 AUDIO_ERR_LOG("pa context connect failed: %{public}s",
153 pa_strerror(pa_context_errno(mContext)));
154 goto Fail;
155 }
156 }
157
158 return true;
159
160 Fail:
161 /* Make sure we don't get any further callbacks */
162 pa_context_set_state_callback(mContext, nullptr, nullptr);
163 pa_context_set_subscribe_callback(mContext, nullptr, nullptr);
164 pa_context_unref(mContext);
165 mContext = nullptr;
166 return false;
167 }
168
ReloadAudioPort(const std::string & audioPortName,const AudioModuleInfo & audioModuleInfo)169 int32_t PulseAudioServiceAdapterImpl::ReloadAudioPort(const std::string &audioPortName,
170 const AudioModuleInfo &audioModuleInfo)
171 {
172 AUDIO_PRERELEASE_LOGE("ReloadAudioPort enter the INCORRECT func.");
173 return 0;
174 }
175
OpenAudioPort(string audioPortName,const AudioModuleInfo & audioModuleInfo)176 int32_t PulseAudioServiceAdapterImpl::OpenAudioPort(string audioPortName, const AudioModuleInfo& audioModuleInfo)
177 {
178 AUDIO_PRERELEASE_LOGE("OpenAudioPort enter the INCORRECT func.");
179 return 0;
180 }
181
OpenAudioPort(string audioPortName,string moduleArgs)182 uint32_t PulseAudioServiceAdapterImpl::OpenAudioPort(string audioPortName, string moduleArgs)
183 {
184 AUDIO_PRERELEASE_LOGI("Enter, port name: %{public}s", audioPortName.c_str());
185 AudioXCollie audioXCollie("PulseAudioServiceAdapterImpl::OpenAudioPort", PA_SERVICE_IMPL_TIMEOUT,
186 [](void *) {
187 AUDIO_ERR_LOG("OpenAudioPort timeout");
188 }, nullptr, AUDIO_XCOLLIE_FLAG_LOG | AUDIO_XCOLLIE_FLAG_RECOVERY);
189 lock_guard<mutex> lock(lock_);
190
191 unique_ptr<UserData> userData = make_unique<UserData>();
192 userData->thiz = this;
193
194 PaLockGuard palock(mMainLoop);
195 Trace trace("PulseAudioServiceAdapterImpl::OpenAudioPort");
196 if (mContext == nullptr) {
197 AUDIO_ERR_LOG("mContext is nullptr");
198 return ERROR;
199 }
200
201 pa_operation *operation = pa_context_load_module(mContext, audioPortName.c_str(), moduleArgs.c_str(),
202 PaModuleLoadCb, reinterpret_cast<void*>(userData.get()));
203 if (operation == nullptr) {
204 AUDIO_ERR_LOG("pa_context_load_module returned nullptr");
205 return PA_INVALID_INDEX;
206 }
207
208 while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) {
209 pa_threaded_mainloop_wait(mMainLoop);
210 }
211
212 pa_operation_unref(operation);
213
214 CHECK_AND_RETURN_RET_LOG(userData->idx != PA_INVALID_INDEX, PA_INVALID_INDEX,
215 "OpenAudioPort returned invalid index");
216
217 return userData->idx;
218 }
219
CloseAudioPort(int32_t audioHandleIndex)220 int32_t PulseAudioServiceAdapterImpl::CloseAudioPort(int32_t audioHandleIndex)
221 {
222 AUDIO_INFO_LOG("try to close module:%{public}d", audioHandleIndex);
223 AudioXCollie audioXCollie("PulseAudioServiceAdapterImpl::CloseAudioPort", PA_SERVICE_IMPL_TIMEOUT,
224 [](void *) {
225 AUDIO_ERR_LOG("CloseAudioPort timeout");
226 }, nullptr, AUDIO_XCOLLIE_FLAG_LOG | AUDIO_XCOLLIE_FLAG_RECOVERY);
227 lock_guard<mutex> lock(lock_);
228
229 PaLockGuard palock(mMainLoop);
230 if (mContext == nullptr) {
231 AUDIO_ERR_LOG("mContext is nullptr");
232 return ERROR;
233 }
234
235 if (audioHandleIndex <= PA_CORE_MODULE_INDEX) {
236 AUDIO_ERR_LOG("Core modules, not allowed to close!");
237 return ERROR;
238 }
239 pa_operation *operation = nullptr;
240 operation = pa_context_unload_module(mContext, audioHandleIndex, PaUnloadModuleCb,
241 reinterpret_cast<void*>(this));
242 CHECK_AND_RETURN_RET_LOG(operation, ERROR, "pa_context_unload_module sync returned nullptr!");
243 while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) {
244 pa_threaded_mainloop_wait(mMainLoop);
245 if (pa_operation_get_state(operation) != PA_OPERATION_RUNNING) {break;}
246 }
247
248 pa_operation_unref(operation);
249 return SUCCESS;
250 }
251
SuspendAudioDevice(string & audioPortName,bool isSuspend)252 int32_t PulseAudioServiceAdapterImpl::SuspendAudioDevice(string &audioPortName, bool isSuspend)
253 {
254 AUDIO_INFO_LOG("[%{public}s] : [%{public}d]", audioPortName.c_str(), isSuspend);
255 PaLockGuard palock(mMainLoop);
256 if (mContext == nullptr) {
257 AUDIO_ERR_LOG("mContext is nullptr");
258 return ERROR;
259 }
260
261 auto suspendFlag = isSuspend ? 1 : 0;
262 pa_operation *operation = pa_context_suspend_sink_by_name(mContext, audioPortName.c_str(), suspendFlag,
263 nullptr, nullptr);
264 if (operation == nullptr) {
265 AUDIO_ERR_LOG("pa_context_suspend_sink_by_name failed!");
266 return ERR_OPERATION_FAILED;
267 }
268
269 pa_operation_unref(operation);
270
271 return SUCCESS;
272 }
273
SetSinkMute(const std::string & sinkName,bool isMute,bool isSync)274 bool PulseAudioServiceAdapterImpl::SetSinkMute(const std::string &sinkName, bool isMute, bool isSync)
275 {
276 AUDIO_DEBUG_LOG("MuteAudioDevice: [%{public}s] : [%{public}d]", sinkName.c_str(), isMute);
277 AudioXCollie audioXCollie("PulseAudioServiceAdapterImpl::SetSinkMute", PA_SERVICE_IMPL_TIMEOUT,
278 [](void *) {
279 AUDIO_ERR_LOG("SetSinkMute timeout");
280 }, nullptr, AUDIO_XCOLLIE_FLAG_LOG | AUDIO_XCOLLIE_FLAG_RECOVERY);
281 unique_ptr<UserData> userData = make_unique<UserData>();
282 userData->thiz = this;
283
284 PaLockGuard palock(mMainLoop);
285 Trace trace("PulseAudioServiceAdapterImpl::SetSinkMute");
286
287 int muteFlag = isMute ? 1 : 0;
288
289 pa_operation *operation = nullptr;
290 if (isSync) {
291 operation = pa_context_set_sink_mute_by_name(mContext, sinkName.c_str(), muteFlag,
292 PulseAudioServiceAdapterImpl::PaSinkMuteCb, reinterpret_cast<void *>(userData.get()));
293 } else {
294 operation = pa_context_set_sink_mute_by_name(mContext, sinkName.c_str(), muteFlag,
295 nullptr, nullptr);
296 }
297
298 if (operation == nullptr) {
299 AUDIO_ERR_LOG("pa_context_suspend_sink_by_name failed!");
300 return false;
301 }
302
303 if (isSync) {
304 while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) {
305 pa_threaded_mainloop_wait(mMainLoop);
306 }
307 }
308
309 pa_operation_unref(operation);
310
311 return true;
312 }
313
SetDefaultSink(string name)314 int32_t PulseAudioServiceAdapterImpl::SetDefaultSink(string name)
315 {
316 PaLockGuard palock(mMainLoop);
317 if (mContext == nullptr) {
318 AUDIO_ERR_LOG("mContext is nullptr");
319 return ERROR;
320 }
321
322 pa_operation *operation = pa_context_set_default_sink(mContext, name.c_str(), nullptr, nullptr);
323 if (operation == nullptr) {
324 AUDIO_ERR_LOG("pa_context_set_default_sink failed!");
325 return ERR_OPERATION_FAILED;
326 }
327 isSetDefaultSink_ = true;
328 pa_operation_unref(operation);
329
330 return SUCCESS;
331 }
332
SetDefaultSource(string name)333 int32_t PulseAudioServiceAdapterImpl::SetDefaultSource(string name)
334 {
335 PaLockGuard palock(mMainLoop);
336 if (mContext == nullptr) {
337 AUDIO_ERR_LOG("mContext is nullptr");
338 return ERROR;
339 }
340
341 pa_operation *operation = pa_context_set_default_source(mContext, name.c_str(), nullptr, nullptr);
342 if (operation == nullptr) {
343 AUDIO_ERR_LOG("pa_context_set_default_source failed!");
344 return ERR_OPERATION_FAILED;
345 }
346 isSetDefaultSource_ = true;
347 pa_operation_unref(operation);
348
349 return SUCCESS;
350 }
351
PaGetSinksCb(pa_context * c,const pa_sink_info * i,int eol,void * userdata)352 void PulseAudioServiceAdapterImpl::PaGetSinksCb(pa_context *c, const pa_sink_info *i, int eol, void *userdata)
353 {
354 UserData *userData = reinterpret_cast<UserData *>(userdata);
355 PulseAudioServiceAdapterImpl *thiz = userData->thiz;
356
357 if (eol < 0) {
358 AUDIO_ERR_LOG("Failed to get sink information: %{public}s", pa_strerror(pa_context_errno(c)));
359 pa_threaded_mainloop_signal(thiz->mMainLoop, 0);
360 return;
361 }
362
363 if (eol) {
364 pa_threaded_mainloop_signal(thiz->mMainLoop, 0);
365 return;
366 }
367
368 CHECK_AND_RETURN_LOG(i->proplist != nullptr, "Invalid Proplist for sink (%{public}d).", i->index);
369
370 const char *adapterCStr = pa_proplist_gets(i->proplist, PA_PROP_DEVICE_STRING);
371 if (adapterCStr == nullptr) {
372 adapterCStr = "";
373 }
374 AUDIO_DEBUG_LOG("sink[%{public}d] device[%{public}s] name[%{public}s]", i->index, adapterCStr,
375 i->name);
376 std::string sinkDeviceName(adapterCStr);
377 std::string sinkName(i->name);
378 SinkInfo sinkInfo = {};
379 sinkInfo.sinkId = i->index;
380 sinkInfo.sinkName = sinkName;
381 sinkInfo.adapterName = sinkDeviceName;
382 userData->sinkInfos.push_back(sinkInfo);
383 }
384
GetAllSinks()385 std::vector<SinkInfo> PulseAudioServiceAdapterImpl::GetAllSinks()
386 {
387 AUDIO_PRERELEASE_LOGI("enter.");
388 AudioXCollie audioXCollie("PulseAudioServiceAdapterImpl::GetAllSinks", PA_SERVICE_IMPL_TIMEOUT,
389 [](void *) {
390 AUDIO_ERR_LOG("GetAllSinks timeout");
391 }, nullptr, AUDIO_XCOLLIE_FLAG_LOG | AUDIO_XCOLLIE_FLAG_RECOVERY);
392 lock_guard<mutex> lock(lock_);
393 unique_ptr<UserData> userData = make_unique<UserData>();
394 userData->thiz = this;
395 userData->sinkInfos = {};
396
397 CHECK_AND_RETURN_RET_LOG(mContext != nullptr, userData->sinkInfos, "mContext is nullptr");
398
399 PaLockGuard palock(mMainLoop);
400 Trace trace("PulseAudioServiceAdapterImpl::GetAllSinks");
401
402 pa_operation *operation = pa_context_get_sink_info_list(mContext,
403 PulseAudioServiceAdapterImpl::PaGetSinksCb, reinterpret_cast<void*>(userData.get()));
404 if (operation == nullptr) {
405 AUDIO_ERR_LOG("pa_context_get_sink_info_list returned nullptr");
406 return userData->sinkInfos;
407 }
408
409 while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) {
410 pa_threaded_mainloop_wait(mMainLoop);
411 }
412
413 pa_operation_unref(operation);
414
415 AUDIO_DEBUG_LOG("end, get [%{public}zu] sinks.", userData->sinkInfos.size());
416 return userData->sinkInfos;
417 }
418
GetTargetSinks(std::string adapterName)419 std::vector<uint32_t> PulseAudioServiceAdapterImpl::GetTargetSinks(std::string adapterName)
420 {
421 std::vector<SinkInfo> sinkInfos = GetAllSinks();
422 std::vector<uint32_t> targetSinkIds = {};
423 for (size_t i = 0; i < sinkInfos.size(); i++) {
424 if (sinkInfos[i].adapterName == adapterName) {
425 targetSinkIds.push_back(sinkInfos[i].sinkId);
426 }
427 }
428 return targetSinkIds;
429 }
430
SetLocalDefaultSink(std::string name)431 int32_t PulseAudioServiceAdapterImpl::SetLocalDefaultSink(std::string name)
432 {
433 std::vector<SinkInput> allSinkInputs = GetAllSinkInputs();
434
435 std::string remoteDevice = "remote";
436 std::vector<uint32_t> remoteSinks = GetTargetSinks(remoteDevice);
437
438 // filter sink-inputs which are not connected with remote sinks.
439 for (auto sinkInput : allSinkInputs) {
440 uint32_t sink = sinkInput.deviceSinkId;
441 // the sink inputs connected to remote device remain the same
442 CHECK_AND_CONTINUE_LOG(std::find(remoteSinks.begin(), remoteSinks.end(), sink) == remoteSinks.end(),
443 "sink-input[%{public}d] connects with remote device[%{public}d]",
444 sinkInput.paStreamId, sinkInput.deviceSinkId);
445 // move the remaining sink inputs to the default sink
446 uint32_t invalidSinkId = PA_INVALID_INDEX;
447 MoveSinkInputByIndexOrName(sinkInput.paStreamId, invalidSinkId, name);
448 }
449
450 return SUCCESS;
451 }
452
MoveSinkInputByIndexOrName(uint32_t sinkInputId,uint32_t sinkIndex,std::string sinkName)453 int32_t PulseAudioServiceAdapterImpl::MoveSinkInputByIndexOrName(uint32_t sinkInputId, uint32_t sinkIndex,
454 std::string sinkName)
455 {
456 AudioXCollie audioXCollie("PulseAudioServiceAdapterImpl::MoveSinkInputByIndexOrName", PA_SERVICE_IMPL_TIMEOUT,
457 [](void *) {
458 AUDIO_ERR_LOG("MoveSinkInputByIndexOrName timeout");
459 }, nullptr, AUDIO_XCOLLIE_FLAG_LOG | AUDIO_XCOLLIE_FLAG_RECOVERY);
460 lock_guard<mutex> lock(lock_);
461 Trace trace("PulseAudioServiceAdapterImpl::MoveSinkInputByIndexOrName:id:" + std::to_string(sinkInputId) +
462 +":index:" + std::to_string(sinkIndex) + ":name:" + sinkName);
463
464 unique_ptr<UserData> userData = make_unique<UserData>();
465 userData->thiz = this;
466
467 CHECK_AND_RETURN_RET_LOG(mContext != nullptr, ERROR, "mContext is nullptr");
468 PaLockGuard palock(mMainLoop);
469 pa_operation *operation = nullptr;
470 if (sinkName.empty()) {
471 operation = pa_context_move_sink_input_by_index(mContext, sinkInputId, sinkIndex,
472 PulseAudioServiceAdapterImpl::PaMoveSinkInputCb, reinterpret_cast<void *>(userData.get()));
473 } else {
474 operation = pa_context_move_sink_input_by_name(mContext, sinkInputId, sinkName.c_str(),
475 PulseAudioServiceAdapterImpl::PaMoveSinkInputCb, reinterpret_cast<void *>(userData.get()));
476 }
477
478 if (operation == nullptr) {
479 AUDIO_ERR_LOG("pa_context_get_sink_input_info_list nullptr");
480 return ERROR;
481 }
482 while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) {
483 pa_threaded_mainloop_wait(mMainLoop);
484 }
485 pa_operation_unref(operation);
486
487 int result = userData->moveResult;
488 AUDIO_DEBUG_LOG("move result:[%{public}d]", result);
489
490 return SUCCESS;
491 }
492
MoveSourceOutputByIndexOrName(uint32_t sourceOutputId,uint32_t sourceIndex,std::string sourceName)493 int32_t PulseAudioServiceAdapterImpl::MoveSourceOutputByIndexOrName(uint32_t sourceOutputId, uint32_t sourceIndex,
494 std::string sourceName)
495 {
496 AudioXCollie audioXCollie("PulseAudioServiceAdapterImpl::MoveSourceOutputByIndexOrName",
497 PA_SERVICE_IMPL_TIMEOUT, [](void *) {
498 AUDIO_ERR_LOG("MoveSourceOutputByIndexOrName timeout");
499 }, nullptr, AUDIO_XCOLLIE_FLAG_LOG | AUDIO_XCOLLIE_FLAG_RECOVERY);
500 lock_guard<mutex> lock(lock_);
501 Trace trace("PulseAudioServiceAdapterImpl::MoveSourceOutputByIndexOrName:id:" + std::to_string(sourceOutputId) +
502 +":index:" + std::to_string(sourceIndex) + ":name:" + sourceName);
503
504 unique_ptr<UserData> userData = make_unique<UserData>();
505 userData->thiz = this;
506
507 if (mContext == nullptr) {
508 AUDIO_ERR_LOG("mContext is nullptr");
509 return ERROR;
510 }
511 PaLockGuard palock(mMainLoop);
512 pa_operation *operation = nullptr;
513 if (sourceName.empty()) {
514 operation = pa_context_move_source_output_by_index(mContext, sourceOutputId, sourceIndex,
515 PulseAudioServiceAdapterImpl::PaMoveSourceOutputCb, reinterpret_cast<void *>(userData.get()));
516 } else {
517 operation = pa_context_move_source_output_by_name(mContext, sourceOutputId, sourceName.c_str(),
518 PulseAudioServiceAdapterImpl::PaMoveSourceOutputCb, reinterpret_cast<void *>(userData.get()));
519 }
520
521 if (operation == nullptr) {
522 AUDIO_ERR_LOG("pa_context_get_sink_input_info_list nullptr");
523 return ERROR;
524 }
525 while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) {
526 pa_threaded_mainloop_wait(mMainLoop);
527 }
528 pa_operation_unref(operation);
529
530 int result = userData->moveResult;
531 AUDIO_DEBUG_LOG("move result:[%{public}d]", result);
532
533 return SUCCESS;
534 }
535
SetSourceOutputMute(int32_t uid,bool setMute)536 int32_t PulseAudioServiceAdapterImpl::SetSourceOutputMute(int32_t uid, bool setMute)
537 {
538 CHECK_AND_RETURN_RET_LOG(mContext != nullptr, ERROR, "mContext is nullptr");
539 vector<SourceOutput> sourOutputs = GetAllSourceOutputs();
540 lock_guard<mutex> lock(lock_);
541 int32_t streamSet = 0;
542 for (uint32_t i = 0; i < sourOutputs.size(); i ++) {
543 if (sourOutputs[i].uid == uid) {
544 PaLockGuard palock(mMainLoop);
545 pa_operation *operation = pa_context_set_source_output_mute(mContext, sourOutputs[i].paStreamId,
546 (setMute ? 1 : 0), nullptr, nullptr);
547 if (operation == nullptr) {
548 AUDIO_ERR_LOG("pa_context_set_source_output_mute nullptr");
549 return ERROR;
550 }
551 pa_operation_unref(operation);
552 AUDIO_DEBUG_LOG("set source output Mute : %{public}s for stream :uid %{public}d",
553 (setMute ? "true" : "false"), sourOutputs[i].uid);
554 streamSet++;
555 }
556 }
557 AUDIO_INFO_LOG("set %{public}d %{public}s", streamSet, (setMute ? "mute" : "unmuted"));
558 return streamSet;
559 }
560
GetAllSinkInputs()561 vector<SinkInput> PulseAudioServiceAdapterImpl::GetAllSinkInputs()
562 {
563 AUDIO_PRERELEASE_LOGI("enter");
564 unique_ptr<UserData> userData = make_unique<UserData>();
565 userData->thiz = this;
566 userData->sinkInfos = GetAllSinks();
567
568 AudioXCollie audioXCollie("PulseAudioServiceAdapterImpl::GetAllSinkInputs", PA_SERVICE_IMPL_TIMEOUT,
569 [](void *) {
570 AUDIO_ERR_LOG("GetAllSinkInputs timeout");
571 }, nullptr, AUDIO_XCOLLIE_FLAG_LOG | AUDIO_XCOLLIE_FLAG_RECOVERY);
572 lock_guard<mutex> lock(lock_);
573 CHECK_AND_RETURN_RET_LOG(mContext != nullptr, userData->sinkInputList, "mContext is nullptr");
574
575 PaLockGuard palock(mMainLoop);
576 Trace trace("PulseAudioServiceAdapterImpl::GetAllSinkInputs");
577
578 pa_operation *operation = pa_context_get_sink_input_info_list(mContext,
579 PulseAudioServiceAdapterImpl::PaGetAllSinkInputsCb, reinterpret_cast<void*>(userData.get()));
580 if (operation == nullptr) {
581 AUDIO_ERR_LOG("pa_context_get_sink_input_info_list returned nullptr");
582 return userData->sinkInputList;
583 }
584
585 while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) {
586 pa_threaded_mainloop_wait(mMainLoop);
587 }
588
589 pa_operation_unref(operation);
590
591 AUDIO_DEBUG_LOG("get:[%{public}zu]", userData->sinkInputList.size());
592 return userData->sinkInputList;
593 }
594
GetAllSourceOutputs()595 vector<SourceOutput> PulseAudioServiceAdapterImpl::GetAllSourceOutputs()
596 {
597 AudioXCollie audioXCollie("PulseAudioServiceAdapterImpl::GetAllSourceOutputs", PA_SERVICE_IMPL_TIMEOUT,
598 [](void *) {
599 AUDIO_ERR_LOG("GetAllSourceOutputs timeout");
600 }, nullptr, AUDIO_XCOLLIE_FLAG_LOG | AUDIO_XCOLLIE_FLAG_RECOVERY);
601 lock_guard<mutex> lock(lock_);
602 Trace trace("PulseAudioServiceAdapterImpl::GetAllSourceOutputs");
603
604 unique_ptr<UserData> userData = make_unique<UserData>();
605 userData->thiz = this;
606
607 CHECK_AND_RETURN_RET_LOG(mContext != nullptr, userData->sourceOutputList, "mContext is nullptr");
608
609 CHECK_AND_RETURN_RET_LOG(isSetDefaultSource_, userData->sourceOutputList, "default source has not been set.");
610
611 PaLockGuard palock(mMainLoop);
612
613 pa_operation *operation = pa_context_get_source_output_info_list(mContext,
614 PulseAudioServiceAdapterImpl::PaGetAllSourceOutputsCb, reinterpret_cast<void*>(userData.get()));
615 if (operation == nullptr) {
616 AUDIO_ERR_LOG("pa_context_get_source_output_info_list returned nullptr");
617 return userData->sourceOutputList;
618 }
619
620 while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) {
621 pa_threaded_mainloop_wait(mMainLoop);
622 }
623
624 pa_operation_unref(operation);
625
626 return userData->sourceOutputList;
627 }
628
Disconnect()629 void PulseAudioServiceAdapterImpl::Disconnect()
630 {
631 if (mContext != nullptr) {
632 AUDIO_WARNING_LOG("disconnect context! should not happen");
633 pa_context_disconnect(mContext);
634 /* Make sure we don't get any further callbacks */
635 pa_context_set_state_callback(mContext, nullptr, nullptr);
636 pa_context_set_subscribe_callback(mContext, nullptr, nullptr);
637 pa_context_unref(mContext);
638 mContext = nullptr;
639 }
640
641 if (mMainLoop != nullptr) {
642 AUDIO_WARNING_LOG("disconnect mainloop! should not happen");
643 pa_threaded_mainloop_stop(mMainLoop);
644 pa_threaded_mainloop_free(mMainLoop);
645 mMainLoop = nullptr;
646 }
647 }
648
GetIdByStreamType(string streamType)649 AudioStreamType PulseAudioServiceAdapterImpl::GetIdByStreamType(string streamType)
650 {
651 AudioStreamType stream = STREAM_MUSIC;
652 if (STREAM_TYPE_STRING_ENUM_MAP.find(streamType) != STREAM_TYPE_STRING_ENUM_MAP.end()) {
653 stream = STREAM_TYPE_STRING_ENUM_MAP.at(streamType);
654 } else {
655 AUDIO_WARNING_LOG("Invalid stream type [%{public}s]. Use default type", streamType.c_str());
656 }
657 return stream;
658 }
659
PaMoveSinkInputCb(pa_context * c,int success,void * userdata)660 void PulseAudioServiceAdapterImpl::PaMoveSinkInputCb(pa_context *c, int success, void *userdata)
661 {
662 UserData *userData = reinterpret_cast<UserData *>(userdata);
663
664 AUDIO_DEBUG_LOG("result[%{public}d]", success);
665 userData->moveResult = success;
666
667 pa_threaded_mainloop_signal(userData->thiz->mMainLoop, 0);
668
669 return;
670 }
671
PaMoveSourceOutputCb(pa_context * c,int success,void * userdata)672 void PulseAudioServiceAdapterImpl::PaMoveSourceOutputCb(pa_context *c, int success, void *userdata)
673 {
674 UserData *userData = reinterpret_cast<UserData *>(userdata);
675
676 AUDIO_INFO_LOG("result[%{public}d]", success);
677 userData->moveResult = success;
678
679 pa_threaded_mainloop_signal(userData->thiz->mMainLoop, 0);
680
681 return;
682 }
683
PaSinkMuteCb(pa_context * c,int success,void * userdata)684 void PulseAudioServiceAdapterImpl::PaSinkMuteCb(pa_context *c, int success, void *userdata)
685 {
686 UserData *userData = reinterpret_cast<UserData *>(userdata);
687 AUDIO_DEBUG_LOG("result[%{public}d]", success);
688 pa_threaded_mainloop_signal(userData->thiz->mMainLoop, 0);
689 }
690
PaContextStateCb(pa_context * c,void * userdata)691 void PulseAudioServiceAdapterImpl::PaContextStateCb(pa_context *c, void *userdata)
692 {
693 PulseAudioServiceAdapterImpl *thiz = reinterpret_cast<PulseAudioServiceAdapterImpl*>(userdata);
694
695 switch (pa_context_get_state(c)) {
696 case PA_CONTEXT_UNCONNECTED:
697 case PA_CONTEXT_CONNECTING:
698 case PA_CONTEXT_AUTHORIZING:
699 case PA_CONTEXT_SETTING_NAME:
700 break;
701
702 case PA_CONTEXT_READY: {
703 pa_context_set_subscribe_callback(c, PulseAudioServiceAdapterImpl::PaSubscribeCb, thiz);
704
705 pa_operation *operation = pa_context_subscribe(c, (pa_subscription_mask_t)
706 (PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE |
707 PA_SUBSCRIPTION_MASK_SINK_INPUT | PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT |
708 PA_SUBSCRIPTION_MASK_CARD), nullptr, nullptr);
709 if (operation == nullptr) {
710 pa_threaded_mainloop_signal(thiz->mMainLoop, 0);
711 return;
712 }
713 pa_operation_unref(operation);
714 pa_threaded_mainloop_signal(thiz->mMainLoop, 0);
715
716 // raise thread priority
717 thiz->SetThreadPriority();
718 break;
719 }
720
721 case PA_CONTEXT_FAILED:
722 case PA_CONTEXT_TERMINATED:
723 AUDIO_ERR_LOG("state is PA_CONTEXT_FAILED or PA_CONTEXT_TERMINATED");
724 pa_threaded_mainloop_signal(thiz->mMainLoop, 0);
725 return;
726
727 default:
728 return;
729 }
730 }
731
SetThreadPriority()732 bool PulseAudioServiceAdapterImpl::SetThreadPriority()
733 {
734 static bool setPriorityFlag = false;
735 if (!setPriorityFlag) {
736 AUDIO_INFO_LOG("set priority for pulse audio adapter mainloop thread");
737 ScheduleThreadInServer(getpid(), gettid());
738 setPriorityFlag = true;
739 return setPriorityFlag;
740 }
741 return setPriorityFlag;
742 }
743
PaModuleLoadCb(pa_context * c,uint32_t idx,void * userdata)744 void PulseAudioServiceAdapterImpl::PaModuleLoadCb(pa_context *c, uint32_t idx, void *userdata)
745 {
746 UserData *userData = reinterpret_cast<UserData*>(userdata);
747 if (idx == PA_INVALID_INDEX) {
748 AUDIO_ERR_LOG("Failure: %{public}s", pa_strerror(pa_context_errno(c)));
749 userData->idx = PA_INVALID_INDEX;
750 } else {
751 userData->idx = idx;
752 }
753 pa_threaded_mainloop_signal(userData->thiz->mMainLoop, 0);
754
755 return;
756 }
757
PaUnloadModuleCb(pa_context * c,int success,void * userdata)758 void PulseAudioServiceAdapterImpl::PaUnloadModuleCb(pa_context *c, int success, void *userdata)
759 {
760 AUDIO_INFO_LOG("Entry. unload module result=%{public}d", success);
761 auto thiz = reinterpret_cast<PulseAudioServiceAdapterImpl *>(userdata);
762 pa_threaded_mainloop_signal(thiz->mMainLoop, 0);
763 }
764
765 template <typename T>
CastValue(T & a,const char * raw)766 inline void CastValue(T &a, const char *raw)
767 {
768 if (raw == nullptr) {
769 return;
770 }
771 std::stringstream valueStr;
772 valueStr << raw;
773 valueStr >> a;
774 }
775
PaGetSourceOutputNoSignalCb(pa_context * c,const pa_source_output_info * i,int eol,void * userdata)776 void PulseAudioServiceAdapterImpl::PaGetSourceOutputNoSignalCb(pa_context *c, const pa_source_output_info *i,
777 int eol, void *userdata)
778 {
779 AUDIO_INFO_LOG("in eol[%{public}d]", eol);
780 UserData *userData = reinterpret_cast<UserData*>(userdata);
781
782 if (eol < 0) {
783 delete userData;
784 AUDIO_ERR_LOG("Failed to get source output information: %{public}s",
785 pa_strerror(pa_context_errno(c)));
786 return;
787 }
788
789 if (eol) {
790 delete userData;
791 return;
792 }
793
794 CHECK_AND_RETURN_LOG(i->proplist != nullptr, "Invalid proplist for source output (%{public}d).", i->index);
795
796 const char *streamSession = pa_proplist_gets(i->proplist, "stream.sessionID");
797 CHECK_AND_RETURN_LOG(streamSession != nullptr, "Invalid stream parameter:sessionID.");
798
799 std::stringstream sessionStr;
800 uint32_t sessionID;
801 sessionStr << streamSession;
802 sessionStr >> sessionID;
803 AUDIO_INFO_LOG("sessionID %{public}u", sessionID);
804 sourceIndexSessionIDMap.Insert(i->index, sessionID);
805 }
806
PaGetAllSinkInputsCb(pa_context * c,const pa_sink_input_info * i,int eol,void * userdata)807 void PulseAudioServiceAdapterImpl::PaGetAllSinkInputsCb(pa_context *c, const pa_sink_input_info *i, int eol,
808 void *userdata)
809 {
810 AUDIO_DEBUG_LOG("in eol[%{public}d]", eol);
811 UserData *userData = reinterpret_cast<UserData *>(userdata);
812 PulseAudioServiceAdapterImpl *thiz = userData->thiz;
813
814 if (eol < 0) {
815 AUDIO_ERR_LOG("Failed to get sink input information: %{public}s", pa_strerror(pa_context_errno(c)));
816 pa_threaded_mainloop_signal(thiz->mMainLoop, 0);
817 return;
818 }
819
820 if (eol) {
821 pa_threaded_mainloop_signal(thiz->mMainLoop, 0);
822 return;
823 }
824
825 CHECK_AND_RETURN_LOG(i->proplist != nullptr,
826 "Invalid Proplist for sink input (%{public}d).", i->index);
827
828 const char *streamMode = pa_proplist_gets(i->proplist, "stream.mode");
829 if (streamMode != nullptr && streamMode == DUP_STREAM) {
830 AUDIO_INFO_LOG("Dup stream dismissed:%{public}u", i->index);
831 return;
832 }
833
834 AudioStreamType audioStreamType = STREAM_DEFAULT;
835 const char *streamType = pa_proplist_gets(i->proplist, "stream.type");
836 if (streamType != nullptr) {
837 audioStreamType = thiz->GetIdByStreamType(streamType);
838 }
839
840 SinkInput sinkInput = {};
841 sinkInput.streamType = audioStreamType;
842
843 sinkInput.deviceSinkId = i->sink;
844 for (auto sinkInfo : userData->sinkInfos) {
845 if (sinkInput.deviceSinkId == sinkInfo.sinkId) {
846 sinkInput.sinkName = sinkInfo.sinkName;
847 break;
848 }
849 }
850 sinkInput.paStreamId = i->index;
851 CastValue<int32_t>(sinkInput.streamId, pa_proplist_gets(i->proplist, "stream.sessionID"));
852 CastValue<int32_t>(sinkInput.uid, pa_proplist_gets(i->proplist, "stream.client.uid"));
853 CastValue<int32_t>(sinkInput.pid, pa_proplist_gets(i->proplist, "stream.client.pid"));
854 CastValue<uint64_t>(sinkInput.startTime, pa_proplist_gets(i->proplist, "stream.startTime"));
855
856 userData->sinkInputList.push_back(sinkInput);
857 }
858
PaGetAllSourceOutputsCb(pa_context * c,const pa_source_output_info * i,int eol,void * userdata)859 void PulseAudioServiceAdapterImpl::PaGetAllSourceOutputsCb(pa_context *c, const pa_source_output_info *i, int eol,
860 void *userdata)
861 {
862 AUDIO_INFO_LOG("in eol[%{public}d]", eol);
863 UserData *userData = reinterpret_cast<UserData *>(userdata);
864 PulseAudioServiceAdapterImpl *thiz = userData->thiz;
865
866 if (eol < 0) {
867 AUDIO_ERR_LOG("Failed to get source output information: %{public}s", pa_strerror(pa_context_errno(c)));
868 pa_threaded_mainloop_signal(thiz->mMainLoop, 0);
869 return;
870 }
871
872 if (eol) {
873 pa_threaded_mainloop_signal(thiz->mMainLoop, 0);
874 return;
875 }
876
877 CHECK_AND_RETURN_LOG(i->proplist != nullptr,
878 "Invalid Proplist for source output (%{public}d).", i->index);
879
880 int32_t sessionID = 0;
881 const char *sessionCStr = pa_proplist_gets(i->proplist, "stream.sessionID");
882 if (sessionCStr != nullptr) {
883 std::stringstream sessionStr;
884 sessionStr << sessionCStr;
885 sessionStr >> sessionID;
886 }
887
888 AudioStreamType audioStreamType = STREAM_DEFAULT;
889 const char *streamType = pa_proplist_gets(i->proplist, "stream.type");
890 if (streamType != nullptr) {
891 audioStreamType = thiz->GetIdByStreamType(streamType);
892 }
893
894 SourceOutput sourceOutput = {};
895 sourceOutput.streamId = sessionID;
896 sourceOutput.streamType = audioStreamType;
897
898 sourceOutput.paStreamId = i->index;
899 sourceOutput.deviceSourceId = i->source;
900 CastValue<int32_t>(sourceOutput.uid, pa_proplist_gets(i->proplist, "stream.client.uid"));
901 CastValue<int32_t>(sourceOutput.pid, pa_proplist_gets(i->proplist, "stream.client.pid"));
902 CastValue<uint64_t>(sourceOutput.startTime, pa_proplist_gets(i->proplist, "stream.startTime"));
903 userData->sourceOutputList.push_back(sourceOutput);
904 }
905
ProcessSourceOutputEvent(pa_context * c,pa_subscription_event_type_t t,uint32_t idx,void * userdata)906 void PulseAudioServiceAdapterImpl::ProcessSourceOutputEvent(pa_context *c, pa_subscription_event_type_t t, uint32_t idx,
907 void *userdata)
908 {
909 unique_ptr<UserData> userData = make_unique<UserData>();
910 PulseAudioServiceAdapterImpl *thiz = reinterpret_cast<PulseAudioServiceAdapterImpl*>(userdata);
911 userData->thiz = thiz;
912 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
913 pa_operation *operation = pa_context_get_source_output_info(c, idx,
914 PulseAudioServiceAdapterImpl::PaGetSourceOutputNoSignalCb, reinterpret_cast<void*>(userData.get()));
915 if (operation == nullptr) {
916 AUDIO_ERR_LOG("pa_context_get_source_output_info nullptr");
917 return;
918 }
919 userData.release();
920 pa_operation_unref(operation);
921 } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
922 uint32_t sessionID = 0;
923 if (sourceIndexSessionIDMap.Find(idx, sessionID) == true) {
924 AUDIO_ERR_LOG("sessionID: %{public}d removed", sessionID);
925 g_audioServiceAdapterCallback->OnAudioStreamRemoved(sessionID);
926 sourceIndexSessionIDMap.Erase(idx);
927 } else {
928 AUDIO_ERR_LOG("cannot find sessionID in sourceIndexSessionIDMap");
929 }
930 }
931 }
932
PaSubscribeCb(pa_context * c,pa_subscription_event_type_t t,uint32_t idx,void * userdata)933 void PulseAudioServiceAdapterImpl::PaSubscribeCb(pa_context *c, pa_subscription_event_type_t t, uint32_t idx,
934 void *userdata)
935 {
936 switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
937 case PA_SUBSCRIPTION_EVENT_SINK:
938 break;
939
940 case PA_SUBSCRIPTION_EVENT_SOURCE:
941 break;
942
943 case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
944 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
945 AUDIO_INFO_LOG("PA_SUBSCRIPTION_EVENT_NEW");
946 g_audioServiceAdapterCallback->OnSetVolumeDbCb();
947 } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
948 AUDIO_INFO_LOG("PA_SUBSCRIPTION_EVENT_REMOVE");
949 }
950 break;
951
952 case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
953 ProcessSourceOutputEvent(c, t, idx, userdata);
954 break;
955
956 default:
957 break;
958 }
959 }
960 } // namespace AudioStandard
961 } // namespace OHOS
962
963 #endif // ST_PULSEAUDIO_AUDIO_SERVICE_ADAPTER_IMPL_H
964