Music Hub  ..
A session-wide music playback service
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
call_monitor.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2014 Canonical Ltd
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License version 3 as
6  * published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * Author: Justin McPherson <justin.mcpherson@canonical.com>
17  */
18 
19 
20 #include "call_monitor.h"
21 
22 #include "qtbridge.h"
23 #include <TelepathyQt/AccountManager>
24 #include <TelepathyQt/SimpleCallObserver>
25 #include <TelepathyQt/PendingOperation>
26 #include <TelepathyQt/PendingReady>
27 #include <TelepathyQt/PendingAccount>
28 
29 #include <list>
30 #include <mutex>
31 
32 
33 namespace {
34 class TelepathyCallMonitor : public QObject
35 {
36  Q_OBJECT
37 public:
38  TelepathyCallMonitor(const Tp::AccountPtr& account):
39  mAccount(account),
40  mCallObserver(Tp::SimpleCallObserver::create(mAccount)) {
41  connect(mCallObserver.data(), SIGNAL(callStarted(Tp::CallChannelPtr)), SIGNAL(offHook()));
42  connect(mCallObserver.data(), SIGNAL(callEnded(Tp::CallChannelPtr,QString,QString)), SIGNAL(onHook()));
43  connect(mCallObserver.data(), SIGNAL(streamedMediaCallStarted(Tp::StreamedMediaChannelPtr)), SIGNAL(offHook()));
44  connect(mCallObserver.data(), SIGNAL(streamedMediaCallEnded(Tp::StreamedMediaChannelPtr,QString,QString)), SIGNAL(onHook()));
45  }
46 
47 Q_SIGNALS:
48  void offHook();
49  void onHook();
50 
51 private:
52  Tp::AccountPtr mAccount;
53  Tp::SimpleCallObserverPtr mCallObserver;
54 };
55 
56 
57 class TelepathyBridge : public QObject
58 {
59  Q_OBJECT
60 public:
61  TelepathyBridge():
62  QObject(0) {
63  Tp::registerTypes();
64 
65  QTimer::singleShot(0, this, SLOT(accountManagerSetup()));
66  }
67 
68  ~TelepathyBridge() {
69  for (std::list<TelepathyCallMonitor*>::iterator it = mCallMonitors.begin();
70  it != mCallMonitors.end();
71  ++it) {
72  delete *it;
73  }
74  }
75 
76  void on_change(const std::function<void(CallMonitor::State)>& func) {
77  std::lock_guard<std::mutex> l(cb_lock);
78  cb = func;
79  }
80 
81 private Q_SLOTS:
82  void accountManagerSetup() {
83  mAccountManager = Tp::AccountManager::create(Tp::AccountFactory::create(QDBusConnection::sessionBus(),
84  Tp::Account::FeatureCore),
85  Tp::ConnectionFactory::create(QDBusConnection::sessionBus(),
86  Tp::Connection::FeatureCore));
87  connect(mAccountManager->becomeReady(),
88  SIGNAL(finished(Tp::PendingOperation*)),
89  SLOT(accountManagerReady(Tp::PendingOperation*)));
90  }
91 
92  void accountManagerReady(Tp::PendingOperation* operation) {
93  if (operation->isError()) {
94  std::cerr << "TelepathyBridge: Operation failed (accountManagerReady)" << std::endl;
95  QTimer::singleShot(1000, this, SLOT(accountManagerSetup())); // again
96  return;
97  }
98 
99  Q_FOREACH(const Tp::AccountPtr& account, mAccountManager->allAccounts()) {
100  connect(account->becomeReady(Tp::Account::FeatureCapabilities),
101  SIGNAL(finished(Tp::PendingOperation*)),
102  SLOT(accountReady(Tp::PendingOperation*)));
103  }
104 
105  connect(mAccountManager.data(), SIGNAL(newAccount(Tp::AccountPtr)), SLOT(newAccount(Tp::AccountPtr)));
106  }
107 
108  void newAccount(const Tp::AccountPtr& account)
109  {
110  connect(account->becomeReady(Tp::Account::FeatureCapabilities),
111  SIGNAL(finished(Tp::PendingOperation*)),
112  SLOT(accountReady(Tp::PendingOperation*)));
113  }
114 
115  void accountReady(Tp::PendingOperation* operation) {
116  if (operation->isError()) {
117  std::cerr << "TelepathyAccount: Operation failed (accountReady)" << std::endl;
118  return;
119  }
120 
121  Tp::PendingReady* pendingReady = qobject_cast<Tp::PendingReady*>(operation);
122  if (pendingReady == 0) {
123  std::cerr << "Rejecting account because could not understand ready status" << std::endl;
124  return;
125  }
126 
127  checkAndAddAccount(Tp::AccountPtr::qObjectCast(pendingReady->proxy()));
128  }
129 
130  void offHook()
131  {
132  std::lock_guard<std::mutex> l(cb_lock);
133  if (cb)
135  }
136 
137  void onHook()
138  {
139  std::lock_guard<std::mutex> l(cb_lock);
140  if (cb)
142  }
143 
144 private:
145  std::mutex cb_lock;
146  std::function<void (CallMonitor::State)> cb;
147  Tp::AccountManagerPtr mAccountManager;
148  std::list<TelepathyCallMonitor*> mCallMonitors;
149 
150  void checkAndAddAccount(const Tp::AccountPtr& account)
151  {
152  Tp::ConnectionCapabilities caps = account->capabilities();
153  // TODO: Later on we will need to filter for the right capabilities, and also allow dynamic account detection
154  // Don't check caps for now as a workaround for https://bugs.launchpad.net/ubuntu/+source/media-hub/+bug/1409125
155  // at least until we are able to find out the root cause of it (check rev 107 for the caps check)
156  auto tcm = new TelepathyCallMonitor(account);
157  connect(tcm, SIGNAL(offHook()), SLOT(offHook()));
158  connect(tcm, SIGNAL(onHook()), SLOT(onHook()));
159  mCallMonitors.push_back(tcm);
160  }
161 };
162 }
163 
165 {
166 public:
168  mBridge = nullptr;
169  try {
170  std::thread([this]() {
171  qt::core::world::build_and_run(0, nullptr, [this]() {
173  std::cout << "CallMonitor: Creating TelepathyBridge" << std::endl;
174  mBridge = new TelepathyBridge();
175  cv.notify_all();
176  });
177  });
178  }).detach();
179  } catch(const std::system_error& error) {
180  std::cerr << "exception(std::system_error) in CallMonitor thread start" << error.what() << std::endl;
181  } catch(...) {
182  std::cerr << "exception(...) in CallMonitor thread start" << std::endl;
183  }
184  // Wait until telepathy bridge is set, so we can hook up the change signals
185  std::unique_lock<std::mutex> lck(mtx);
186  cv.wait_for(lck, std::chrono::seconds(3));
187  }
188 
191  }
192 
193  TelepathyBridge *mBridge;
194 
195 private:
196  std::mutex mtx;
197  std::condition_variable cv;
198 };
199 
200 
202  d(new CallMonitorPrivate)
203 {
204 }
205 
207 {
208  delete d->mBridge;
209  delete d;
210 }
211 
212 void CallMonitor::on_change(const std::function<void(CallMonitor::State)>& func)
213 {
214  if (d->mBridge != nullptr) {
215  std::cout << "CallMonitor: Setting up callback for TelepathyBridge on_change" << std::endl;
216  d->mBridge->on_change(func);
217  } else
218  std::cerr << "TelepathyBridge: Failed to hook on_change signal, bridge not yet set" << std::endl;
219 }
220 
221 #include "call_monitor.moc"
222 
void destroy()
Destroys the Qt core world and quits its event loop.
Definition: qtbridge.cpp:154
void build_and_run(int argc, char **argv, const std::function< void()> &ready)
Sets up the Qt core world and executes its event loop. Blocks until destroy() is called.
Definition: qtbridge.cpp:132
void on_change(const std::function< void(CallMonitor::State)> &func)
TelepathyBridge * mBridge
std::future< void > enter_with_task(const std::function< void()> &task)
Enters the Qt core world and schedules the given task for execution.
Definition: qtbridge.cpp:163