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