1 /*
2 * Copyright (c) 2021-2024 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 #ifndef LOG_TAG
16 #define LOG_TAG "AudioServerDump"
17 #endif
18
19 #include "audio_server_dump.h"
20 #include "audio_utils.h"
21 #include "audio_service.h"
22 #include "pa_adapter_tools.h"
23
24 using namespace std;
25
26 namespace OHOS {
27 namespace AudioStandard {
28
AudioServerDump()29 AudioServerDump::AudioServerDump() : mainLoop(nullptr),
30 api(nullptr),
31 context(nullptr),
32 isMainLoopStarted_(false),
33 isContextConnected_(false)
34 {
35 AUDIO_DEBUG_LOG("AudioServerDump construct");
36 InitDumpFuncMap();
37 }
38
~AudioServerDump()39 AudioServerDump::~AudioServerDump()
40 {
41 ResetPAAudioDump();
42 }
43
InitDumpFuncMap()44 void AudioServerDump::InitDumpFuncMap()
45 {
46 dumpFuncMap[u"-h"] = &AudioServerDump::HelpInfoDump;
47 dumpFuncMap[u"-p"] = &AudioServerDump::PlaybackSinkDump;
48 dumpFuncMap[u"-r"] = &AudioServerDump::RecordSourceDump;
49 dumpFuncMap[u"-m"] = &AudioServerDump::HDFModulesDump;
50 dumpFuncMap[u"-ep"] = &AudioServerDump::PolicyHandlerDump;
51 }
52
ResetPAAudioDump()53 void AudioServerDump::ResetPAAudioDump()
54 {
55 lock_guard<mutex> lock(ctrlMutex_);
56 if (mainLoop && (isMainLoopStarted_ == true)) {
57 pa_threaded_mainloop_stop(mainLoop);
58 }
59
60 if (context) {
61 pa_context_set_state_callback(context, nullptr, nullptr);
62 if (isContextConnected_ == true) {
63 AUDIO_INFO_LOG("[AudioServerDump] disconnect context!");
64 pa_context_disconnect(context);
65 }
66 pa_context_unref(context);
67 }
68
69 if (mainLoop) {
70 pa_threaded_mainloop_free(mainLoop);
71 }
72
73 isMainLoopStarted_ = false;
74 isContextConnected_ = false;
75 mainLoop = nullptr;
76 context = nullptr;
77 api = nullptr;
78 }
79
Initialize()80 int32_t AudioServerDump::Initialize()
81 {
82 mainLoop = pa_threaded_mainloop_new();
83 if (mainLoop == nullptr) {
84 return AUDIO_DUMP_INIT_ERR;
85 }
86
87 api = pa_threaded_mainloop_get_api(mainLoop);
88 if (api == nullptr) {
89 ResetPAAudioDump();
90 return AUDIO_DUMP_INIT_ERR;
91 }
92
93 context = pa_context_new(api, "AudioServerDump");
94 if (context == nullptr) {
95 ResetPAAudioDump();
96 return AUDIO_DUMP_INIT_ERR;
97 }
98
99 pa_context_set_state_callback(context, PAContextStateCb, mainLoop);
100
101 if (pa_context_connect(context, nullptr, PA_CONTEXT_NOFAIL, nullptr) < 0) {
102 int error = pa_context_errno(context);
103 AUDIO_ERR_LOG("context connect error: %{public}s", pa_strerror(error));
104 ResetPAAudioDump();
105 return AUDIO_DUMP_INIT_ERR;
106 }
107
108 isContextConnected_ = true;
109 PaLockGuard lock(mainLoop);
110
111 if (pa_threaded_mainloop_start(mainLoop) < 0) {
112 AUDIO_ERR_LOG("Audio Service not started");
113 ResetPAAudioDump();
114 return AUDIO_DUMP_INIT_ERR;
115 }
116
117 isMainLoopStarted_ = true;
118 while (isMainLoopStarted_) {
119 pa_context_state_t state = pa_context_get_state(context);
120 if (state == PA_CONTEXT_READY) {
121 break;
122 }
123
124 if (!PA_CONTEXT_IS_GOOD(state)) {
125 int error = pa_context_errno(context);
126 AUDIO_ERR_LOG("context bad state error: %{public}s", pa_strerror(error));
127 ResetPAAudioDump();
128 return AUDIO_DUMP_INIT_ERR;
129 }
130
131 pa_threaded_mainloop_wait(mainLoop);
132 }
133
134 return AUDIO_DUMP_SUCCESS;
135 }
136
OnTimeOut()137 void AudioServerDump::OnTimeOut()
138 {
139 PaLockGuard lock(mainLoop);
140 pa_threaded_mainloop_signal(mainLoop, 0);
141 }
142
IsEndWith(const std::string & mainStr,const std::string & toMatch)143 bool AudioServerDump::IsEndWith(const std::string &mainStr, const std::string &toMatch)
144 {
145 if (mainStr.size() >= toMatch.size() &&
146 mainStr.compare(mainStr.size() - toMatch.size(), toMatch.size(), toMatch) == 0) {
147 return true;
148 }
149 return false;
150 }
151
IsValidModule(const std::string moduleName)152 bool AudioServerDump::IsValidModule(const std::string moduleName)
153 {
154 if (moduleName.rfind("fifo", 0) == SUCCESS) {
155 return false; // Module starts with fifo, Not valid module
156 }
157
158 if (IsEndWith(moduleName, "monitor")) {
159 return false; // Module ends with monitor, Not valid module
160 }
161 return true;
162 }
163
ServerDataDump(string & dumpString)164 void AudioServerDump::ServerDataDump(string &dumpString)
165 {
166 PlaybackSinkDump(dumpString);
167 RecordSourceDump(dumpString);
168 HDFModulesDump(dumpString);
169 PolicyHandlerDump(dumpString);
170 }
171
ArgDataDump(std::string & dumpString,std::queue<std::u16string> & argQue)172 void AudioServerDump::ArgDataDump(std::string &dumpString, std::queue<std::u16string>& argQue)
173 {
174 dumpString += "AudioServer Data Dump:\n\n";
175 if (argQue.empty()) {
176 ServerDataDump(dumpString);
177 return;
178 }
179 while (!argQue.empty()) {
180 std::u16string para = argQue.front();
181 if (para == u"-h") {
182 dumpString.clear();
183 (this->*dumpFuncMap[para])(dumpString);
184 return;
185 } else if (dumpFuncMap.count(para) == 0) {
186 dumpString.clear();
187 AppendFormat(dumpString, "Please input correct param:\n");
188 HelpInfoDump(dumpString);
189 return;
190 } else {
191 (this->*dumpFuncMap[para])(dumpString);
192 }
193 argQue.pop();
194 }
195 }
196
HelpInfoDump(string & dumpString)197 void AudioServerDump::HelpInfoDump(string &dumpString)
198 {
199 AppendFormat(dumpString, "usage:\n");
200 AppendFormat(dumpString, " -h\t\t\t|help text for hidumper audio\n");
201 AppendFormat(dumpString, " -p\t\t\t|dump pa playback streams\n");
202 AppendFormat(dumpString, " -r\t\t\t|dump pa record streams\n");
203 AppendFormat(dumpString, " -m\t\t\t|dump hdf input modules\n");
204 AppendFormat(dumpString, " -ep\t\t\t|dump policyhandler info\n");
205 }
206
AudioDataDump(string & dumpString,std::queue<std::u16string> & argQue)207 void AudioServerDump::AudioDataDump(string &dumpString, std::queue<std::u16string>& argQue)
208 {
209 if (mainLoop == nullptr || context == nullptr) {
210 AUDIO_ERR_LOG("Audio Service Not running");
211 return;
212 }
213
214 PaLockGuard lock(mainLoop);
215 pa_operation *operation = nullptr;
216 operation = pa_context_get_sink_info_list(context,
217 AudioServerDump::PASinkInfoCallback, reinterpret_cast<void *>(this));
218
219 while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) {
220 pa_threaded_mainloop_wait(mainLoop);
221 }
222
223 pa_operation_unref(operation);
224 operation = pa_context_get_sink_input_info_list(context,
225 AudioServerDump::PASinkInputInfoCallback, reinterpret_cast<void *>(this));
226
227 while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) {
228 pa_threaded_mainloop_wait(mainLoop);
229 }
230
231 pa_operation_unref(operation);
232 operation = pa_context_get_source_info_list(context,
233 AudioServerDump::PASourceInfoCallback, reinterpret_cast<void *>(this));
234
235 while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) {
236 pa_threaded_mainloop_wait(mainLoop);
237 }
238
239 pa_operation_unref(operation);
240 operation = pa_context_get_source_output_info_list(context,
241 AudioServerDump::PASourceOutputInfoCallback, reinterpret_cast<void *>(this));
242
243 while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) {
244 pa_threaded_mainloop_wait(mainLoop);
245 }
246
247 pa_operation_unref(operation);
248
249 ArgDataDump(dumpString, argQue);
250
251 return;
252 }
253
PAContextStateCb(pa_context * context,void * userdata)254 void AudioServerDump::PAContextStateCb(pa_context *context, void *userdata)
255 {
256 pa_threaded_mainloop *mainLoop = reinterpret_cast<pa_threaded_mainloop *>(userdata);
257
258 switch (pa_context_get_state(context)) {
259 case PA_CONTEXT_READY:
260 case PA_CONTEXT_TERMINATED:
261 case PA_CONTEXT_FAILED:
262 pa_threaded_mainloop_signal(mainLoop, 0);
263 break;
264
265 case PA_CONTEXT_UNCONNECTED:
266 case PA_CONTEXT_CONNECTING:
267 case PA_CONTEXT_AUTHORIZING:
268 case PA_CONTEXT_SETTING_NAME:
269 default:
270 break;
271 }
272 return;
273 }
274
PASinkInfoCallback(pa_context * c,const pa_sink_info * i,int eol,void * userdata)275 void AudioServerDump::PASinkInfoCallback(pa_context *c, const pa_sink_info *i, int eol, void *userdata)
276 {
277 AudioServerDump *asDump = reinterpret_cast<AudioServerDump *>(userdata);
278 CHECK_AND_RETURN_LOG(asDump != nullptr, "Failed to get sink information");
279
280 pa_threaded_mainloop *mainLoop = reinterpret_cast<pa_threaded_mainloop *>(asDump->mainLoop);
281
282 CHECK_AND_RETURN_LOG(eol >= 0, "Failed to get sink information: %{public}s", pa_strerror(pa_context_errno(c)));
283
284 if (eol) {
285 pa_threaded_mainloop_signal(mainLoop, 0);
286 return;
287 }
288
289 SinkSourceInfo sinkInfo;
290
291 if (i->name != nullptr) {
292 string sinkName(i->name);
293 if (IsValidModule(sinkName)) {
294 (sinkInfo.name).assign(sinkName);
295 sinkInfo.sampleSpec = i->sample_spec;
296 asDump->streamData_.sinkDevices.push_back(sinkInfo);
297 }
298 }
299 }
300
PASinkInputInfoCallback(pa_context * c,const pa_sink_input_info * i,int eol,void * userdata)301 void AudioServerDump::PASinkInputInfoCallback(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata)
302 {
303 AudioServerDump *asDump = reinterpret_cast<AudioServerDump *>(userdata);
304 CHECK_AND_RETURN_LOG(asDump != nullptr, "Failed to get sink input information");
305 pa_threaded_mainloop *mainLoop = reinterpret_cast<pa_threaded_mainloop *>(asDump->mainLoop);
306 CHECK_AND_RETURN_LOG(eol >= 0, "Failed to get sink input information: %{public}s",
307 pa_strerror(pa_context_errno(c)));
308 if (eol) {
309 pa_threaded_mainloop_signal(mainLoop, 0);
310 return;
311 }
312 InputOutputInfo sinkInputInfo;
313 sinkInputInfo.sampleSpec = i->sample_spec;
314 sinkInputInfo.corked = i->corked;
315 if (i->proplist != nullptr) {
316 const char *applicationname = pa_proplist_gets(i->proplist, "application.name");
317 const char *processid = pa_proplist_gets(i->proplist, "application.process.id");
318 const char *user = pa_proplist_gets(i->proplist, "application.process.user");
319 const char *sessionid = pa_proplist_gets(i->proplist, "stream.sessionID");
320 const char *sessionstarttime = pa_proplist_gets(i->proplist, "stream.startTime");
321 const char *privacytype = pa_proplist_gets(i->proplist, "stream.privacyType");
322 if (applicationname != nullptr) {
323 string applicationName(applicationname);
324 (sinkInputInfo.applicationName).assign(applicationName);
325 }
326 if (processid != nullptr) {
327 string processId(processid);
328 (sinkInputInfo.processId).assign(processId);
329 }
330 if (user != nullptr) {
331 struct passwd *p;
332 if ((p = getpwnam(user)) != nullptr) {
333 sinkInputInfo.userId = uint32_t(p->pw_uid);
334 }
335 }
336 if (sessionid != nullptr) {
337 string sessionId(sessionid);
338 (sinkInputInfo.sessionId).assign(sessionId);
339 }
340 if (sessionstarttime != nullptr) {
341 string sessionStartTime(sessionstarttime);
342 (sinkInputInfo.sessionStartTime).assign(sessionStartTime);
343 }
344 if (privacytype != nullptr) {
345 string privacyType(privacytype);
346 (sinkInputInfo.privacyType).assign(privacyType);
347 }
348 }
349 asDump->streamData_.sinkInputs.push_back(sinkInputInfo);
350 }
351
PASourceInfoCallback(pa_context * c,const pa_source_info * i,int eol,void * userdata)352 void AudioServerDump::PASourceInfoCallback(pa_context *c, const pa_source_info *i, int eol, void *userdata)
353 {
354 AudioServerDump *asDump = reinterpret_cast<AudioServerDump *>(userdata);
355 CHECK_AND_RETURN_LOG(asDump != nullptr, "Failed to get source information");
356
357 pa_threaded_mainloop *mainLoop = reinterpret_cast<pa_threaded_mainloop *>(asDump->mainLoop);
358 CHECK_AND_RETURN_LOG(eol >= 0, "Failed to get source information: %{public}s",
359 pa_strerror(pa_context_errno(c)));
360
361 if (eol) {
362 pa_threaded_mainloop_signal(mainLoop, 0);
363 return;
364 }
365
366 SinkSourceInfo sourceInfo;
367
368 if (i->name != nullptr) {
369 string sourceName(i->name);
370 if (IsValidModule(sourceName)) {
371 (sourceInfo.name).assign(sourceName);
372 sourceInfo.sampleSpec = i->sample_spec;
373 asDump->streamData_.sourceDevices.push_back(sourceInfo);
374 }
375 }
376 }
377
PASourceOutputInfoCallback(pa_context * c,const pa_source_output_info * i,int eol,void * userdata)378 void AudioServerDump::PASourceOutputInfoCallback(pa_context *c, const pa_source_output_info *i, int eol,
379 void *userdata)
380 {
381 AudioServerDump *asDump = reinterpret_cast<AudioServerDump *>(userdata);
382 CHECK_AND_RETURN_LOG(asDump != nullptr, "Failed to get source output information");
383 pa_threaded_mainloop *mainLoop = reinterpret_cast<pa_threaded_mainloop *>(asDump->mainLoop);
384 CHECK_AND_RETURN_LOG(eol >= 0, "Failed to get source output information: %{public}s",
385 pa_strerror(pa_context_errno(c)));
386 if (eol) {
387 pa_threaded_mainloop_signal(mainLoop, 0);
388 return;
389 }
390 InputOutputInfo sourceOutputInfo;
391 sourceOutputInfo.sampleSpec = i->sample_spec;
392 sourceOutputInfo.corked = i->corked;
393 if (i->proplist != nullptr) {
394 const char *applicationname = pa_proplist_gets(i->proplist, "application.name");
395 const char *processid = pa_proplist_gets(i->proplist, "application.process.id");
396 const char *user = pa_proplist_gets(i->proplist, "application.process.user");
397 const char *sessionid = pa_proplist_gets(i->proplist, "stream.sessionID");
398 const char *sessionstarttime = pa_proplist_gets(i->proplist, "stream.startTime");
399 const char *privacytype = pa_proplist_gets(i->proplist, "stream.privacyType");
400 if (applicationname != nullptr) {
401 string applicationName(applicationname);
402 (sourceOutputInfo.applicationName).assign(applicationName);
403 }
404 if (processid != nullptr) {
405 string processId(processid);
406 (sourceOutputInfo.processId).assign(processId);
407 }
408 if (user != nullptr) {
409 struct passwd *p;
410 if ((p = getpwnam(user)) != nullptr) {
411 sourceOutputInfo.userId = uint32_t(p->pw_uid);
412 }
413 }
414 if (sessionid != nullptr) {
415 string sessionId(sessionid);
416 (sourceOutputInfo.sessionId).assign(sessionId);
417 }
418 if (sessionstarttime != nullptr) {
419 string sessionStartTime(sessionstarttime);
420 (sourceOutputInfo.sessionStartTime).assign(sessionStartTime);
421 }
422 if (privacytype != nullptr) {
423 string privacyType(privacytype);
424 (sourceOutputInfo.privacyType).assign(privacyType);
425 }
426 }
427 asDump->streamData_.sourceOutputs.push_back(sourceOutputInfo);
428 }
429
PlaybackSinkDump(std::string & dumpString)430 void AudioServerDump::PlaybackSinkDump(std::string &dumpString)
431 {
432 AUDIO_INFO_LOG("PlaybackSinkDump enter");
433 char s[PA_SAMPLE_SPEC_SNPRINT_MAX];
434
435 dumpString += "Playback Streams\n";
436
437 AppendFormat(dumpString, "- %zu Playback stream (s) available:\n", streamData_.sinkInputs.size());
438
439 for (auto it = streamData_.sinkInputs.begin(); it != streamData_.sinkInputs.end(); it++) {
440 InputOutputInfo sinkInputInfo = *it;
441
442 AppendFormat(dumpString, " Stream %d\n", it - streamData_.sinkInputs.begin() + 1);
443 AppendFormat(dumpString, " - Stream Id: %s\n", (sinkInputInfo.sessionId).c_str());
444 AppendFormat(dumpString, " - Application Name: %s\n", ((sinkInputInfo.applicationName).c_str()));
445 AppendFormat(dumpString, " - Process Id: %s\n", (sinkInputInfo.processId).c_str());
446 AppendFormat(dumpString, " - User Id: %u\n", sinkInputInfo.userId);
447 AppendFormat(dumpString, " - stream can be captured: %s\n",
448 sinkInputInfo.privacyType == "0" ? "true" : "false");
449
450 char *inputSampleSpec = pa_sample_spec_snprint(s, sizeof(s), &(sinkInputInfo.sampleSpec));
451 AppendFormat(dumpString, " - Stream Configuration: %s\n", inputSampleSpec);
452 dumpString += " - Status:";
453 dumpString += (sinkInputInfo.corked) ? "STOPPED/PAUSED" : "RUNNING";
454 AppendFormat(dumpString, "\n - Stream Start Time: %s\n", (sinkInputInfo.sessionStartTime).c_str());
455 dumpString += "\n";
456 }
457 }
458
RecordSourceDump(std::string & dumpString)459 void AudioServerDump::RecordSourceDump(std::string &dumpString)
460 {
461 char s[PA_SAMPLE_SPEC_SNPRINT_MAX];
462 dumpString += "Record Streams \n";
463 AppendFormat(dumpString, "- %zu Record stream (s) available:\n", streamData_.sourceOutputs.size());
464
465 for (auto it = streamData_.sourceOutputs.begin(); it != streamData_.sourceOutputs.end(); it++) {
466 InputOutputInfo sourceOutputInfo = *it;
467 AppendFormat(dumpString, " Stream %d\n", it - streamData_.sourceOutputs.begin() + 1);
468 AppendFormat(dumpString, " - Stream Id: %s\n", (sourceOutputInfo.sessionId).c_str());
469 AppendFormat(dumpString, " - Application Name: %s\n", (sourceOutputInfo.applicationName).c_str());
470 AppendFormat(dumpString, " - Process Id: %s\n", sourceOutputInfo.processId.c_str());
471 AppendFormat(dumpString, " - User Id: %u\n", sourceOutputInfo.userId);
472
473 char *outputSampleSpec = pa_sample_spec_snprint(s, sizeof(s), &(sourceOutputInfo.sampleSpec));
474 AppendFormat(dumpString, " - Stream Configuration: %s\n", outputSampleSpec);
475 dumpString += " - Status:";
476 dumpString += (sourceOutputInfo.corked) ? "STOPPED/PAUSED" : "RUNNING";
477 AppendFormat(dumpString, "\n - Stream Start Time: %s\n", (sourceOutputInfo.sessionStartTime).c_str());
478 dumpString += "\n";
479 }
480 }
481
HDFModulesDump(std::string & dumpString)482 void AudioServerDump::HDFModulesDump(std::string &dumpString)
483 {
484 char s[PA_SAMPLE_SPEC_SNPRINT_MAX];
485
486 dumpString += "\nHDF Input Modules\n";
487 AppendFormat(dumpString, "- %zu HDF Input Modules (s) available:\n", streamData_.sourceDevices.size());
488
489 for (auto it = streamData_.sourceDevices.begin(); it != streamData_.sourceDevices.end(); it++) {
490 SinkSourceInfo sourceInfo = *it;
491
492 AppendFormat(dumpString, " Module %d\n", it - streamData_.sourceDevices.begin() + 1);
493 AppendFormat(dumpString, " - Module Name: %s\n", (sourceInfo.name).c_str());
494 char *hdfOutSampleSpec = pa_sample_spec_snprint(s, sizeof(s), &(sourceInfo.sampleSpec));
495 AppendFormat(dumpString, " - Module Configuration: %s\n\n", hdfOutSampleSpec);
496 }
497
498 dumpString += "HDF Output Modules\n";
499 AppendFormat(dumpString, "- %zu HDF Output Modules (s) available:\n", streamData_.sinkDevices.size());
500
501 for (auto it = streamData_.sinkDevices.begin(); it != streamData_.sinkDevices.end(); it++) {
502 SinkSourceInfo sinkInfo = *it;
503 AppendFormat(dumpString, " Module %d\n", it - streamData_.sinkDevices.begin() + 1);
504 AppendFormat(dumpString, " - Module Name: %s\n", (sinkInfo.name).c_str());
505 char *hdfInSampleSpec = pa_sample_spec_snprint(s, sizeof(s), &(sinkInfo.sampleSpec));
506 AppendFormat(dumpString, " - Module Configuration: %s\n\n", hdfInSampleSpec);
507 }
508 }
509
PolicyHandlerDump(std::string & dumpString)510 void AudioServerDump::PolicyHandlerDump(std::string &dumpString)
511 {
512 AUDIO_INFO_LOG("PolicyHandlerDump");
513 AudioService::GetInstance()->Dump(dumpString);
514 }
515 } // namespace AudioStandard
516 } // namespace OHOS
517