Merge lp:~phablet-team/telephony-service/multiple_performance_improvements into lp:telephony-service/staging
- multiple_performance_improvements
- Merge into staging
Proposed by
Tiago Salem Herrmann
Status: | Needs review |
---|---|
Proposed branch: | lp:~phablet-team/telephony-service/multiple_performance_improvements |
Merge into: | lp:telephony-service/staging |
Prerequisite: | lp:~phablet-team/telephony-service/audio_route_manager |
Diff against target: |
3337 lines (+41/-1537) 13 files modified
handler/audioroutemanager.cpp (+0/-231) handler/audioroutemanager.h (+0/-75) handler/powerd.h (+0/-34) handler/powerdaudiomodemediator.cpp (+0/-63) handler/powerdaudiomodemediator.h (+0/-46) handler/powerddbus.cpp (+0/-43) handler/powerddbus.h (+0/-38) handler/qpulseaudioengine.cpp (+0/-853) handler/qpulseaudioengine.h (+0/-126) libtelephonyservice/chatentry.cpp (+26/-16) libtelephonyservice/chatentry.h (+1/-0) libtelephonyservice/contactwatcher.cpp (+1/-7) tests/Ubuntu.Telephony/ContactWatcherTest.cpp (+13/-5) |
To merge this branch: | bzr merge lp:~phablet-team/telephony-service/multiple_performance_improvements |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Phablet Team | Pending | ||
Review via email: mp+316375@code.launchpad.net |
Commit message
Multiple performance improvements on Roles Interfaces and ContactWatcher
Description of the change
Multiple performance improvements on Roles Interfaces and ContactWatcher
To post a comment you must log in.
- 1242. By Tiago Salem Herrmann
-
merge parent branch
Unmerged revisions
- 1242. By Tiago Salem Herrmann
-
merge parent branch
- 1241. By Tiago Salem Herrmann
-
merge parent
- 1240. By Tiago Salem Herrmann
-
Multiple performance fixes for contact watcher and roles interface
- 1239. By Tiago Salem Herrmann
-
merge parent
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file 'handler/audioroutemanager.cpp' |
2 | --- handler/audioroutemanager.cpp 1970-01-01 00:00:00 +0000 |
3 | +++ handler/audioroutemanager.cpp 2017-02-06 13:26:33 +0000 |
4 | @@ -0,0 +1,231 @@ |
5 | +/* |
6 | + * Copyright (C) 2016 Canonical, Ltd. |
7 | + * |
8 | + * Authors: |
9 | + * Tiago Salem Herrmann <tiago.herrmann@canonical.com> |
10 | + * |
11 | + * This file is part of telephony-service. |
12 | + * |
13 | + * telephony-service is free software; you can redistribute it and/or modify |
14 | + * it under the terms of the GNU General Public License as published by |
15 | + * the Free Software Foundation; version 3. |
16 | + * |
17 | + * telephony-service is distributed in the hope that it will be useful, |
18 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
19 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
20 | + * GNU General Public License for more details. |
21 | + * |
22 | + * You should have received a copy of the GNU General Public License |
23 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
24 | + */ |
25 | + |
26 | +#include "audioroutemanager.h" |
27 | +#include "telepathyhelper.h" |
28 | +#include "accountentry.h" |
29 | +#include <TelepathyQt/Contact> |
30 | +#include <TelepathyQt/Functors> |
31 | + |
32 | + |
33 | +static void enable_earpiece() |
34 | +{ |
35 | +#ifdef USE_PULSEAUDIO |
36 | + QPulseAudioEngine::instance()->setCallMode(CallActive, AudioModeBtOrWiredOrEarpiece); |
37 | +#endif |
38 | +} |
39 | + |
40 | +static void enable_normal() |
41 | +{ |
42 | +#ifdef USE_PULSEAUDIO |
43 | + QTimer* timer = new QTimer(); |
44 | + timer->setSingleShot(true); |
45 | + QObject::connect(timer, &QTimer::timeout, [=](){ |
46 | + QPulseAudioEngine::instance()->setMicMute(false); |
47 | + QPulseAudioEngine::instance()->setCallMode(CallEnded, AudioModeWiredOrSpeaker); |
48 | + timer->deleteLater(); |
49 | + }); |
50 | + timer->start(2000); |
51 | +#endif |
52 | +} |
53 | + |
54 | +static void enable_speaker() |
55 | +{ |
56 | +#ifdef USE_PULSEAUDIO |
57 | + QPulseAudioEngine::instance()->setCallMode(CallActive, AudioModeSpeaker); |
58 | +#endif |
59 | +} |
60 | + |
61 | +static void enable_ringtone() |
62 | +{ |
63 | +#ifdef USE_PULSEAUDIO |
64 | + QPulseAudioEngine::instance()->setCallMode(CallRinging, AudioModeBtOrWiredOrSpeaker); |
65 | +#endif |
66 | +} |
67 | + |
68 | +AudioRouteManager *AudioRouteManager::instance() |
69 | +{ |
70 | + static AudioRouteManager *self = new AudioRouteManager(); |
71 | + return self; |
72 | +} |
73 | + |
74 | +AudioRouteManager::AudioRouteManager(QObject *parent) : |
75 | + QObject(parent), mAudioModeMediator(mPowerDDBus) |
76 | +{ |
77 | + TelepathyHelper::instance()->registerChannelObserver("TelephonyServiceHandlerAudioRouteManager"); |
78 | + |
79 | + QObject::connect(TelepathyHelper::instance()->channelObserver(), SIGNAL(callChannelAvailable(Tp::CallChannelPtr)), |
80 | + this, SLOT(onCallChannelAvailable(Tp::CallChannelPtr))); |
81 | + |
82 | +#ifdef USE_PULSEAUDIO |
83 | + // update audio modes |
84 | + QObject::connect(QPulseAudioEngine::instance(), SIGNAL(audioModeChanged(AudioMode)), SLOT(onAudioModeChanged(AudioMode))); |
85 | + QObject::connect(QPulseAudioEngine::instance(), SIGNAL(availableAudioModesChanged(AudioModes)), SLOT(onAvailableAudioModesChanged(AudioModes))); |
86 | + |
87 | + // check if we should indeed use pulseaudio |
88 | + QByteArray pulseAudioDisabled = qgetenv("PA_DISABLED"); |
89 | + mHasPulseAudio = true; |
90 | + if (!pulseAudioDisabled.isEmpty()) |
91 | + mHasPulseAudio = false; |
92 | +#endif |
93 | + |
94 | + connect(this, &AudioRouteManager::activeAudioOutputChanged, Tp::memFun(&mAudioModeMediator, &PowerDAudioModeMediator::audioModeChanged)); |
95 | + connect(this, &AudioRouteManager::lastChannelClosed, Tp::memFun(&mAudioModeMediator, &PowerDAudioModeMediator::audioOutputClosed)); |
96 | +} |
97 | + |
98 | +void AudioRouteManager::onCallChannelAvailable(Tp::CallChannelPtr callChannel) |
99 | +{ |
100 | + connect(callChannel.data(), |
101 | + SIGNAL(callStateChanged(Tp::CallState)), |
102 | + SLOT(onCallStateChanged(Tp::CallState))); |
103 | + |
104 | + mChannels.append(callChannel); |
105 | + updateAudioRoute(true); |
106 | +} |
107 | + |
108 | +void AudioRouteManager::onCallStateChanged(Tp::CallState state) |
109 | +{ |
110 | + Tp::CallChannelPtr channel(qobject_cast<Tp::CallChannel*>(sender())); |
111 | + if (!channel) { |
112 | + return; |
113 | + } |
114 | + |
115 | + if (channel->callState() == Tp::CallStateEnded) { |
116 | + mChannels.removeOne(channel); |
117 | + } |
118 | + updateAudioRoute(false); |
119 | +} |
120 | + |
121 | +void AudioRouteManager::setActiveAudioOutput(const QString &id) |
122 | +{ |
123 | +#ifdef USE_PULSEAUDIO |
124 | + // fallback to earpiece/headset |
125 | + AudioMode mode = AudioModeWiredOrEarpiece; |
126 | + if (id == "bluetooth") { |
127 | + mode = AudioModeBluetooth; |
128 | + } else if (id == "speaker") { |
129 | + mode = AudioModeSpeaker; |
130 | + } |
131 | + if (mHasPulseAudio) |
132 | + QPulseAudioEngine::instance()->setCallMode(CallActive, mode); |
133 | +#endif |
134 | +} |
135 | + |
136 | +QString AudioRouteManager::activeAudioOutput() |
137 | +{ |
138 | + return mActiveAudioOutput; |
139 | +} |
140 | + |
141 | +AudioOutputDBusList AudioRouteManager::audioOutputs() const |
142 | +{ |
143 | + return mAudioOutputs; |
144 | +} |
145 | + |
146 | +void AudioRouteManager::updateAudioRoute(bool newCall) |
147 | +{ |
148 | +#ifdef USE_PULSEAUDIO |
149 | + if (!mHasPulseAudio) |
150 | + return; |
151 | +#endif |
152 | + |
153 | + int currentCalls = mChannels.size(); |
154 | + if (currentCalls != 0) { |
155 | + if (currentCalls == 1) { |
156 | + // if we have only one call, check if it's incoming and |
157 | + // enable speaker mode so the ringtone is audible |
158 | + Tp::CallChannelPtr callChannel = mChannels.first(); |
159 | + AccountEntry *accountEntry = TelepathyHelper::instance()->accountForConnection(callChannel->connection()); |
160 | + if (!accountEntry || !callChannel) { |
161 | + return; |
162 | + } |
163 | + |
164 | + bool incoming = callChannel->initiatorContact() != accountEntry->account()->connection()->selfContact(); |
165 | + Tp::CallState state = callChannel->callState(); |
166 | + if (incoming && newCall) { |
167 | + enable_ringtone(); |
168 | + return; |
169 | + } |
170 | + if (state == Tp::CallStateEnded) { |
171 | + enable_normal(); |
172 | + return; |
173 | + } |
174 | + // if only one call and dialing, or incoming call just accepted, then default to earpiece |
175 | + if (newCall || (state == Tp::CallStateAccepted && incoming)) { |
176 | + enable_earpiece(); |
177 | + return; |
178 | + } |
179 | + } |
180 | + } else { |
181 | + enable_normal(); |
182 | + Q_EMIT lastChannelClosed(); |
183 | + } |
184 | +} |
185 | + |
186 | +#ifdef USE_PULSEAUDIO |
187 | +void AudioRouteManager::onAudioModeChanged(AudioMode mode) |
188 | +{ |
189 | + qDebug("PulseAudio audio mode changed: 0x%x", mode); |
190 | + |
191 | + if (mode == AudioModeEarpiece && mActiveAudioOutput != "earpiece") { |
192 | + mActiveAudioOutput = "earpiece"; |
193 | + } else if (mode == AudioModeWiredHeadset && mActiveAudioOutput != "wired_headset") { |
194 | + mActiveAudioOutput = "wired_headset"; |
195 | + } else if (mode == AudioModeSpeaker && mActiveAudioOutput != "speaker") { |
196 | + mActiveAudioOutput = "speaker"; |
197 | + } else if (mode == AudioModeBluetooth && mActiveAudioOutput != "bluetooth") { |
198 | + mActiveAudioOutput = "bluetooth"; |
199 | + } |
200 | + Q_EMIT activeAudioOutputChanged(mActiveAudioOutput); |
201 | +} |
202 | + |
203 | +void AudioRouteManager::onAvailableAudioModesChanged(AudioModes modes) |
204 | +{ |
205 | + qDebug("PulseAudio available audio modes changed"); |
206 | + bool defaultFound = false; |
207 | + mAudioOutputs.clear(); |
208 | + Q_FOREACH(const AudioMode &mode, modes) { |
209 | + AudioOutputDBus output; |
210 | + if (mode == AudioModeBluetooth) { |
211 | + // there can be only one bluetooth |
212 | + output.id = "bluetooth"; |
213 | + output.type = "bluetooth"; |
214 | + // we dont support names for now, so we set a default value |
215 | + output.name = "bluetooth"; |
216 | + } else if (mode == AudioModeEarpiece || mode == AudioModeWiredHeadset) { |
217 | + if (!defaultFound) { |
218 | + defaultFound = true; |
219 | + output.id = "default"; |
220 | + output.type = "default"; |
221 | + output.name = "default"; |
222 | + } else { |
223 | + continue; |
224 | + } |
225 | + } else if (mode == AudioModeSpeaker) { |
226 | + output.id = "speaker"; |
227 | + output.type = "speaker"; |
228 | + output.name = "speaker"; |
229 | + } |
230 | + mAudioOutputs << output; |
231 | + } |
232 | + Q_EMIT audioOutputsChanged(mAudioOutputs); |
233 | +} |
234 | +#endif |
235 | + |
236 | |
237 | === removed file 'handler/audioroutemanager.cpp' |
238 | --- handler/audioroutemanager.cpp 2017-02-06 13:26:33 +0000 |
239 | +++ handler/audioroutemanager.cpp 1970-01-01 00:00:00 +0000 |
240 | @@ -1,231 +0,0 @@ |
241 | -/* |
242 | - * Copyright (C) 2016 Canonical, Ltd. |
243 | - * |
244 | - * Authors: |
245 | - * Tiago Salem Herrmann <tiago.herrmann@canonical.com> |
246 | - * |
247 | - * This file is part of telephony-service. |
248 | - * |
249 | - * telephony-service is free software; you can redistribute it and/or modify |
250 | - * it under the terms of the GNU General Public License as published by |
251 | - * the Free Software Foundation; version 3. |
252 | - * |
253 | - * telephony-service is distributed in the hope that it will be useful, |
254 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
255 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
256 | - * GNU General Public License for more details. |
257 | - * |
258 | - * You should have received a copy of the GNU General Public License |
259 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
260 | - */ |
261 | - |
262 | -#include "audioroutemanager.h" |
263 | -#include "telepathyhelper.h" |
264 | -#include "accountentry.h" |
265 | -#include <TelepathyQt/Contact> |
266 | -#include <TelepathyQt/Functors> |
267 | - |
268 | - |
269 | -static void enable_earpiece() |
270 | -{ |
271 | -#ifdef USE_PULSEAUDIO |
272 | - QPulseAudioEngine::instance()->setCallMode(CallActive, AudioModeBtOrWiredOrEarpiece); |
273 | -#endif |
274 | -} |
275 | - |
276 | -static void enable_normal() |
277 | -{ |
278 | -#ifdef USE_PULSEAUDIO |
279 | - QTimer* timer = new QTimer(); |
280 | - timer->setSingleShot(true); |
281 | - QObject::connect(timer, &QTimer::timeout, [=](){ |
282 | - QPulseAudioEngine::instance()->setMicMute(false); |
283 | - QPulseAudioEngine::instance()->setCallMode(CallEnded, AudioModeWiredOrSpeaker); |
284 | - timer->deleteLater(); |
285 | - }); |
286 | - timer->start(2000); |
287 | -#endif |
288 | -} |
289 | - |
290 | -static void enable_speaker() |
291 | -{ |
292 | -#ifdef USE_PULSEAUDIO |
293 | - QPulseAudioEngine::instance()->setCallMode(CallActive, AudioModeSpeaker); |
294 | -#endif |
295 | -} |
296 | - |
297 | -static void enable_ringtone() |
298 | -{ |
299 | -#ifdef USE_PULSEAUDIO |
300 | - QPulseAudioEngine::instance()->setCallMode(CallRinging, AudioModeBtOrWiredOrSpeaker); |
301 | -#endif |
302 | -} |
303 | - |
304 | -AudioRouteManager *AudioRouteManager::instance() |
305 | -{ |
306 | - static AudioRouteManager *self = new AudioRouteManager(); |
307 | - return self; |
308 | -} |
309 | - |
310 | -AudioRouteManager::AudioRouteManager(QObject *parent) : |
311 | - QObject(parent), mAudioModeMediator(mPowerDDBus) |
312 | -{ |
313 | - TelepathyHelper::instance()->registerChannelObserver("TelephonyServiceHandlerAudioRouteManager"); |
314 | - |
315 | - QObject::connect(TelepathyHelper::instance()->channelObserver(), SIGNAL(callChannelAvailable(Tp::CallChannelPtr)), |
316 | - this, SLOT(onCallChannelAvailable(Tp::CallChannelPtr))); |
317 | - |
318 | -#ifdef USE_PULSEAUDIO |
319 | - // update audio modes |
320 | - QObject::connect(QPulseAudioEngine::instance(), SIGNAL(audioModeChanged(AudioMode)), SLOT(onAudioModeChanged(AudioMode))); |
321 | - QObject::connect(QPulseAudioEngine::instance(), SIGNAL(availableAudioModesChanged(AudioModes)), SLOT(onAvailableAudioModesChanged(AudioModes))); |
322 | - |
323 | - // check if we should indeed use pulseaudio |
324 | - QByteArray pulseAudioDisabled = qgetenv("PA_DISABLED"); |
325 | - mHasPulseAudio = true; |
326 | - if (!pulseAudioDisabled.isEmpty()) |
327 | - mHasPulseAudio = false; |
328 | -#endif |
329 | - |
330 | - connect(this, &AudioRouteManager::activeAudioOutputChanged, Tp::memFun(&mAudioModeMediator, &PowerDAudioModeMediator::audioModeChanged)); |
331 | - connect(this, &AudioRouteManager::lastChannelClosed, Tp::memFun(&mAudioModeMediator, &PowerDAudioModeMediator::audioOutputClosed)); |
332 | -} |
333 | - |
334 | -void AudioRouteManager::onCallChannelAvailable(Tp::CallChannelPtr callChannel) |
335 | -{ |
336 | - connect(callChannel.data(), |
337 | - SIGNAL(callStateChanged(Tp::CallState)), |
338 | - SLOT(onCallStateChanged(Tp::CallState))); |
339 | - |
340 | - mChannels.append(callChannel); |
341 | - updateAudioRoute(true); |
342 | -} |
343 | - |
344 | -void AudioRouteManager::onCallStateChanged(Tp::CallState state) |
345 | -{ |
346 | - Tp::CallChannelPtr channel(qobject_cast<Tp::CallChannel*>(sender())); |
347 | - if (!channel) { |
348 | - return; |
349 | - } |
350 | - |
351 | - if (channel->callState() == Tp::CallStateEnded) { |
352 | - mChannels.removeOne(channel); |
353 | - } |
354 | - updateAudioRoute(false); |
355 | -} |
356 | - |
357 | -void AudioRouteManager::setActiveAudioOutput(const QString &id) |
358 | -{ |
359 | -#ifdef USE_PULSEAUDIO |
360 | - // fallback to earpiece/headset |
361 | - AudioMode mode = AudioModeWiredOrEarpiece; |
362 | - if (id == "bluetooth") { |
363 | - mode = AudioModeBluetooth; |
364 | - } else if (id == "speaker") { |
365 | - mode = AudioModeSpeaker; |
366 | - } |
367 | - if (mHasPulseAudio) |
368 | - QPulseAudioEngine::instance()->setCallMode(CallActive, mode); |
369 | -#endif |
370 | -} |
371 | - |
372 | -QString AudioRouteManager::activeAudioOutput() |
373 | -{ |
374 | - return mActiveAudioOutput; |
375 | -} |
376 | - |
377 | -AudioOutputDBusList AudioRouteManager::audioOutputs() const |
378 | -{ |
379 | - return mAudioOutputs; |
380 | -} |
381 | - |
382 | -void AudioRouteManager::updateAudioRoute(bool newCall) |
383 | -{ |
384 | -#ifdef USE_PULSEAUDIO |
385 | - if (!mHasPulseAudio) |
386 | - return; |
387 | -#endif |
388 | - |
389 | - int currentCalls = mChannels.size(); |
390 | - if (currentCalls != 0) { |
391 | - if (currentCalls == 1) { |
392 | - // if we have only one call, check if it's incoming and |
393 | - // enable speaker mode so the ringtone is audible |
394 | - Tp::CallChannelPtr callChannel = mChannels.first(); |
395 | - AccountEntry *accountEntry = TelepathyHelper::instance()->accountForConnection(callChannel->connection()); |
396 | - if (!accountEntry || !callChannel) { |
397 | - return; |
398 | - } |
399 | - |
400 | - bool incoming = callChannel->initiatorContact() != accountEntry->account()->connection()->selfContact(); |
401 | - Tp::CallState state = callChannel->callState(); |
402 | - if (incoming && newCall) { |
403 | - enable_ringtone(); |
404 | - return; |
405 | - } |
406 | - if (state == Tp::CallStateEnded) { |
407 | - enable_normal(); |
408 | - return; |
409 | - } |
410 | - // if only one call and dialing, or incoming call just accepted, then default to earpiece |
411 | - if (newCall || (state == Tp::CallStateAccepted && incoming)) { |
412 | - enable_earpiece(); |
413 | - return; |
414 | - } |
415 | - } |
416 | - } else { |
417 | - enable_normal(); |
418 | - Q_EMIT lastChannelClosed(); |
419 | - } |
420 | -} |
421 | - |
422 | -#ifdef USE_PULSEAUDIO |
423 | -void AudioRouteManager::onAudioModeChanged(AudioMode mode) |
424 | -{ |
425 | - qDebug("PulseAudio audio mode changed: 0x%x", mode); |
426 | - |
427 | - if (mode == AudioModeEarpiece && mActiveAudioOutput != "earpiece") { |
428 | - mActiveAudioOutput = "earpiece"; |
429 | - } else if (mode == AudioModeWiredHeadset && mActiveAudioOutput != "wired_headset") { |
430 | - mActiveAudioOutput = "wired_headset"; |
431 | - } else if (mode == AudioModeSpeaker && mActiveAudioOutput != "speaker") { |
432 | - mActiveAudioOutput = "speaker"; |
433 | - } else if (mode == AudioModeBluetooth && mActiveAudioOutput != "bluetooth") { |
434 | - mActiveAudioOutput = "bluetooth"; |
435 | - } |
436 | - Q_EMIT activeAudioOutputChanged(mActiveAudioOutput); |
437 | -} |
438 | - |
439 | -void AudioRouteManager::onAvailableAudioModesChanged(AudioModes modes) |
440 | -{ |
441 | - qDebug("PulseAudio available audio modes changed"); |
442 | - bool defaultFound = false; |
443 | - mAudioOutputs.clear(); |
444 | - Q_FOREACH(const AudioMode &mode, modes) { |
445 | - AudioOutputDBus output; |
446 | - if (mode == AudioModeBluetooth) { |
447 | - // there can be only one bluetooth |
448 | - output.id = "bluetooth"; |
449 | - output.type = "bluetooth"; |
450 | - // we dont support names for now, so we set a default value |
451 | - output.name = "bluetooth"; |
452 | - } else if (mode == AudioModeEarpiece || mode == AudioModeWiredHeadset) { |
453 | - if (!defaultFound) { |
454 | - defaultFound = true; |
455 | - output.id = "default"; |
456 | - output.type = "default"; |
457 | - output.name = "default"; |
458 | - } else { |
459 | - continue; |
460 | - } |
461 | - } else if (mode == AudioModeSpeaker) { |
462 | - output.id = "speaker"; |
463 | - output.type = "speaker"; |
464 | - output.name = "speaker"; |
465 | - } |
466 | - mAudioOutputs << output; |
467 | - } |
468 | - Q_EMIT audioOutputsChanged(mAudioOutputs); |
469 | -} |
470 | -#endif |
471 | - |
472 | |
473 | === added file 'handler/audioroutemanager.h' |
474 | --- handler/audioroutemanager.h 1970-01-01 00:00:00 +0000 |
475 | +++ handler/audioroutemanager.h 2017-02-06 13:26:33 +0000 |
476 | @@ -0,0 +1,75 @@ |
477 | +/* |
478 | + * Copyright (C) 2016 Canonical, Ltd. |
479 | + * |
480 | + * Authors: |
481 | + * Tiago Salem Herrmann <tiago.herrmann@canonical.com> |
482 | + * |
483 | + * This file is part of telephony-service. |
484 | + * |
485 | + * telephony-service is free software; you can redistribute it and/or modify |
486 | + * it under the terms of the GNU General Public License as published by |
487 | + * the Free Software Foundation; version 3. |
488 | + * |
489 | + * telephony-service is distributed in the hope that it will be useful, |
490 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
491 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
492 | + * GNU General Public License for more details. |
493 | + * |
494 | + * You should have received a copy of the GNU General Public License |
495 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
496 | + */ |
497 | + |
498 | +#ifndef AUDIOROUTEMANAGER_H |
499 | +#define AUDIOROUTEMANAGER_H |
500 | + |
501 | +#ifdef USE_PULSEAUDIO |
502 | +#include "qpulseaudioengine.h" |
503 | +#endif |
504 | +#include "audiooutput.h" |
505 | +#include "powerdaudiomodemediator.h" |
506 | +#include "powerddbus.h" |
507 | +#include <QObject> |
508 | +#include <TelepathyQt/CallChannel> |
509 | + |
510 | + |
511 | +class AudioRouteManager : public QObject |
512 | +{ |
513 | + Q_OBJECT |
514 | + |
515 | +public: |
516 | + static AudioRouteManager *instance(); |
517 | + void setActiveAudioOutput(const QString &id); |
518 | + QString activeAudioOutput(); |
519 | + AudioOutputDBusList audioOutputs() const; |
520 | + void updateAudioRoute(bool newCall = false); |
521 | + |
522 | +public Q_SLOTS: |
523 | + void onCallChannelAvailable(Tp::CallChannelPtr callChannel); |
524 | + |
525 | +Q_SIGNALS: |
526 | + void audioOutputsChanged(const AudioOutputDBusList &outputs); |
527 | + void activeAudioOutputChanged(const QString &id); |
528 | + void lastChannelClosed(); |
529 | + |
530 | +protected Q_SLOTS: |
531 | + void onCallStateChanged(Tp::CallState state); |
532 | + |
533 | +private Q_SLOTS: |
534 | +#ifdef USE_PULSEAUDIO |
535 | + void onAudioModeChanged(AudioMode mode); |
536 | + void onAvailableAudioModesChanged(AudioModes modes); |
537 | +#endif |
538 | + |
539 | +private: |
540 | + explicit AudioRouteManager(QObject *parent = 0); |
541 | + QList<Tp::CallChannelPtr> mChannels; |
542 | + AudioOutputDBusList mAudioOutputs; |
543 | + QString mActiveAudioOutput; |
544 | + PowerDDBus mPowerDDBus; |
545 | + PowerDAudioModeMediator mAudioModeMediator; |
546 | +#ifdef USE_PULSEAUDIO |
547 | + bool mHasPulseAudio; |
548 | +#endif |
549 | +}; |
550 | + |
551 | +#endif // AUDIOROUTEMANAGER_H |
552 | |
553 | === removed file 'handler/audioroutemanager.h' |
554 | --- handler/audioroutemanager.h 2017-02-06 13:26:33 +0000 |
555 | +++ handler/audioroutemanager.h 1970-01-01 00:00:00 +0000 |
556 | @@ -1,75 +0,0 @@ |
557 | -/* |
558 | - * Copyright (C) 2016 Canonical, Ltd. |
559 | - * |
560 | - * Authors: |
561 | - * Tiago Salem Herrmann <tiago.herrmann@canonical.com> |
562 | - * |
563 | - * This file is part of telephony-service. |
564 | - * |
565 | - * telephony-service is free software; you can redistribute it and/or modify |
566 | - * it under the terms of the GNU General Public License as published by |
567 | - * the Free Software Foundation; version 3. |
568 | - * |
569 | - * telephony-service is distributed in the hope that it will be useful, |
570 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
571 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
572 | - * GNU General Public License for more details. |
573 | - * |
574 | - * You should have received a copy of the GNU General Public License |
575 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
576 | - */ |
577 | - |
578 | -#ifndef AUDIOROUTEMANAGER_H |
579 | -#define AUDIOROUTEMANAGER_H |
580 | - |
581 | -#ifdef USE_PULSEAUDIO |
582 | -#include "qpulseaudioengine.h" |
583 | -#endif |
584 | -#include "audiooutput.h" |
585 | -#include "powerdaudiomodemediator.h" |
586 | -#include "powerddbus.h" |
587 | -#include <QObject> |
588 | -#include <TelepathyQt/CallChannel> |
589 | - |
590 | - |
591 | -class AudioRouteManager : public QObject |
592 | -{ |
593 | - Q_OBJECT |
594 | - |
595 | -public: |
596 | - static AudioRouteManager *instance(); |
597 | - void setActiveAudioOutput(const QString &id); |
598 | - QString activeAudioOutput(); |
599 | - AudioOutputDBusList audioOutputs() const; |
600 | - void updateAudioRoute(bool newCall = false); |
601 | - |
602 | -public Q_SLOTS: |
603 | - void onCallChannelAvailable(Tp::CallChannelPtr callChannel); |
604 | - |
605 | -Q_SIGNALS: |
606 | - void audioOutputsChanged(const AudioOutputDBusList &outputs); |
607 | - void activeAudioOutputChanged(const QString &id); |
608 | - void lastChannelClosed(); |
609 | - |
610 | -protected Q_SLOTS: |
611 | - void onCallStateChanged(Tp::CallState state); |
612 | - |
613 | -private Q_SLOTS: |
614 | -#ifdef USE_PULSEAUDIO |
615 | - void onAudioModeChanged(AudioMode mode); |
616 | - void onAvailableAudioModesChanged(AudioModes modes); |
617 | -#endif |
618 | - |
619 | -private: |
620 | - explicit AudioRouteManager(QObject *parent = 0); |
621 | - QList<Tp::CallChannelPtr> mChannels; |
622 | - AudioOutputDBusList mAudioOutputs; |
623 | - QString mActiveAudioOutput; |
624 | - PowerDDBus mPowerDDBus; |
625 | - PowerDAudioModeMediator mAudioModeMediator; |
626 | -#ifdef USE_PULSEAUDIO |
627 | - bool mHasPulseAudio; |
628 | -#endif |
629 | -}; |
630 | - |
631 | -#endif // AUDIOROUTEMANAGER_H |
632 | |
633 | === added file 'handler/powerd.h' |
634 | --- handler/powerd.h 1970-01-01 00:00:00 +0000 |
635 | +++ handler/powerd.h 2017-02-06 13:26:33 +0000 |
636 | @@ -0,0 +1,34 @@ |
637 | +/** |
638 | + * Copyright (C) 2014 Canonical, Ltd. |
639 | + * |
640 | + * This program is free software: you can redistribute it and/or modify it under |
641 | + * the terms of the GNU Lesser General Public License version 3, as published by |
642 | + * the Free Software Foundation. |
643 | + * |
644 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
645 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
646 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
647 | + * Lesser General Public License for more details. |
648 | + * |
649 | + * You should have received a copy of the GNU Lesser General Public License |
650 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
651 | + * |
652 | + * Authors: Andreas Pokorny <andreas.pokorny@canonical.com> |
653 | + */ |
654 | + |
655 | +#ifndef POWERD_H |
656 | +#define POWERD_H |
657 | + |
658 | +class PowerD |
659 | +{ |
660 | +public: |
661 | + PowerD() = default; |
662 | + virtual ~PowerD() = default; |
663 | + PowerD(PowerD const&) = delete; |
664 | + PowerD& operator=(PowerD const&) = delete; |
665 | + |
666 | + virtual void enableProximityHandling() = 0; |
667 | + virtual void disableProximityHandling() = 0; |
668 | +}; |
669 | + |
670 | +#endif // POWERD_H |
671 | |
672 | === removed file 'handler/powerd.h' |
673 | --- handler/powerd.h 2017-02-06 13:26:33 +0000 |
674 | +++ handler/powerd.h 1970-01-01 00:00:00 +0000 |
675 | @@ -1,34 +0,0 @@ |
676 | -/** |
677 | - * Copyright (C) 2014 Canonical, Ltd. |
678 | - * |
679 | - * This program is free software: you can redistribute it and/or modify it under |
680 | - * the terms of the GNU Lesser General Public License version 3, as published by |
681 | - * the Free Software Foundation. |
682 | - * |
683 | - * This program is distributed in the hope that it will be useful, but WITHOUT |
684 | - * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
685 | - * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
686 | - * Lesser General Public License for more details. |
687 | - * |
688 | - * You should have received a copy of the GNU Lesser General Public License |
689 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
690 | - * |
691 | - * Authors: Andreas Pokorny <andreas.pokorny@canonical.com> |
692 | - */ |
693 | - |
694 | -#ifndef POWERD_H |
695 | -#define POWERD_H |
696 | - |
697 | -class PowerD |
698 | -{ |
699 | -public: |
700 | - PowerD() = default; |
701 | - virtual ~PowerD() = default; |
702 | - PowerD(PowerD const&) = delete; |
703 | - PowerD& operator=(PowerD const&) = delete; |
704 | - |
705 | - virtual void enableProximityHandling() = 0; |
706 | - virtual void disableProximityHandling() = 0; |
707 | -}; |
708 | - |
709 | -#endif // POWERD_H |
710 | |
711 | === added file 'handler/powerdaudiomodemediator.cpp' |
712 | --- handler/powerdaudiomodemediator.cpp 1970-01-01 00:00:00 +0000 |
713 | +++ handler/powerdaudiomodemediator.cpp 2017-02-06 13:26:33 +0000 |
714 | @@ -0,0 +1,63 @@ |
715 | +/** |
716 | + * Copyright (C) 2014 Canonical, Ltd. |
717 | + * |
718 | + * This program is free software: you can redistribute it and/or modify it under |
719 | + * the terms of the GNU Lesser General Public License version 3, as published by |
720 | + * the Free Software Foundation. |
721 | + * |
722 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
723 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
724 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
725 | + * Lesser General Public License for more details. |
726 | + * |
727 | + * You should have received a copy of the GNU Lesser General Public License |
728 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
729 | + * |
730 | + * Authors: Andreas Pokorny <andreas.pokorny@canonical.com> |
731 | + */ |
732 | + |
733 | +#include <QDBusInterface> |
734 | +#include "powerdaudiomodemediator.h" |
735 | + |
736 | +PowerDAudioModeMediator::PowerDAudioModeMediator(PowerD &powerd) |
737 | + : powerd(powerd) |
738 | +{ |
739 | +} |
740 | + |
741 | +void PowerDAudioModeMediator::audioModeChanged(const QString &mode) |
742 | +{ |
743 | + bool enableProximity = !(mode == "speaker" || mode == "bluetooth" || mode == "wired_headset"); |
744 | + |
745 | + if (mProximityEnabled != enableProximity) |
746 | + { |
747 | + mProximityEnabled = enableProximity; |
748 | + apply(); |
749 | + } |
750 | +} |
751 | + |
752 | +void PowerDAudioModeMediator::apply() const |
753 | +{ |
754 | + if (mProximityEnabled) { |
755 | + powerd.enableProximityHandling(); |
756 | + } else { |
757 | + // we need to power the screen on before disabling the proximity handling |
758 | + QDBusInterface unityIface("com.canonical.Unity.Screen", |
759 | + "/com/canonical/Unity/Screen", |
760 | + "com.canonical.Unity.Screen", |
761 | + QDBusConnection::systemBus()); |
762 | + QList<QVariant> args; |
763 | + args.append("on"); |
764 | + args.append(3); |
765 | + unityIface.callWithArgumentList(QDBus::NoBlock, "setScreenPowerMode", args); |
766 | + powerd.disableProximityHandling(); |
767 | + } |
768 | +} |
769 | + |
770 | +void PowerDAudioModeMediator::audioOutputClosed() |
771 | +{ |
772 | + if (mProximityEnabled) |
773 | + { |
774 | + mProximityEnabled = false; |
775 | + apply(); |
776 | + } |
777 | +} |
778 | |
779 | === removed file 'handler/powerdaudiomodemediator.cpp' |
780 | --- handler/powerdaudiomodemediator.cpp 2017-02-06 13:26:33 +0000 |
781 | +++ handler/powerdaudiomodemediator.cpp 1970-01-01 00:00:00 +0000 |
782 | @@ -1,63 +0,0 @@ |
783 | -/** |
784 | - * Copyright (C) 2014 Canonical, Ltd. |
785 | - * |
786 | - * This program is free software: you can redistribute it and/or modify it under |
787 | - * the terms of the GNU Lesser General Public License version 3, as published by |
788 | - * the Free Software Foundation. |
789 | - * |
790 | - * This program is distributed in the hope that it will be useful, but WITHOUT |
791 | - * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
792 | - * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
793 | - * Lesser General Public License for more details. |
794 | - * |
795 | - * You should have received a copy of the GNU Lesser General Public License |
796 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
797 | - * |
798 | - * Authors: Andreas Pokorny <andreas.pokorny@canonical.com> |
799 | - */ |
800 | - |
801 | -#include <QDBusInterface> |
802 | -#include "powerdaudiomodemediator.h" |
803 | - |
804 | -PowerDAudioModeMediator::PowerDAudioModeMediator(PowerD &powerd) |
805 | - : powerd(powerd) |
806 | -{ |
807 | -} |
808 | - |
809 | -void PowerDAudioModeMediator::audioModeChanged(const QString &mode) |
810 | -{ |
811 | - bool enableProximity = !(mode == "speaker" || mode == "bluetooth" || mode == "wired_headset"); |
812 | - |
813 | - if (mProximityEnabled != enableProximity) |
814 | - { |
815 | - mProximityEnabled = enableProximity; |
816 | - apply(); |
817 | - } |
818 | -} |
819 | - |
820 | -void PowerDAudioModeMediator::apply() const |
821 | -{ |
822 | - if (mProximityEnabled) { |
823 | - powerd.enableProximityHandling(); |
824 | - } else { |
825 | - // we need to power the screen on before disabling the proximity handling |
826 | - QDBusInterface unityIface("com.canonical.Unity.Screen", |
827 | - "/com/canonical/Unity/Screen", |
828 | - "com.canonical.Unity.Screen", |
829 | - QDBusConnection::systemBus()); |
830 | - QList<QVariant> args; |
831 | - args.append("on"); |
832 | - args.append(3); |
833 | - unityIface.callWithArgumentList(QDBus::NoBlock, "setScreenPowerMode", args); |
834 | - powerd.disableProximityHandling(); |
835 | - } |
836 | -} |
837 | - |
838 | -void PowerDAudioModeMediator::audioOutputClosed() |
839 | -{ |
840 | - if (mProximityEnabled) |
841 | - { |
842 | - mProximityEnabled = false; |
843 | - apply(); |
844 | - } |
845 | -} |
846 | |
847 | === added file 'handler/powerdaudiomodemediator.h' |
848 | --- handler/powerdaudiomodemediator.h 1970-01-01 00:00:00 +0000 |
849 | +++ handler/powerdaudiomodemediator.h 2017-02-06 13:26:33 +0000 |
850 | @@ -0,0 +1,46 @@ |
851 | +/**************************************************************************** |
852 | +** |
853 | +** Copyright (C) 2014 Canonical, Ltd. |
854 | +** |
855 | +** Authors: |
856 | +** Andreas Pokorny <andreas.pokorny@canonical.com> |
857 | +** |
858 | +** GNU Lesser General Public License Usage |
859 | +** Alternatively, this file may be used under the terms of the GNU Lesser |
860 | +** General Public License version 2.1 as published by the Free Software |
861 | +** Foundation and appearing in the file LICENSE.LGPL included in the |
862 | +** packaging of this file. Please review the following information to |
863 | +** ensure the GNU Lesser General Public License version 2.1 requirements |
864 | +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
865 | +** |
866 | +****************************************************************************/ |
867 | + |
868 | +#ifndef POWERDAUDIOMODEMEDIATOR_H |
869 | +#define POWERDAUDIOMODEMEDIATOR_H |
870 | + |
871 | +#include "powerd.h" |
872 | + |
873 | +#include <QString> |
874 | +#include <fstream> |
875 | +#include <memory> |
876 | + |
877 | +class PowerD; |
878 | +/*! |
879 | + * \brief PowerDAudioModeMediator is responsible for configuring proximity |
880 | + * handling of powerd during different call states and used audio outputs. |
881 | + * In General that mean enabling sreen blanking on proximity events, when |
882 | + * a call is active and neither a bluetooth headset nor the speakers are used. |
883 | + */ |
884 | +class PowerDAudioModeMediator |
885 | +{ |
886 | +public: |
887 | + PowerDAudioModeMediator(PowerD &powerd); |
888 | + void audioModeChanged(const QString &mode); |
889 | + void audioOutputClosed(); |
890 | +private: |
891 | + void apply() const; |
892 | + PowerD &powerd; |
893 | + bool mProximityEnabled{false}; |
894 | +}; |
895 | + |
896 | +#endif |
897 | |
898 | === removed file 'handler/powerdaudiomodemediator.h' |
899 | --- handler/powerdaudiomodemediator.h 2017-02-06 13:26:33 +0000 |
900 | +++ handler/powerdaudiomodemediator.h 1970-01-01 00:00:00 +0000 |
901 | @@ -1,46 +0,0 @@ |
902 | -/**************************************************************************** |
903 | -** |
904 | -** Copyright (C) 2014 Canonical, Ltd. |
905 | -** |
906 | -** Authors: |
907 | -** Andreas Pokorny <andreas.pokorny@canonical.com> |
908 | -** |
909 | -** GNU Lesser General Public License Usage |
910 | -** Alternatively, this file may be used under the terms of the GNU Lesser |
911 | -** General Public License version 2.1 as published by the Free Software |
912 | -** Foundation and appearing in the file LICENSE.LGPL included in the |
913 | -** packaging of this file. Please review the following information to |
914 | -** ensure the GNU Lesser General Public License version 2.1 requirements |
915 | -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
916 | -** |
917 | -****************************************************************************/ |
918 | - |
919 | -#ifndef POWERDAUDIOMODEMEDIATOR_H |
920 | -#define POWERDAUDIOMODEMEDIATOR_H |
921 | - |
922 | -#include "powerd.h" |
923 | - |
924 | -#include <QString> |
925 | -#include <fstream> |
926 | -#include <memory> |
927 | - |
928 | -class PowerD; |
929 | -/*! |
930 | - * \brief PowerDAudioModeMediator is responsible for configuring proximity |
931 | - * handling of powerd during different call states and used audio outputs. |
932 | - * In General that mean enabling sreen blanking on proximity events, when |
933 | - * a call is active and neither a bluetooth headset nor the speakers are used. |
934 | - */ |
935 | -class PowerDAudioModeMediator |
936 | -{ |
937 | -public: |
938 | - PowerDAudioModeMediator(PowerD &powerd); |
939 | - void audioModeChanged(const QString &mode); |
940 | - void audioOutputClosed(); |
941 | -private: |
942 | - void apply() const; |
943 | - PowerD &powerd; |
944 | - bool mProximityEnabled{false}; |
945 | -}; |
946 | - |
947 | -#endif |
948 | |
949 | === added file 'handler/powerddbus.cpp' |
950 | --- handler/powerddbus.cpp 1970-01-01 00:00:00 +0000 |
951 | +++ handler/powerddbus.cpp 2017-02-06 13:26:33 +0000 |
952 | @@ -0,0 +1,43 @@ |
953 | +/** |
954 | + * Copyright (C) 2014 Canonical, Ltd. |
955 | + * |
956 | + * This program is free software: you can redistribute it and/or modify it under |
957 | + * the terms of the GNU Lesser General Public License version 3, as published by |
958 | + * the Free Software Foundation. |
959 | + * |
960 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
961 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
962 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
963 | + * Lesser General Public License for more details. |
964 | + * |
965 | + * You should have received a copy of the GNU Lesser General Public License |
966 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
967 | + * |
968 | + * Authors: Andreas Pokorny <andreas.pokorny@canonical.com> |
969 | + */ |
970 | + |
971 | +#include "powerddbus.h" |
972 | + |
973 | +#include <QDBusConnection> |
974 | +#include <QDBusInterface> |
975 | +#include <QDBusReply> |
976 | + |
977 | +PowerDDBus::PowerDDBus() |
978 | + : mPowerDIface{ |
979 | + new QDBusInterface( |
980 | + "com.canonical.powerd", |
981 | + "/com/canonical/powerd", |
982 | + "com.canonical.powerd", |
983 | + QDBusConnection::systemBus())} |
984 | +{ |
985 | +} |
986 | + |
987 | +void PowerDDBus::enableProximityHandling() |
988 | +{ |
989 | + mPowerDIface->call("enableProximityHandling", "telephony-service-handler"); |
990 | +} |
991 | + |
992 | +void PowerDDBus::disableProximityHandling() |
993 | +{ |
994 | + mPowerDIface->call("disableProximityHandling", "telephony-service-handler"); |
995 | +} |
996 | |
997 | === removed file 'handler/powerddbus.cpp' |
998 | --- handler/powerddbus.cpp 2017-02-06 13:26:33 +0000 |
999 | +++ handler/powerddbus.cpp 1970-01-01 00:00:00 +0000 |
1000 | @@ -1,43 +0,0 @@ |
1001 | -/** |
1002 | - * Copyright (C) 2014 Canonical, Ltd. |
1003 | - * |
1004 | - * This program is free software: you can redistribute it and/or modify it under |
1005 | - * the terms of the GNU Lesser General Public License version 3, as published by |
1006 | - * the Free Software Foundation. |
1007 | - * |
1008 | - * This program is distributed in the hope that it will be useful, but WITHOUT |
1009 | - * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1010 | - * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1011 | - * Lesser General Public License for more details. |
1012 | - * |
1013 | - * You should have received a copy of the GNU Lesser General Public License |
1014 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1015 | - * |
1016 | - * Authors: Andreas Pokorny <andreas.pokorny@canonical.com> |
1017 | - */ |
1018 | - |
1019 | -#include "powerddbus.h" |
1020 | - |
1021 | -#include <QDBusConnection> |
1022 | -#include <QDBusInterface> |
1023 | -#include <QDBusReply> |
1024 | - |
1025 | -PowerDDBus::PowerDDBus() |
1026 | - : mPowerDIface{ |
1027 | - new QDBusInterface( |
1028 | - "com.canonical.powerd", |
1029 | - "/com/canonical/powerd", |
1030 | - "com.canonical.powerd", |
1031 | - QDBusConnection::systemBus())} |
1032 | -{ |
1033 | -} |
1034 | - |
1035 | -void PowerDDBus::enableProximityHandling() |
1036 | -{ |
1037 | - mPowerDIface->call("enableProximityHandling", "telephony-service-handler"); |
1038 | -} |
1039 | - |
1040 | -void PowerDDBus::disableProximityHandling() |
1041 | -{ |
1042 | - mPowerDIface->call("disableProximityHandling", "telephony-service-handler"); |
1043 | -} |
1044 | |
1045 | === added file 'handler/powerddbus.h' |
1046 | --- handler/powerddbus.h 1970-01-01 00:00:00 +0000 |
1047 | +++ handler/powerddbus.h 2017-02-06 13:26:33 +0000 |
1048 | @@ -0,0 +1,38 @@ |
1049 | +/** |
1050 | + * Copyright (C) 2014 Canonical, Ltd. |
1051 | + * |
1052 | + * This program is free software: you can redistribute it and/or modify it under |
1053 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1054 | + * the Free Software Foundation. |
1055 | + * |
1056 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1057 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1058 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1059 | + * Lesser General Public License for more details. |
1060 | + * |
1061 | + * You should have received a copy of the GNU Lesser General Public License |
1062 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1063 | + * |
1064 | + * Authors: Andreas Pokorny <andreas.pokorny@canonical.com> |
1065 | + */ |
1066 | + |
1067 | +#ifndef POWERD_DUBS_H |
1068 | +#define POWERD_DBUS_H |
1069 | + |
1070 | +#include "powerd.h" |
1071 | + |
1072 | +#include <memory> |
1073 | + |
1074 | +class QDBusInterface; |
1075 | + |
1076 | +class PowerDDBus : public PowerD |
1077 | +{ |
1078 | +public: |
1079 | + PowerDDBus(); |
1080 | + void enableProximityHandling() override; |
1081 | + void disableProximityHandling() override; |
1082 | +private: |
1083 | + std::unique_ptr<QDBusInterface> mPowerDIface; |
1084 | +}; |
1085 | + |
1086 | +#endif |
1087 | |
1088 | === removed file 'handler/powerddbus.h' |
1089 | --- handler/powerddbus.h 2017-02-06 13:26:33 +0000 |
1090 | +++ handler/powerddbus.h 1970-01-01 00:00:00 +0000 |
1091 | @@ -1,38 +0,0 @@ |
1092 | -/** |
1093 | - * Copyright (C) 2014 Canonical, Ltd. |
1094 | - * |
1095 | - * This program is free software: you can redistribute it and/or modify it under |
1096 | - * the terms of the GNU Lesser General Public License version 3, as published by |
1097 | - * the Free Software Foundation. |
1098 | - * |
1099 | - * This program is distributed in the hope that it will be useful, but WITHOUT |
1100 | - * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1101 | - * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1102 | - * Lesser General Public License for more details. |
1103 | - * |
1104 | - * You should have received a copy of the GNU Lesser General Public License |
1105 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1106 | - * |
1107 | - * Authors: Andreas Pokorny <andreas.pokorny@canonical.com> |
1108 | - */ |
1109 | - |
1110 | -#ifndef POWERD_DUBS_H |
1111 | -#define POWERD_DBUS_H |
1112 | - |
1113 | -#include "powerd.h" |
1114 | - |
1115 | -#include <memory> |
1116 | - |
1117 | -class QDBusInterface; |
1118 | - |
1119 | -class PowerDDBus : public PowerD |
1120 | -{ |
1121 | -public: |
1122 | - PowerDDBus(); |
1123 | - void enableProximityHandling() override; |
1124 | - void disableProximityHandling() override; |
1125 | -private: |
1126 | - std::unique_ptr<QDBusInterface> mPowerDIface; |
1127 | -}; |
1128 | - |
1129 | -#endif |
1130 | |
1131 | === added file 'handler/qpulseaudioengine.cpp' |
1132 | --- handler/qpulseaudioengine.cpp 1970-01-01 00:00:00 +0000 |
1133 | +++ handler/qpulseaudioengine.cpp 2017-02-06 13:26:33 +0000 |
1134 | @@ -0,0 +1,853 @@ |
1135 | +/**************************************************************************** |
1136 | +** |
1137 | +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). |
1138 | +** Contact: http://www.qt-project.org/legal |
1139 | +** |
1140 | +** This file was taken from qt5 and modified by |
1141 | +** David Henningsson <david.henningsson@canonical.com> for usage in |
1142 | +** telepathy-ofono. |
1143 | +** |
1144 | +** GNU Lesser General Public License Usage |
1145 | +** Alternatively, this file may be used under the terms of the GNU Lesser |
1146 | +** General Public License version 2.1 as published by the Free Software |
1147 | +** Foundation and appearing in the file LICENSE.LGPL included in the |
1148 | +** packaging of this file. Please review the following information to |
1149 | +** ensure the GNU Lesser General Public License version 2.1 requirements |
1150 | +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
1151 | +** |
1152 | +****************************************************************************/ |
1153 | + |
1154 | +#include <QtCore/qdebug.h> |
1155 | + |
1156 | +#include "qpulseaudioengine.h" |
1157 | +#include <sys/types.h> |
1158 | +#include <unistd.h> |
1159 | + |
1160 | +#define PULSEAUDIO_PROFILE_HSP "headset_head_unit" |
1161 | +#define PULSEAUDIO_PROFILE_A2DP "a2dp_sink" |
1162 | + |
1163 | +QT_BEGIN_NAMESPACE |
1164 | + |
1165 | +static void contextStateCallbackInit(pa_context *context, void *userdata) |
1166 | +{ |
1167 | + Q_UNUSED(context); |
1168 | + QPulseAudioEngineWorker *pulseEngine = reinterpret_cast<QPulseAudioEngineWorker*>(userdata); |
1169 | + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
1170 | +} |
1171 | + |
1172 | +static void contextStateCallback(pa_context *context, void *userdata) |
1173 | +{ |
1174 | + Q_UNUSED(userdata); |
1175 | + Q_UNUSED(context); |
1176 | +} |
1177 | + |
1178 | +static void success_cb(pa_context *context, int success, void *userdata) |
1179 | +{ |
1180 | + QPulseAudioEngineWorker *pulseEngine = reinterpret_cast<QPulseAudioEngineWorker*>(userdata); |
1181 | + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
1182 | +} |
1183 | + |
1184 | +/* Callbacks used when handling events from PulseAudio */ |
1185 | +static void plug_card_cb(pa_context *c, const pa_card_info *info, int isLast, void *userdata) |
1186 | +{ |
1187 | + QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata); |
1188 | + if (isLast != 0 || !pulseEngine || !info) { |
1189 | + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
1190 | + return; |
1191 | + } |
1192 | + pulseEngine->plugCardCallback(info); |
1193 | +} |
1194 | + |
1195 | +static void update_card_cb(pa_context *c, const pa_card_info *info, int isLast, void *userdata) |
1196 | +{ |
1197 | + QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata); |
1198 | + if (isLast != 0 || !pulseEngine || !info) { |
1199 | + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
1200 | + return; |
1201 | + } |
1202 | + pulseEngine->updateCardCallback(info); |
1203 | +} |
1204 | + |
1205 | +static void unplug_card_cb(pa_context *c, const pa_card_info *info, int isLast, void *userdata) |
1206 | +{ |
1207 | + QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata); |
1208 | + if (!pulseEngine) { |
1209 | + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
1210 | + return; |
1211 | + } |
1212 | + |
1213 | + if (info == NULL) { |
1214 | + /* That means that the card used to query card_info was removed */ |
1215 | + pulseEngine->unplugCardCallback(); |
1216 | + } |
1217 | +} |
1218 | + |
1219 | +static void subscribeCallback(pa_context *context, pa_subscription_event_type_t t, uint32_t idx, void *userdata) |
1220 | +{ |
1221 | + /* For card change events (slot plug/unplug and add/remove card) */ |
1222 | + if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_CARD) { |
1223 | + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { |
1224 | + QMetaObject::invokeMethod((QPulseAudioEngineWorker *) userdata, "handleCardEvent", |
1225 | + Qt::QueuedConnection, Q_ARG(int, PA_SUBSCRIPTION_EVENT_CHANGE), Q_ARG(unsigned int, idx)); |
1226 | + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { |
1227 | + QMetaObject::invokeMethod((QPulseAudioEngineWorker *) userdata, "handleCardEvent", |
1228 | + Qt::QueuedConnection, Q_ARG(int, PA_SUBSCRIPTION_EVENT_NEW), Q_ARG(unsigned int, idx)); |
1229 | + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { |
1230 | + QMetaObject::invokeMethod((QPulseAudioEngineWorker *) userdata, "handleCardEvent", |
1231 | + Qt::QueuedConnection, Q_ARG(int, PA_SUBSCRIPTION_EVENT_REMOVE), Q_ARG(unsigned int, idx)); |
1232 | + } |
1233 | + } |
1234 | +} |
1235 | + |
1236 | +QPulseAudioEngineWorker::QPulseAudioEngineWorker(QObject *parent) |
1237 | + : QObject(parent) |
1238 | + , m_mainLoopApi(0) |
1239 | + , m_context(0) |
1240 | + , m_callstatus(CallEnded) |
1241 | + , m_audiomode(AudioModeSpeaker) |
1242 | + , m_micmute(false) |
1243 | + , m_defaultsink("sink.primary") |
1244 | + , m_defaultsource("source.primary") |
1245 | + , m_voicecallcard("") |
1246 | + , m_voicecallhighest("") |
1247 | + , m_voicecallprofile("") |
1248 | + , m_bt_hsp("") |
1249 | + , m_bt_hsp_a2dp("") |
1250 | + , m_default_bt_card_fallback("") |
1251 | + |
1252 | +{ |
1253 | + m_mainLoop = pa_threaded_mainloop_new(); |
1254 | + if (m_mainLoop == 0) { |
1255 | + qWarning("Unable to create pulseaudio mainloop"); |
1256 | + return; |
1257 | + } |
1258 | + |
1259 | + if (pa_threaded_mainloop_start(m_mainLoop) != 0) { |
1260 | + qWarning("Unable to start pulseaudio mainloop"); |
1261 | + pa_threaded_mainloop_free(m_mainLoop); |
1262 | + m_mainLoop = 0; |
1263 | + return; |
1264 | + } |
1265 | + |
1266 | + createPulseContext(); |
1267 | +} |
1268 | + |
1269 | +bool QPulseAudioEngineWorker::createPulseContext() |
1270 | +{ |
1271 | + bool keepGoing = true; |
1272 | + bool ok = true; |
1273 | + |
1274 | + if (m_context) |
1275 | + return true; |
1276 | + |
1277 | + m_mainLoopApi = pa_threaded_mainloop_get_api(m_mainLoop); |
1278 | + |
1279 | + pa_threaded_mainloop_lock(m_mainLoop); |
1280 | + |
1281 | + m_context = pa_context_new(m_mainLoopApi, QString(QLatin1String("QtmPulseContext:%1")).arg(::getpid()).toLatin1().constData()); |
1282 | + pa_context_set_state_callback(m_context, contextStateCallbackInit, this); |
1283 | + |
1284 | + if (!m_context) { |
1285 | + qWarning("Unable to create new pulseaudio context"); |
1286 | + pa_threaded_mainloop_unlock(m_mainLoop); |
1287 | + return false; |
1288 | + } |
1289 | + |
1290 | + if (pa_context_connect(m_context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0) { |
1291 | + qWarning("Unable to create a connection to the pulseaudio context"); |
1292 | + pa_threaded_mainloop_unlock(m_mainLoop); |
1293 | + releasePulseContext(); |
1294 | + return false; |
1295 | + } |
1296 | + |
1297 | + pa_threaded_mainloop_wait(m_mainLoop); |
1298 | + |
1299 | + while (keepGoing) { |
1300 | + switch (pa_context_get_state(m_context)) { |
1301 | + case PA_CONTEXT_CONNECTING: |
1302 | + case PA_CONTEXT_AUTHORIZING: |
1303 | + case PA_CONTEXT_SETTING_NAME: |
1304 | + break; |
1305 | + |
1306 | + case PA_CONTEXT_READY: |
1307 | + qDebug("Pulseaudio connection established."); |
1308 | + keepGoing = false; |
1309 | + break; |
1310 | + |
1311 | + case PA_CONTEXT_TERMINATED: |
1312 | + qCritical("Pulseaudio context terminated."); |
1313 | + keepGoing = false; |
1314 | + ok = false; |
1315 | + break; |
1316 | + |
1317 | + case PA_CONTEXT_FAILED: |
1318 | + default: |
1319 | + qCritical() << QString("Pulseaudio connection failure: %1").arg(pa_strerror(pa_context_errno(m_context))); |
1320 | + keepGoing = false; |
1321 | + ok = false; |
1322 | + } |
1323 | + |
1324 | + if (keepGoing) { |
1325 | + pa_threaded_mainloop_wait(m_mainLoop); |
1326 | + } |
1327 | + } |
1328 | + |
1329 | + if (ok) { |
1330 | + pa_context_set_state_callback(m_context, contextStateCallback, this); |
1331 | + pa_context_set_subscribe_callback(m_context, subscribeCallback, this); |
1332 | + pa_context_subscribe(m_context, PA_SUBSCRIPTION_MASK_CARD, NULL, this); |
1333 | + } else { |
1334 | + if (m_context) { |
1335 | + pa_context_unref(m_context); |
1336 | + m_context = 0; |
1337 | + } |
1338 | + } |
1339 | + |
1340 | + pa_threaded_mainloop_unlock(m_mainLoop); |
1341 | + return true; |
1342 | +} |
1343 | + |
1344 | + |
1345 | +void QPulseAudioEngineWorker::releasePulseContext() |
1346 | +{ |
1347 | + if (m_context) { |
1348 | + pa_threaded_mainloop_lock(m_mainLoop); |
1349 | + pa_context_disconnect(m_context); |
1350 | + pa_context_unref(m_context); |
1351 | + pa_threaded_mainloop_unlock(m_mainLoop); |
1352 | + m_context = 0; |
1353 | + } |
1354 | + |
1355 | +} |
1356 | + |
1357 | +QPulseAudioEngineWorker::~QPulseAudioEngineWorker() |
1358 | +{ |
1359 | + releasePulseContext(); |
1360 | + |
1361 | + if (m_mainLoop) { |
1362 | + pa_threaded_mainloop_stop(m_mainLoop); |
1363 | + pa_threaded_mainloop_free(m_mainLoop); |
1364 | + m_mainLoop = 0; |
1365 | + } |
1366 | +} |
1367 | + |
1368 | +void QPulseAudioEngineWorker::cardInfoCallback(const pa_card_info *info) |
1369 | +{ |
1370 | + pa_card_profile_info2 *voice_call = NULL, *highest = NULL; |
1371 | + pa_card_profile_info2 *hsp = NULL, *a2dp = NULL; |
1372 | + |
1373 | + /* For now we only support one card with the voicecall feature */ |
1374 | + for (int i = 0; i < info->n_profiles; i++) { |
1375 | + if (!highest || info->profiles2[i]->priority > highest->priority) |
1376 | + highest = info->profiles2[i]; |
1377 | + if (!strcmp(info->profiles2[i]->name, "voicecall")) |
1378 | + voice_call = info->profiles2[i]; |
1379 | + else if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_HSP) && |
1380 | + info->profiles2[i]->available != 0) |
1381 | + hsp = info->profiles2[i]; |
1382 | + else if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_A2DP) && |
1383 | + info->profiles2[i]->available != 0) |
1384 | + a2dp = info->profiles2[i]; |
1385 | + } |
1386 | + |
1387 | + /* Record the card that supports voicecall (default one to be used) */ |
1388 | + if (voice_call) { |
1389 | + qDebug("Found card that supports voicecall: '%s'", info->name); |
1390 | + m_voicecallcard = info->name; |
1391 | + m_voicecallhighest = highest->name; |
1392 | + qDebug("1"); |
1393 | + m_voicecallprofile = voice_call->name; |
1394 | + qDebug("2"); |
1395 | + } |
1396 | + |
1397 | + /* Handle the use cases needed for bluetooth */ |
1398 | + if (hsp && a2dp) { |
1399 | + qDebug("Found card that supports hsp and a2dp: '%s'", info->name); |
1400 | + m_bt_hsp_a2dp = info->name; |
1401 | + } else if (hsp && (a2dp == NULL)) { |
1402 | + /* This card only provides the hsp profile */ |
1403 | + qDebug("Found card that supports only hsp: '%s'", info->name); |
1404 | + m_bt_hsp = info->name; |
1405 | + } |
1406 | + qDebug("3"); |
1407 | +} |
1408 | + |
1409 | +void QPulseAudioEngineWorker::sinkInfoCallback(const pa_sink_info *info) |
1410 | +{ |
1411 | + pa_sink_port_info *earpiece = NULL, *speaker = NULL; |
1412 | + pa_sink_port_info *wired_headset = NULL, *wired_headphone = NULL; |
1413 | + pa_sink_port_info *preferred = NULL; |
1414 | + pa_sink_port_info *bluetooth_sco = NULL; |
1415 | + pa_sink_port_info *speaker_and_wired_headphone = NULL; |
1416 | + AudioMode audiomodetoset; |
1417 | + AudioModes modes; |
1418 | + |
1419 | + for (int i = 0; i < info->n_ports; i++) { |
1420 | + if (!strcmp(info->ports[i]->name, "output-earpiece")) |
1421 | + earpiece = info->ports[i]; |
1422 | + else if (!strcmp(info->ports[i]->name, "output-wired_headset") && |
1423 | + (info->ports[i]->available != PA_PORT_AVAILABLE_NO)) |
1424 | + wired_headset = info->ports[i]; |
1425 | + else if (!strcmp(info->ports[i]->name, "output-wired_headphone") && |
1426 | + (info->ports[i]->available != PA_PORT_AVAILABLE_NO)) |
1427 | + wired_headphone = info->ports[i]; |
1428 | + else if (!strcmp(info->ports[i]->name, "output-speaker")) |
1429 | + speaker = info->ports[i]; |
1430 | + else if (!strcmp(info->ports[i]->name, "output-bluetooth_sco")) |
1431 | + bluetooth_sco = info->ports[i]; |
1432 | + else if (!strcmp(info->ports[i]->name, "output-speaker+wired_headphone")) |
1433 | + speaker_and_wired_headphone = info->ports[i]; |
1434 | + } |
1435 | + |
1436 | + if (!earpiece || !speaker) |
1437 | + return; /* Not the right sink */ |
1438 | + |
1439 | + /* Refresh list of available audio modes */ |
1440 | + modes.append(AudioModeEarpiece); |
1441 | + modes.append(AudioModeSpeaker); |
1442 | + if (wired_headset || wired_headphone) |
1443 | + modes.append(AudioModeWiredHeadset); |
1444 | + if (bluetooth_sco && ((m_bt_hsp != "") || (m_bt_hsp_a2dp != ""))) |
1445 | + modes.append(AudioModeBluetooth); |
1446 | + |
1447 | + /* Check if the requested mode is available (earpiece*/ |
1448 | + if (((m_audiomode == AudioModeWiredHeadset) && !modes.contains(AudioModeWiredHeadset)) || |
1449 | + ((m_audiomode == AudioModeBluetooth) && !modes.contains(AudioModeBluetooth))) |
1450 | + return; |
1451 | + |
1452 | + /* Now to decide which output to be used, depending on the active mode */ |
1453 | + if (m_audiomode & AudioModeEarpiece) { |
1454 | + preferred = earpiece; |
1455 | + audiomodetoset = AudioModeEarpiece; |
1456 | + } |
1457 | + if (m_audiomode & AudioModeSpeaker) { |
1458 | + preferred = speaker; |
1459 | + audiomodetoset = AudioModeSpeaker; |
1460 | + } |
1461 | + if ((m_audiomode & AudioModeWiredHeadset) && (modes.contains(AudioModeWiredHeadset))) { |
1462 | + preferred = wired_headset ? wired_headset : wired_headphone; |
1463 | + audiomodetoset = AudioModeWiredHeadset; |
1464 | + } |
1465 | + if (m_callstatus == CallRinging && speaker_and_wired_headphone) { |
1466 | + preferred = speaker_and_wired_headphone; |
1467 | + } |
1468 | + if ((m_audiomode & AudioModeBluetooth) && (modes.contains(AudioModeBluetooth))) { |
1469 | + preferred = bluetooth_sco; |
1470 | + audiomodetoset = AudioModeBluetooth; |
1471 | + } |
1472 | + |
1473 | + m_audiomode = audiomodetoset; |
1474 | + |
1475 | + m_nametoset = info->name; |
1476 | + if (info->active_port != preferred) |
1477 | + m_valuetoset = preferred->name; |
1478 | + |
1479 | + if (modes != m_availableAudioModes) |
1480 | + m_availableAudioModes = modes; |
1481 | +} |
1482 | + |
1483 | +void QPulseAudioEngineWorker::sourceInfoCallback(const pa_source_info *info) |
1484 | +{ |
1485 | + pa_source_port_info *builtin_mic = NULL, *preferred = NULL; |
1486 | + pa_source_port_info *wired_headset = NULL, *bluetooth_sco = NULL; |
1487 | + |
1488 | + if (info->monitor_of_sink != PA_INVALID_INDEX) |
1489 | + return; /* Not the right source */ |
1490 | + |
1491 | + for (int i = 0; i < info->n_ports; i++) { |
1492 | + if (!strcmp(info->ports[i]->name, "input-builtin_mic")) |
1493 | + builtin_mic = info->ports[i]; |
1494 | + else if (!strcmp(info->ports[i]->name, "input-wired_headset") && |
1495 | + (info->ports[i]->available != PA_PORT_AVAILABLE_NO)) |
1496 | + wired_headset = info->ports[i]; |
1497 | + else if (!strcmp(info->ports[i]->name, "input-bluetooth_sco_headset")) |
1498 | + bluetooth_sco = info->ports[i]; |
1499 | + } |
1500 | + |
1501 | + if (!builtin_mic) |
1502 | + return; /* Not the right source */ |
1503 | + |
1504 | + /* Now to decide which output to be used, depending on the active mode */ |
1505 | + if ((m_audiomode & AudioModeEarpiece) || (m_audiomode & AudioModeSpeaker)) |
1506 | + preferred = builtin_mic; |
1507 | + if ((m_audiomode & AudioModeWiredHeadset) && (m_availableAudioModes.contains(AudioModeWiredHeadset))) |
1508 | + preferred = wired_headset ? wired_headset : builtin_mic; |
1509 | + if ((m_audiomode & AudioModeBluetooth) && (m_availableAudioModes.contains(AudioModeBluetooth))) |
1510 | + preferred = bluetooth_sco; |
1511 | + |
1512 | + m_nametoset = info->name; |
1513 | + if (info->active_port != preferred) |
1514 | + m_valuetoset = preferred->name; |
1515 | +} |
1516 | + |
1517 | +void QPulseAudioEngineWorker::serverInfoCallback(const pa_server_info *info) |
1518 | +{ |
1519 | + /* Saving default sink/source to restore after call hangup */ |
1520 | + m_defaultsink = info->default_sink_name; |
1521 | + m_defaultsource = info->default_source_name; |
1522 | + |
1523 | + /* In the case of a server callback we need to signal the mainloop */ |
1524 | + pa_threaded_mainloop_signal(mainloop(), 0); |
1525 | +} |
1526 | + |
1527 | +static void cardinfo_cb(pa_context *context, const pa_card_info *info, int isLast, void *userdata) |
1528 | +{ |
1529 | + QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata); |
1530 | + if (isLast != 0 || !pulseEngine || !info) { |
1531 | + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
1532 | + return; |
1533 | + } |
1534 | + pulseEngine->cardInfoCallback(info); |
1535 | +} |
1536 | + |
1537 | +static void sinkinfo_cb(pa_context *context, const pa_sink_info *info, int isLast, void *userdata) |
1538 | +{ |
1539 | + QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata); |
1540 | + if (isLast != 0 || !pulseEngine || !info) { |
1541 | + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
1542 | + return; |
1543 | + } |
1544 | + pulseEngine->sinkInfoCallback(info); |
1545 | +} |
1546 | + |
1547 | +static void sourceinfo_cb(pa_context *context, const pa_source_info *info, int isLast, void *userdata) |
1548 | +{ |
1549 | + QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata); |
1550 | + if (isLast != 0 || !pulseEngine || !info) { |
1551 | + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
1552 | + return; |
1553 | + } |
1554 | + pulseEngine->sourceInfoCallback(info); |
1555 | +} |
1556 | + |
1557 | +static void serverinfo_cb(pa_context *context, const pa_server_info *info, void *userdata) |
1558 | +{ |
1559 | + QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata); |
1560 | + if (!pulseEngine || !info) { |
1561 | + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
1562 | + return; |
1563 | + } |
1564 | + pulseEngine->serverInfoCallback(info); |
1565 | +} |
1566 | + |
1567 | +bool QPulseAudioEngineWorker::handleOperation(pa_operation *operation, const char *func_name) |
1568 | +{ |
1569 | + if (!operation) { |
1570 | + qCritical("'%s' failed (lost PulseAudio connection?)", func_name); |
1571 | + /* Free resources so it can retry a new connection during next operation */ |
1572 | + pa_threaded_mainloop_unlock(m_mainLoop); |
1573 | + releasePulseContext(); |
1574 | + return false; |
1575 | + } |
1576 | + |
1577 | + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) |
1578 | + pa_threaded_mainloop_wait(m_mainLoop); |
1579 | + pa_operation_unref(operation); |
1580 | + return true; |
1581 | +} |
1582 | + |
1583 | +int QPulseAudioEngineWorker::setupVoiceCall() |
1584 | +{ |
1585 | + pa_operation *o; |
1586 | + |
1587 | + qDebug("Setting up pulseaudio for voice call"); |
1588 | + |
1589 | + pa_threaded_mainloop_lock(m_mainLoop); |
1590 | + |
1591 | + /* Get and set the default sink/source to be restored later */ |
1592 | + o = pa_context_get_server_info(m_context, serverinfo_cb, this); |
1593 | + if (!handleOperation(o, "pa_context_get_server_info")) |
1594 | + return -1; |
1595 | + |
1596 | + qDebug("Recorded default sink: %s default source: %s", |
1597 | + m_defaultsink.c_str(), m_defaultsource.c_str()); |
1598 | + |
1599 | + /* Walk through the list of devices, find the voice call capable card and |
1600 | + * identify if we have bluetooth capable devices (hsp and a2dp) */ |
1601 | + m_voicecallcard = m_voicecallhighest = m_voicecallprofile = ""; |
1602 | + m_bt_hsp = m_bt_hsp_a2dp = ""; |
1603 | + o = pa_context_get_card_info_list(m_context, cardinfo_cb, this); |
1604 | + if (!handleOperation(o, "pa_context_get_card_info_list")) |
1605 | + return -1; |
1606 | + /* In case we have only one bt device that provides hsp and a2dp, we need |
1607 | + * to make sure we switch the default profile for that card (to hsp) */ |
1608 | + if ((m_bt_hsp_a2dp != "") && (m_bt_hsp == "")) { |
1609 | + qDebug("Setting PulseAudio card '%s' profile '%s'", |
1610 | + m_bt_hsp_a2dp.c_str(), PULSEAUDIO_PROFILE_HSP); |
1611 | + o = pa_context_set_card_profile_by_name(m_context, |
1612 | + m_bt_hsp_a2dp.c_str(), PULSEAUDIO_PROFILE_HSP, success_cb, this); |
1613 | + if (!handleOperation(o, "pa_context_set_card_profile_by_name")) |
1614 | + return -1; |
1615 | + } |
1616 | + |
1617 | + pa_threaded_mainloop_unlock(m_mainLoop); |
1618 | + |
1619 | + return 0; |
1620 | +} |
1621 | + |
1622 | +void QPulseAudioEngineWorker::restoreVoiceCall() |
1623 | +{ |
1624 | + pa_operation *o; |
1625 | + |
1626 | + qDebug("Restoring pulseaudio previous state"); |
1627 | + |
1628 | + /* Then restore previous settings */ |
1629 | + pa_threaded_mainloop_lock(m_mainLoop); |
1630 | + |
1631 | + /* See if we need to restore any HSP+AD2P device state */ |
1632 | + if ((m_bt_hsp_a2dp != "") && (m_bt_hsp == "")) { |
1633 | + qDebug("Restoring PulseAudio card '%s' to profile '%s'", |
1634 | + m_bt_hsp_a2dp.c_str(), PULSEAUDIO_PROFILE_A2DP); |
1635 | + o = pa_context_set_card_profile_by_name(m_context, |
1636 | + m_bt_hsp_a2dp.c_str(), PULSEAUDIO_PROFILE_A2DP, success_cb, this); |
1637 | + if (!handleOperation(o, "pa_context_set_card_profile_by_name")) |
1638 | + return; |
1639 | + } |
1640 | + |
1641 | + /* Restore default sink/source */ |
1642 | + if (m_defaultsink != "") { |
1643 | + qDebug("Restoring PulseAudio default sink to '%s'", m_defaultsink.c_str()); |
1644 | + o = pa_context_set_default_sink(m_context, m_defaultsink.c_str(), success_cb, this); |
1645 | + if (!handleOperation(o, "pa_context_set_default_sink")) |
1646 | + return; |
1647 | + } |
1648 | + if (m_defaultsource != "") { |
1649 | + qDebug("Restoring PulseAudio default source to '%s'", m_defaultsource.c_str()); |
1650 | + o = pa_context_set_default_source(m_context, m_defaultsource.c_str(), success_cb, this); |
1651 | + if (!handleOperation(o, "pa_context_set_default_source")) |
1652 | + return; |
1653 | + } |
1654 | + |
1655 | + pa_threaded_mainloop_unlock(m_mainLoop); |
1656 | +} |
1657 | + |
1658 | +void QPulseAudioEngineWorker::setCallMode(CallStatus callstatus, AudioMode audiomode) |
1659 | +{ |
1660 | + if (!createPulseContext()) { |
1661 | + return; |
1662 | + } |
1663 | + CallStatus p_callstatus = m_callstatus; |
1664 | + AudioMode p_audiomode = m_audiomode; |
1665 | + AudioModes p_availableAudioModes = m_availableAudioModes; |
1666 | + pa_operation *o; |
1667 | + |
1668 | + /* Check if we need to save the current pulseaudio state (e.g. when starting a call) */ |
1669 | + if ((callstatus != CallEnded) && (p_callstatus == CallEnded)) { |
1670 | + if (setupVoiceCall() < 0) { |
1671 | + qCritical("Failed to setup PulseAudio for Voice Call"); |
1672 | + return; |
1673 | + } |
1674 | + } |
1675 | + |
1676 | + /* If we have an active call, update internal state (used later when updating sink/source ports) */ |
1677 | + m_callstatus = callstatus; |
1678 | + m_audiomode = audiomode; |
1679 | + |
1680 | + pa_threaded_mainloop_lock(m_mainLoop); |
1681 | + |
1682 | + /* Switch the virtual card mode when call is active and not active |
1683 | + * This needs to be done before sink/source gets updated, because after changing mode |
1684 | + * it will automatically move to input/output-parking */ |
1685 | + if ((m_callstatus == CallActive) && (p_callstatus != CallActive) && |
1686 | + (m_voicecallcard != "") && (m_voicecallprofile != "")) { |
1687 | + qDebug("Setting PulseAudio card '%s' profile '%s'", |
1688 | + m_voicecallcard.c_str(), m_voicecallprofile.c_str()); |
1689 | + o = pa_context_set_card_profile_by_name(m_context, |
1690 | + m_voicecallcard.c_str(), m_voicecallprofile.c_str(), success_cb, this); |
1691 | + if (!handleOperation(o, "pa_context_set_card_profile_by_name")) |
1692 | + return; |
1693 | + } else if ((m_callstatus == CallEnded) && (m_voicecallcard != "") && (m_voicecallhighest != "")) { |
1694 | + /* If using droid, make sure to restore to the profile that has the highest score */ |
1695 | + qDebug("Restoring PulseAudio card '%s' to profile '%s'", |
1696 | + m_voicecallcard.c_str(), m_voicecallhighest.c_str()); |
1697 | + o = pa_context_set_card_profile_by_name(m_context, |
1698 | + m_voicecallcard.c_str(), m_voicecallhighest.c_str(), success_cb, this); |
1699 | + if (!handleOperation(o, "pa_context_set_card_profile_by_name")) |
1700 | + return; |
1701 | + } |
1702 | + |
1703 | + /* Find highest compatible sink/source elements from the voicecall |
1704 | + compatible card (on touch this means the pulse droid element) */ |
1705 | + m_nametoset = m_valuetoset = ""; |
1706 | + o = pa_context_get_sink_info_list(m_context, sinkinfo_cb, this); |
1707 | + if (!handleOperation(o, "pa_context_get_sink_info_list")) |
1708 | + return; |
1709 | + if ((m_nametoset != "") && (m_nametoset != m_defaultsink)) { |
1710 | + qDebug("Setting PulseAudio default sink to '%s'", m_nametoset.c_str()); |
1711 | + o = pa_context_set_default_sink(m_context, m_nametoset.c_str(), success_cb, this); |
1712 | + if (!handleOperation(o, "pa_context_set_default_sink")) |
1713 | + return; |
1714 | + } |
1715 | + if (m_valuetoset != "") { |
1716 | + qDebug("Setting PulseAudio sink '%s' port '%s'", |
1717 | + m_nametoset.c_str(), m_valuetoset.c_str()); |
1718 | + o = pa_context_set_sink_port_by_name(m_context, m_nametoset.c_str(), |
1719 | + m_valuetoset.c_str(), success_cb, this); |
1720 | + if (!handleOperation(o, "pa_context_set_sink_port_by_name")) |
1721 | + return; |
1722 | + } |
1723 | + |
1724 | + /* Same for source */ |
1725 | + m_nametoset = m_valuetoset = ""; |
1726 | + o = pa_context_get_source_info_list(m_context, sourceinfo_cb, this); |
1727 | + if (!handleOperation(o, "pa_context_get_source_info_list")) |
1728 | + return; |
1729 | + if ((m_nametoset != "") && (m_nametoset != m_defaultsource)) { |
1730 | + qDebug("Setting PulseAudio default source to '%s'", m_nametoset.c_str()); |
1731 | + o = pa_context_set_default_source(m_context, m_nametoset.c_str(), success_cb, this); |
1732 | + if (!handleOperation(o, "pa_context_set_default_source")) |
1733 | + return; |
1734 | + } |
1735 | + if (m_valuetoset != "") { |
1736 | + qDebug("Setting PulseAudio source '%s' port '%s'", |
1737 | + m_nametoset.c_str(), m_valuetoset.c_str()); |
1738 | + o = pa_context_set_source_port_by_name(m_context, m_nametoset.c_str(), |
1739 | + m_valuetoset.c_str(), success_cb, this); |
1740 | + if (!handleOperation(o, "pa_context_set_source_port_by_name")) |
1741 | + return; |
1742 | + } |
1743 | + |
1744 | + pa_threaded_mainloop_unlock(m_mainLoop); |
1745 | + |
1746 | + /* Notify if the list of audio modes changed */ |
1747 | + if (p_availableAudioModes != m_availableAudioModes) |
1748 | + Q_EMIT availableAudioModesChanged(m_availableAudioModes); |
1749 | + |
1750 | + /* Notify if call mode changed */ |
1751 | + if (p_audiomode != m_audiomode) { |
1752 | + Q_EMIT audioModeChanged(m_audiomode); |
1753 | + } |
1754 | + |
1755 | + /* If no more active voicecall, restore previous saved pulseaudio state */ |
1756 | + if (callstatus == CallEnded) { |
1757 | + restoreVoiceCall(); |
1758 | + } |
1759 | + |
1760 | + /* In case the app had set mute when the call wasn't active, make sure we reflect it here */ |
1761 | + if (m_callstatus != CallEnded) |
1762 | + setMicMute(m_micmute); |
1763 | +} |
1764 | + |
1765 | +void QPulseAudioEngineWorker::setMicMute(bool muted) |
1766 | +{ |
1767 | + if (!createPulseContext()) { |
1768 | + return; |
1769 | + } |
1770 | + |
1771 | + m_micmute = muted; |
1772 | + |
1773 | + if (m_callstatus == CallEnded) |
1774 | + return; |
1775 | + |
1776 | + pa_threaded_mainloop_lock(m_mainLoop); |
1777 | + |
1778 | + m_nametoset = ""; |
1779 | + pa_operation *o = pa_context_get_source_info_list(m_context, sourceinfo_cb, this); |
1780 | + if (!handleOperation(o, "pa_context_get_source_info_list")) |
1781 | + return; |
1782 | + |
1783 | + if (m_nametoset != "") { |
1784 | + int m = m_micmute ? 1 : 0; |
1785 | + qDebug("Setting PulseAudio source '%s' muted '%d'", m_nametoset.c_str(), m); |
1786 | + o = pa_context_set_source_mute_by_name(m_context, |
1787 | + m_nametoset.c_str(), m, success_cb, this); |
1788 | + if (!handleOperation(o, "pa_context_set_source_mute_by_name")) |
1789 | + return; |
1790 | + } |
1791 | + |
1792 | + pa_threaded_mainloop_unlock(m_mainLoop); |
1793 | +} |
1794 | + |
1795 | +void QPulseAudioEngineWorker::plugCardCallback(const pa_card_info *info) |
1796 | +{ |
1797 | + qDebug("Notified about card (%s) add event from PulseAudio", info->name); |
1798 | + |
1799 | + /* Check if it's indeed a BT device (with at least one hsp profile) */ |
1800 | + pa_card_profile_info2 *hsp = NULL, *a2dp = NULL; |
1801 | + for (int i = 0; i < info->n_profiles; i++) { |
1802 | + if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_HSP)) |
1803 | + hsp = info->profiles2[i]; |
1804 | + else if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_A2DP) && |
1805 | + info->profiles2[i]->available != 0) { |
1806 | + qDebug("Found a2dp"); |
1807 | + a2dp = info->profiles2[i]; |
1808 | + } |
1809 | + qDebug("%s", info->profiles2[i]->name); |
1810 | + } |
1811 | + |
1812 | + if ((!info->active_profile || !strcmp(info->active_profile->name, "off")) && a2dp) { |
1813 | + qDebug("No profile set"); |
1814 | + m_default_bt_card_fallback = info->name; |
1815 | + } |
1816 | + |
1817 | + /* We only care about BT (HSP) devices, and if one is not already available */ |
1818 | + if ((m_callstatus != CallEnded) && ((m_bt_hsp == "") || (m_bt_hsp_a2dp == ""))) { |
1819 | + if (hsp) |
1820 | + m_handleevent = true; |
1821 | + } |
1822 | +} |
1823 | + |
1824 | +void QPulseAudioEngineWorker::updateCardCallback(const pa_card_info *info) |
1825 | +{ |
1826 | + qDebug("Notified about card (%s) changes event from PulseAudio", info->name); |
1827 | + |
1828 | + /* Check if it's indeed a BT device (with at least one hsp profile) */ |
1829 | + pa_card_profile_info2 *hsp = NULL, *a2dp = NULL; |
1830 | + for (int i = 0; i < info->n_profiles; i++) { |
1831 | + if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_HSP)) |
1832 | + hsp = info->profiles2[i]; |
1833 | + else if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_A2DP) && |
1834 | + info->profiles2[i]->available != 0) { |
1835 | + qDebug("Found a2dp"); |
1836 | + a2dp = info->profiles2[i]; |
1837 | + } |
1838 | + qDebug("%s", info->profiles2[i]->name); |
1839 | + } |
1840 | + |
1841 | + if ((!info->active_profile || !strcmp(info->active_profile->name, "off")) && a2dp) { |
1842 | + qDebug("No profile set"); |
1843 | + m_default_bt_card_fallback = info->name; |
1844 | + } |
1845 | + |
1846 | + |
1847 | + /* We only care if the card event for the voicecall capable card */ |
1848 | + if ((m_callstatus == CallActive) && (!strcmp(info->name, m_voicecallcard.c_str()))) { |
1849 | + if (m_audiomode == AudioModeWiredHeadset) { |
1850 | + /* If previous mode is wired, it means it got unplugged */ |
1851 | + m_handleevent = true; |
1852 | + m_audiomodetoset = AudioModeBtOrWiredOrEarpiece; |
1853 | + } else if ((m_audiomode == AudioModeEarpiece) || ((m_audiomode == AudioModeSpeaker))) { |
1854 | + /* Now only trigger the event in case wired headset/headphone is now available */ |
1855 | + pa_card_port_info *port_info = NULL; |
1856 | + for (int i = 0; i < info->n_ports; i++) { |
1857 | + if (info->ports[i] && (info->ports[i]->available == PA_PORT_AVAILABLE_YES) && ( |
1858 | + !strcmp(info->ports[i]->name, "output-wired_headset") || |
1859 | + !strcmp(info->ports[i]->name, "output-wired_headphone"))) { |
1860 | + m_handleevent = true; |
1861 | + m_audiomodetoset = AudioModeWiredOrEarpiece; |
1862 | + } |
1863 | + } |
1864 | + } else if (m_audiomode == AudioModeBluetooth) { |
1865 | + /* Handle the event so we can update the audiomodes */ |
1866 | + m_handleevent = true; |
1867 | + m_audiomodetoset = AudioModeBluetooth; |
1868 | + } |
1869 | + } |
1870 | +} |
1871 | + |
1872 | +void QPulseAudioEngineWorker::unplugCardCallback() |
1873 | +{ |
1874 | + if (m_callstatus != CallEnded) { |
1875 | + m_handleevent = true; |
1876 | + } |
1877 | +} |
1878 | + |
1879 | +void QPulseAudioEngineWorker::handleCardEvent(const int evt, const unsigned int idx) |
1880 | +{ |
1881 | + pa_operation *o = NULL; |
1882 | + |
1883 | + /* Internal state var used to know if we need to update our internal state */ |
1884 | + m_handleevent = false; |
1885 | + |
1886 | + if (evt == PA_SUBSCRIPTION_EVENT_NEW) { |
1887 | + o = pa_context_get_card_info_by_index(m_context, idx, plug_card_cb, this); |
1888 | + if (!handleOperation(o, "pa_context_get_card_info_by_index")) |
1889 | + return; |
1890 | + |
1891 | + if (m_default_bt_card_fallback != "") { |
1892 | + o = pa_context_set_card_profile_by_name(m_context, |
1893 | + m_default_bt_card_fallback.c_str(), PULSEAUDIO_PROFILE_A2DP, success_cb, this); |
1894 | + if (!handleOperation(o, "pa_context_set_card_profile_by_name")) |
1895 | + return; |
1896 | + m_default_bt_card_fallback = ""; |
1897 | + } |
1898 | + |
1899 | + if (m_handleevent) { |
1900 | + qDebug("Adding new BT-HSP capable device"); |
1901 | + /* In case A2DP is available, switch to HSP */ |
1902 | + if (setupVoiceCall() < 0) |
1903 | + return; |
1904 | + /* Enable the HSP output port */ |
1905 | + setCallMode(m_callstatus, AudioModeBluetooth); |
1906 | + } |
1907 | + } else if (evt == PA_SUBSCRIPTION_EVENT_CHANGE) { |
1908 | + o = pa_context_get_card_info_by_index(m_context, idx, update_card_cb, this); |
1909 | + if (!handleOperation(o, "pa_context_get_card_info_by_index")) |
1910 | + return; |
1911 | + |
1912 | + if (m_default_bt_card_fallback != "") { |
1913 | + o = pa_context_set_card_profile_by_name(m_context, |
1914 | + m_default_bt_card_fallback.c_str(), PULSEAUDIO_PROFILE_A2DP, success_cb, this); |
1915 | + if (!handleOperation(o, "pa_context_set_card_profile_by_name")) |
1916 | + return; |
1917 | + m_default_bt_card_fallback = ""; |
1918 | + } |
1919 | + |
1920 | + if (m_handleevent) { |
1921 | + /* In this case it means the handset state changed */ |
1922 | + qDebug("Notifying card changes for the voicecall capable card"); |
1923 | + setCallMode(m_callstatus, m_audiomodetoset); |
1924 | + } |
1925 | + } else if (evt == PA_SUBSCRIPTION_EVENT_REMOVE) { |
1926 | + /* Check if the main HSP card was removed */ |
1927 | + if (m_bt_hsp != "") { |
1928 | + o = pa_context_get_card_info_by_name(m_context, m_bt_hsp.c_str(), unplug_card_cb, this); |
1929 | + if (!handleOperation(o, "pa_context_get_sink_info_by_name")) |
1930 | + return; |
1931 | + } |
1932 | + if (m_bt_hsp_a2dp != "") { |
1933 | + o = pa_context_get_card_info_by_name(m_context, m_bt_hsp_a2dp.c_str(), unplug_card_cb, this); |
1934 | + if (!handleOperation(o, "pa_context_get_sink_info_by_name")) |
1935 | + return; |
1936 | + } |
1937 | + if (m_handleevent) { |
1938 | + qDebug("Notifying about BT-HSP card removal"); |
1939 | + /* Needed in order to save the default sink/source */ |
1940 | + if (setupVoiceCall() < 0) |
1941 | + return; |
1942 | + /* Enable the default handset output port */ |
1943 | + setCallMode(m_callstatus, AudioModeWiredOrEarpiece); |
1944 | + } |
1945 | + } |
1946 | +} |
1947 | + |
1948 | +Q_GLOBAL_STATIC(QPulseAudioEngine, pulseEngine); |
1949 | + |
1950 | +QPulseAudioEngine::QPulseAudioEngine(QObject *parent) : |
1951 | + QObject(parent) |
1952 | +{ |
1953 | + qRegisterMetaType<CallStatus>(); |
1954 | + qRegisterMetaType<AudioMode>(); |
1955 | + qRegisterMetaType<AudioModes>(); |
1956 | + mWorker = new QPulseAudioEngineWorker(); |
1957 | + QObject::connect(mWorker, SIGNAL(audioModeChanged(const AudioMode)), this, SIGNAL(audioModeChanged(const AudioMode)), Qt::QueuedConnection); |
1958 | + QObject::connect(mWorker, SIGNAL(availableAudioModesChanged(const AudioModes)), this, SIGNAL(availableAudioModesChanged(const AudioModes)), Qt::QueuedConnection); |
1959 | + mWorker->createPulseContext(); |
1960 | + mWorker->moveToThread(&mThread); |
1961 | + mThread.start(); |
1962 | +} |
1963 | + |
1964 | +QPulseAudioEngine::~QPulseAudioEngine() |
1965 | +{ |
1966 | + mThread.quit(); |
1967 | + mThread.wait(); |
1968 | +} |
1969 | + |
1970 | +QPulseAudioEngine *QPulseAudioEngine::instance() |
1971 | +{ |
1972 | + QPulseAudioEngine *engine = pulseEngine(); |
1973 | + return engine; |
1974 | +} |
1975 | + |
1976 | +void QPulseAudioEngine::setCallMode(CallStatus callstatus, AudioMode audiomode) |
1977 | +{ |
1978 | + QMetaObject::invokeMethod(mWorker, "setCallMode", Qt::QueuedConnection, Q_ARG(CallStatus, callstatus), Q_ARG(AudioMode, audiomode)); |
1979 | +} |
1980 | + |
1981 | +void QPulseAudioEngine::setMicMute(bool muted) |
1982 | +{ |
1983 | + QMetaObject::invokeMethod(mWorker, "setMicMute", Qt::QueuedConnection, Q_ARG(bool, muted)); |
1984 | +} |
1985 | + |
1986 | +QT_END_NAMESPACE |
1987 | + |
1988 | |
1989 | === removed file 'handler/qpulseaudioengine.cpp' |
1990 | --- handler/qpulseaudioengine.cpp 2017-02-06 13:26:33 +0000 |
1991 | +++ handler/qpulseaudioengine.cpp 1970-01-01 00:00:00 +0000 |
1992 | @@ -1,853 +0,0 @@ |
1993 | -/**************************************************************************** |
1994 | -** |
1995 | -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). |
1996 | -** Contact: http://www.qt-project.org/legal |
1997 | -** |
1998 | -** This file was taken from qt5 and modified by |
1999 | -** David Henningsson <david.henningsson@canonical.com> for usage in |
2000 | -** telepathy-ofono. |
2001 | -** |
2002 | -** GNU Lesser General Public License Usage |
2003 | -** Alternatively, this file may be used under the terms of the GNU Lesser |
2004 | -** General Public License version 2.1 as published by the Free Software |
2005 | -** Foundation and appearing in the file LICENSE.LGPL included in the |
2006 | -** packaging of this file. Please review the following information to |
2007 | -** ensure the GNU Lesser General Public License version 2.1 requirements |
2008 | -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
2009 | -** |
2010 | -****************************************************************************/ |
2011 | - |
2012 | -#include <QtCore/qdebug.h> |
2013 | - |
2014 | -#include "qpulseaudioengine.h" |
2015 | -#include <sys/types.h> |
2016 | -#include <unistd.h> |
2017 | - |
2018 | -#define PULSEAUDIO_PROFILE_HSP "headset_head_unit" |
2019 | -#define PULSEAUDIO_PROFILE_A2DP "a2dp_sink" |
2020 | - |
2021 | -QT_BEGIN_NAMESPACE |
2022 | - |
2023 | -static void contextStateCallbackInit(pa_context *context, void *userdata) |
2024 | -{ |
2025 | - Q_UNUSED(context); |
2026 | - QPulseAudioEngineWorker *pulseEngine = reinterpret_cast<QPulseAudioEngineWorker*>(userdata); |
2027 | - pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
2028 | -} |
2029 | - |
2030 | -static void contextStateCallback(pa_context *context, void *userdata) |
2031 | -{ |
2032 | - Q_UNUSED(userdata); |
2033 | - Q_UNUSED(context); |
2034 | -} |
2035 | - |
2036 | -static void success_cb(pa_context *context, int success, void *userdata) |
2037 | -{ |
2038 | - QPulseAudioEngineWorker *pulseEngine = reinterpret_cast<QPulseAudioEngineWorker*>(userdata); |
2039 | - pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
2040 | -} |
2041 | - |
2042 | -/* Callbacks used when handling events from PulseAudio */ |
2043 | -static void plug_card_cb(pa_context *c, const pa_card_info *info, int isLast, void *userdata) |
2044 | -{ |
2045 | - QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata); |
2046 | - if (isLast != 0 || !pulseEngine || !info) { |
2047 | - pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
2048 | - return; |
2049 | - } |
2050 | - pulseEngine->plugCardCallback(info); |
2051 | -} |
2052 | - |
2053 | -static void update_card_cb(pa_context *c, const pa_card_info *info, int isLast, void *userdata) |
2054 | -{ |
2055 | - QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata); |
2056 | - if (isLast != 0 || !pulseEngine || !info) { |
2057 | - pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
2058 | - return; |
2059 | - } |
2060 | - pulseEngine->updateCardCallback(info); |
2061 | -} |
2062 | - |
2063 | -static void unplug_card_cb(pa_context *c, const pa_card_info *info, int isLast, void *userdata) |
2064 | -{ |
2065 | - QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata); |
2066 | - if (!pulseEngine) { |
2067 | - pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
2068 | - return; |
2069 | - } |
2070 | - |
2071 | - if (info == NULL) { |
2072 | - /* That means that the card used to query card_info was removed */ |
2073 | - pulseEngine->unplugCardCallback(); |
2074 | - } |
2075 | -} |
2076 | - |
2077 | -static void subscribeCallback(pa_context *context, pa_subscription_event_type_t t, uint32_t idx, void *userdata) |
2078 | -{ |
2079 | - /* For card change events (slot plug/unplug and add/remove card) */ |
2080 | - if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_CARD) { |
2081 | - if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { |
2082 | - QMetaObject::invokeMethod((QPulseAudioEngineWorker *) userdata, "handleCardEvent", |
2083 | - Qt::QueuedConnection, Q_ARG(int, PA_SUBSCRIPTION_EVENT_CHANGE), Q_ARG(unsigned int, idx)); |
2084 | - } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { |
2085 | - QMetaObject::invokeMethod((QPulseAudioEngineWorker *) userdata, "handleCardEvent", |
2086 | - Qt::QueuedConnection, Q_ARG(int, PA_SUBSCRIPTION_EVENT_NEW), Q_ARG(unsigned int, idx)); |
2087 | - } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { |
2088 | - QMetaObject::invokeMethod((QPulseAudioEngineWorker *) userdata, "handleCardEvent", |
2089 | - Qt::QueuedConnection, Q_ARG(int, PA_SUBSCRIPTION_EVENT_REMOVE), Q_ARG(unsigned int, idx)); |
2090 | - } |
2091 | - } |
2092 | -} |
2093 | - |
2094 | -QPulseAudioEngineWorker::QPulseAudioEngineWorker(QObject *parent) |
2095 | - : QObject(parent) |
2096 | - , m_mainLoopApi(0) |
2097 | - , m_context(0) |
2098 | - , m_callstatus(CallEnded) |
2099 | - , m_audiomode(AudioModeSpeaker) |
2100 | - , m_micmute(false) |
2101 | - , m_defaultsink("sink.primary") |
2102 | - , m_defaultsource("source.primary") |
2103 | - , m_voicecallcard("") |
2104 | - , m_voicecallhighest("") |
2105 | - , m_voicecallprofile("") |
2106 | - , m_bt_hsp("") |
2107 | - , m_bt_hsp_a2dp("") |
2108 | - , m_default_bt_card_fallback("") |
2109 | - |
2110 | -{ |
2111 | - m_mainLoop = pa_threaded_mainloop_new(); |
2112 | - if (m_mainLoop == 0) { |
2113 | - qWarning("Unable to create pulseaudio mainloop"); |
2114 | - return; |
2115 | - } |
2116 | - |
2117 | - if (pa_threaded_mainloop_start(m_mainLoop) != 0) { |
2118 | - qWarning("Unable to start pulseaudio mainloop"); |
2119 | - pa_threaded_mainloop_free(m_mainLoop); |
2120 | - m_mainLoop = 0; |
2121 | - return; |
2122 | - } |
2123 | - |
2124 | - createPulseContext(); |
2125 | -} |
2126 | - |
2127 | -bool QPulseAudioEngineWorker::createPulseContext() |
2128 | -{ |
2129 | - bool keepGoing = true; |
2130 | - bool ok = true; |
2131 | - |
2132 | - if (m_context) |
2133 | - return true; |
2134 | - |
2135 | - m_mainLoopApi = pa_threaded_mainloop_get_api(m_mainLoop); |
2136 | - |
2137 | - pa_threaded_mainloop_lock(m_mainLoop); |
2138 | - |
2139 | - m_context = pa_context_new(m_mainLoopApi, QString(QLatin1String("QtmPulseContext:%1")).arg(::getpid()).toLatin1().constData()); |
2140 | - pa_context_set_state_callback(m_context, contextStateCallbackInit, this); |
2141 | - |
2142 | - if (!m_context) { |
2143 | - qWarning("Unable to create new pulseaudio context"); |
2144 | - pa_threaded_mainloop_unlock(m_mainLoop); |
2145 | - return false; |
2146 | - } |
2147 | - |
2148 | - if (pa_context_connect(m_context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0) { |
2149 | - qWarning("Unable to create a connection to the pulseaudio context"); |
2150 | - pa_threaded_mainloop_unlock(m_mainLoop); |
2151 | - releasePulseContext(); |
2152 | - return false; |
2153 | - } |
2154 | - |
2155 | - pa_threaded_mainloop_wait(m_mainLoop); |
2156 | - |
2157 | - while (keepGoing) { |
2158 | - switch (pa_context_get_state(m_context)) { |
2159 | - case PA_CONTEXT_CONNECTING: |
2160 | - case PA_CONTEXT_AUTHORIZING: |
2161 | - case PA_CONTEXT_SETTING_NAME: |
2162 | - break; |
2163 | - |
2164 | - case PA_CONTEXT_READY: |
2165 | - qDebug("Pulseaudio connection established."); |
2166 | - keepGoing = false; |
2167 | - break; |
2168 | - |
2169 | - case PA_CONTEXT_TERMINATED: |
2170 | - qCritical("Pulseaudio context terminated."); |
2171 | - keepGoing = false; |
2172 | - ok = false; |
2173 | - break; |
2174 | - |
2175 | - case PA_CONTEXT_FAILED: |
2176 | - default: |
2177 | - qCritical() << QString("Pulseaudio connection failure: %1").arg(pa_strerror(pa_context_errno(m_context))); |
2178 | - keepGoing = false; |
2179 | - ok = false; |
2180 | - } |
2181 | - |
2182 | - if (keepGoing) { |
2183 | - pa_threaded_mainloop_wait(m_mainLoop); |
2184 | - } |
2185 | - } |
2186 | - |
2187 | - if (ok) { |
2188 | - pa_context_set_state_callback(m_context, contextStateCallback, this); |
2189 | - pa_context_set_subscribe_callback(m_context, subscribeCallback, this); |
2190 | - pa_context_subscribe(m_context, PA_SUBSCRIPTION_MASK_CARD, NULL, this); |
2191 | - } else { |
2192 | - if (m_context) { |
2193 | - pa_context_unref(m_context); |
2194 | - m_context = 0; |
2195 | - } |
2196 | - } |
2197 | - |
2198 | - pa_threaded_mainloop_unlock(m_mainLoop); |
2199 | - return true; |
2200 | -} |
2201 | - |
2202 | - |
2203 | -void QPulseAudioEngineWorker::releasePulseContext() |
2204 | -{ |
2205 | - if (m_context) { |
2206 | - pa_threaded_mainloop_lock(m_mainLoop); |
2207 | - pa_context_disconnect(m_context); |
2208 | - pa_context_unref(m_context); |
2209 | - pa_threaded_mainloop_unlock(m_mainLoop); |
2210 | - m_context = 0; |
2211 | - } |
2212 | - |
2213 | -} |
2214 | - |
2215 | -QPulseAudioEngineWorker::~QPulseAudioEngineWorker() |
2216 | -{ |
2217 | - releasePulseContext(); |
2218 | - |
2219 | - if (m_mainLoop) { |
2220 | - pa_threaded_mainloop_stop(m_mainLoop); |
2221 | - pa_threaded_mainloop_free(m_mainLoop); |
2222 | - m_mainLoop = 0; |
2223 | - } |
2224 | -} |
2225 | - |
2226 | -void QPulseAudioEngineWorker::cardInfoCallback(const pa_card_info *info) |
2227 | -{ |
2228 | - pa_card_profile_info2 *voice_call = NULL, *highest = NULL; |
2229 | - pa_card_profile_info2 *hsp = NULL, *a2dp = NULL; |
2230 | - |
2231 | - /* For now we only support one card with the voicecall feature */ |
2232 | - for (int i = 0; i < info->n_profiles; i++) { |
2233 | - if (!highest || info->profiles2[i]->priority > highest->priority) |
2234 | - highest = info->profiles2[i]; |
2235 | - if (!strcmp(info->profiles2[i]->name, "voicecall")) |
2236 | - voice_call = info->profiles2[i]; |
2237 | - else if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_HSP) && |
2238 | - info->profiles2[i]->available != 0) |
2239 | - hsp = info->profiles2[i]; |
2240 | - else if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_A2DP) && |
2241 | - info->profiles2[i]->available != 0) |
2242 | - a2dp = info->profiles2[i]; |
2243 | - } |
2244 | - |
2245 | - /* Record the card that supports voicecall (default one to be used) */ |
2246 | - if (voice_call) { |
2247 | - qDebug("Found card that supports voicecall: '%s'", info->name); |
2248 | - m_voicecallcard = info->name; |
2249 | - m_voicecallhighest = highest->name; |
2250 | - qDebug("1"); |
2251 | - m_voicecallprofile = voice_call->name; |
2252 | - qDebug("2"); |
2253 | - } |
2254 | - |
2255 | - /* Handle the use cases needed for bluetooth */ |
2256 | - if (hsp && a2dp) { |
2257 | - qDebug("Found card that supports hsp and a2dp: '%s'", info->name); |
2258 | - m_bt_hsp_a2dp = info->name; |
2259 | - } else if (hsp && (a2dp == NULL)) { |
2260 | - /* This card only provides the hsp profile */ |
2261 | - qDebug("Found card that supports only hsp: '%s'", info->name); |
2262 | - m_bt_hsp = info->name; |
2263 | - } |
2264 | - qDebug("3"); |
2265 | -} |
2266 | - |
2267 | -void QPulseAudioEngineWorker::sinkInfoCallback(const pa_sink_info *info) |
2268 | -{ |
2269 | - pa_sink_port_info *earpiece = NULL, *speaker = NULL; |
2270 | - pa_sink_port_info *wired_headset = NULL, *wired_headphone = NULL; |
2271 | - pa_sink_port_info *preferred = NULL; |
2272 | - pa_sink_port_info *bluetooth_sco = NULL; |
2273 | - pa_sink_port_info *speaker_and_wired_headphone = NULL; |
2274 | - AudioMode audiomodetoset; |
2275 | - AudioModes modes; |
2276 | - |
2277 | - for (int i = 0; i < info->n_ports; i++) { |
2278 | - if (!strcmp(info->ports[i]->name, "output-earpiece")) |
2279 | - earpiece = info->ports[i]; |
2280 | - else if (!strcmp(info->ports[i]->name, "output-wired_headset") && |
2281 | - (info->ports[i]->available != PA_PORT_AVAILABLE_NO)) |
2282 | - wired_headset = info->ports[i]; |
2283 | - else if (!strcmp(info->ports[i]->name, "output-wired_headphone") && |
2284 | - (info->ports[i]->available != PA_PORT_AVAILABLE_NO)) |
2285 | - wired_headphone = info->ports[i]; |
2286 | - else if (!strcmp(info->ports[i]->name, "output-speaker")) |
2287 | - speaker = info->ports[i]; |
2288 | - else if (!strcmp(info->ports[i]->name, "output-bluetooth_sco")) |
2289 | - bluetooth_sco = info->ports[i]; |
2290 | - else if (!strcmp(info->ports[i]->name, "output-speaker+wired_headphone")) |
2291 | - speaker_and_wired_headphone = info->ports[i]; |
2292 | - } |
2293 | - |
2294 | - if (!earpiece || !speaker) |
2295 | - return; /* Not the right sink */ |
2296 | - |
2297 | - /* Refresh list of available audio modes */ |
2298 | - modes.append(AudioModeEarpiece); |
2299 | - modes.append(AudioModeSpeaker); |
2300 | - if (wired_headset || wired_headphone) |
2301 | - modes.append(AudioModeWiredHeadset); |
2302 | - if (bluetooth_sco && ((m_bt_hsp != "") || (m_bt_hsp_a2dp != ""))) |
2303 | - modes.append(AudioModeBluetooth); |
2304 | - |
2305 | - /* Check if the requested mode is available (earpiece*/ |
2306 | - if (((m_audiomode == AudioModeWiredHeadset) && !modes.contains(AudioModeWiredHeadset)) || |
2307 | - ((m_audiomode == AudioModeBluetooth) && !modes.contains(AudioModeBluetooth))) |
2308 | - return; |
2309 | - |
2310 | - /* Now to decide which output to be used, depending on the active mode */ |
2311 | - if (m_audiomode & AudioModeEarpiece) { |
2312 | - preferred = earpiece; |
2313 | - audiomodetoset = AudioModeEarpiece; |
2314 | - } |
2315 | - if (m_audiomode & AudioModeSpeaker) { |
2316 | - preferred = speaker; |
2317 | - audiomodetoset = AudioModeSpeaker; |
2318 | - } |
2319 | - if ((m_audiomode & AudioModeWiredHeadset) && (modes.contains(AudioModeWiredHeadset))) { |
2320 | - preferred = wired_headset ? wired_headset : wired_headphone; |
2321 | - audiomodetoset = AudioModeWiredHeadset; |
2322 | - } |
2323 | - if (m_callstatus == CallRinging && speaker_and_wired_headphone) { |
2324 | - preferred = speaker_and_wired_headphone; |
2325 | - } |
2326 | - if ((m_audiomode & AudioModeBluetooth) && (modes.contains(AudioModeBluetooth))) { |
2327 | - preferred = bluetooth_sco; |
2328 | - audiomodetoset = AudioModeBluetooth; |
2329 | - } |
2330 | - |
2331 | - m_audiomode = audiomodetoset; |
2332 | - |
2333 | - m_nametoset = info->name; |
2334 | - if (info->active_port != preferred) |
2335 | - m_valuetoset = preferred->name; |
2336 | - |
2337 | - if (modes != m_availableAudioModes) |
2338 | - m_availableAudioModes = modes; |
2339 | -} |
2340 | - |
2341 | -void QPulseAudioEngineWorker::sourceInfoCallback(const pa_source_info *info) |
2342 | -{ |
2343 | - pa_source_port_info *builtin_mic = NULL, *preferred = NULL; |
2344 | - pa_source_port_info *wired_headset = NULL, *bluetooth_sco = NULL; |
2345 | - |
2346 | - if (info->monitor_of_sink != PA_INVALID_INDEX) |
2347 | - return; /* Not the right source */ |
2348 | - |
2349 | - for (int i = 0; i < info->n_ports; i++) { |
2350 | - if (!strcmp(info->ports[i]->name, "input-builtin_mic")) |
2351 | - builtin_mic = info->ports[i]; |
2352 | - else if (!strcmp(info->ports[i]->name, "input-wired_headset") && |
2353 | - (info->ports[i]->available != PA_PORT_AVAILABLE_NO)) |
2354 | - wired_headset = info->ports[i]; |
2355 | - else if (!strcmp(info->ports[i]->name, "input-bluetooth_sco_headset")) |
2356 | - bluetooth_sco = info->ports[i]; |
2357 | - } |
2358 | - |
2359 | - if (!builtin_mic) |
2360 | - return; /* Not the right source */ |
2361 | - |
2362 | - /* Now to decide which output to be used, depending on the active mode */ |
2363 | - if ((m_audiomode & AudioModeEarpiece) || (m_audiomode & AudioModeSpeaker)) |
2364 | - preferred = builtin_mic; |
2365 | - if ((m_audiomode & AudioModeWiredHeadset) && (m_availableAudioModes.contains(AudioModeWiredHeadset))) |
2366 | - preferred = wired_headset ? wired_headset : builtin_mic; |
2367 | - if ((m_audiomode & AudioModeBluetooth) && (m_availableAudioModes.contains(AudioModeBluetooth))) |
2368 | - preferred = bluetooth_sco; |
2369 | - |
2370 | - m_nametoset = info->name; |
2371 | - if (info->active_port != preferred) |
2372 | - m_valuetoset = preferred->name; |
2373 | -} |
2374 | - |
2375 | -void QPulseAudioEngineWorker::serverInfoCallback(const pa_server_info *info) |
2376 | -{ |
2377 | - /* Saving default sink/source to restore after call hangup */ |
2378 | - m_defaultsink = info->default_sink_name; |
2379 | - m_defaultsource = info->default_source_name; |
2380 | - |
2381 | - /* In the case of a server callback we need to signal the mainloop */ |
2382 | - pa_threaded_mainloop_signal(mainloop(), 0); |
2383 | -} |
2384 | - |
2385 | -static void cardinfo_cb(pa_context *context, const pa_card_info *info, int isLast, void *userdata) |
2386 | -{ |
2387 | - QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata); |
2388 | - if (isLast != 0 || !pulseEngine || !info) { |
2389 | - pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
2390 | - return; |
2391 | - } |
2392 | - pulseEngine->cardInfoCallback(info); |
2393 | -} |
2394 | - |
2395 | -static void sinkinfo_cb(pa_context *context, const pa_sink_info *info, int isLast, void *userdata) |
2396 | -{ |
2397 | - QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata); |
2398 | - if (isLast != 0 || !pulseEngine || !info) { |
2399 | - pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
2400 | - return; |
2401 | - } |
2402 | - pulseEngine->sinkInfoCallback(info); |
2403 | -} |
2404 | - |
2405 | -static void sourceinfo_cb(pa_context *context, const pa_source_info *info, int isLast, void *userdata) |
2406 | -{ |
2407 | - QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata); |
2408 | - if (isLast != 0 || !pulseEngine || !info) { |
2409 | - pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
2410 | - return; |
2411 | - } |
2412 | - pulseEngine->sourceInfoCallback(info); |
2413 | -} |
2414 | - |
2415 | -static void serverinfo_cb(pa_context *context, const pa_server_info *info, void *userdata) |
2416 | -{ |
2417 | - QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata); |
2418 | - if (!pulseEngine || !info) { |
2419 | - pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
2420 | - return; |
2421 | - } |
2422 | - pulseEngine->serverInfoCallback(info); |
2423 | -} |
2424 | - |
2425 | -bool QPulseAudioEngineWorker::handleOperation(pa_operation *operation, const char *func_name) |
2426 | -{ |
2427 | - if (!operation) { |
2428 | - qCritical("'%s' failed (lost PulseAudio connection?)", func_name); |
2429 | - /* Free resources so it can retry a new connection during next operation */ |
2430 | - pa_threaded_mainloop_unlock(m_mainLoop); |
2431 | - releasePulseContext(); |
2432 | - return false; |
2433 | - } |
2434 | - |
2435 | - while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) |
2436 | - pa_threaded_mainloop_wait(m_mainLoop); |
2437 | - pa_operation_unref(operation); |
2438 | - return true; |
2439 | -} |
2440 | - |
2441 | -int QPulseAudioEngineWorker::setupVoiceCall() |
2442 | -{ |
2443 | - pa_operation *o; |
2444 | - |
2445 | - qDebug("Setting up pulseaudio for voice call"); |
2446 | - |
2447 | - pa_threaded_mainloop_lock(m_mainLoop); |
2448 | - |
2449 | - /* Get and set the default sink/source to be restored later */ |
2450 | - o = pa_context_get_server_info(m_context, serverinfo_cb, this); |
2451 | - if (!handleOperation(o, "pa_context_get_server_info")) |
2452 | - return -1; |
2453 | - |
2454 | - qDebug("Recorded default sink: %s default source: %s", |
2455 | - m_defaultsink.c_str(), m_defaultsource.c_str()); |
2456 | - |
2457 | - /* Walk through the list of devices, find the voice call capable card and |
2458 | - * identify if we have bluetooth capable devices (hsp and a2dp) */ |
2459 | - m_voicecallcard = m_voicecallhighest = m_voicecallprofile = ""; |
2460 | - m_bt_hsp = m_bt_hsp_a2dp = ""; |
2461 | - o = pa_context_get_card_info_list(m_context, cardinfo_cb, this); |
2462 | - if (!handleOperation(o, "pa_context_get_card_info_list")) |
2463 | - return -1; |
2464 | - /* In case we have only one bt device that provides hsp and a2dp, we need |
2465 | - * to make sure we switch the default profile for that card (to hsp) */ |
2466 | - if ((m_bt_hsp_a2dp != "") && (m_bt_hsp == "")) { |
2467 | - qDebug("Setting PulseAudio card '%s' profile '%s'", |
2468 | - m_bt_hsp_a2dp.c_str(), PULSEAUDIO_PROFILE_HSP); |
2469 | - o = pa_context_set_card_profile_by_name(m_context, |
2470 | - m_bt_hsp_a2dp.c_str(), PULSEAUDIO_PROFILE_HSP, success_cb, this); |
2471 | - if (!handleOperation(o, "pa_context_set_card_profile_by_name")) |
2472 | - return -1; |
2473 | - } |
2474 | - |
2475 | - pa_threaded_mainloop_unlock(m_mainLoop); |
2476 | - |
2477 | - return 0; |
2478 | -} |
2479 | - |
2480 | -void QPulseAudioEngineWorker::restoreVoiceCall() |
2481 | -{ |
2482 | - pa_operation *o; |
2483 | - |
2484 | - qDebug("Restoring pulseaudio previous state"); |
2485 | - |
2486 | - /* Then restore previous settings */ |
2487 | - pa_threaded_mainloop_lock(m_mainLoop); |
2488 | - |
2489 | - /* See if we need to restore any HSP+AD2P device state */ |
2490 | - if ((m_bt_hsp_a2dp != "") && (m_bt_hsp == "")) { |
2491 | - qDebug("Restoring PulseAudio card '%s' to profile '%s'", |
2492 | - m_bt_hsp_a2dp.c_str(), PULSEAUDIO_PROFILE_A2DP); |
2493 | - o = pa_context_set_card_profile_by_name(m_context, |
2494 | - m_bt_hsp_a2dp.c_str(), PULSEAUDIO_PROFILE_A2DP, success_cb, this); |
2495 | - if (!handleOperation(o, "pa_context_set_card_profile_by_name")) |
2496 | - return; |
2497 | - } |
2498 | - |
2499 | - /* Restore default sink/source */ |
2500 | - if (m_defaultsink != "") { |
2501 | - qDebug("Restoring PulseAudio default sink to '%s'", m_defaultsink.c_str()); |
2502 | - o = pa_context_set_default_sink(m_context, m_defaultsink.c_str(), success_cb, this); |
2503 | - if (!handleOperation(o, "pa_context_set_default_sink")) |
2504 | - return; |
2505 | - } |
2506 | - if (m_defaultsource != "") { |
2507 | - qDebug("Restoring PulseAudio default source to '%s'", m_defaultsource.c_str()); |
2508 | - o = pa_context_set_default_source(m_context, m_defaultsource.c_str(), success_cb, this); |
2509 | - if (!handleOperation(o, "pa_context_set_default_source")) |
2510 | - return; |
2511 | - } |
2512 | - |
2513 | - pa_threaded_mainloop_unlock(m_mainLoop); |
2514 | -} |
2515 | - |
2516 | -void QPulseAudioEngineWorker::setCallMode(CallStatus callstatus, AudioMode audiomode) |
2517 | -{ |
2518 | - if (!createPulseContext()) { |
2519 | - return; |
2520 | - } |
2521 | - CallStatus p_callstatus = m_callstatus; |
2522 | - AudioMode p_audiomode = m_audiomode; |
2523 | - AudioModes p_availableAudioModes = m_availableAudioModes; |
2524 | - pa_operation *o; |
2525 | - |
2526 | - /* Check if we need to save the current pulseaudio state (e.g. when starting a call) */ |
2527 | - if ((callstatus != CallEnded) && (p_callstatus == CallEnded)) { |
2528 | - if (setupVoiceCall() < 0) { |
2529 | - qCritical("Failed to setup PulseAudio for Voice Call"); |
2530 | - return; |
2531 | - } |
2532 | - } |
2533 | - |
2534 | - /* If we have an active call, update internal state (used later when updating sink/source ports) */ |
2535 | - m_callstatus = callstatus; |
2536 | - m_audiomode = audiomode; |
2537 | - |
2538 | - pa_threaded_mainloop_lock(m_mainLoop); |
2539 | - |
2540 | - /* Switch the virtual card mode when call is active and not active |
2541 | - * This needs to be done before sink/source gets updated, because after changing mode |
2542 | - * it will automatically move to input/output-parking */ |
2543 | - if ((m_callstatus == CallActive) && (p_callstatus != CallActive) && |
2544 | - (m_voicecallcard != "") && (m_voicecallprofile != "")) { |
2545 | - qDebug("Setting PulseAudio card '%s' profile '%s'", |
2546 | - m_voicecallcard.c_str(), m_voicecallprofile.c_str()); |
2547 | - o = pa_context_set_card_profile_by_name(m_context, |
2548 | - m_voicecallcard.c_str(), m_voicecallprofile.c_str(), success_cb, this); |
2549 | - if (!handleOperation(o, "pa_context_set_card_profile_by_name")) |
2550 | - return; |
2551 | - } else if ((m_callstatus == CallEnded) && (m_voicecallcard != "") && (m_voicecallhighest != "")) { |
2552 | - /* If using droid, make sure to restore to the profile that has the highest score */ |
2553 | - qDebug("Restoring PulseAudio card '%s' to profile '%s'", |
2554 | - m_voicecallcard.c_str(), m_voicecallhighest.c_str()); |
2555 | - o = pa_context_set_card_profile_by_name(m_context, |
2556 | - m_voicecallcard.c_str(), m_voicecallhighest.c_str(), success_cb, this); |
2557 | - if (!handleOperation(o, "pa_context_set_card_profile_by_name")) |
2558 | - return; |
2559 | - } |
2560 | - |
2561 | - /* Find highest compatible sink/source elements from the voicecall |
2562 | - compatible card (on touch this means the pulse droid element) */ |
2563 | - m_nametoset = m_valuetoset = ""; |
2564 | - o = pa_context_get_sink_info_list(m_context, sinkinfo_cb, this); |
2565 | - if (!handleOperation(o, "pa_context_get_sink_info_list")) |
2566 | - return; |
2567 | - if ((m_nametoset != "") && (m_nametoset != m_defaultsink)) { |
2568 | - qDebug("Setting PulseAudio default sink to '%s'", m_nametoset.c_str()); |
2569 | - o = pa_context_set_default_sink(m_context, m_nametoset.c_str(), success_cb, this); |
2570 | - if (!handleOperation(o, "pa_context_set_default_sink")) |
2571 | - return; |
2572 | - } |
2573 | - if (m_valuetoset != "") { |
2574 | - qDebug("Setting PulseAudio sink '%s' port '%s'", |
2575 | - m_nametoset.c_str(), m_valuetoset.c_str()); |
2576 | - o = pa_context_set_sink_port_by_name(m_context, m_nametoset.c_str(), |
2577 | - m_valuetoset.c_str(), success_cb, this); |
2578 | - if (!handleOperation(o, "pa_context_set_sink_port_by_name")) |
2579 | - return; |
2580 | - } |
2581 | - |
2582 | - /* Same for source */ |
2583 | - m_nametoset = m_valuetoset = ""; |
2584 | - o = pa_context_get_source_info_list(m_context, sourceinfo_cb, this); |
2585 | - if (!handleOperation(o, "pa_context_get_source_info_list")) |
2586 | - return; |
2587 | - if ((m_nametoset != "") && (m_nametoset != m_defaultsource)) { |
2588 | - qDebug("Setting PulseAudio default source to '%s'", m_nametoset.c_str()); |
2589 | - o = pa_context_set_default_source(m_context, m_nametoset.c_str(), success_cb, this); |
2590 | - if (!handleOperation(o, "pa_context_set_default_source")) |
2591 | - return; |
2592 | - } |
2593 | - if (m_valuetoset != "") { |
2594 | - qDebug("Setting PulseAudio source '%s' port '%s'", |
2595 | - m_nametoset.c_str(), m_valuetoset.c_str()); |
2596 | - o = pa_context_set_source_port_by_name(m_context, m_nametoset.c_str(), |
2597 | - m_valuetoset.c_str(), success_cb, this); |
2598 | - if (!handleOperation(o, "pa_context_set_source_port_by_name")) |
2599 | - return; |
2600 | - } |
2601 | - |
2602 | - pa_threaded_mainloop_unlock(m_mainLoop); |
2603 | - |
2604 | - /* Notify if the list of audio modes changed */ |
2605 | - if (p_availableAudioModes != m_availableAudioModes) |
2606 | - Q_EMIT availableAudioModesChanged(m_availableAudioModes); |
2607 | - |
2608 | - /* Notify if call mode changed */ |
2609 | - if (p_audiomode != m_audiomode) { |
2610 | - Q_EMIT audioModeChanged(m_audiomode); |
2611 | - } |
2612 | - |
2613 | - /* If no more active voicecall, restore previous saved pulseaudio state */ |
2614 | - if (callstatus == CallEnded) { |
2615 | - restoreVoiceCall(); |
2616 | - } |
2617 | - |
2618 | - /* In case the app had set mute when the call wasn't active, make sure we reflect it here */ |
2619 | - if (m_callstatus != CallEnded) |
2620 | - setMicMute(m_micmute); |
2621 | -} |
2622 | - |
2623 | -void QPulseAudioEngineWorker::setMicMute(bool muted) |
2624 | -{ |
2625 | - if (!createPulseContext()) { |
2626 | - return; |
2627 | - } |
2628 | - |
2629 | - m_micmute = muted; |
2630 | - |
2631 | - if (m_callstatus == CallEnded) |
2632 | - return; |
2633 | - |
2634 | - pa_threaded_mainloop_lock(m_mainLoop); |
2635 | - |
2636 | - m_nametoset = ""; |
2637 | - pa_operation *o = pa_context_get_source_info_list(m_context, sourceinfo_cb, this); |
2638 | - if (!handleOperation(o, "pa_context_get_source_info_list")) |
2639 | - return; |
2640 | - |
2641 | - if (m_nametoset != "") { |
2642 | - int m = m_micmute ? 1 : 0; |
2643 | - qDebug("Setting PulseAudio source '%s' muted '%d'", m_nametoset.c_str(), m); |
2644 | - o = pa_context_set_source_mute_by_name(m_context, |
2645 | - m_nametoset.c_str(), m, success_cb, this); |
2646 | - if (!handleOperation(o, "pa_context_set_source_mute_by_name")) |
2647 | - return; |
2648 | - } |
2649 | - |
2650 | - pa_threaded_mainloop_unlock(m_mainLoop); |
2651 | -} |
2652 | - |
2653 | -void QPulseAudioEngineWorker::plugCardCallback(const pa_card_info *info) |
2654 | -{ |
2655 | - qDebug("Notified about card (%s) add event from PulseAudio", info->name); |
2656 | - |
2657 | - /* Check if it's indeed a BT device (with at least one hsp profile) */ |
2658 | - pa_card_profile_info2 *hsp = NULL, *a2dp = NULL; |
2659 | - for (int i = 0; i < info->n_profiles; i++) { |
2660 | - if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_HSP)) |
2661 | - hsp = info->profiles2[i]; |
2662 | - else if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_A2DP) && |
2663 | - info->profiles2[i]->available != 0) { |
2664 | - qDebug("Found a2dp"); |
2665 | - a2dp = info->profiles2[i]; |
2666 | - } |
2667 | - qDebug("%s", info->profiles2[i]->name); |
2668 | - } |
2669 | - |
2670 | - if ((!info->active_profile || !strcmp(info->active_profile->name, "off")) && a2dp) { |
2671 | - qDebug("No profile set"); |
2672 | - m_default_bt_card_fallback = info->name; |
2673 | - } |
2674 | - |
2675 | - /* We only care about BT (HSP) devices, and if one is not already available */ |
2676 | - if ((m_callstatus != CallEnded) && ((m_bt_hsp == "") || (m_bt_hsp_a2dp == ""))) { |
2677 | - if (hsp) |
2678 | - m_handleevent = true; |
2679 | - } |
2680 | -} |
2681 | - |
2682 | -void QPulseAudioEngineWorker::updateCardCallback(const pa_card_info *info) |
2683 | -{ |
2684 | - qDebug("Notified about card (%s) changes event from PulseAudio", info->name); |
2685 | - |
2686 | - /* Check if it's indeed a BT device (with at least one hsp profile) */ |
2687 | - pa_card_profile_info2 *hsp = NULL, *a2dp = NULL; |
2688 | - for (int i = 0; i < info->n_profiles; i++) { |
2689 | - if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_HSP)) |
2690 | - hsp = info->profiles2[i]; |
2691 | - else if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_A2DP) && |
2692 | - info->profiles2[i]->available != 0) { |
2693 | - qDebug("Found a2dp"); |
2694 | - a2dp = info->profiles2[i]; |
2695 | - } |
2696 | - qDebug("%s", info->profiles2[i]->name); |
2697 | - } |
2698 | - |
2699 | - if ((!info->active_profile || !strcmp(info->active_profile->name, "off")) && a2dp) { |
2700 | - qDebug("No profile set"); |
2701 | - m_default_bt_card_fallback = info->name; |
2702 | - } |
2703 | - |
2704 | - |
2705 | - /* We only care if the card event for the voicecall capable card */ |
2706 | - if ((m_callstatus == CallActive) && (!strcmp(info->name, m_voicecallcard.c_str()))) { |
2707 | - if (m_audiomode == AudioModeWiredHeadset) { |
2708 | - /* If previous mode is wired, it means it got unplugged */ |
2709 | - m_handleevent = true; |
2710 | - m_audiomodetoset = AudioModeBtOrWiredOrEarpiece; |
2711 | - } else if ((m_audiomode == AudioModeEarpiece) || ((m_audiomode == AudioModeSpeaker))) { |
2712 | - /* Now only trigger the event in case wired headset/headphone is now available */ |
2713 | - pa_card_port_info *port_info = NULL; |
2714 | - for (int i = 0; i < info->n_ports; i++) { |
2715 | - if (info->ports[i] && (info->ports[i]->available == PA_PORT_AVAILABLE_YES) && ( |
2716 | - !strcmp(info->ports[i]->name, "output-wired_headset") || |
2717 | - !strcmp(info->ports[i]->name, "output-wired_headphone"))) { |
2718 | - m_handleevent = true; |
2719 | - m_audiomodetoset = AudioModeWiredOrEarpiece; |
2720 | - } |
2721 | - } |
2722 | - } else if (m_audiomode == AudioModeBluetooth) { |
2723 | - /* Handle the event so we can update the audiomodes */ |
2724 | - m_handleevent = true; |
2725 | - m_audiomodetoset = AudioModeBluetooth; |
2726 | - } |
2727 | - } |
2728 | -} |
2729 | - |
2730 | -void QPulseAudioEngineWorker::unplugCardCallback() |
2731 | -{ |
2732 | - if (m_callstatus != CallEnded) { |
2733 | - m_handleevent = true; |
2734 | - } |
2735 | -} |
2736 | - |
2737 | -void QPulseAudioEngineWorker::handleCardEvent(const int evt, const unsigned int idx) |
2738 | -{ |
2739 | - pa_operation *o = NULL; |
2740 | - |
2741 | - /* Internal state var used to know if we need to update our internal state */ |
2742 | - m_handleevent = false; |
2743 | - |
2744 | - if (evt == PA_SUBSCRIPTION_EVENT_NEW) { |
2745 | - o = pa_context_get_card_info_by_index(m_context, idx, plug_card_cb, this); |
2746 | - if (!handleOperation(o, "pa_context_get_card_info_by_index")) |
2747 | - return; |
2748 | - |
2749 | - if (m_default_bt_card_fallback != "") { |
2750 | - o = pa_context_set_card_profile_by_name(m_context, |
2751 | - m_default_bt_card_fallback.c_str(), PULSEAUDIO_PROFILE_A2DP, success_cb, this); |
2752 | - if (!handleOperation(o, "pa_context_set_card_profile_by_name")) |
2753 | - return; |
2754 | - m_default_bt_card_fallback = ""; |
2755 | - } |
2756 | - |
2757 | - if (m_handleevent) { |
2758 | - qDebug("Adding new BT-HSP capable device"); |
2759 | - /* In case A2DP is available, switch to HSP */ |
2760 | - if (setupVoiceCall() < 0) |
2761 | - return; |
2762 | - /* Enable the HSP output port */ |
2763 | - setCallMode(m_callstatus, AudioModeBluetooth); |
2764 | - } |
2765 | - } else if (evt == PA_SUBSCRIPTION_EVENT_CHANGE) { |
2766 | - o = pa_context_get_card_info_by_index(m_context, idx, update_card_cb, this); |
2767 | - if (!handleOperation(o, "pa_context_get_card_info_by_index")) |
2768 | - return; |
2769 | - |
2770 | - if (m_default_bt_card_fallback != "") { |
2771 | - o = pa_context_set_card_profile_by_name(m_context, |
2772 | - m_default_bt_card_fallback.c_str(), PULSEAUDIO_PROFILE_A2DP, success_cb, this); |
2773 | - if (!handleOperation(o, "pa_context_set_card_profile_by_name")) |
2774 | - return; |
2775 | - m_default_bt_card_fallback = ""; |
2776 | - } |
2777 | - |
2778 | - if (m_handleevent) { |
2779 | - /* In this case it means the handset state changed */ |
2780 | - qDebug("Notifying card changes for the voicecall capable card"); |
2781 | - setCallMode(m_callstatus, m_audiomodetoset); |
2782 | - } |
2783 | - } else if (evt == PA_SUBSCRIPTION_EVENT_REMOVE) { |
2784 | - /* Check if the main HSP card was removed */ |
2785 | - if (m_bt_hsp != "") { |
2786 | - o = pa_context_get_card_info_by_name(m_context, m_bt_hsp.c_str(), unplug_card_cb, this); |
2787 | - if (!handleOperation(o, "pa_context_get_sink_info_by_name")) |
2788 | - return; |
2789 | - } |
2790 | - if (m_bt_hsp_a2dp != "") { |
2791 | - o = pa_context_get_card_info_by_name(m_context, m_bt_hsp_a2dp.c_str(), unplug_card_cb, this); |
2792 | - if (!handleOperation(o, "pa_context_get_sink_info_by_name")) |
2793 | - return; |
2794 | - } |
2795 | - if (m_handleevent) { |
2796 | - qDebug("Notifying about BT-HSP card removal"); |
2797 | - /* Needed in order to save the default sink/source */ |
2798 | - if (setupVoiceCall() < 0) |
2799 | - return; |
2800 | - /* Enable the default handset output port */ |
2801 | - setCallMode(m_callstatus, AudioModeWiredOrEarpiece); |
2802 | - } |
2803 | - } |
2804 | -} |
2805 | - |
2806 | -Q_GLOBAL_STATIC(QPulseAudioEngine, pulseEngine); |
2807 | - |
2808 | -QPulseAudioEngine::QPulseAudioEngine(QObject *parent) : |
2809 | - QObject(parent) |
2810 | -{ |
2811 | - qRegisterMetaType<CallStatus>(); |
2812 | - qRegisterMetaType<AudioMode>(); |
2813 | - qRegisterMetaType<AudioModes>(); |
2814 | - mWorker = new QPulseAudioEngineWorker(); |
2815 | - QObject::connect(mWorker, SIGNAL(audioModeChanged(const AudioMode)), this, SIGNAL(audioModeChanged(const AudioMode)), Qt::QueuedConnection); |
2816 | - QObject::connect(mWorker, SIGNAL(availableAudioModesChanged(const AudioModes)), this, SIGNAL(availableAudioModesChanged(const AudioModes)), Qt::QueuedConnection); |
2817 | - mWorker->createPulseContext(); |
2818 | - mWorker->moveToThread(&mThread); |
2819 | - mThread.start(); |
2820 | -} |
2821 | - |
2822 | -QPulseAudioEngine::~QPulseAudioEngine() |
2823 | -{ |
2824 | - mThread.quit(); |
2825 | - mThread.wait(); |
2826 | -} |
2827 | - |
2828 | -QPulseAudioEngine *QPulseAudioEngine::instance() |
2829 | -{ |
2830 | - QPulseAudioEngine *engine = pulseEngine(); |
2831 | - return engine; |
2832 | -} |
2833 | - |
2834 | -void QPulseAudioEngine::setCallMode(CallStatus callstatus, AudioMode audiomode) |
2835 | -{ |
2836 | - QMetaObject::invokeMethod(mWorker, "setCallMode", Qt::QueuedConnection, Q_ARG(CallStatus, callstatus), Q_ARG(AudioMode, audiomode)); |
2837 | -} |
2838 | - |
2839 | -void QPulseAudioEngine::setMicMute(bool muted) |
2840 | -{ |
2841 | - QMetaObject::invokeMethod(mWorker, "setMicMute", Qt::QueuedConnection, Q_ARG(bool, muted)); |
2842 | -} |
2843 | - |
2844 | -QT_END_NAMESPACE |
2845 | - |
2846 | |
2847 | === added file 'handler/qpulseaudioengine.h' |
2848 | --- handler/qpulseaudioengine.h 1970-01-01 00:00:00 +0000 |
2849 | +++ handler/qpulseaudioengine.h 2017-02-06 13:26:33 +0000 |
2850 | @@ -0,0 +1,126 @@ |
2851 | +/**************************************************************************** |
2852 | +** |
2853 | +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). |
2854 | +** Contact: http://www.qt-project.org/legal |
2855 | +** |
2856 | +** This file was taken from qt5 and modified by |
2857 | +** David Henningsson <david.henningsson@canonical.com> for usage in |
2858 | +** telepathy-ofono. |
2859 | +** |
2860 | +** GNU Lesser General Public License Usage |
2861 | +** Alternatively, this file may be used under the terms of the GNU Lesser |
2862 | +** General Public License version 2.1 as published by the Free Software |
2863 | +** Foundation and appearing in the file LICENSE.LGPL included in the |
2864 | +** packaging of this file. Please review the following information to |
2865 | +** ensure the GNU Lesser General Public License version 2.1 requirements |
2866 | +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
2867 | +** |
2868 | +****************************************************************************/ |
2869 | + |
2870 | +#ifndef QPULSEAUDIOENGINE_H |
2871 | +#define QPULSEAUDIOENGINE_H |
2872 | + |
2873 | +#include <QtCore/qmap.h> |
2874 | +#include <QtCore/qbytearray.h> |
2875 | +#include <QThread> |
2876 | +#include <pulse/pulseaudio.h> |
2877 | + |
2878 | +enum AudioMode { |
2879 | + AudioModeEarpiece = 0x0001, |
2880 | + AudioModeWiredHeadset = 0x0002, |
2881 | + AudioModeSpeaker = 0x0004, |
2882 | + AudioModeBluetooth = 0x0008, |
2883 | + AudioModeBtOrWiredOrEarpiece = AudioModeBluetooth | AudioModeWiredHeadset | AudioModeEarpiece, |
2884 | + AudioModeWiredOrEarpiece = AudioModeWiredHeadset | AudioModeEarpiece, |
2885 | + AudioModeWiredOrSpeaker = AudioModeWiredHeadset | AudioModeSpeaker, |
2886 | + AudioModeBtOrWiredOrSpeaker = AudioModeBluetooth | AudioModeWiredOrSpeaker |
2887 | +}; |
2888 | + |
2889 | +Q_DECLARE_METATYPE(AudioMode) |
2890 | + |
2891 | +typedef QList<AudioMode> AudioModes; |
2892 | +Q_DECLARE_METATYPE(AudioModes) |
2893 | + |
2894 | +enum CallStatus { |
2895 | + CallRinging, |
2896 | + CallActive, |
2897 | + CallEnded |
2898 | +}; |
2899 | + |
2900 | +Q_DECLARE_METATYPE(CallStatus) |
2901 | + |
2902 | +QT_BEGIN_NAMESPACE |
2903 | + |
2904 | +class QPulseAudioEngineWorker : public QObject |
2905 | +{ |
2906 | + Q_OBJECT |
2907 | + |
2908 | +public: |
2909 | + QPulseAudioEngineWorker(QObject *parent = 0); |
2910 | + ~QPulseAudioEngineWorker(); |
2911 | + |
2912 | + pa_threaded_mainloop *mainloop() { return m_mainLoop; } |
2913 | + pa_context *context() { return m_context; } |
2914 | + bool createPulseContext(void); |
2915 | + int setupVoiceCall(void); |
2916 | + void restoreVoiceCall(void); |
2917 | + /* Callbacks to be used internally */ |
2918 | + void cardInfoCallback(const pa_card_info *card); |
2919 | + void sinkInfoCallback(const pa_sink_info *sink); |
2920 | + void sourceInfoCallback(const pa_source_info *source); |
2921 | + void serverInfoCallback(const pa_server_info *server); |
2922 | + void plugCardCallback(const pa_card_info *card); |
2923 | + void updateCardCallback(const pa_card_info *card); |
2924 | + void unplugCardCallback(); |
2925 | + |
2926 | +Q_SIGNALS: |
2927 | + void audioModeChanged(const AudioMode mode); |
2928 | + void availableAudioModesChanged(const AudioModes modes); |
2929 | + |
2930 | +public Q_SLOTS: |
2931 | + void handleCardEvent(const int evt, const unsigned int idx); |
2932 | + void setCallMode(CallStatus callstatus, AudioMode audiomode); |
2933 | + void setMicMute(bool muted); /* True if muted, false if unmuted */ |
2934 | + |
2935 | +private: |
2936 | + pa_mainloop_api *m_mainLoopApi; |
2937 | + pa_threaded_mainloop *m_mainLoop; |
2938 | + pa_context *m_context; |
2939 | + |
2940 | + AudioModes m_availableAudioModes; |
2941 | + CallStatus m_callstatus; |
2942 | + AudioMode m_audiomode; |
2943 | + AudioMode m_audiomodetoset; |
2944 | + bool m_micmute, m_handleevent; |
2945 | + std::string m_nametoset, m_valuetoset; |
2946 | + std::string m_defaultsink, m_defaultsource; |
2947 | + std::string m_bt_hsp, m_bt_hsp_a2dp; |
2948 | + std::string m_default_bt_card_fallback; |
2949 | + std::string m_voicecallcard, m_voicecallhighest, m_voicecallprofile; |
2950 | + |
2951 | + bool handleOperation(pa_operation *operation, const char *func_name); |
2952 | + void releasePulseContext(void); |
2953 | +}; |
2954 | + |
2955 | +class QPulseAudioEngine : public QObject |
2956 | +{ |
2957 | + Q_OBJECT |
2958 | +public: |
2959 | + explicit QPulseAudioEngine(QObject *parent = 0); |
2960 | + ~QPulseAudioEngine(); |
2961 | + static QPulseAudioEngine *instance(); |
2962 | + |
2963 | + void setCallMode(CallStatus callstatus, AudioMode audiomode); |
2964 | + void setMicMute(bool muted); /* True if muted, false if unmuted */ |
2965 | + |
2966 | +Q_SIGNALS: |
2967 | + void audioModeChanged(const AudioMode mode); |
2968 | + void availableAudioModesChanged(const AudioModes modes); |
2969 | +private: |
2970 | + QPulseAudioEngineWorker *mWorker; |
2971 | + QThread mThread; |
2972 | +}; |
2973 | + |
2974 | +QT_END_NAMESPACE |
2975 | + |
2976 | +#endif |
2977 | |
2978 | === removed file 'handler/qpulseaudioengine.h' |
2979 | --- handler/qpulseaudioengine.h 2017-02-06 13:26:33 +0000 |
2980 | +++ handler/qpulseaudioengine.h 1970-01-01 00:00:00 +0000 |
2981 | @@ -1,126 +0,0 @@ |
2982 | -/**************************************************************************** |
2983 | -** |
2984 | -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). |
2985 | -** Contact: http://www.qt-project.org/legal |
2986 | -** |
2987 | -** This file was taken from qt5 and modified by |
2988 | -** David Henningsson <david.henningsson@canonical.com> for usage in |
2989 | -** telepathy-ofono. |
2990 | -** |
2991 | -** GNU Lesser General Public License Usage |
2992 | -** Alternatively, this file may be used under the terms of the GNU Lesser |
2993 | -** General Public License version 2.1 as published by the Free Software |
2994 | -** Foundation and appearing in the file LICENSE.LGPL included in the |
2995 | -** packaging of this file. Please review the following information to |
2996 | -** ensure the GNU Lesser General Public License version 2.1 requirements |
2997 | -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
2998 | -** |
2999 | -****************************************************************************/ |
3000 | - |
3001 | -#ifndef QPULSEAUDIOENGINE_H |
3002 | -#define QPULSEAUDIOENGINE_H |
3003 | - |
3004 | -#include <QtCore/qmap.h> |
3005 | -#include <QtCore/qbytearray.h> |
3006 | -#include <QThread> |
3007 | -#include <pulse/pulseaudio.h> |
3008 | - |
3009 | -enum AudioMode { |
3010 | - AudioModeEarpiece = 0x0001, |
3011 | - AudioModeWiredHeadset = 0x0002, |
3012 | - AudioModeSpeaker = 0x0004, |
3013 | - AudioModeBluetooth = 0x0008, |
3014 | - AudioModeBtOrWiredOrEarpiece = AudioModeBluetooth | AudioModeWiredHeadset | AudioModeEarpiece, |
3015 | - AudioModeWiredOrEarpiece = AudioModeWiredHeadset | AudioModeEarpiece, |
3016 | - AudioModeWiredOrSpeaker = AudioModeWiredHeadset | AudioModeSpeaker, |
3017 | - AudioModeBtOrWiredOrSpeaker = AudioModeBluetooth | AudioModeWiredOrSpeaker |
3018 | -}; |
3019 | - |
3020 | -Q_DECLARE_METATYPE(AudioMode) |
3021 | - |
3022 | -typedef QList<AudioMode> AudioModes; |
3023 | -Q_DECLARE_METATYPE(AudioModes) |
3024 | - |
3025 | -enum CallStatus { |
3026 | - CallRinging, |
3027 | - CallActive, |
3028 | - CallEnded |
3029 | -}; |
3030 | - |
3031 | -Q_DECLARE_METATYPE(CallStatus) |
3032 | - |
3033 | -QT_BEGIN_NAMESPACE |
3034 | - |
3035 | -class QPulseAudioEngineWorker : public QObject |
3036 | -{ |
3037 | - Q_OBJECT |
3038 | - |
3039 | -public: |
3040 | - QPulseAudioEngineWorker(QObject *parent = 0); |
3041 | - ~QPulseAudioEngineWorker(); |
3042 | - |
3043 | - pa_threaded_mainloop *mainloop() { return m_mainLoop; } |
3044 | - pa_context *context() { return m_context; } |
3045 | - bool createPulseContext(void); |
3046 | - int setupVoiceCall(void); |
3047 | - void restoreVoiceCall(void); |
3048 | - /* Callbacks to be used internally */ |
3049 | - void cardInfoCallback(const pa_card_info *card); |
3050 | - void sinkInfoCallback(const pa_sink_info *sink); |
3051 | - void sourceInfoCallback(const pa_source_info *source); |
3052 | - void serverInfoCallback(const pa_server_info *server); |
3053 | - void plugCardCallback(const pa_card_info *card); |
3054 | - void updateCardCallback(const pa_card_info *card); |
3055 | - void unplugCardCallback(); |
3056 | - |
3057 | -Q_SIGNALS: |
3058 | - void audioModeChanged(const AudioMode mode); |
3059 | - void availableAudioModesChanged(const AudioModes modes); |
3060 | - |
3061 | -public Q_SLOTS: |
3062 | - void handleCardEvent(const int evt, const unsigned int idx); |
3063 | - void setCallMode(CallStatus callstatus, AudioMode audiomode); |
3064 | - void setMicMute(bool muted); /* True if muted, false if unmuted */ |
3065 | - |
3066 | -private: |
3067 | - pa_mainloop_api *m_mainLoopApi; |
3068 | - pa_threaded_mainloop *m_mainLoop; |
3069 | - pa_context *m_context; |
3070 | - |
3071 | - AudioModes m_availableAudioModes; |
3072 | - CallStatus m_callstatus; |
3073 | - AudioMode m_audiomode; |
3074 | - AudioMode m_audiomodetoset; |
3075 | - bool m_micmute, m_handleevent; |
3076 | - std::string m_nametoset, m_valuetoset; |
3077 | - std::string m_defaultsink, m_defaultsource; |
3078 | - std::string m_bt_hsp, m_bt_hsp_a2dp; |
3079 | - std::string m_default_bt_card_fallback; |
3080 | - std::string m_voicecallcard, m_voicecallhighest, m_voicecallprofile; |
3081 | - |
3082 | - bool handleOperation(pa_operation *operation, const char *func_name); |
3083 | - void releasePulseContext(void); |
3084 | -}; |
3085 | - |
3086 | -class QPulseAudioEngine : public QObject |
3087 | -{ |
3088 | - Q_OBJECT |
3089 | -public: |
3090 | - explicit QPulseAudioEngine(QObject *parent = 0); |
3091 | - ~QPulseAudioEngine(); |
3092 | - static QPulseAudioEngine *instance(); |
3093 | - |
3094 | - void setCallMode(CallStatus callstatus, AudioMode audiomode); |
3095 | - void setMicMute(bool muted); /* True if muted, false if unmuted */ |
3096 | - |
3097 | -Q_SIGNALS: |
3098 | - void audioModeChanged(const AudioMode mode); |
3099 | - void availableAudioModesChanged(const AudioModes modes); |
3100 | -private: |
3101 | - QPulseAudioEngineWorker *mWorker; |
3102 | - QThread mThread; |
3103 | -}; |
3104 | - |
3105 | -QT_END_NAMESPACE |
3106 | - |
3107 | -#endif |
3108 | |
3109 | === modified file 'libtelephonyservice/chatentry.cpp' |
3110 | --- libtelephonyservice/chatentry.cpp 2016-11-23 19:37:30 +0000 |
3111 | +++ libtelephonyservice/chatentry.cpp 2017-02-06 13:26:33 +0000 |
3112 | @@ -165,31 +165,42 @@ |
3113 | |
3114 | void ChatEntry::onRolesChanged(const HandleRolesMap &added, const HandleRolesMap &removed) |
3115 | { |
3116 | - Q_UNUSED(added); |
3117 | - Q_UNUSED(removed); |
3118 | - |
3119 | - RolesMap rolesMap; |
3120 | Tp::TextChannel* channel = 0; |
3121 | if (rolesInterface) { |
3122 | - rolesMap = rolesInterface->getRoles(); |
3123 | + if (mRolesMap.isEmpty()) { |
3124 | + mRolesMap = rolesInterface->getRoles(); |
3125 | + } |
3126 | channel = qvariant_cast<Tp::TextChannel*>(rolesInterface->property("channel")); |
3127 | } |
3128 | |
3129 | + QMapIterator<uint, uint> it(removed); |
3130 | + while (it.hasNext()) { |
3131 | + it.next(); |
3132 | + mRolesMap.remove(it.key()); |
3133 | + } |
3134 | + |
3135 | + QMapIterator<uint, uint> it2(added); |
3136 | + while (it2.hasNext()) { |
3137 | + it2.next(); |
3138 | + mRolesMap[it2.key()] = it2.value(); |
3139 | + } |
3140 | + |
3141 | + // TODO avoid iterating over all participants when not needed |
3142 | Q_FOREACH(Participant* participant, mParticipants) { |
3143 | - if (rolesMap.contains(participant->handle())) { |
3144 | - participant->setRoles(rolesMap[participant->handle()]); |
3145 | + if (mRolesMap.contains(participant->handle())) { |
3146 | + participant->setRoles(mRolesMap[participant->handle()]); |
3147 | } |
3148 | } |
3149 | |
3150 | Q_FOREACH(Participant* participant, mLocalPendingParticipants) { |
3151 | - if (rolesMap.contains(participant->handle())) { |
3152 | - participant->setRoles(rolesMap[participant->handle()]); |
3153 | + if (mRolesMap.contains(participant->handle())) { |
3154 | + participant->setRoles(mRolesMap[participant->handle()]); |
3155 | } |
3156 | } |
3157 | |
3158 | Q_FOREACH(Participant* participant, mRemotePendingParticipants) { |
3159 | - if (rolesMap.contains(participant->handle())) { |
3160 | - participant->setRoles(rolesMap[participant->handle()]); |
3161 | + if (mRolesMap.contains(participant->handle())) { |
3162 | + participant->setRoles(mRolesMap[participant->handle()]); |
3163 | } |
3164 | } |
3165 | |
3166 | @@ -202,7 +213,7 @@ |
3167 | return; |
3168 | } |
3169 | |
3170 | - mSelfContactRoles = rolesMap[selfContact->handle().at(0)]; |
3171 | + mSelfContactRoles = mRolesMap[selfContact->handle().at(0)]; |
3172 | Q_EMIT selfContactRolesChanged(); |
3173 | } |
3174 | |
3175 | @@ -685,15 +696,14 @@ |
3176 | } |
3177 | } |
3178 | |
3179 | - RolesMap rolesMap; |
3180 | - if (rolesInterface) { |
3181 | - rolesMap = rolesInterface->getRoles(); |
3182 | + if (rolesInterface && mRolesMap.isEmpty()) { |
3183 | + mRolesMap = rolesInterface->getRoles(); |
3184 | } |
3185 | // now add the new participants |
3186 | // FIXME: check for duplicates? |
3187 | Q_FOREACH(Tp::ContactPtr contact, added) { |
3188 | uint handle = contact->handle().at(0); |
3189 | - list << new Participant(contact->id(), rolesMap[handle], handle, this); |
3190 | + list << new Participant(contact->id(), mRolesMap[handle], handle, this); |
3191 | } |
3192 | } |
3193 | |
3194 | |
3195 | === modified file 'libtelephonyservice/chatentry.h' |
3196 | --- libtelephonyservice/chatentry.h 2017-02-06 13:26:33 +0000 |
3197 | +++ libtelephonyservice/chatentry.h 2017-02-06 13:26:33 +0000 |
3198 | @@ -226,6 +226,7 @@ |
3199 | Tp::Client::ChannelInterfaceRoomConfigInterface *roomConfigInterface; |
3200 | Tp::Client::ChannelInterfaceSubjectInterface *subjectInterface; |
3201 | ChannelInterfaceRolesInterface *rolesInterface; |
3202 | + RolesMap mRolesMap; |
3203 | }; |
3204 | |
3205 | #endif // CHATENTRY_H |
3206 | |
3207 | === modified file 'libtelephonyservice/contactwatcher.cpp' |
3208 | --- libtelephonyservice/contactwatcher.cpp 2017-02-06 13:26:33 +0000 |
3209 | +++ libtelephonyservice/contactwatcher.cpp 2017-02-06 13:26:33 +0000 |
3210 | @@ -42,8 +42,6 @@ |
3211 | ContactWatcher::ContactWatcher(QObject *parent) : |
3212 | QObject(parent), mRequest(0), mInteractive(false), mCompleted(false) |
3213 | { |
3214 | - // addressable VCard fields defaults to "tel" only |
3215 | - mAddressableFields << "tel"; |
3216 | connect(ContactUtils::sharedManager(), |
3217 | SIGNAL(contactsAdded(QList<QContactId>)), |
3218 | SLOT(onContactsAdded(QList<QContactId>))); |
3219 | @@ -67,7 +65,7 @@ |
3220 | |
3221 | void ContactWatcher::startSearching() |
3222 | { |
3223 | - if (!mCompleted || mIdentifier.isEmpty() || !mInteractive) { |
3224 | + if (!mCompleted || mIdentifier.isEmpty() || !mInteractive || mAddressableFields.isEmpty()) { |
3225 | // component is not ready yet or no identifier given, |
3226 | // or the number is not interactive and thus doesn't need contact info at all |
3227 | return; |
3228 | @@ -271,10 +269,6 @@ |
3229 | void ContactWatcher::setAddressableFields(const QStringList &fields) |
3230 | { |
3231 | mAddressableFields = fields; |
3232 | - // if the addressable fields is empty, fall back to matching phone numbers |
3233 | - if (mAddressableFields.isEmpty()) { |
3234 | - mAddressableFields << "tel"; |
3235 | - } |
3236 | Q_EMIT addressableFieldsChanged(); |
3237 | |
3238 | startSearching(); |
3239 | |
3240 | === modified file 'tests/Ubuntu.Telephony/ContactWatcherTest.cpp' |
3241 | --- tests/Ubuntu.Telephony/ContactWatcherTest.cpp 2015-10-09 19:48:07 +0000 |
3242 | +++ tests/Ubuntu.Telephony/ContactWatcherTest.cpp 2017-02-06 13:26:33 +0000 |
3243 | @@ -95,7 +95,7 @@ |
3244 | |
3245 | // set the phone number and wait for the match to happen |
3246 | watcher.setIdentifier(identifier); |
3247 | - |
3248 | + watcher.setAddressableFields(QStringList() << "tel"); |
3249 | |
3250 | // contact fetching is asynchronous so use QTRY_COMPARE for the first signal spy |
3251 | // for the subsequent ones it is fine to use just QCOMPARE |
3252 | @@ -128,6 +128,7 @@ |
3253 | QSignalSpy unknownSpy(&watcher, SIGNAL(isUnknownChanged())); |
3254 | |
3255 | watcher.setIdentifier(identifier); |
3256 | + watcher.setAddressableFields(QStringList() << "tel"); |
3257 | |
3258 | // now create the contact and wait to see if it gets matched |
3259 | QContact contact = createContact("FirstName", |
3260 | @@ -169,6 +170,7 @@ |
3261 | ContactWatcher watcher; |
3262 | watcher.componentComplete(); |
3263 | watcher.setIdentifier(identifier); |
3264 | + watcher.setAddressableFields(QStringList() << "tel"); |
3265 | |
3266 | QSignalSpy contactIdSpy(&watcher, SIGNAL(contactIdChanged())); |
3267 | QSignalSpy aliasSpy(&watcher, SIGNAL(aliasChanged())); |
3268 | @@ -218,6 +220,7 @@ |
3269 | |
3270 | // set the phone number and wait for the match to happen |
3271 | watcher.setIdentifier(identifier); |
3272 | + watcher.setAddressableFields(QStringList() << "tel"); |
3273 | |
3274 | // at this point we just need to make sure the contactId is correct, the other fields |
3275 | // are tested in a separate test |
3276 | @@ -269,6 +272,7 @@ |
3277 | |
3278 | // set the phone number and wait for the match to happen |
3279 | watcher.setIdentifier(identifier); |
3280 | + watcher.setAddressableFields(QStringList() << "tel"); |
3281 | |
3282 | // at this point we just need to make sure the contactId is correct, the other fields |
3283 | // are tested in a separate test |
3284 | @@ -318,6 +322,7 @@ |
3285 | |
3286 | // set the phone number and wait for the match to happen |
3287 | watcher.setIdentifier(identifier); |
3288 | + watcher.setAddressableFields(QStringList() << "tel"); |
3289 | |
3290 | // at this point we just need to make sure the contactId is correct, the other fields |
3291 | // are tested in a separate test |
3292 | @@ -396,6 +401,7 @@ |
3293 | |
3294 | // set the phone number and wait for the match to happen |
3295 | watcher.setIdentifier(identifier); |
3296 | + watcher.setAddressableFields(QStringList() << "tel"); |
3297 | |
3298 | // component not complete yet |
3299 | QTRY_COMPARE(contactIdSpy.count(), 0); |
3300 | @@ -430,8 +436,8 @@ |
3301 | ContactWatcher watcher; |
3302 | |
3303 | // check that addressable fields contains "tel" by default |
3304 | - QCOMPARE(watcher.addressableFields().count(), 1); |
3305 | - QCOMPARE(watcher.addressableFields()[0], QString("tel")); |
3306 | + QCOMPARE(watcher.addressableFields().count(), 0); |
3307 | + QCOMPARE(watcher.addressableFields(), QStringList()); |
3308 | |
3309 | QSignalSpy addressableFieldsSpy(&watcher, SIGNAL(addressableFieldsChanged())); |
3310 | QStringList addressableFields; |
3311 | @@ -442,8 +448,8 @@ |
3312 | |
3313 | // set the addressable fields to an empty value and make sure it falls back to "tel" |
3314 | watcher.setAddressableFields(QStringList()); |
3315 | - QCOMPARE(watcher.addressableFields().count(), 1); |
3316 | - QCOMPARE(watcher.addressableFields()[0], QString("tel")); |
3317 | + QCOMPARE(watcher.addressableFields().count(), 0); |
3318 | + QCOMPARE(watcher.addressableFields(), QStringList()); |
3319 | } |
3320 | |
3321 | void ContactWatcherTest::testExtendedFieldMatch() |
3322 | @@ -496,6 +502,7 @@ |
3323 | |
3324 | // try to match contact A |
3325 | watcherA.setIdentifier(contactIdentifierA); |
3326 | + watcherA.setAddressableFields(QStringList() << "tel"); |
3327 | |
3328 | // mark as complete |
3329 | watcherA.componentComplete(); |
3330 | @@ -513,6 +520,7 @@ |
3331 | |
3332 | // try to match contact B |
3333 | watcherB.setIdentifier(contactIdentifierB); |
3334 | + watcherB.setAddressableFields(QStringList() << "tel"); |
3335 | |
3336 | // signal will be fired now |
3337 | QTRY_COMPARE(contactIdSpyB.count(), 1); |