Merge lp:~phablet-team/telephony-service/multiple_performance_improvements into lp:telephony-service/staging

Proposed by Tiago Salem Herrmann
Status: Needs review
Proposed branch: lp:~phablet-team/telephony-service/multiple_performance_improvements
Merge into: lp:telephony-service/staging
Prerequisite: lp:~phablet-team/telephony-service/audio_route_manager
Diff against target: 3337 lines (+41/-1537)
13 files modified
handler/audioroutemanager.cpp (+0/-231)
handler/audioroutemanager.h (+0/-75)
handler/powerd.h (+0/-34)
handler/powerdaudiomodemediator.cpp (+0/-63)
handler/powerdaudiomodemediator.h (+0/-46)
handler/powerddbus.cpp (+0/-43)
handler/powerddbus.h (+0/-38)
handler/qpulseaudioengine.cpp (+0/-853)
handler/qpulseaudioengine.h (+0/-126)
libtelephonyservice/chatentry.cpp (+26/-16)
libtelephonyservice/chatentry.h (+1/-0)
libtelephonyservice/contactwatcher.cpp (+1/-7)
tests/Ubuntu.Telephony/ContactWatcherTest.cpp (+13/-5)
To merge this branch: bzr merge lp:~phablet-team/telephony-service/multiple_performance_improvements
Reviewer Review Type Date Requested Status
Ubuntu Phablet Team Pending
Review via email: mp+316375@code.launchpad.net

Commit message

Multiple performance improvements on Roles Interfaces and ContactWatcher

Description of the change

Multiple performance improvements on Roles Interfaces and ContactWatcher

To post a comment you must log in.
1242. By Tiago Salem Herrmann

merge parent branch

Unmerged revisions

1242. By Tiago Salem Herrmann

merge parent branch

1241. By Tiago Salem Herrmann

merge parent

1240. By Tiago Salem Herrmann

Multiple performance fixes for contact watcher and roles interface

1239. By Tiago Salem Herrmann

merge parent

Preview Diff

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

Subscribers

People subscribed via source and target branches

to all changes: