Merge lp:~tiagosh/telephony-service/audio-route-manager into lp:telephony-service
- audio-route-manager
- Merge into trunk
Status: | Needs review |
---|---|
Proposed branch: | lp:~tiagosh/telephony-service/audio-route-manager |
Merge into: | lp:telephony-service |
Diff against target: |
2127 lines (+1661/-89) 24 files modified
CMakeLists.txt (+1/-0) debian/control (+1/-0) handler/CMakeLists.txt (+11/-0) handler/Handler.xml (+23/-7) handler/audioroutemanager.cpp (+231/-0) handler/audioroutemanager.h (+75/-0) handler/callhandler.cpp (+0/-11) handler/callhandler.h (+0/-1) handler/handlerdbus.cpp (+24/-5) handler/handlerdbus.h (+11/-1) handler/main.cpp (+12/-5) handler/powerd.h (+34/-0) handler/powerdaudiomodemediator.cpp (+63/-0) handler/powerdaudiomodemediator.h (+46/-0) handler/powerddbus.cpp (+43/-0) handler/powerddbus.h (+38/-0) handler/qpulseaudioengine.cpp (+853/-0) handler/qpulseaudioengine.h (+126/-0) libtelephonyservice/audiooutput.cpp (+17/-0) libtelephonyservice/audiooutput.h (+4/-0) libtelephonyservice/callentry.cpp (+38/-50) libtelephonyservice/callentry.h (+1/-1) libtelephonyservice/telepathyhelper.cpp (+9/-7) libtelephonyservice/telepathyhelper.h (+0/-1) |
To merge this branch: | bzr merge lp:~tiagosh/telephony-service/audio-route-manager |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
system-apps-ci-bot | continuous-integration | Needs Fixing | |
PS Jenkins bot | continuous-integration | Approve | |
Ubuntu Phablet Team | Pending | ||
Review via email: mp+283200@code.launchpad.net |
Commit message
Implement AudioRouteManager in telephony-
Description of the change
Implement AudioRouteManager in telephony-
PS Jenkins bot (ps-jenkins) wrote : | # |
- 1161. By Tiago Salem Herrmann
-
fix get AudioOutputs
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1161
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
deb: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 1162. By Tiago Salem Herrmann
-
remove unused include
- 1163. By Tiago Salem Herrmann
-
add ifdef to avoid including qpulseaudioengine.h
- 1164. By Tiago Salem Herrmann
-
include qpulseaudioengine only when libpulse is installed
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1163
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
deb: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1164
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1164
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unmerged revisions
- 1164. By Tiago Salem Herrmann
-
include qpulseaudioengine only when libpulse is installed
- 1163. By Tiago Salem Herrmann
-
add ifdef to avoid including qpulseaudioengine.h
- 1162. By Tiago Salem Herrmann
-
remove unused include
- 1161. By Tiago Salem Herrmann
-
fix get AudioOutputs
- 1160. By Tiago Salem Herrmann
-
Implement AudioRouteManager in telephony-
service- handler
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2015-10-13 14:42:57 +0000 |
3 | +++ CMakeLists.txt 2016-01-20 11:56:12 +0000 |
4 | @@ -73,6 +73,7 @@ |
5 | pkg_check_modules(UserMetrics REQUIRED libusermetricsinput-1) |
6 | pkg_check_modules(HISTORY REQUIRED history-service) |
7 | pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt) |
8 | +pkg_check_modules(PULSEAUDIO libpulse) |
9 | |
10 | add_definitions(-DQT_NO_KEYWORDS) |
11 | |
12 | |
13 | === modified file 'debian/control' |
14 | --- debian/control 2014-09-05 13:47:18 +0000 |
15 | +++ debian/control 2016-01-20 11:56:12 +0000 |
16 | @@ -18,6 +18,7 @@ |
17 | libtelepathy-qt5-dev, |
18 | libubuntu-application-api-dev [armhf], |
19 | libprotobuf-dev, |
20 | + libpulse-dev [armhf], |
21 | pkg-config, |
22 | python:any, |
23 | qml-module-qttest, |
24 | |
25 | === modified file 'handler/CMakeLists.txt' |
26 | --- handler/CMakeLists.txt 2015-03-10 21:06:56 +0000 |
27 | +++ handler/CMakeLists.txt 2016-01-20 11:56:12 +0000 |
28 | @@ -1,10 +1,19 @@ |
29 | +if (PULSEAUDIO_FOUND) |
30 | + add_definitions(-DUSE_PULSEAUDIO) |
31 | + set(USE_PULSEAUDIO ON) |
32 | + set(QPULSEAUDIOENGINE_CPP qpulseaudioengine.cpp) |
33 | +endif (PULSEAUDIO_FOUND) |
34 | |
35 | set(qt_SRCS |
36 | + audioroutemanager.cpp |
37 | callhandler.cpp |
38 | displaynamesettings.cpp |
39 | handler.cpp |
40 | handlerdbus.cpp |
41 | + powerdaudiomodemediator.cpp |
42 | + powerddbus.cpp |
43 | texthandler.cpp |
44 | + ${QPULSEAUDIOENGINE_CPP} |
45 | ) |
46 | |
47 | set(handler_SRCS main.cpp ${qt_SRCS}) |
48 | @@ -15,6 +24,7 @@ |
49 | ${CMAKE_SOURCE_DIR}/libtelephonyservice |
50 | ${CMAKE_CURRENT_BINARY_DIR} |
51 | ${GSETTINGS_QT_INCLUDE_DIRS} |
52 | + ${PULSEAUDIO_INCLUDE_DIRS} |
53 | ) |
54 | |
55 | add_executable(telephony-service-handler ${handler_SRCS} ${handler_HDRS}) |
56 | @@ -24,6 +34,7 @@ |
57 | ${TP_QT5_LIBRARIES} |
58 | ${GSETTINGS_QT_LDFLAGS} |
59 | telephonyservice |
60 | + ${PULSEAUDIO_LIBRARIES} |
61 | ) |
62 | |
63 | enable_coverage(telephony-service-handler) |
64 | |
65 | === modified file 'handler/Handler.xml' |
66 | --- handler/Handler.xml 2015-11-17 17:23:54 +0000 |
67 | +++ handler/Handler.xml 2016-01-20 11:56:12 +0000 |
68 | @@ -82,13 +82,6 @@ |
69 | <arg name="objectPath" type="s" direction="in"/> |
70 | <arg name="muted" type="b" direction="in"/> |
71 | </method> |
72 | - <method name="SetActiveAudioOutput"> |
73 | - <dox:d><![CDATA[ |
74 | - Change the current active audio output |
75 | - ]]></dox:d> |
76 | - <arg name="objectPath" type="s" direction="in"/> |
77 | - <arg name="id" type="s" direction="in"/> |
78 | - </method> |
79 | <method name="SendDTMF"> |
80 | <dox:d><![CDATA[ |
81 | Send a DTMF to the given channel |
82 | @@ -170,5 +163,28 @@ |
83 | ]]></dox:d> |
84 | <arg name="visible" type="b"/> |
85 | </signal> |
86 | + <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/> |
87 | + <property name="ActiveAudioOutput" type="s" access="readwrite"/> |
88 | + <signal name="ActiveAudioOutputChanged"> |
89 | + <dox:d><![CDATA[ |
90 | + The active audio output has changed |
91 | + ]]></dox:d> |
92 | + <arg name="id" type="s"/> |
93 | + </signal> |
94 | + <method name="AudioOutputs"> |
95 | + <dox:d><![CDATA[ |
96 | + ]]></dox:d> |
97 | + <arg name="outputs" type="a(sss)" direction="out"/> |
98 | + <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="AudioOutputDBusList"/> |
99 | + <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="AudioOutputDBusList"/> |
100 | + </method> |
101 | + <signal name="AudioOutputsChanged"> |
102 | + <dox:d><![CDATA[ |
103 | + The available audio outputs have changed |
104 | + ]]></dox:d> |
105 | + <arg name="outputs" type="a(sss)"/> |
106 | + <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="AudioOutputDBusList"/> |
107 | + <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="AudioOutputDBusList"/> |
108 | + </signal> |
109 | </interface> |
110 | </node> |
111 | |
112 | === added file 'handler/audioroutemanager.cpp' |
113 | --- handler/audioroutemanager.cpp 1970-01-01 00:00:00 +0000 |
114 | +++ handler/audioroutemanager.cpp 2016-01-20 11:56:12 +0000 |
115 | @@ -0,0 +1,231 @@ |
116 | +/* |
117 | + * Copyright (C) 2016 Canonical, Ltd. |
118 | + * |
119 | + * Authors: |
120 | + * Tiago Salem Herrmann <tiago.herrmann@canonical.com> |
121 | + * |
122 | + * This file is part of telephony-service. |
123 | + * |
124 | + * telephony-service is free software; you can redistribute it and/or modify |
125 | + * it under the terms of the GNU General Public License as published by |
126 | + * the Free Software Foundation; version 3. |
127 | + * |
128 | + * telephony-service is distributed in the hope that it will be useful, |
129 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
130 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
131 | + * GNU General Public License for more details. |
132 | + * |
133 | + * You should have received a copy of the GNU General Public License |
134 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
135 | + */ |
136 | + |
137 | +#include "audioroutemanager.h" |
138 | +#include "telepathyhelper.h" |
139 | +#include "accountentry.h" |
140 | +#include <TelepathyQt/Contact> |
141 | +#include <TelepathyQt/Functors> |
142 | + |
143 | + |
144 | +static void enable_earpiece() |
145 | +{ |
146 | +#ifdef USE_PULSEAUDIO |
147 | + QPulseAudioEngine::instance()->setCallMode(CallActive, AudioModeBtOrWiredOrEarpiece); |
148 | +#endif |
149 | +} |
150 | + |
151 | +static void enable_normal() |
152 | +{ |
153 | +#ifdef USE_PULSEAUDIO |
154 | + QTimer* timer = new QTimer(); |
155 | + timer->setSingleShot(true); |
156 | + QObject::connect(timer, &QTimer::timeout, [=](){ |
157 | + QPulseAudioEngine::instance()->setMicMute(false); |
158 | + QPulseAudioEngine::instance()->setCallMode(CallEnded, AudioModeWiredOrSpeaker); |
159 | + timer->deleteLater(); |
160 | + }); |
161 | + timer->start(2000); |
162 | +#endif |
163 | +} |
164 | + |
165 | +static void enable_speaker() |
166 | +{ |
167 | +#ifdef USE_PULSEAUDIO |
168 | + QPulseAudioEngine::instance()->setCallMode(CallActive, AudioModeSpeaker); |
169 | +#endif |
170 | +} |
171 | + |
172 | +static void enable_ringtone() |
173 | +{ |
174 | +#ifdef USE_PULSEAUDIO |
175 | + QPulseAudioEngine::instance()->setCallMode(CallRinging, AudioModeBtOrWiredOrSpeaker); |
176 | +#endif |
177 | +} |
178 | + |
179 | +AudioRouteManager *AudioRouteManager::instance() |
180 | +{ |
181 | + static AudioRouteManager *self = new AudioRouteManager(); |
182 | + return self; |
183 | +} |
184 | + |
185 | +AudioRouteManager::AudioRouteManager(QObject *parent) : |
186 | + QObject(parent), mAudioModeMediator(mPowerDDBus) |
187 | +{ |
188 | + TelepathyHelper::instance()->registerChannelObserver("TelephonyServiceHandlerAudioRouteManager"); |
189 | + |
190 | + QObject::connect(TelepathyHelper::instance()->channelObserver(), SIGNAL(callChannelAvailable(Tp::CallChannelPtr)), |
191 | + this, SLOT(onCallChannelAvailable(Tp::CallChannelPtr))); |
192 | + |
193 | +#ifdef USE_PULSEAUDIO |
194 | + // update audio modes |
195 | + QObject::connect(QPulseAudioEngine::instance(), SIGNAL(audioModeChanged(AudioMode)), SLOT(onAudioModeChanged(AudioMode))); |
196 | + QObject::connect(QPulseAudioEngine::instance(), SIGNAL(availableAudioModesChanged(AudioModes)), SLOT(onAvailableAudioModesChanged(AudioModes))); |
197 | + |
198 | + // check if we should indeed use pulseaudio |
199 | + QByteArray pulseAudioDisabled = qgetenv("PA_DISABLED"); |
200 | + mHasPulseAudio = true; |
201 | + if (!pulseAudioDisabled.isEmpty()) |
202 | + mHasPulseAudio = false; |
203 | +#endif |
204 | + |
205 | + connect(this, &AudioRouteManager::activeAudioOutputChanged, Tp::memFun(&mAudioModeMediator, &PowerDAudioModeMediator::audioModeChanged)); |
206 | + connect(this, &AudioRouteManager::lastChannelClosed, Tp::memFun(&mAudioModeMediator, &PowerDAudioModeMediator::audioOutputClosed)); |
207 | +} |
208 | + |
209 | +void AudioRouteManager::onCallChannelAvailable(Tp::CallChannelPtr callChannel) |
210 | +{ |
211 | + connect(callChannel.data(), |
212 | + SIGNAL(callStateChanged(Tp::CallState)), |
213 | + SLOT(onCallStateChanged(Tp::CallState))); |
214 | + |
215 | + mChannels.append(callChannel); |
216 | + updateAudioRoute(true); |
217 | +} |
218 | + |
219 | +void AudioRouteManager::onCallStateChanged(Tp::CallState state) |
220 | +{ |
221 | + Tp::CallChannelPtr channel(qobject_cast<Tp::CallChannel*>(sender())); |
222 | + if (!channel) { |
223 | + return; |
224 | + } |
225 | + |
226 | + if (channel->callState() == Tp::CallStateEnded) { |
227 | + mChannels.removeOne(channel); |
228 | + } |
229 | + updateAudioRoute(false); |
230 | +} |
231 | + |
232 | +void AudioRouteManager::setActiveAudioOutput(const QString &id) |
233 | +{ |
234 | +#ifdef USE_PULSEAUDIO |
235 | + // fallback to earpiece/headset |
236 | + AudioMode mode = AudioModeWiredOrEarpiece; |
237 | + if (id == "bluetooth") { |
238 | + mode = AudioModeBluetooth; |
239 | + } else if (id == "speaker") { |
240 | + mode = AudioModeSpeaker; |
241 | + } |
242 | + if (mHasPulseAudio) |
243 | + QPulseAudioEngine::instance()->setCallMode(CallActive, mode); |
244 | +#endif |
245 | +} |
246 | + |
247 | +QString AudioRouteManager::activeAudioOutput() |
248 | +{ |
249 | + return mActiveAudioOutput; |
250 | +} |
251 | + |
252 | +AudioOutputDBusList AudioRouteManager::audioOutputs() const |
253 | +{ |
254 | + return mAudioOutputs; |
255 | +} |
256 | + |
257 | +void AudioRouteManager::updateAudioRoute(bool newCall) |
258 | +{ |
259 | +#ifdef USE_PULSEAUDIO |
260 | + if (!mHasPulseAudio) |
261 | + return; |
262 | +#endif |
263 | + |
264 | + int currentCalls = mChannels.size(); |
265 | + if (currentCalls != 0) { |
266 | + if (currentCalls == 1) { |
267 | + // if we have only one call, check if it's incoming and |
268 | + // enable speaker mode so the ringtone is audible |
269 | + Tp::CallChannelPtr callChannel = mChannels.first(); |
270 | + AccountEntry *accountEntry = TelepathyHelper::instance()->accountForConnection(callChannel->connection()); |
271 | + if (!accountEntry || !callChannel) { |
272 | + return; |
273 | + } |
274 | + |
275 | + bool incoming = callChannel->initiatorContact() != accountEntry->account()->connection()->selfContact(); |
276 | + Tp::CallState state = callChannel->callState(); |
277 | + if (incoming && newCall) { |
278 | + enable_ringtone(); |
279 | + return; |
280 | + } |
281 | + if (state == Tp::CallStateEnded) { |
282 | + enable_normal(); |
283 | + return; |
284 | + } |
285 | + // if only one call and dialing, or incoming call just accepted, then default to earpiece |
286 | + if (newCall || (state == Tp::CallStateAccepted && incoming)) { |
287 | + enable_earpiece(); |
288 | + return; |
289 | + } |
290 | + } |
291 | + } else { |
292 | + enable_normal(); |
293 | + Q_EMIT lastChannelClosed(); |
294 | + } |
295 | +} |
296 | + |
297 | +#ifdef USE_PULSEAUDIO |
298 | +void AudioRouteManager::onAudioModeChanged(AudioMode mode) |
299 | +{ |
300 | + qDebug("PulseAudio audio mode changed: 0x%x", mode); |
301 | + |
302 | + if (mode == AudioModeEarpiece && mActiveAudioOutput != "earpiece") { |
303 | + mActiveAudioOutput = "earpiece"; |
304 | + } else if (mode == AudioModeWiredHeadset && mActiveAudioOutput != "wired_headset") { |
305 | + mActiveAudioOutput = "wired_headset"; |
306 | + } else if (mode == AudioModeSpeaker && mActiveAudioOutput != "speaker") { |
307 | + mActiveAudioOutput = "speaker"; |
308 | + } else if (mode == AudioModeBluetooth && mActiveAudioOutput != "bluetooth") { |
309 | + mActiveAudioOutput = "bluetooth"; |
310 | + } |
311 | + Q_EMIT activeAudioOutputChanged(mActiveAudioOutput); |
312 | +} |
313 | + |
314 | +void AudioRouteManager::onAvailableAudioModesChanged(AudioModes modes) |
315 | +{ |
316 | + qDebug("PulseAudio available audio modes changed"); |
317 | + bool defaultFound = false; |
318 | + mAudioOutputs.clear(); |
319 | + Q_FOREACH(const AudioMode &mode, modes) { |
320 | + AudioOutputDBus output; |
321 | + if (mode == AudioModeBluetooth) { |
322 | + // there can be only one bluetooth |
323 | + output.id = "bluetooth"; |
324 | + output.type = "bluetooth"; |
325 | + // we dont support names for now, so we set a default value |
326 | + output.name = "bluetooth"; |
327 | + } else if (mode == AudioModeEarpiece || mode == AudioModeWiredHeadset) { |
328 | + if (!defaultFound) { |
329 | + defaultFound = true; |
330 | + output.id = "default"; |
331 | + output.type = "default"; |
332 | + output.name = "default"; |
333 | + } else { |
334 | + continue; |
335 | + } |
336 | + } else if (mode == AudioModeSpeaker) { |
337 | + output.id = "speaker"; |
338 | + output.type = "speaker"; |
339 | + output.name = "speaker"; |
340 | + } |
341 | + mAudioOutputs << output; |
342 | + } |
343 | + Q_EMIT audioOutputsChanged(mAudioOutputs); |
344 | +} |
345 | +#endif |
346 | + |
347 | |
348 | === added file 'handler/audioroutemanager.h' |
349 | --- handler/audioroutemanager.h 1970-01-01 00:00:00 +0000 |
350 | +++ handler/audioroutemanager.h 2016-01-20 11:56:12 +0000 |
351 | @@ -0,0 +1,75 @@ |
352 | +/* |
353 | + * Copyright (C) 2016 Canonical, Ltd. |
354 | + * |
355 | + * Authors: |
356 | + * Tiago Salem Herrmann <tiago.herrmann@canonical.com> |
357 | + * |
358 | + * This file is part of telephony-service. |
359 | + * |
360 | + * telephony-service is free software; you can redistribute it and/or modify |
361 | + * it under the terms of the GNU General Public License as published by |
362 | + * the Free Software Foundation; version 3. |
363 | + * |
364 | + * telephony-service is distributed in the hope that it will be useful, |
365 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
366 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
367 | + * GNU General Public License for more details. |
368 | + * |
369 | + * You should have received a copy of the GNU General Public License |
370 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
371 | + */ |
372 | + |
373 | +#ifndef AUDIOROUTEMANAGER_H |
374 | +#define AUDIOROUTEMANAGER_H |
375 | + |
376 | +#ifdef USE_PULSEAUDIO |
377 | +#include "qpulseaudioengine.h" |
378 | +#endif |
379 | +#include "audiooutput.h" |
380 | +#include "powerdaudiomodemediator.h" |
381 | +#include "powerddbus.h" |
382 | +#include <QObject> |
383 | +#include <TelepathyQt/CallChannel> |
384 | + |
385 | + |
386 | +class AudioRouteManager : public QObject |
387 | +{ |
388 | + Q_OBJECT |
389 | + |
390 | +public: |
391 | + static AudioRouteManager *instance(); |
392 | + void setActiveAudioOutput(const QString &id); |
393 | + QString activeAudioOutput(); |
394 | + AudioOutputDBusList audioOutputs() const; |
395 | + void updateAudioRoute(bool newCall = false); |
396 | + |
397 | +public Q_SLOTS: |
398 | + void onCallChannelAvailable(Tp::CallChannelPtr callChannel); |
399 | + |
400 | +Q_SIGNALS: |
401 | + void audioOutputsChanged(const AudioOutputDBusList &outputs); |
402 | + void activeAudioOutputChanged(const QString &id); |
403 | + void lastChannelClosed(); |
404 | + |
405 | +protected Q_SLOTS: |
406 | + void onCallStateChanged(Tp::CallState state); |
407 | + |
408 | +private Q_SLOTS: |
409 | +#ifdef USE_PULSEAUDIO |
410 | + void onAudioModeChanged(AudioMode mode); |
411 | + void onAvailableAudioModesChanged(AudioModes modes); |
412 | +#endif |
413 | + |
414 | +private: |
415 | + explicit AudioRouteManager(QObject *parent = 0); |
416 | + QList<Tp::CallChannelPtr> mChannels; |
417 | + AudioOutputDBusList mAudioOutputs; |
418 | + QString mActiveAudioOutput; |
419 | + PowerDDBus mPowerDDBus; |
420 | + PowerDAudioModeMediator mAudioModeMediator; |
421 | +#ifdef USE_PULSEAUDIO |
422 | + bool mHasPulseAudio; |
423 | +#endif |
424 | +}; |
425 | + |
426 | +#endif // AUDIOROUTEMANAGER_H |
427 | |
428 | === modified file 'handler/callhandler.cpp' |
429 | --- handler/callhandler.cpp 2015-07-06 23:32:13 +0000 |
430 | +++ handler/callhandler.cpp 2016-01-20 11:56:12 +0000 |
431 | @@ -151,14 +151,6 @@ |
432 | muteInterface.call("RequestMuted", muted); |
433 | } |
434 | |
435 | -void CallHandler::setActiveAudioOutput(const QString &objectPath, const QString &id) |
436 | -{ |
437 | - Tp::CallChannelPtr channel = callFromObjectPath(objectPath); |
438 | - |
439 | - QDBusInterface audioOutputsInterface(channel->busName(), channel->objectPath(), CANONICAL_TELEPHONY_AUDIOOUTPUTS_IFACE); |
440 | - audioOutputsInterface.call("SetActiveAudioOutput", id); |
441 | -} |
442 | - |
443 | void CallHandler::sendDTMF(const QString &objectPath, const QString &key) |
444 | { |
445 | bool ok; |
446 | @@ -266,9 +258,6 @@ |
447 | void CallHandler::onCallChannelAvailable(Tp::CallChannelPtr channel) |
448 | { |
449 | QDBusInterface callChannelIface(channel->busName(), channel->objectPath(), DBUS_PROPERTIES_IFACE); |
450 | - QDBusMessage reply = callChannelIface.call("GetAll", CANONICAL_TELEPHONY_AUDIOOUTPUTS_IFACE); |
451 | - QVariantList args = reply.arguments(); |
452 | - QMap<QString, QVariant> map = qdbus_cast<QMap<QString, QVariant> >(args[0]); |
453 | channel->setProperty("timestamp", QDateTime::currentDateTimeUtc()); |
454 | |
455 | if (channel->callState() == Tp::CallStateActive) { |
456 | |
457 | === modified file 'handler/callhandler.h' |
458 | --- handler/callhandler.h 2015-03-04 18:02:13 +0000 |
459 | +++ handler/callhandler.h 2016-01-20 11:56:12 +0000 |
460 | @@ -44,7 +44,6 @@ |
461 | void hangUpCall(const QString &objectPath); |
462 | void setHold(const QString &objectPath, bool hold); |
463 | void setMuted(const QString &objectPath, bool muted); |
464 | - void setActiveAudioOutput(const QString &objectPath, const QString &id); |
465 | void sendDTMF(const QString &objectPath, const QString &key); |
466 | |
467 | // conference call related |
468 | |
469 | === modified file 'handler/handlerdbus.cpp' |
470 | --- handler/handlerdbus.cpp 2015-11-17 17:23:54 +0000 |
471 | +++ handler/handlerdbus.cpp 2016-01-20 11:56:12 +0000 |
472 | @@ -21,6 +21,8 @@ |
473 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
474 | */ |
475 | |
476 | +#include "audiooutput.h" |
477 | +#include "audioroutemanager.h" |
478 | #include "callhandler.h" |
479 | #include "handlerdbus.h" |
480 | #include "handleradaptor.h" |
481 | @@ -44,6 +46,13 @@ |
482 | connect(CallHandler::instance(), |
483 | SIGNAL(conferenceCallRequestFinished(bool)), |
484 | SIGNAL(ConferenceCallRequestFinished(bool))); |
485 | + connect(AudioRouteManager::instance(), |
486 | + SIGNAL(audioOutputsChanged(AudioOutputDBusList)), |
487 | + SIGNAL(AudioOutputsChanged(AudioOutputDBusList))); |
488 | + connect(AudioRouteManager::instance(), |
489 | + SIGNAL(activeAudioOutputChanged(QString)), |
490 | + SIGNAL(ActiveAudioOutputChanged(QString))); |
491 | + |
492 | } |
493 | |
494 | HandlerDBus::~HandlerDBus() |
495 | @@ -81,6 +90,21 @@ |
496 | Q_EMIT CallIndicatorVisibleChanged(visible); |
497 | } |
498 | |
499 | +void HandlerDBus::setActiveAudioOutput(const QString &id) |
500 | +{ |
501 | + AudioRouteManager::instance()->setActiveAudioOutput(id); |
502 | +} |
503 | + |
504 | +QString HandlerDBus::activeAudioOutput() const |
505 | +{ |
506 | + return AudioRouteManager::instance()->activeAudioOutput(); |
507 | +} |
508 | + |
509 | +AudioOutputDBusList HandlerDBus::AudioOutputs() const |
510 | +{ |
511 | + return AudioRouteManager::instance()->audioOutputs(); |
512 | +} |
513 | + |
514 | bool HandlerDBus::connectToBus() |
515 | { |
516 | bool ok = QDBusConnection::sessionBus().registerService(DBUS_SERVICE); |
517 | @@ -138,11 +162,6 @@ |
518 | CallHandler::instance()->setMuted(objectPath, muted); |
519 | } |
520 | |
521 | -void HandlerDBus::SetActiveAudioOutput(const QString &objectPath, const QString &id) |
522 | -{ |
523 | - CallHandler::instance()->setActiveAudioOutput(objectPath, id); |
524 | -} |
525 | - |
526 | void HandlerDBus::SendDTMF(const QString &objectPath, const QString &key) |
527 | { |
528 | CallHandler::instance()->sendDTMF(objectPath, key); |
529 | |
530 | === modified file 'handler/handlerdbus.h' |
531 | --- handler/handlerdbus.h 2015-11-17 17:23:54 +0000 |
532 | +++ handler/handlerdbus.h 2016-01-20 11:56:12 +0000 |
533 | @@ -28,6 +28,7 @@ |
534 | #include <QtDBus/QDBusContext> |
535 | #include "chatmanager.h" |
536 | #include "dbustypes.h" |
537 | +#include "audiooutput.h" |
538 | |
539 | /** |
540 | * DBus interface for the phone approver |
541 | @@ -40,6 +41,11 @@ |
542 | WRITE setCallIndicatorVisible |
543 | NOTIFY CallIndicatorVisibleChanged) |
544 | |
545 | + Q_PROPERTY(QString ActiveAudioOutput |
546 | + READ activeAudioOutput |
547 | + WRITE setActiveAudioOutput |
548 | + NOTIFY ActiveAudioOutputChanged) |
549 | + |
550 | public: |
551 | HandlerDBus(QObject* parent=0); |
552 | ~HandlerDBus(); |
553 | @@ -50,6 +56,8 @@ |
554 | bool IsReady(); |
555 | bool callIndicatorVisible() const; |
556 | void setCallIndicatorVisible(bool visible); |
557 | + QString activeAudioOutput() const; |
558 | + void setActiveAudioOutput(const QString &id); |
559 | |
560 | public Q_SLOTS: |
561 | bool connectToBus(); |
562 | @@ -66,13 +74,13 @@ |
563 | Q_NOREPLY void HangUpCall(const QString &objectPath); |
564 | Q_NOREPLY void SetHold(const QString &objectPath, bool hold); |
565 | Q_NOREPLY void SetMuted(const QString &objectPath, bool muted); |
566 | - Q_NOREPLY void SetActiveAudioOutput(const QString &objectPath, const QString &id); |
567 | Q_NOREPLY void SendDTMF(const QString &objectPath, const QString &key); |
568 | |
569 | // conference call related |
570 | Q_NOREPLY void CreateConferenceCall(const QStringList &objectPaths); |
571 | Q_NOREPLY void MergeCall(const QString &conferenceObjectPath, const QString &callObjectPath); |
572 | Q_NOREPLY void SplitCall(const QString &objectPath); |
573 | + AudioOutputDBusList AudioOutputs() const; |
574 | |
575 | Q_SIGNALS: |
576 | void onMessageSent(const QString &number, const QString &message); |
577 | @@ -80,6 +88,8 @@ |
578 | void CallIndicatorVisibleChanged(bool visible); |
579 | void ConferenceCallRequestFinished(bool succeeded); |
580 | void CallHoldingFailed(const QString &objectPath); |
581 | + void ActiveAudioOutputChanged(const QString &id); |
582 | + void AudioOutputsChanged(const AudioOutputDBusList &audioOutputs); |
583 | |
584 | private: |
585 | bool mCallIndicatorVisible; |
586 | |
587 | === modified file 'handler/main.cpp' |
588 | --- handler/main.cpp 2015-01-08 01:05:51 +0000 |
589 | +++ handler/main.cpp 2016-01-20 11:56:12 +0000 |
590 | @@ -31,6 +31,9 @@ |
591 | #include <TelepathyQt/AbstractClient> |
592 | #include <TelepathyQt/AccountManager> |
593 | #include <TelepathyQt/Contact> |
594 | +#include <TelepathyQt/CallChannel> |
595 | + |
596 | +Q_DECLARE_METATYPE(Tp::CallChannelPtr) |
597 | |
598 | int main(int argc, char **argv) |
599 | { |
600 | @@ -38,6 +41,12 @@ |
601 | QCoreApplication::setApplicationName("telephony-service-handler"); |
602 | |
603 | Tp::registerTypes(); |
604 | + qRegisterMetaType<Tp::CallChannelPtr>(); |
605 | + qRegisterMetaType<AudioOutputDBus>(); |
606 | + qRegisterMetaType<AudioOutputDBusList>(); |
607 | + |
608 | + qDBusRegisterMetaType<AudioOutputDBus>(); |
609 | + qDBusRegisterMetaType<AudioOutputDBusList>(); |
610 | |
611 | // check if there is already an instance of the handler running |
612 | if (ApplicationUtils::checkApplicationRunning(TP_QT_IFACE_CLIENT + ".TelephonyServiceHandler")) { |
613 | @@ -45,9 +54,11 @@ |
614 | return 1; |
615 | } |
616 | |
617 | + HandlerDBus dbus; |
618 | Handler *handler = new Handler(); |
619 | - QObject::connect(TelepathyHelper::instance(), &TelepathyHelper::setupReady, [handler]() { |
620 | + QObject::connect(TelepathyHelper::instance(), &TelepathyHelper::setupReady, [&]() { |
621 | TelepathyHelper::instance()->registerClient(handler, "TelephonyServiceHandler"); |
622 | + dbus.connectToBus(); |
623 | }); |
624 | |
625 | QObject::connect(handler, SIGNAL(callChannelAvailable(Tp::CallChannelPtr)), |
626 | @@ -55,10 +66,6 @@ |
627 | QObject::connect(handler, SIGNAL(textChannelAvailable(Tp::TextChannelPtr)), |
628 | TextHandler::instance(), SLOT(onTextChannelAvailable(Tp::TextChannelPtr))); |
629 | |
630 | - HandlerDBus dbus; |
631 | - |
632 | - QObject::connect(TelepathyHelper::instance(), SIGNAL(setupReady()), |
633 | - &dbus, SLOT(connectToBus())); |
634 | |
635 | // instanciate the display name settings singleton, it will work by itself |
636 | DisplayNameSettings::instance(); |
637 | |
638 | === added file 'handler/powerd.h' |
639 | --- handler/powerd.h 1970-01-01 00:00:00 +0000 |
640 | +++ handler/powerd.h 2016-01-20 11:56:12 +0000 |
641 | @@ -0,0 +1,34 @@ |
642 | +/** |
643 | + * Copyright (C) 2014 Canonical, Ltd. |
644 | + * |
645 | + * This program is free software: you can redistribute it and/or modify it under |
646 | + * the terms of the GNU Lesser General Public License version 3, as published by |
647 | + * the Free Software Foundation. |
648 | + * |
649 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
650 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
651 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
652 | + * Lesser General Public License for more details. |
653 | + * |
654 | + * You should have received a copy of the GNU Lesser General Public License |
655 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
656 | + * |
657 | + * Authors: Andreas Pokorny <andreas.pokorny@canonical.com> |
658 | + */ |
659 | + |
660 | +#ifndef POWERD_H |
661 | +#define POWERD_H |
662 | + |
663 | +class PowerD |
664 | +{ |
665 | +public: |
666 | + PowerD() = default; |
667 | + virtual ~PowerD() = default; |
668 | + PowerD(PowerD const&) = delete; |
669 | + PowerD& operator=(PowerD const&) = delete; |
670 | + |
671 | + virtual void enableProximityHandling() = 0; |
672 | + virtual void disableProximityHandling() = 0; |
673 | +}; |
674 | + |
675 | +#endif // POWERD_H |
676 | |
677 | === added file 'handler/powerdaudiomodemediator.cpp' |
678 | --- handler/powerdaudiomodemediator.cpp 1970-01-01 00:00:00 +0000 |
679 | +++ handler/powerdaudiomodemediator.cpp 2016-01-20 11:56:12 +0000 |
680 | @@ -0,0 +1,63 @@ |
681 | +/** |
682 | + * Copyright (C) 2014 Canonical, Ltd. |
683 | + * |
684 | + * This program is free software: you can redistribute it and/or modify it under |
685 | + * the terms of the GNU Lesser General Public License version 3, as published by |
686 | + * the Free Software Foundation. |
687 | + * |
688 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
689 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
690 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
691 | + * Lesser General Public License for more details. |
692 | + * |
693 | + * You should have received a copy of the GNU Lesser General Public License |
694 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
695 | + * |
696 | + * Authors: Andreas Pokorny <andreas.pokorny@canonical.com> |
697 | + */ |
698 | + |
699 | +#include <QDBusInterface> |
700 | +#include "powerdaudiomodemediator.h" |
701 | + |
702 | +PowerDAudioModeMediator::PowerDAudioModeMediator(PowerD &powerd) |
703 | + : powerd(powerd) |
704 | +{ |
705 | +} |
706 | + |
707 | +void PowerDAudioModeMediator::audioModeChanged(const QString &mode) |
708 | +{ |
709 | + bool enableProximity = !(mode == "speaker" || mode == "bluetooth" || mode == "wired_headset"); |
710 | + |
711 | + if (mProximityEnabled != enableProximity) |
712 | + { |
713 | + mProximityEnabled = enableProximity; |
714 | + apply(); |
715 | + } |
716 | +} |
717 | + |
718 | +void PowerDAudioModeMediator::apply() const |
719 | +{ |
720 | + if (mProximityEnabled) { |
721 | + powerd.enableProximityHandling(); |
722 | + } else { |
723 | + // we need to power the screen on before disabling the proximity handling |
724 | + QDBusInterface unityIface("com.canonical.Unity.Screen", |
725 | + "/com/canonical/Unity/Screen", |
726 | + "com.canonical.Unity.Screen", |
727 | + QDBusConnection::systemBus()); |
728 | + QList<QVariant> args; |
729 | + args.append("on"); |
730 | + args.append(3); |
731 | + unityIface.callWithArgumentList(QDBus::NoBlock, "setScreenPowerMode", args); |
732 | + powerd.disableProximityHandling(); |
733 | + } |
734 | +} |
735 | + |
736 | +void PowerDAudioModeMediator::audioOutputClosed() |
737 | +{ |
738 | + if (mProximityEnabled) |
739 | + { |
740 | + mProximityEnabled = false; |
741 | + apply(); |
742 | + } |
743 | +} |
744 | |
745 | === added file 'handler/powerdaudiomodemediator.h' |
746 | --- handler/powerdaudiomodemediator.h 1970-01-01 00:00:00 +0000 |
747 | +++ handler/powerdaudiomodemediator.h 2016-01-20 11:56:12 +0000 |
748 | @@ -0,0 +1,46 @@ |
749 | +/**************************************************************************** |
750 | +** |
751 | +** Copyright (C) 2014 Canonical, Ltd. |
752 | +** |
753 | +** Authors: |
754 | +** Andreas Pokorny <andreas.pokorny@canonical.com> |
755 | +** |
756 | +** GNU Lesser General Public License Usage |
757 | +** Alternatively, this file may be used under the terms of the GNU Lesser |
758 | +** General Public License version 2.1 as published by the Free Software |
759 | +** Foundation and appearing in the file LICENSE.LGPL included in the |
760 | +** packaging of this file. Please review the following information to |
761 | +** ensure the GNU Lesser General Public License version 2.1 requirements |
762 | +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
763 | +** |
764 | +****************************************************************************/ |
765 | + |
766 | +#ifndef POWERDAUDIOMODEMEDIATOR_H |
767 | +#define POWERDAUDIOMODEMEDIATOR_H |
768 | + |
769 | +#include "powerd.h" |
770 | + |
771 | +#include <QString> |
772 | +#include <fstream> |
773 | +#include <memory> |
774 | + |
775 | +class PowerD; |
776 | +/*! |
777 | + * \brief PowerDAudioModeMediator is responsible for configuring proximity |
778 | + * handling of powerd during different call states and used audio outputs. |
779 | + * In General that mean enabling sreen blanking on proximity events, when |
780 | + * a call is active and neither a bluetooth headset nor the speakers are used. |
781 | + */ |
782 | +class PowerDAudioModeMediator |
783 | +{ |
784 | +public: |
785 | + PowerDAudioModeMediator(PowerD &powerd); |
786 | + void audioModeChanged(const QString &mode); |
787 | + void audioOutputClosed(); |
788 | +private: |
789 | + void apply() const; |
790 | + PowerD &powerd; |
791 | + bool mProximityEnabled{false}; |
792 | +}; |
793 | + |
794 | +#endif |
795 | |
796 | === added file 'handler/powerddbus.cpp' |
797 | --- handler/powerddbus.cpp 1970-01-01 00:00:00 +0000 |
798 | +++ handler/powerddbus.cpp 2016-01-20 11:56:12 +0000 |
799 | @@ -0,0 +1,43 @@ |
800 | +/** |
801 | + * Copyright (C) 2014 Canonical, Ltd. |
802 | + * |
803 | + * This program is free software: you can redistribute it and/or modify it under |
804 | + * the terms of the GNU Lesser General Public License version 3, as published by |
805 | + * the Free Software Foundation. |
806 | + * |
807 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
808 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
809 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
810 | + * Lesser General Public License for more details. |
811 | + * |
812 | + * You should have received a copy of the GNU Lesser General Public License |
813 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
814 | + * |
815 | + * Authors: Andreas Pokorny <andreas.pokorny@canonical.com> |
816 | + */ |
817 | + |
818 | +#include "powerddbus.h" |
819 | + |
820 | +#include <QDBusConnection> |
821 | +#include <QDBusInterface> |
822 | +#include <QDBusReply> |
823 | + |
824 | +PowerDDBus::PowerDDBus() |
825 | + : mPowerDIface{ |
826 | + new QDBusInterface( |
827 | + "com.canonical.powerd", |
828 | + "/com/canonical/powerd", |
829 | + "com.canonical.powerd", |
830 | + QDBusConnection::systemBus())} |
831 | +{ |
832 | +} |
833 | + |
834 | +void PowerDDBus::enableProximityHandling() |
835 | +{ |
836 | + mPowerDIface->call("enableProximityHandling", "telephony-service-handler"); |
837 | +} |
838 | + |
839 | +void PowerDDBus::disableProximityHandling() |
840 | +{ |
841 | + mPowerDIface->call("disableProximityHandling", "telephony-service-handler"); |
842 | +} |
843 | |
844 | === added file 'handler/powerddbus.h' |
845 | --- handler/powerddbus.h 1970-01-01 00:00:00 +0000 |
846 | +++ handler/powerddbus.h 2016-01-20 11:56:12 +0000 |
847 | @@ -0,0 +1,38 @@ |
848 | +/** |
849 | + * Copyright (C) 2014 Canonical, Ltd. |
850 | + * |
851 | + * This program is free software: you can redistribute it and/or modify it under |
852 | + * the terms of the GNU Lesser General Public License version 3, as published by |
853 | + * the Free Software Foundation. |
854 | + * |
855 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
856 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
857 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
858 | + * Lesser General Public License for more details. |
859 | + * |
860 | + * You should have received a copy of the GNU Lesser General Public License |
861 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
862 | + * |
863 | + * Authors: Andreas Pokorny <andreas.pokorny@canonical.com> |
864 | + */ |
865 | + |
866 | +#ifndef POWERD_DUBS_H |
867 | +#define POWERD_DBUS_H |
868 | + |
869 | +#include "powerd.h" |
870 | + |
871 | +#include <memory> |
872 | + |
873 | +class QDBusInterface; |
874 | + |
875 | +class PowerDDBus : public PowerD |
876 | +{ |
877 | +public: |
878 | + PowerDDBus(); |
879 | + void enableProximityHandling() override; |
880 | + void disableProximityHandling() override; |
881 | +private: |
882 | + std::unique_ptr<QDBusInterface> mPowerDIface; |
883 | +}; |
884 | + |
885 | +#endif |
886 | |
887 | === added file 'handler/qpulseaudioengine.cpp' |
888 | --- handler/qpulseaudioengine.cpp 1970-01-01 00:00:00 +0000 |
889 | +++ handler/qpulseaudioengine.cpp 2016-01-20 11:56:12 +0000 |
890 | @@ -0,0 +1,853 @@ |
891 | +/**************************************************************************** |
892 | +** |
893 | +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). |
894 | +** Contact: http://www.qt-project.org/legal |
895 | +** |
896 | +** This file was taken from qt5 and modified by |
897 | +** David Henningsson <david.henningsson@canonical.com> for usage in |
898 | +** telepathy-ofono. |
899 | +** |
900 | +** GNU Lesser General Public License Usage |
901 | +** Alternatively, this file may be used under the terms of the GNU Lesser |
902 | +** General Public License version 2.1 as published by the Free Software |
903 | +** Foundation and appearing in the file LICENSE.LGPL included in the |
904 | +** packaging of this file. Please review the following information to |
905 | +** ensure the GNU Lesser General Public License version 2.1 requirements |
906 | +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
907 | +** |
908 | +****************************************************************************/ |
909 | + |
910 | +#include <QtCore/qdebug.h> |
911 | + |
912 | +#include "qpulseaudioengine.h" |
913 | +#include <sys/types.h> |
914 | +#include <unistd.h> |
915 | + |
916 | +#define PULSEAUDIO_PROFILE_HSP "headset_head_unit" |
917 | +#define PULSEAUDIO_PROFILE_A2DP "a2dp_sink" |
918 | + |
919 | +QT_BEGIN_NAMESPACE |
920 | + |
921 | +static void contextStateCallbackInit(pa_context *context, void *userdata) |
922 | +{ |
923 | + Q_UNUSED(context); |
924 | + QPulseAudioEngineWorker *pulseEngine = reinterpret_cast<QPulseAudioEngineWorker*>(userdata); |
925 | + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
926 | +} |
927 | + |
928 | +static void contextStateCallback(pa_context *context, void *userdata) |
929 | +{ |
930 | + Q_UNUSED(userdata); |
931 | + Q_UNUSED(context); |
932 | +} |
933 | + |
934 | +static void success_cb(pa_context *context, int success, void *userdata) |
935 | +{ |
936 | + QPulseAudioEngineWorker *pulseEngine = reinterpret_cast<QPulseAudioEngineWorker*>(userdata); |
937 | + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
938 | +} |
939 | + |
940 | +/* Callbacks used when handling events from PulseAudio */ |
941 | +static void plug_card_cb(pa_context *c, const pa_card_info *info, int isLast, void *userdata) |
942 | +{ |
943 | + QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata); |
944 | + if (isLast != 0 || !pulseEngine || !info) { |
945 | + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
946 | + return; |
947 | + } |
948 | + pulseEngine->plugCardCallback(info); |
949 | +} |
950 | + |
951 | +static void update_card_cb(pa_context *c, const pa_card_info *info, int isLast, void *userdata) |
952 | +{ |
953 | + QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata); |
954 | + if (isLast != 0 || !pulseEngine || !info) { |
955 | + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
956 | + return; |
957 | + } |
958 | + pulseEngine->updateCardCallback(info); |
959 | +} |
960 | + |
961 | +static void unplug_card_cb(pa_context *c, const pa_card_info *info, int isLast, void *userdata) |
962 | +{ |
963 | + QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata); |
964 | + if (!pulseEngine) { |
965 | + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
966 | + return; |
967 | + } |
968 | + |
969 | + if (info == NULL) { |
970 | + /* That means that the card used to query card_info was removed */ |
971 | + pulseEngine->unplugCardCallback(); |
972 | + } |
973 | +} |
974 | + |
975 | +static void subscribeCallback(pa_context *context, pa_subscription_event_type_t t, uint32_t idx, void *userdata) |
976 | +{ |
977 | + /* For card change events (slot plug/unplug and add/remove card) */ |
978 | + if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_CARD) { |
979 | + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { |
980 | + QMetaObject::invokeMethod((QPulseAudioEngineWorker *) userdata, "handleCardEvent", |
981 | + Qt::QueuedConnection, Q_ARG(int, PA_SUBSCRIPTION_EVENT_CHANGE), Q_ARG(unsigned int, idx)); |
982 | + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { |
983 | + QMetaObject::invokeMethod((QPulseAudioEngineWorker *) userdata, "handleCardEvent", |
984 | + Qt::QueuedConnection, Q_ARG(int, PA_SUBSCRIPTION_EVENT_NEW), Q_ARG(unsigned int, idx)); |
985 | + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { |
986 | + QMetaObject::invokeMethod((QPulseAudioEngineWorker *) userdata, "handleCardEvent", |
987 | + Qt::QueuedConnection, Q_ARG(int, PA_SUBSCRIPTION_EVENT_REMOVE), Q_ARG(unsigned int, idx)); |
988 | + } |
989 | + } |
990 | +} |
991 | + |
992 | +QPulseAudioEngineWorker::QPulseAudioEngineWorker(QObject *parent) |
993 | + : QObject(parent) |
994 | + , m_mainLoopApi(0) |
995 | + , m_context(0) |
996 | + , m_callstatus(CallEnded) |
997 | + , m_audiomode(AudioModeSpeaker) |
998 | + , m_micmute(false) |
999 | + , m_defaultsink("sink.primary") |
1000 | + , m_defaultsource("source.primary") |
1001 | + , m_voicecallcard("") |
1002 | + , m_voicecallhighest("") |
1003 | + , m_voicecallprofile("") |
1004 | + , m_bt_hsp("") |
1005 | + , m_bt_hsp_a2dp("") |
1006 | + , m_default_bt_card_fallback("") |
1007 | + |
1008 | +{ |
1009 | + m_mainLoop = pa_threaded_mainloop_new(); |
1010 | + if (m_mainLoop == 0) { |
1011 | + qWarning("Unable to create pulseaudio mainloop"); |
1012 | + return; |
1013 | + } |
1014 | + |
1015 | + if (pa_threaded_mainloop_start(m_mainLoop) != 0) { |
1016 | + qWarning("Unable to start pulseaudio mainloop"); |
1017 | + pa_threaded_mainloop_free(m_mainLoop); |
1018 | + m_mainLoop = 0; |
1019 | + return; |
1020 | + } |
1021 | + |
1022 | + createPulseContext(); |
1023 | +} |
1024 | + |
1025 | +bool QPulseAudioEngineWorker::createPulseContext() |
1026 | +{ |
1027 | + bool keepGoing = true; |
1028 | + bool ok = true; |
1029 | + |
1030 | + if (m_context) |
1031 | + return true; |
1032 | + |
1033 | + m_mainLoopApi = pa_threaded_mainloop_get_api(m_mainLoop); |
1034 | + |
1035 | + pa_threaded_mainloop_lock(m_mainLoop); |
1036 | + |
1037 | + m_context = pa_context_new(m_mainLoopApi, QString(QLatin1String("QtmPulseContext:%1")).arg(::getpid()).toLatin1().constData()); |
1038 | + pa_context_set_state_callback(m_context, contextStateCallbackInit, this); |
1039 | + |
1040 | + if (!m_context) { |
1041 | + qWarning("Unable to create new pulseaudio context"); |
1042 | + pa_threaded_mainloop_unlock(m_mainLoop); |
1043 | + return false; |
1044 | + } |
1045 | + |
1046 | + if (pa_context_connect(m_context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0) { |
1047 | + qWarning("Unable to create a connection to the pulseaudio context"); |
1048 | + pa_threaded_mainloop_unlock(m_mainLoop); |
1049 | + releasePulseContext(); |
1050 | + return false; |
1051 | + } |
1052 | + |
1053 | + pa_threaded_mainloop_wait(m_mainLoop); |
1054 | + |
1055 | + while (keepGoing) { |
1056 | + switch (pa_context_get_state(m_context)) { |
1057 | + case PA_CONTEXT_CONNECTING: |
1058 | + case PA_CONTEXT_AUTHORIZING: |
1059 | + case PA_CONTEXT_SETTING_NAME: |
1060 | + break; |
1061 | + |
1062 | + case PA_CONTEXT_READY: |
1063 | + qDebug("Pulseaudio connection established."); |
1064 | + keepGoing = false; |
1065 | + break; |
1066 | + |
1067 | + case PA_CONTEXT_TERMINATED: |
1068 | + qCritical("Pulseaudio context terminated."); |
1069 | + keepGoing = false; |
1070 | + ok = false; |
1071 | + break; |
1072 | + |
1073 | + case PA_CONTEXT_FAILED: |
1074 | + default: |
1075 | + qCritical() << QString("Pulseaudio connection failure: %1").arg(pa_strerror(pa_context_errno(m_context))); |
1076 | + keepGoing = false; |
1077 | + ok = false; |
1078 | + } |
1079 | + |
1080 | + if (keepGoing) { |
1081 | + pa_threaded_mainloop_wait(m_mainLoop); |
1082 | + } |
1083 | + } |
1084 | + |
1085 | + if (ok) { |
1086 | + pa_context_set_state_callback(m_context, contextStateCallback, this); |
1087 | + pa_context_set_subscribe_callback(m_context, subscribeCallback, this); |
1088 | + pa_context_subscribe(m_context, PA_SUBSCRIPTION_MASK_CARD, NULL, this); |
1089 | + } else { |
1090 | + if (m_context) { |
1091 | + pa_context_unref(m_context); |
1092 | + m_context = 0; |
1093 | + } |
1094 | + } |
1095 | + |
1096 | + pa_threaded_mainloop_unlock(m_mainLoop); |
1097 | + return true; |
1098 | +} |
1099 | + |
1100 | + |
1101 | +void QPulseAudioEngineWorker::releasePulseContext() |
1102 | +{ |
1103 | + if (m_context) { |
1104 | + pa_threaded_mainloop_lock(m_mainLoop); |
1105 | + pa_context_disconnect(m_context); |
1106 | + pa_context_unref(m_context); |
1107 | + pa_threaded_mainloop_unlock(m_mainLoop); |
1108 | + m_context = 0; |
1109 | + } |
1110 | + |
1111 | +} |
1112 | + |
1113 | +QPulseAudioEngineWorker::~QPulseAudioEngineWorker() |
1114 | +{ |
1115 | + releasePulseContext(); |
1116 | + |
1117 | + if (m_mainLoop) { |
1118 | + pa_threaded_mainloop_stop(m_mainLoop); |
1119 | + pa_threaded_mainloop_free(m_mainLoop); |
1120 | + m_mainLoop = 0; |
1121 | + } |
1122 | +} |
1123 | + |
1124 | +void QPulseAudioEngineWorker::cardInfoCallback(const pa_card_info *info) |
1125 | +{ |
1126 | + pa_card_profile_info2 *voice_call = NULL, *highest = NULL; |
1127 | + pa_card_profile_info2 *hsp = NULL, *a2dp = NULL; |
1128 | + |
1129 | + /* For now we only support one card with the voicecall feature */ |
1130 | + for (int i = 0; i < info->n_profiles; i++) { |
1131 | + if (!highest || info->profiles2[i]->priority > highest->priority) |
1132 | + highest = info->profiles2[i]; |
1133 | + if (!strcmp(info->profiles2[i]->name, "voicecall")) |
1134 | + voice_call = info->profiles2[i]; |
1135 | + else if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_HSP) && |
1136 | + info->profiles2[i]->available != 0) |
1137 | + hsp = info->profiles2[i]; |
1138 | + else if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_A2DP) && |
1139 | + info->profiles2[i]->available != 0) |
1140 | + a2dp = info->profiles2[i]; |
1141 | + } |
1142 | + |
1143 | + /* Record the card that supports voicecall (default one to be used) */ |
1144 | + if (voice_call) { |
1145 | + qDebug("Found card that supports voicecall: '%s'", info->name); |
1146 | + m_voicecallcard = info->name; |
1147 | + m_voicecallhighest = highest->name; |
1148 | + qDebug("1"); |
1149 | + m_voicecallprofile = voice_call->name; |
1150 | + qDebug("2"); |
1151 | + } |
1152 | + |
1153 | + /* Handle the use cases needed for bluetooth */ |
1154 | + if (hsp && a2dp) { |
1155 | + qDebug("Found card that supports hsp and a2dp: '%s'", info->name); |
1156 | + m_bt_hsp_a2dp = info->name; |
1157 | + } else if (hsp && (a2dp == NULL)) { |
1158 | + /* This card only provides the hsp profile */ |
1159 | + qDebug("Found card that supports only hsp: '%s'", info->name); |
1160 | + m_bt_hsp = info->name; |
1161 | + } |
1162 | + qDebug("3"); |
1163 | +} |
1164 | + |
1165 | +void QPulseAudioEngineWorker::sinkInfoCallback(const pa_sink_info *info) |
1166 | +{ |
1167 | + pa_sink_port_info *earpiece = NULL, *speaker = NULL; |
1168 | + pa_sink_port_info *wired_headset = NULL, *wired_headphone = NULL; |
1169 | + pa_sink_port_info *preferred = NULL; |
1170 | + pa_sink_port_info *bluetooth_sco = NULL; |
1171 | + pa_sink_port_info *speaker_and_wired_headphone = NULL; |
1172 | + AudioMode audiomodetoset; |
1173 | + AudioModes modes; |
1174 | + |
1175 | + for (int i = 0; i < info->n_ports; i++) { |
1176 | + if (!strcmp(info->ports[i]->name, "output-earpiece")) |
1177 | + earpiece = info->ports[i]; |
1178 | + else if (!strcmp(info->ports[i]->name, "output-wired_headset") && |
1179 | + (info->ports[i]->available != PA_PORT_AVAILABLE_NO)) |
1180 | + wired_headset = info->ports[i]; |
1181 | + else if (!strcmp(info->ports[i]->name, "output-wired_headphone") && |
1182 | + (info->ports[i]->available != PA_PORT_AVAILABLE_NO)) |
1183 | + wired_headphone = info->ports[i]; |
1184 | + else if (!strcmp(info->ports[i]->name, "output-speaker")) |
1185 | + speaker = info->ports[i]; |
1186 | + else if (!strcmp(info->ports[i]->name, "output-bluetooth_sco")) |
1187 | + bluetooth_sco = info->ports[i]; |
1188 | + else if (!strcmp(info->ports[i]->name, "output-speaker+wired_headphone")) |
1189 | + speaker_and_wired_headphone = info->ports[i]; |
1190 | + } |
1191 | + |
1192 | + if (!earpiece || !speaker) |
1193 | + return; /* Not the right sink */ |
1194 | + |
1195 | + /* Refresh list of available audio modes */ |
1196 | + modes.append(AudioModeEarpiece); |
1197 | + modes.append(AudioModeSpeaker); |
1198 | + if (wired_headset || wired_headphone) |
1199 | + modes.append(AudioModeWiredHeadset); |
1200 | + if (bluetooth_sco && ((m_bt_hsp != "") || (m_bt_hsp_a2dp != ""))) |
1201 | + modes.append(AudioModeBluetooth); |
1202 | + |
1203 | + /* Check if the requested mode is available (earpiece*/ |
1204 | + if (((m_audiomode == AudioModeWiredHeadset) && !modes.contains(AudioModeWiredHeadset)) || |
1205 | + ((m_audiomode == AudioModeBluetooth) && !modes.contains(AudioModeBluetooth))) |
1206 | + return; |
1207 | + |
1208 | + /* Now to decide which output to be used, depending on the active mode */ |
1209 | + if (m_audiomode & AudioModeEarpiece) { |
1210 | + preferred = earpiece; |
1211 | + audiomodetoset = AudioModeEarpiece; |
1212 | + } |
1213 | + if (m_audiomode & AudioModeSpeaker) { |
1214 | + preferred = speaker; |
1215 | + audiomodetoset = AudioModeSpeaker; |
1216 | + } |
1217 | + if ((m_audiomode & AudioModeWiredHeadset) && (modes.contains(AudioModeWiredHeadset))) { |
1218 | + preferred = wired_headset ? wired_headset : wired_headphone; |
1219 | + audiomodetoset = AudioModeWiredHeadset; |
1220 | + } |
1221 | + if (m_callstatus == CallRinging && speaker_and_wired_headphone) { |
1222 | + preferred = speaker_and_wired_headphone; |
1223 | + } |
1224 | + if ((m_audiomode & AudioModeBluetooth) && (modes.contains(AudioModeBluetooth))) { |
1225 | + preferred = bluetooth_sco; |
1226 | + audiomodetoset = AudioModeBluetooth; |
1227 | + } |
1228 | + |
1229 | + m_audiomode = audiomodetoset; |
1230 | + |
1231 | + m_nametoset = info->name; |
1232 | + if (info->active_port != preferred) |
1233 | + m_valuetoset = preferred->name; |
1234 | + |
1235 | + if (modes != m_availableAudioModes) |
1236 | + m_availableAudioModes = modes; |
1237 | +} |
1238 | + |
1239 | +void QPulseAudioEngineWorker::sourceInfoCallback(const pa_source_info *info) |
1240 | +{ |
1241 | + pa_source_port_info *builtin_mic = NULL, *preferred = NULL; |
1242 | + pa_source_port_info *wired_headset = NULL, *bluetooth_sco = NULL; |
1243 | + |
1244 | + if (info->monitor_of_sink != PA_INVALID_INDEX) |
1245 | + return; /* Not the right source */ |
1246 | + |
1247 | + for (int i = 0; i < info->n_ports; i++) { |
1248 | + if (!strcmp(info->ports[i]->name, "input-builtin_mic")) |
1249 | + builtin_mic = info->ports[i]; |
1250 | + else if (!strcmp(info->ports[i]->name, "input-wired_headset") && |
1251 | + (info->ports[i]->available != PA_PORT_AVAILABLE_NO)) |
1252 | + wired_headset = info->ports[i]; |
1253 | + else if (!strcmp(info->ports[i]->name, "input-bluetooth_sco_headset")) |
1254 | + bluetooth_sco = info->ports[i]; |
1255 | + } |
1256 | + |
1257 | + if (!builtin_mic) |
1258 | + return; /* Not the right source */ |
1259 | + |
1260 | + /* Now to decide which output to be used, depending on the active mode */ |
1261 | + if ((m_audiomode & AudioModeEarpiece) || (m_audiomode & AudioModeSpeaker)) |
1262 | + preferred = builtin_mic; |
1263 | + if ((m_audiomode & AudioModeWiredHeadset) && (m_availableAudioModes.contains(AudioModeWiredHeadset))) |
1264 | + preferred = wired_headset ? wired_headset : builtin_mic; |
1265 | + if ((m_audiomode & AudioModeBluetooth) && (m_availableAudioModes.contains(AudioModeBluetooth))) |
1266 | + preferred = bluetooth_sco; |
1267 | + |
1268 | + m_nametoset = info->name; |
1269 | + if (info->active_port != preferred) |
1270 | + m_valuetoset = preferred->name; |
1271 | +} |
1272 | + |
1273 | +void QPulseAudioEngineWorker::serverInfoCallback(const pa_server_info *info) |
1274 | +{ |
1275 | + /* Saving default sink/source to restore after call hangup */ |
1276 | + m_defaultsink = info->default_sink_name; |
1277 | + m_defaultsource = info->default_source_name; |
1278 | + |
1279 | + /* In the case of a server callback we need to signal the mainloop */ |
1280 | + pa_threaded_mainloop_signal(mainloop(), 0); |
1281 | +} |
1282 | + |
1283 | +static void cardinfo_cb(pa_context *context, const pa_card_info *info, int isLast, void *userdata) |
1284 | +{ |
1285 | + QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata); |
1286 | + if (isLast != 0 || !pulseEngine || !info) { |
1287 | + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
1288 | + return; |
1289 | + } |
1290 | + pulseEngine->cardInfoCallback(info); |
1291 | +} |
1292 | + |
1293 | +static void sinkinfo_cb(pa_context *context, const pa_sink_info *info, int isLast, void *userdata) |
1294 | +{ |
1295 | + QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata); |
1296 | + if (isLast != 0 || !pulseEngine || !info) { |
1297 | + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
1298 | + return; |
1299 | + } |
1300 | + pulseEngine->sinkInfoCallback(info); |
1301 | +} |
1302 | + |
1303 | +static void sourceinfo_cb(pa_context *context, const pa_source_info *info, int isLast, void *userdata) |
1304 | +{ |
1305 | + QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata); |
1306 | + if (isLast != 0 || !pulseEngine || !info) { |
1307 | + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
1308 | + return; |
1309 | + } |
1310 | + pulseEngine->sourceInfoCallback(info); |
1311 | +} |
1312 | + |
1313 | +static void serverinfo_cb(pa_context *context, const pa_server_info *info, void *userdata) |
1314 | +{ |
1315 | + QPulseAudioEngineWorker *pulseEngine = static_cast<QPulseAudioEngineWorker*>(userdata); |
1316 | + if (!pulseEngine || !info) { |
1317 | + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); |
1318 | + return; |
1319 | + } |
1320 | + pulseEngine->serverInfoCallback(info); |
1321 | +} |
1322 | + |
1323 | +bool QPulseAudioEngineWorker::handleOperation(pa_operation *operation, const char *func_name) |
1324 | +{ |
1325 | + if (!operation) { |
1326 | + qCritical("'%s' failed (lost PulseAudio connection?)", func_name); |
1327 | + /* Free resources so it can retry a new connection during next operation */ |
1328 | + pa_threaded_mainloop_unlock(m_mainLoop); |
1329 | + releasePulseContext(); |
1330 | + return false; |
1331 | + } |
1332 | + |
1333 | + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) |
1334 | + pa_threaded_mainloop_wait(m_mainLoop); |
1335 | + pa_operation_unref(operation); |
1336 | + return true; |
1337 | +} |
1338 | + |
1339 | +int QPulseAudioEngineWorker::setupVoiceCall() |
1340 | +{ |
1341 | + pa_operation *o; |
1342 | + |
1343 | + qDebug("Setting up pulseaudio for voice call"); |
1344 | + |
1345 | + pa_threaded_mainloop_lock(m_mainLoop); |
1346 | + |
1347 | + /* Get and set the default sink/source to be restored later */ |
1348 | + o = pa_context_get_server_info(m_context, serverinfo_cb, this); |
1349 | + if (!handleOperation(o, "pa_context_get_server_info")) |
1350 | + return -1; |
1351 | + |
1352 | + qDebug("Recorded default sink: %s default source: %s", |
1353 | + m_defaultsink.c_str(), m_defaultsource.c_str()); |
1354 | + |
1355 | + /* Walk through the list of devices, find the voice call capable card and |
1356 | + * identify if we have bluetooth capable devices (hsp and a2dp) */ |
1357 | + m_voicecallcard = m_voicecallhighest = m_voicecallprofile = ""; |
1358 | + m_bt_hsp = m_bt_hsp_a2dp = ""; |
1359 | + o = pa_context_get_card_info_list(m_context, cardinfo_cb, this); |
1360 | + if (!handleOperation(o, "pa_context_get_card_info_list")) |
1361 | + return -1; |
1362 | + /* In case we have only one bt device that provides hsp and a2dp, we need |
1363 | + * to make sure we switch the default profile for that card (to hsp) */ |
1364 | + if ((m_bt_hsp_a2dp != "") && (m_bt_hsp == "")) { |
1365 | + qDebug("Setting PulseAudio card '%s' profile '%s'", |
1366 | + m_bt_hsp_a2dp.c_str(), PULSEAUDIO_PROFILE_HSP); |
1367 | + o = pa_context_set_card_profile_by_name(m_context, |
1368 | + m_bt_hsp_a2dp.c_str(), PULSEAUDIO_PROFILE_HSP, success_cb, this); |
1369 | + if (!handleOperation(o, "pa_context_set_card_profile_by_name")) |
1370 | + return -1; |
1371 | + } |
1372 | + |
1373 | + pa_threaded_mainloop_unlock(m_mainLoop); |
1374 | + |
1375 | + return 0; |
1376 | +} |
1377 | + |
1378 | +void QPulseAudioEngineWorker::restoreVoiceCall() |
1379 | +{ |
1380 | + pa_operation *o; |
1381 | + |
1382 | + qDebug("Restoring pulseaudio previous state"); |
1383 | + |
1384 | + /* Then restore previous settings */ |
1385 | + pa_threaded_mainloop_lock(m_mainLoop); |
1386 | + |
1387 | + /* See if we need to restore any HSP+AD2P device state */ |
1388 | + if ((m_bt_hsp_a2dp != "") && (m_bt_hsp == "")) { |
1389 | + qDebug("Restoring PulseAudio card '%s' to profile '%s'", |
1390 | + m_bt_hsp_a2dp.c_str(), PULSEAUDIO_PROFILE_A2DP); |
1391 | + o = pa_context_set_card_profile_by_name(m_context, |
1392 | + m_bt_hsp_a2dp.c_str(), PULSEAUDIO_PROFILE_A2DP, success_cb, this); |
1393 | + if (!handleOperation(o, "pa_context_set_card_profile_by_name")) |
1394 | + return; |
1395 | + } |
1396 | + |
1397 | + /* Restore default sink/source */ |
1398 | + if (m_defaultsink != "") { |
1399 | + qDebug("Restoring PulseAudio default sink to '%s'", m_defaultsink.c_str()); |
1400 | + o = pa_context_set_default_sink(m_context, m_defaultsink.c_str(), success_cb, this); |
1401 | + if (!handleOperation(o, "pa_context_set_default_sink")) |
1402 | + return; |
1403 | + } |
1404 | + if (m_defaultsource != "") { |
1405 | + qDebug("Restoring PulseAudio default source to '%s'", m_defaultsource.c_str()); |
1406 | + o = pa_context_set_default_source(m_context, m_defaultsource.c_str(), success_cb, this); |
1407 | + if (!handleOperation(o, "pa_context_set_default_source")) |
1408 | + return; |
1409 | + } |
1410 | + |
1411 | + pa_threaded_mainloop_unlock(m_mainLoop); |
1412 | +} |
1413 | + |
1414 | +void QPulseAudioEngineWorker::setCallMode(CallStatus callstatus, AudioMode audiomode) |
1415 | +{ |
1416 | + if (!createPulseContext()) { |
1417 | + return; |
1418 | + } |
1419 | + CallStatus p_callstatus = m_callstatus; |
1420 | + AudioMode p_audiomode = m_audiomode; |
1421 | + AudioModes p_availableAudioModes = m_availableAudioModes; |
1422 | + pa_operation *o; |
1423 | + |
1424 | + /* Check if we need to save the current pulseaudio state (e.g. when starting a call) */ |
1425 | + if ((callstatus != CallEnded) && (p_callstatus == CallEnded)) { |
1426 | + if (setupVoiceCall() < 0) { |
1427 | + qCritical("Failed to setup PulseAudio for Voice Call"); |
1428 | + return; |
1429 | + } |
1430 | + } |
1431 | + |
1432 | + /* If we have an active call, update internal state (used later when updating sink/source ports) */ |
1433 | + m_callstatus = callstatus; |
1434 | + m_audiomode = audiomode; |
1435 | + |
1436 | + pa_threaded_mainloop_lock(m_mainLoop); |
1437 | + |
1438 | + /* Switch the virtual card mode when call is active and not active |
1439 | + * This needs to be done before sink/source gets updated, because after changing mode |
1440 | + * it will automatically move to input/output-parking */ |
1441 | + if ((m_callstatus == CallActive) && (p_callstatus != CallActive) && |
1442 | + (m_voicecallcard != "") && (m_voicecallprofile != "")) { |
1443 | + qDebug("Setting PulseAudio card '%s' profile '%s'", |
1444 | + m_voicecallcard.c_str(), m_voicecallprofile.c_str()); |
1445 | + o = pa_context_set_card_profile_by_name(m_context, |
1446 | + m_voicecallcard.c_str(), m_voicecallprofile.c_str(), success_cb, this); |
1447 | + if (!handleOperation(o, "pa_context_set_card_profile_by_name")) |
1448 | + return; |
1449 | + } else if ((m_callstatus == CallEnded) && (m_voicecallcard != "") && (m_voicecallhighest != "")) { |
1450 | + /* If using droid, make sure to restore to the profile that has the highest score */ |
1451 | + qDebug("Restoring PulseAudio card '%s' to profile '%s'", |
1452 | + m_voicecallcard.c_str(), m_voicecallhighest.c_str()); |
1453 | + o = pa_context_set_card_profile_by_name(m_context, |
1454 | + m_voicecallcard.c_str(), m_voicecallhighest.c_str(), success_cb, this); |
1455 | + if (!handleOperation(o, "pa_context_set_card_profile_by_name")) |
1456 | + return; |
1457 | + } |
1458 | + |
1459 | + /* Find highest compatible sink/source elements from the voicecall |
1460 | + compatible card (on touch this means the pulse droid element) */ |
1461 | + m_nametoset = m_valuetoset = ""; |
1462 | + o = pa_context_get_sink_info_list(m_context, sinkinfo_cb, this); |
1463 | + if (!handleOperation(o, "pa_context_get_sink_info_list")) |
1464 | + return; |
1465 | + if ((m_nametoset != "") && (m_nametoset != m_defaultsink)) { |
1466 | + qDebug("Setting PulseAudio default sink to '%s'", m_nametoset.c_str()); |
1467 | + o = pa_context_set_default_sink(m_context, m_nametoset.c_str(), success_cb, this); |
1468 | + if (!handleOperation(o, "pa_context_set_default_sink")) |
1469 | + return; |
1470 | + } |
1471 | + if (m_valuetoset != "") { |
1472 | + qDebug("Setting PulseAudio sink '%s' port '%s'", |
1473 | + m_nametoset.c_str(), m_valuetoset.c_str()); |
1474 | + o = pa_context_set_sink_port_by_name(m_context, m_nametoset.c_str(), |
1475 | + m_valuetoset.c_str(), success_cb, this); |
1476 | + if (!handleOperation(o, "pa_context_set_sink_port_by_name")) |
1477 | + return; |
1478 | + } |
1479 | + |
1480 | + /* Same for source */ |
1481 | + m_nametoset = m_valuetoset = ""; |
1482 | + o = pa_context_get_source_info_list(m_context, sourceinfo_cb, this); |
1483 | + if (!handleOperation(o, "pa_context_get_source_info_list")) |
1484 | + return; |
1485 | + if ((m_nametoset != "") && (m_nametoset != m_defaultsource)) { |
1486 | + qDebug("Setting PulseAudio default source to '%s'", m_nametoset.c_str()); |
1487 | + o = pa_context_set_default_source(m_context, m_nametoset.c_str(), success_cb, this); |
1488 | + if (!handleOperation(o, "pa_context_set_default_source")) |
1489 | + return; |
1490 | + } |
1491 | + if (m_valuetoset != "") { |
1492 | + qDebug("Setting PulseAudio source '%s' port '%s'", |
1493 | + m_nametoset.c_str(), m_valuetoset.c_str()); |
1494 | + o = pa_context_set_source_port_by_name(m_context, m_nametoset.c_str(), |
1495 | + m_valuetoset.c_str(), success_cb, this); |
1496 | + if (!handleOperation(o, "pa_context_set_source_port_by_name")) |
1497 | + return; |
1498 | + } |
1499 | + |
1500 | + pa_threaded_mainloop_unlock(m_mainLoop); |
1501 | + |
1502 | + /* Notify if the list of audio modes changed */ |
1503 | + if (p_availableAudioModes != m_availableAudioModes) |
1504 | + Q_EMIT availableAudioModesChanged(m_availableAudioModes); |
1505 | + |
1506 | + /* Notify if call mode changed */ |
1507 | + if (p_audiomode != m_audiomode) { |
1508 | + Q_EMIT audioModeChanged(m_audiomode); |
1509 | + } |
1510 | + |
1511 | + /* If no more active voicecall, restore previous saved pulseaudio state */ |
1512 | + if (callstatus == CallEnded) { |
1513 | + restoreVoiceCall(); |
1514 | + } |
1515 | + |
1516 | + /* In case the app had set mute when the call wasn't active, make sure we reflect it here */ |
1517 | + if (m_callstatus != CallEnded) |
1518 | + setMicMute(m_micmute); |
1519 | +} |
1520 | + |
1521 | +void QPulseAudioEngineWorker::setMicMute(bool muted) |
1522 | +{ |
1523 | + if (!createPulseContext()) { |
1524 | + return; |
1525 | + } |
1526 | + |
1527 | + m_micmute = muted; |
1528 | + |
1529 | + if (m_callstatus == CallEnded) |
1530 | + return; |
1531 | + |
1532 | + pa_threaded_mainloop_lock(m_mainLoop); |
1533 | + |
1534 | + m_nametoset = ""; |
1535 | + pa_operation *o = pa_context_get_source_info_list(m_context, sourceinfo_cb, this); |
1536 | + if (!handleOperation(o, "pa_context_get_source_info_list")) |
1537 | + return; |
1538 | + |
1539 | + if (m_nametoset != "") { |
1540 | + int m = m_micmute ? 1 : 0; |
1541 | + qDebug("Setting PulseAudio source '%s' muted '%d'", m_nametoset.c_str(), m); |
1542 | + o = pa_context_set_source_mute_by_name(m_context, |
1543 | + m_nametoset.c_str(), m, success_cb, this); |
1544 | + if (!handleOperation(o, "pa_context_set_source_mute_by_name")) |
1545 | + return; |
1546 | + } |
1547 | + |
1548 | + pa_threaded_mainloop_unlock(m_mainLoop); |
1549 | +} |
1550 | + |
1551 | +void QPulseAudioEngineWorker::plugCardCallback(const pa_card_info *info) |
1552 | +{ |
1553 | + qDebug("Notified about card (%s) add event from PulseAudio", info->name); |
1554 | + |
1555 | + /* Check if it's indeed a BT device (with at least one hsp profile) */ |
1556 | + pa_card_profile_info2 *hsp = NULL, *a2dp = NULL; |
1557 | + for (int i = 0; i < info->n_profiles; i++) { |
1558 | + if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_HSP)) |
1559 | + hsp = info->profiles2[i]; |
1560 | + else if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_A2DP) && |
1561 | + info->profiles2[i]->available != 0) { |
1562 | + qDebug("Found a2dp"); |
1563 | + a2dp = info->profiles2[i]; |
1564 | + } |
1565 | + qDebug("%s", info->profiles2[i]->name); |
1566 | + } |
1567 | + |
1568 | + if ((!info->active_profile || !strcmp(info->active_profile->name, "off")) && a2dp) { |
1569 | + qDebug("No profile set"); |
1570 | + m_default_bt_card_fallback = info->name; |
1571 | + } |
1572 | + |
1573 | + /* We only care about BT (HSP) devices, and if one is not already available */ |
1574 | + if ((m_callstatus != CallEnded) && ((m_bt_hsp == "") || (m_bt_hsp_a2dp == ""))) { |
1575 | + if (hsp) |
1576 | + m_handleevent = true; |
1577 | + } |
1578 | +} |
1579 | + |
1580 | +void QPulseAudioEngineWorker::updateCardCallback(const pa_card_info *info) |
1581 | +{ |
1582 | + qDebug("Notified about card (%s) changes event from PulseAudio", info->name); |
1583 | + |
1584 | + /* Check if it's indeed a BT device (with at least one hsp profile) */ |
1585 | + pa_card_profile_info2 *hsp = NULL, *a2dp = NULL; |
1586 | + for (int i = 0; i < info->n_profiles; i++) { |
1587 | + if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_HSP)) |
1588 | + hsp = info->profiles2[i]; |
1589 | + else if (!strcmp(info->profiles2[i]->name, PULSEAUDIO_PROFILE_A2DP) && |
1590 | + info->profiles2[i]->available != 0) { |
1591 | + qDebug("Found a2dp"); |
1592 | + a2dp = info->profiles2[i]; |
1593 | + } |
1594 | + qDebug("%s", info->profiles2[i]->name); |
1595 | + } |
1596 | + |
1597 | + if ((!info->active_profile || !strcmp(info->active_profile->name, "off")) && a2dp) { |
1598 | + qDebug("No profile set"); |
1599 | + m_default_bt_card_fallback = info->name; |
1600 | + } |
1601 | + |
1602 | + |
1603 | + /* We only care if the card event for the voicecall capable card */ |
1604 | + if ((m_callstatus == CallActive) && (!strcmp(info->name, m_voicecallcard.c_str()))) { |
1605 | + if (m_audiomode == AudioModeWiredHeadset) { |
1606 | + /* If previous mode is wired, it means it got unplugged */ |
1607 | + m_handleevent = true; |
1608 | + m_audiomodetoset = AudioModeBtOrWiredOrEarpiece; |
1609 | + } else if ((m_audiomode == AudioModeEarpiece) || ((m_audiomode == AudioModeSpeaker))) { |
1610 | + /* Now only trigger the event in case wired headset/headphone is now available */ |
1611 | + pa_card_port_info *port_info = NULL; |
1612 | + for (int i = 0; i < info->n_ports; i++) { |
1613 | + if (info->ports[i] && (info->ports[i]->available == PA_PORT_AVAILABLE_YES) && ( |
1614 | + !strcmp(info->ports[i]->name, "output-wired_headset") || |
1615 | + !strcmp(info->ports[i]->name, "output-wired_headphone"))) { |
1616 | + m_handleevent = true; |
1617 | + m_audiomodetoset = AudioModeWiredOrEarpiece; |
1618 | + } |
1619 | + } |
1620 | + } else if (m_audiomode == AudioModeBluetooth) { |
1621 | + /* Handle the event so we can update the audiomodes */ |
1622 | + m_handleevent = true; |
1623 | + m_audiomodetoset = AudioModeBluetooth; |
1624 | + } |
1625 | + } |
1626 | +} |
1627 | + |
1628 | +void QPulseAudioEngineWorker::unplugCardCallback() |
1629 | +{ |
1630 | + if (m_callstatus != CallEnded) { |
1631 | + m_handleevent = true; |
1632 | + } |
1633 | +} |
1634 | + |
1635 | +void QPulseAudioEngineWorker::handleCardEvent(const int evt, const unsigned int idx) |
1636 | +{ |
1637 | + pa_operation *o = NULL; |
1638 | + |
1639 | + /* Internal state var used to know if we need to update our internal state */ |
1640 | + m_handleevent = false; |
1641 | + |
1642 | + if (evt == PA_SUBSCRIPTION_EVENT_NEW) { |
1643 | + o = pa_context_get_card_info_by_index(m_context, idx, plug_card_cb, this); |
1644 | + if (!handleOperation(o, "pa_context_get_card_info_by_index")) |
1645 | + return; |
1646 | + |
1647 | + if (m_default_bt_card_fallback != "") { |
1648 | + o = pa_context_set_card_profile_by_name(m_context, |
1649 | + m_default_bt_card_fallback.c_str(), PULSEAUDIO_PROFILE_A2DP, success_cb, this); |
1650 | + if (!handleOperation(o, "pa_context_set_card_profile_by_name")) |
1651 | + return; |
1652 | + m_default_bt_card_fallback = ""; |
1653 | + } |
1654 | + |
1655 | + if (m_handleevent) { |
1656 | + qDebug("Adding new BT-HSP capable device"); |
1657 | + /* In case A2DP is available, switch to HSP */ |
1658 | + if (setupVoiceCall() < 0) |
1659 | + return; |
1660 | + /* Enable the HSP output port */ |
1661 | + setCallMode(m_callstatus, AudioModeBluetooth); |
1662 | + } |
1663 | + } else if (evt == PA_SUBSCRIPTION_EVENT_CHANGE) { |
1664 | + o = pa_context_get_card_info_by_index(m_context, idx, update_card_cb, this); |
1665 | + if (!handleOperation(o, "pa_context_get_card_info_by_index")) |
1666 | + return; |
1667 | + |
1668 | + if (m_default_bt_card_fallback != "") { |
1669 | + o = pa_context_set_card_profile_by_name(m_context, |
1670 | + m_default_bt_card_fallback.c_str(), PULSEAUDIO_PROFILE_A2DP, success_cb, this); |
1671 | + if (!handleOperation(o, "pa_context_set_card_profile_by_name")) |
1672 | + return; |
1673 | + m_default_bt_card_fallback = ""; |
1674 | + } |
1675 | + |
1676 | + if (m_handleevent) { |
1677 | + /* In this case it means the handset state changed */ |
1678 | + qDebug("Notifying card changes for the voicecall capable card"); |
1679 | + setCallMode(m_callstatus, m_audiomodetoset); |
1680 | + } |
1681 | + } else if (evt == PA_SUBSCRIPTION_EVENT_REMOVE) { |
1682 | + /* Check if the main HSP card was removed */ |
1683 | + if (m_bt_hsp != "") { |
1684 | + o = pa_context_get_card_info_by_name(m_context, m_bt_hsp.c_str(), unplug_card_cb, this); |
1685 | + if (!handleOperation(o, "pa_context_get_sink_info_by_name")) |
1686 | + return; |
1687 | + } |
1688 | + if (m_bt_hsp_a2dp != "") { |
1689 | + o = pa_context_get_card_info_by_name(m_context, m_bt_hsp_a2dp.c_str(), unplug_card_cb, this); |
1690 | + if (!handleOperation(o, "pa_context_get_sink_info_by_name")) |
1691 | + return; |
1692 | + } |
1693 | + if (m_handleevent) { |
1694 | + qDebug("Notifying about BT-HSP card removal"); |
1695 | + /* Needed in order to save the default sink/source */ |
1696 | + if (setupVoiceCall() < 0) |
1697 | + return; |
1698 | + /* Enable the default handset output port */ |
1699 | + setCallMode(m_callstatus, AudioModeWiredOrEarpiece); |
1700 | + } |
1701 | + } |
1702 | +} |
1703 | + |
1704 | +Q_GLOBAL_STATIC(QPulseAudioEngine, pulseEngine); |
1705 | + |
1706 | +QPulseAudioEngine::QPulseAudioEngine(QObject *parent) : |
1707 | + QObject(parent) |
1708 | +{ |
1709 | + qRegisterMetaType<CallStatus>(); |
1710 | + qRegisterMetaType<AudioMode>(); |
1711 | + qRegisterMetaType<AudioModes>(); |
1712 | + mWorker = new QPulseAudioEngineWorker(); |
1713 | + QObject::connect(mWorker, SIGNAL(audioModeChanged(const AudioMode)), this, SIGNAL(audioModeChanged(const AudioMode)), Qt::QueuedConnection); |
1714 | + QObject::connect(mWorker, SIGNAL(availableAudioModesChanged(const AudioModes)), this, SIGNAL(availableAudioModesChanged(const AudioModes)), Qt::QueuedConnection); |
1715 | + mWorker->createPulseContext(); |
1716 | + mWorker->moveToThread(&mThread); |
1717 | + mThread.start(); |
1718 | +} |
1719 | + |
1720 | +QPulseAudioEngine::~QPulseAudioEngine() |
1721 | +{ |
1722 | + mThread.quit(); |
1723 | + mThread.wait(); |
1724 | +} |
1725 | + |
1726 | +QPulseAudioEngine *QPulseAudioEngine::instance() |
1727 | +{ |
1728 | + QPulseAudioEngine *engine = pulseEngine(); |
1729 | + return engine; |
1730 | +} |
1731 | + |
1732 | +void QPulseAudioEngine::setCallMode(CallStatus callstatus, AudioMode audiomode) |
1733 | +{ |
1734 | + QMetaObject::invokeMethod(mWorker, "setCallMode", Qt::QueuedConnection, Q_ARG(CallStatus, callstatus), Q_ARG(AudioMode, audiomode)); |
1735 | +} |
1736 | + |
1737 | +void QPulseAudioEngine::setMicMute(bool muted) |
1738 | +{ |
1739 | + QMetaObject::invokeMethod(mWorker, "setMicMute", Qt::QueuedConnection, Q_ARG(bool, muted)); |
1740 | +} |
1741 | + |
1742 | +QT_END_NAMESPACE |
1743 | + |
1744 | |
1745 | === added file 'handler/qpulseaudioengine.h' |
1746 | --- handler/qpulseaudioengine.h 1970-01-01 00:00:00 +0000 |
1747 | +++ handler/qpulseaudioengine.h 2016-01-20 11:56:12 +0000 |
1748 | @@ -0,0 +1,126 @@ |
1749 | +/**************************************************************************** |
1750 | +** |
1751 | +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). |
1752 | +** Contact: http://www.qt-project.org/legal |
1753 | +** |
1754 | +** This file was taken from qt5 and modified by |
1755 | +** David Henningsson <david.henningsson@canonical.com> for usage in |
1756 | +** telepathy-ofono. |
1757 | +** |
1758 | +** GNU Lesser General Public License Usage |
1759 | +** Alternatively, this file may be used under the terms of the GNU Lesser |
1760 | +** General Public License version 2.1 as published by the Free Software |
1761 | +** Foundation and appearing in the file LICENSE.LGPL included in the |
1762 | +** packaging of this file. Please review the following information to |
1763 | +** ensure the GNU Lesser General Public License version 2.1 requirements |
1764 | +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
1765 | +** |
1766 | +****************************************************************************/ |
1767 | + |
1768 | +#ifndef QPULSEAUDIOENGINE_H |
1769 | +#define QPULSEAUDIOENGINE_H |
1770 | + |
1771 | +#include <QtCore/qmap.h> |
1772 | +#include <QtCore/qbytearray.h> |
1773 | +#include <QThread> |
1774 | +#include <pulse/pulseaudio.h> |
1775 | + |
1776 | +enum AudioMode { |
1777 | + AudioModeEarpiece = 0x0001, |
1778 | + AudioModeWiredHeadset = 0x0002, |
1779 | + AudioModeSpeaker = 0x0004, |
1780 | + AudioModeBluetooth = 0x0008, |
1781 | + AudioModeBtOrWiredOrEarpiece = AudioModeBluetooth | AudioModeWiredHeadset | AudioModeEarpiece, |
1782 | + AudioModeWiredOrEarpiece = AudioModeWiredHeadset | AudioModeEarpiece, |
1783 | + AudioModeWiredOrSpeaker = AudioModeWiredHeadset | AudioModeSpeaker, |
1784 | + AudioModeBtOrWiredOrSpeaker = AudioModeBluetooth | AudioModeWiredOrSpeaker |
1785 | +}; |
1786 | + |
1787 | +Q_DECLARE_METATYPE(AudioMode) |
1788 | + |
1789 | +typedef QList<AudioMode> AudioModes; |
1790 | +Q_DECLARE_METATYPE(AudioModes) |
1791 | + |
1792 | +enum CallStatus { |
1793 | + CallRinging, |
1794 | + CallActive, |
1795 | + CallEnded |
1796 | +}; |
1797 | + |
1798 | +Q_DECLARE_METATYPE(CallStatus) |
1799 | + |
1800 | +QT_BEGIN_NAMESPACE |
1801 | + |
1802 | +class QPulseAudioEngineWorker : public QObject |
1803 | +{ |
1804 | + Q_OBJECT |
1805 | + |
1806 | +public: |
1807 | + QPulseAudioEngineWorker(QObject *parent = 0); |
1808 | + ~QPulseAudioEngineWorker(); |
1809 | + |
1810 | + pa_threaded_mainloop *mainloop() { return m_mainLoop; } |
1811 | + pa_context *context() { return m_context; } |
1812 | + bool createPulseContext(void); |
1813 | + int setupVoiceCall(void); |
1814 | + void restoreVoiceCall(void); |
1815 | + /* Callbacks to be used internally */ |
1816 | + void cardInfoCallback(const pa_card_info *card); |
1817 | + void sinkInfoCallback(const pa_sink_info *sink); |
1818 | + void sourceInfoCallback(const pa_source_info *source); |
1819 | + void serverInfoCallback(const pa_server_info *server); |
1820 | + void plugCardCallback(const pa_card_info *card); |
1821 | + void updateCardCallback(const pa_card_info *card); |
1822 | + void unplugCardCallback(); |
1823 | + |
1824 | +Q_SIGNALS: |
1825 | + void audioModeChanged(const AudioMode mode); |
1826 | + void availableAudioModesChanged(const AudioModes modes); |
1827 | + |
1828 | +public Q_SLOTS: |
1829 | + void handleCardEvent(const int evt, const unsigned int idx); |
1830 | + void setCallMode(CallStatus callstatus, AudioMode audiomode); |
1831 | + void setMicMute(bool muted); /* True if muted, false if unmuted */ |
1832 | + |
1833 | +private: |
1834 | + pa_mainloop_api *m_mainLoopApi; |
1835 | + pa_threaded_mainloop *m_mainLoop; |
1836 | + pa_context *m_context; |
1837 | + |
1838 | + AudioModes m_availableAudioModes; |
1839 | + CallStatus m_callstatus; |
1840 | + AudioMode m_audiomode; |
1841 | + AudioMode m_audiomodetoset; |
1842 | + bool m_micmute, m_handleevent; |
1843 | + std::string m_nametoset, m_valuetoset; |
1844 | + std::string m_defaultsink, m_defaultsource; |
1845 | + std::string m_bt_hsp, m_bt_hsp_a2dp; |
1846 | + std::string m_default_bt_card_fallback; |
1847 | + std::string m_voicecallcard, m_voicecallhighest, m_voicecallprofile; |
1848 | + |
1849 | + bool handleOperation(pa_operation *operation, const char *func_name); |
1850 | + void releasePulseContext(void); |
1851 | +}; |
1852 | + |
1853 | +class QPulseAudioEngine : public QObject |
1854 | +{ |
1855 | + Q_OBJECT |
1856 | +public: |
1857 | + explicit QPulseAudioEngine(QObject *parent = 0); |
1858 | + ~QPulseAudioEngine(); |
1859 | + static QPulseAudioEngine *instance(); |
1860 | + |
1861 | + void setCallMode(CallStatus callstatus, AudioMode audiomode); |
1862 | + void setMicMute(bool muted); /* True if muted, false if unmuted */ |
1863 | + |
1864 | +Q_SIGNALS: |
1865 | + void audioModeChanged(const AudioMode mode); |
1866 | + void availableAudioModesChanged(const AudioModes modes); |
1867 | +private: |
1868 | + QPulseAudioEngineWorker *mWorker; |
1869 | + QThread mThread; |
1870 | +}; |
1871 | + |
1872 | +QT_END_NAMESPACE |
1873 | + |
1874 | +#endif |
1875 | |
1876 | === modified file 'libtelephonyservice/audiooutput.cpp' |
1877 | --- libtelephonyservice/audiooutput.cpp 2014-08-25 14:49:53 +0000 |
1878 | +++ libtelephonyservice/audiooutput.cpp 2016-01-20 11:56:12 +0000 |
1879 | @@ -21,6 +21,23 @@ |
1880 | |
1881 | #include "audiooutput.h" |
1882 | |
1883 | +QDBusArgument &operator<<(QDBusArgument &argument, const AudioOutputDBus &output) |
1884 | +{ |
1885 | + argument.beginStructure(); |
1886 | + argument << output.id << output.type << output.name; |
1887 | + argument.endStructure(); |
1888 | + return argument; |
1889 | +} |
1890 | + |
1891 | +const QDBusArgument &operator>>(const QDBusArgument &argument, AudioOutputDBus &output) |
1892 | +{ |
1893 | + argument.beginStructure(); |
1894 | + argument >> output.id >> output.type >> output.name; |
1895 | + argument.endStructure(); |
1896 | + return argument; |
1897 | +} |
1898 | + |
1899 | + |
1900 | AudioOutput::AudioOutput(const QString& id, const QString& name, const QString& type, QObject *parent) : |
1901 | QObject(parent), mId(id), mName(name), mType(type) |
1902 | { |
1903 | |
1904 | === modified file 'libtelephonyservice/audiooutput.h' |
1905 | --- libtelephonyservice/audiooutput.h 2014-08-25 14:49:53 +0000 |
1906 | +++ libtelephonyservice/audiooutput.h 2016-01-20 11:56:12 +0000 |
1907 | @@ -23,6 +23,7 @@ |
1908 | #define AUDIOOUTPUT_H |
1909 | |
1910 | #include <QObject> |
1911 | +#include <QDBusArgument> |
1912 | |
1913 | struct AudioOutputDBus { |
1914 | QString id; |
1915 | @@ -53,4 +54,7 @@ |
1916 | QString mType; |
1917 | }; |
1918 | |
1919 | +QDBusArgument &operator<<(QDBusArgument &argument, const AudioOutputDBus &output); |
1920 | +const QDBusArgument &operator>>(const QDBusArgument &argument, AudioOutputDBus &output); |
1921 | + |
1922 | #endif // AUDIOOUTPUT_H |
1923 | |
1924 | === modified file 'libtelephonyservice/callentry.cpp' |
1925 | --- libtelephonyservice/callentry.cpp 2015-06-09 21:59:29 +0000 |
1926 | +++ libtelephonyservice/callentry.cpp 2016-01-20 11:56:12 +0000 |
1927 | @@ -38,30 +38,16 @@ |
1928 | #define PROPERTY_AUDIO_OUTPUTS "AudioOutputs" |
1929 | #define PROPERTY_ACTIVE_AUDIO_OUTPUT "ActiveAudioOutput" |
1930 | |
1931 | -QDBusArgument &operator<<(QDBusArgument &argument, const AudioOutputDBus &output) |
1932 | -{ |
1933 | - argument.beginStructure(); |
1934 | - argument << output.id << output.type << output.name; |
1935 | - argument.endStructure(); |
1936 | - return argument; |
1937 | -} |
1938 | - |
1939 | -const QDBusArgument &operator>>(const QDBusArgument &argument, AudioOutputDBus &output) |
1940 | -{ |
1941 | - argument.beginStructure(); |
1942 | - argument >> output.id >> output.type >> output.name; |
1943 | - argument.endStructure(); |
1944 | - return argument; |
1945 | -} |
1946 | - |
1947 | CallEntry::CallEntry(const Tp::CallChannelPtr &channel, QObject *parent) : |
1948 | QObject(parent), |
1949 | mChannel(channel), |
1950 | mVoicemail(false), |
1951 | mLocalMuteState(false), |
1952 | - mMuteInterface(channel->busName(), channel->objectPath(), TELEPATHY_MUTE_IFACE), |
1953 | - mAudioOutputsInterface(channel->busName(), channel->objectPath(), CANONICAL_TELEPHONY_AUDIOOUTPUTS_IFACE) |
1954 | + mMuteInterface(channel->busName(), channel->objectPath(), TELEPATHY_MUTE_IFACE) |
1955 | { |
1956 | + qRegisterMetaType<AudioOutputDBus>(); |
1957 | + qRegisterMetaType<AudioOutputDBusList>(); |
1958 | + |
1959 | qDBusRegisterMetaType<AudioOutputDBus>(); |
1960 | qDBusRegisterMetaType<AudioOutputDBusList>(); |
1961 | |
1962 | @@ -77,12 +63,32 @@ |
1963 | SIGNAL(CallHoldingFailed(QString)), |
1964 | SLOT(onCallHoldingFailed(QString))); |
1965 | |
1966 | + connect(TelepathyHelper::instance()->handlerInterface(), |
1967 | + SIGNAL(ActiveAudioOutputChanged(QString)), |
1968 | + SLOT(onActiveAudioOutputChanged(QString))); |
1969 | + |
1970 | + QDBusConnection::sessionBus().connect(TelepathyHelper::instance()->handlerInterface()->service(), |
1971 | + TelepathyHelper::instance()->handlerInterface()->path(), |
1972 | + TelepathyHelper::instance()->handlerInterface()->interface(), |
1973 | + "AudioOutputsChanged", |
1974 | + this, |
1975 | + SLOT(onAudioOutputsChanged(AudioOutputDBusList))); |
1976 | + |
1977 | // in case the account is an ofono account, we can check the voicemail number |
1978 | OfonoAccountEntry *ofonoAccount = qobject_cast<OfonoAccountEntry*>(mAccount); |
1979 | if (ofonoAccount && !ofonoAccount->voicemailNumber().isEmpty()) { |
1980 | setVoicemail(phoneNumber() == ofonoAccount->voicemailNumber()); |
1981 | } |
1982 | |
1983 | + QDBusInterface *phoneAppHandler = TelepathyHelper::instance()->handlerInterface(); |
1984 | + |
1985 | + QDBusMessage reply = phoneAppHandler->call(PROPERTY_AUDIO_OUTPUTS); |
1986 | + AudioOutputDBusList audioOutputList = qdbus_cast<AudioOutputDBusList>(reply.arguments().first()); |
1987 | + onAudioOutputsChanged(audioOutputList); |
1988 | + |
1989 | + QString activeAudioOutput = phoneAppHandler->property(PROPERTY_ACTIVE_AUDIO_OUTPUT).toString(); |
1990 | + onActiveAudioOutputChanged(activeAudioOutput); |
1991 | + |
1992 | Q_EMIT incomingChanged(); |
1993 | } |
1994 | |
1995 | @@ -105,7 +111,8 @@ |
1996 | |
1997 | void CallEntry::setActiveAudioOutput(const QString &id) |
1998 | { |
1999 | - mAudioOutputsInterface.call("SetActiveAudioOutput", id); |
2000 | + QDBusInterface *phoneAppHandler = TelepathyHelper::instance()->handlerInterface(); |
2001 | + phoneAppHandler->setProperty("ActiveAudioOutput", id); |
2002 | } |
2003 | |
2004 | void CallEntry::onActiveAudioOutputChanged(const QString &id) |
2005 | @@ -198,13 +205,6 @@ |
2006 | |
2007 | refreshProperties(); |
2008 | |
2009 | - QDBusConnection::sessionBus().connect(mChannel->busName(), mChannel->objectPath(), |
2010 | - CANONICAL_TELEPHONY_AUDIOOUTPUTS_IFACE, |
2011 | - "AudioOutputsChanged", |
2012 | - this, |
2013 | - SLOT(onAudioOutputsChanged(AudioOutputDBusList))); |
2014 | - connect(&mAudioOutputsInterface, SIGNAL(ActiveAudioOutputChanged(QString)), SLOT(onActiveAudioOutputChanged(QString))); |
2015 | - |
2016 | onCallStateChanged(mChannel->callState()); |
2017 | |
2018 | Q_EMIT heldChanged(); |
2019 | @@ -248,30 +248,18 @@ |
2020 | |
2021 | void CallEntry::refreshProperties() |
2022 | { |
2023 | - QDBusInterface callChannelIface(mChannel->busName(), mChannel->objectPath(), DBUS_PROPERTIES_IFACE); |
2024 | - |
2025 | - QDBusMessage reply = callChannelIface.call("GetAll", TELEPATHY_CALL_IFACE); |
2026 | - QVariantList args = reply.arguments(); |
2027 | - QMap<QString, QVariant> map = qdbus_cast<QMap<QString, QVariant> >(args[0]); |
2028 | - |
2029 | - reply = callChannelIface.call("GetAll", CANONICAL_TELEPHONY_AUDIOOUTPUTS_IFACE); |
2030 | - args = reply.arguments(); |
2031 | - QMap<QString, QVariant> map2 = qdbus_cast<QMap<QString, QVariant> >(args[0]); |
2032 | - |
2033 | - mProperties.clear(); |
2034 | - QMapIterator<QString, QVariant> i(map); |
2035 | - while(i.hasNext()) { |
2036 | - i.next(); |
2037 | - mProperties[i.key()] = i.value(); |
2038 | - } |
2039 | - QMapIterator<QString, QVariant> i2(map2); |
2040 | - while(i2.hasNext()) { |
2041 | - i2.next(); |
2042 | - mProperties[i2.key()] = i2.value(); |
2043 | - } |
2044 | - |
2045 | - onAudioOutputsChanged(qdbus_cast<AudioOutputDBusList>(mProperties[PROPERTY_AUDIO_OUTPUTS])); |
2046 | - onActiveAudioOutputChanged(mProperties[PROPERTY_ACTIVE_AUDIO_OUTPUT].toString()); |
2047 | + QDBusInterface callChannelIface(mChannel->busName(), mChannel->objectPath(), DBUS_PROPERTIES_IFACE); |
2048 | + |
2049 | + QDBusMessage reply = callChannelIface.call("GetAll", TELEPATHY_CALL_IFACE); |
2050 | + QVariantList args = reply.arguments(); |
2051 | + QMap<QString, QVariant> map = qdbus_cast<QMap<QString, QVariant> >(args[0]); |
2052 | + |
2053 | + mProperties.clear(); |
2054 | + QMapIterator<QString, QVariant> i(map); |
2055 | + while(i.hasNext()) { |
2056 | + i.next(); |
2057 | + mProperties[i.key()] = i.value(); |
2058 | + } |
2059 | } |
2060 | |
2061 | bool CallEntry::dialing() const |
2062 | |
2063 | === modified file 'libtelephonyservice/callentry.h' |
2064 | --- libtelephonyservice/callentry.h 2015-03-04 18:02:13 +0000 |
2065 | +++ libtelephonyservice/callentry.h 2016-01-20 11:56:12 +0000 |
2066 | @@ -29,6 +29,7 @@ |
2067 | #include <TelepathyQt/CallChannel> |
2068 | #include "audiooutput.h" |
2069 | |
2070 | + |
2071 | class AccountEntry; |
2072 | |
2073 | class CallEntry : public QObject |
2074 | @@ -179,7 +180,6 @@ |
2075 | AccountEntry *mAccount; |
2076 | Tp::CallChannelPtr mChannel; |
2077 | QDBusInterface mMuteInterface; |
2078 | - QDBusInterface mAudioOutputsInterface; |
2079 | QMap<QString, QVariant> mProperties; |
2080 | bool mVoicemail; |
2081 | bool mLocalMuteState; |
2082 | |
2083 | === modified file 'libtelephonyservice/telepathyhelper.cpp' |
2084 | --- libtelephonyservice/telepathyhelper.cpp 2015-11-23 20:01:58 +0000 |
2085 | +++ libtelephonyservice/telepathyhelper.cpp 2016-01-20 11:56:12 +0000 |
2086 | @@ -243,7 +243,6 @@ |
2087 | if (name.isEmpty()) { |
2088 | name = "TelephonyPluginObserver"; |
2089 | } |
2090 | - |
2091 | if (mChannelObserver) { |
2092 | unregisterClient(mChannelObserver); |
2093 | } |
2094 | @@ -251,13 +250,16 @@ |
2095 | mChannelObserver = new ChannelObserver(this); |
2096 | mChannelObserverPtr = Tp::AbstractClientPtr(mChannelObserver); |
2097 | if (registerClient(mChannelObserver, name)) { |
2098 | - // messages |
2099 | - connect(mChannelObserver, SIGNAL(textChannelAvailable(Tp::TextChannelPtr)), |
2100 | - ChatManager::instance(), SLOT(onTextChannelAvailable(Tp::TextChannelPtr))); |
2101 | + // we don't connect managers in handler, as they query the handler and cause a deadlock |
2102 | + if (QCoreApplication::applicationName() != "telephony-service-handler") { |
2103 | + // messages |
2104 | + connect(mChannelObserver, SIGNAL(textChannelAvailable(Tp::TextChannelPtr)), |
2105 | + ChatManager::instance(), SLOT(onTextChannelAvailable(Tp::TextChannelPtr))); |
2106 | |
2107 | - // calls |
2108 | - connect(mChannelObserver, SIGNAL(callChannelAvailable(Tp::CallChannelPtr)), |
2109 | - CallManager::instance(), SLOT(onCallChannelAvailable(Tp::CallChannelPtr))); |
2110 | + // calls |
2111 | + connect(mChannelObserver, SIGNAL(callChannelAvailable(Tp::CallChannelPtr)), |
2112 | + CallManager::instance(), SLOT(onCallChannelAvailable(Tp::CallChannelPtr))); |
2113 | + } |
2114 | |
2115 | Q_EMIT channelObserverCreated(mChannelObserver); |
2116 | } |
2117 | |
2118 | === modified file 'libtelephonyservice/telepathyhelper.h' |
2119 | --- libtelephonyservice/telepathyhelper.h 2015-11-23 20:01:58 +0000 |
2120 | +++ libtelephonyservice/telepathyhelper.h 2016-01-20 11:56:12 +0000 |
2121 | @@ -33,7 +33,6 @@ |
2122 | #include "channelobserver.h" |
2123 | |
2124 | #define CANONICAL_TELEPHONY_VOICEMAIL_IFACE "com.canonical.Telephony.Voicemail" |
2125 | -#define CANONICAL_TELEPHONY_AUDIOOUTPUTS_IFACE "com.canonical.Telephony.AudioOutputs" |
2126 | #define CANONICAL_TELEPHONY_USSD_IFACE "com.canonical.Telephony.USSD" |
2127 | #define CANONICAL_TELEPHONY_EMERGENCYMODE_IFACE "com.canonical.Telephony.EmergencyMode" |
2128 |
FAILED: Continuous integration, rev:1160 jenkins. qa.ubuntu. com/job/ telephony- service- ci/662/ jenkins. qa.ubuntu. com/job/ telephony- service- vivid-amd64- ci/334/ console jenkins. qa.ubuntu. com/job/ telephony- service- vivid-armhf- ci/330 jenkins. qa.ubuntu. com/job/ telephony- service- vivid-armhf- ci/330/ artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ telephony- service- vivid-i386- ci/330/ console
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
deb: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/telephony- service- ci/662/ rebuild
http://