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