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