Merge lp:~marcustomlinson/unity-scopes-api/fix_ui_policy into lp:unity-scopes-api

Proposed by Marcus Tomlinson
Status: Superseded
Proposed branch: lp:~marcustomlinson/unity-scopes-api/fix_ui_policy
Merge into: lp:unity-scopes-api
Diff against target: 867 lines (+426/-137)
9 files modified
CMakeLists.txt (+1/-1)
HACKING (+22/-1)
RELEASE_NOTES.md (+5/-0)
debian/changelog (+7/-0)
include/unity/scopes/OnlineAccountClient.h (+8/-9)
include/unity/scopes/internal/OnlineAccountClientImpl.h (+23/-13)
src/scopes/internal/OnlineAccountClientImpl.cpp (+347/-112)
src/scopes/internal/RegistryObject.cpp (+11/-1)
test/gtest/scopes/OnlineAccountClient/OnlineAccountClient_test.cpp (+2/-0)
To merge this branch: bzr merge lp:~marcustomlinson/unity-scopes-api/fix_ui_policy
Reviewer Review Type Date Requested Status
Paweł Stołowski (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+237216@code.launchpad.net

This proposal has been superseded by a proposal from 2014-10-08.

Commit message

1. Depending on whether OnlineAccountClient is used by the shell or by a scope, set the SIGNON_SESSION_DATA_UI_POLICY accordingly.

2. Inform scopes of successful user authentications via a pub/sub network.

3. Removed inc_logins() and dec_logins() as only one session can be active per service (now using info->session != nullptr).

4. Added better thread and exception safety.

5. Run the internal event loop within its own context to avoid clashing with external main loops.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Paweł Stołowski (stolowski) wrote :

+1

review: Approve
264. By Marcus Tomlinson

Merged devel

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2014-09-11 08:35:01 +0000
+++ CMakeLists.txt 2014-10-08 10:59:41 +0000
@@ -195,7 +195,7 @@
195# API version195# API version
196set(UNITY_SCOPES_MAJOR 0)196set(UNITY_SCOPES_MAJOR 0)
197set(UNITY_SCOPES_MINOR 6)197set(UNITY_SCOPES_MINOR 6)
198set(UNITY_SCOPES_MICRO 6)198set(UNITY_SCOPES_MICRO 7)
199set(UNITY_SCOPES_SOVERSION 3)199set(UNITY_SCOPES_SOVERSION 3)
200200
201# Version for testing, with all symbols visible201# Version for testing, with all symbols visible
202202
=== modified file 'HACKING'
--- HACKING 2014-09-08 22:15:40 +0000
+++ HACKING 2014-10-08 10:59:41 +0000
@@ -156,7 +156,28 @@
156This creates a diff of the symbols in /tmp/symbols.diff.156This creates a diff of the symbols in /tmp/symbols.diff.
157(The demangled symbols from the debian build are in ./new_symbols.)157(The demangled symbols from the debian build are in ./new_symbols.)
158158
159Review any changes in /tmp/symbols.diff. If they are OK:159NOTE: We currently have one architecture-specific symbol:
160
161(c++|arch=amd64)"unity::scopes::internal::RegistryObject::ScopeExecData::ScopeExecData(unity::scopes::internal::RegistryObject::ScopeExecData const&)@Base" 0.6.6+14.10.20140916
162
163We need this because the copy-constructor is inlined on i386 and armhf.
164
165The above script can't cope with the arch tag at the moment. Until we get
166around to fixing this, you need to delete the corresponding change from
167the diff output before applying the diff as a patch. The offending diff
168will look something like this:
169
170@@ -377,7 +378,7 @@
171 (c++)"unity::scopes::internal::RegistryObject::remove_desktop_file(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.6.6+14.10.20140916
172 (c++)"unity::scopes::internal::RegistryObject::remove_local_scope(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.4.0+14.04.20140312.1
173 (c++)"unity::scopes::internal::RegistryObject::ScopeExecData::~ScopeExecData()@Base" 0.4.2+14.04.20140404.2
174- (c++|arch=amd64)"unity::scopes::internal::RegistryObject::ScopeExecData::ScopeExecData(unity::scopes::internal::RegistryObject::ScopeExecData const&)@Base" 0.6.6+14.10.20140916
175+ (c++)"unity::scopes::internal::RegistryObject::ScopeExecData::ScopeExecData(unity::scopes::internal::RegistryObject::ScopeExecData const&)@Base" 0replaceme
176 (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::clear_handle_unlocked()@Base" 0.4.2+14.04.20140404.2
177 (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::exec(core::posix::ChildProcess::DeathObserver&, std::shared_ptr<unity::scopes::internal::Executor>)@Base" 0.4.3+14.10.20140428
178 (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::expand_custom_exec()@Base" 0.5.0+14.10.20140619
179
180Review any other changes in /tmp/symbols.diff. If they are OK:
160181
161 $ cd -182 $ cd -
162 $ patch -p0 < /tmp/symbols.diff183 $ patch -p0 < /tmp/symbols.diff
163184
=== modified file 'RELEASE_NOTES.md'
--- RELEASE_NOTES.md 2014-09-11 08:42:38 +0000
+++ RELEASE_NOTES.md 2014-10-08 10:59:41 +0000
@@ -1,6 +1,11 @@
1Release notes1Release notes
2=============2=============
33
4Changes in version 0.6.7
5========================
6 - Added new RunInExternalUiMainLoop option to OnlineAccountClient::MainLoopSelect.
7 - OnlineAccountClient signon UI policy determined by main loop used (shell: show / scope: hide).
8
4Changes in version 0.6.69Changes in version 0.6.6
5========================10========================
6 - Added support for online accounts (via new OnlineAccountClient class).11 - Added support for online accounts (via new OnlineAccountClient class).
712
=== modified file 'debian/changelog'
--- debian/changelog 2014-10-06 12:03:20 +0000
+++ debian/changelog 2014-10-08 10:59:41 +0000
@@ -1,3 +1,10 @@
1unity-scopes-api (0.6.7-0ubuntu1) UNRELEASED; urgency=medium
2
3 * Added new RunInExternalUiMainLoop option to OnlineAccountClient::MainLoopSelect.
4 * OnlineAccountClient signon UI policy determined by main loop used (shell: show / scope: hide).
5
6 -- Marcus Tomlinson <marcus.tomlinson@canonical.com> Wed, 01 Oct 2014 15:46:30 +0200
7
1unity-scopes-api (0.6.6+14.10.20141006-0ubuntu1) utopic; urgency=medium8unity-scopes-api (0.6.6+14.10.20141006-0ubuntu1) utopic; urgency=medium
29
3 [ Pawel Stolowski ]10 [ Pawel Stolowski ]
411
=== modified file 'include/unity/scopes/OnlineAccountClient.h'
--- include/unity/scopes/OnlineAccountClient.h 2014-09-15 05:10:17 +0000
+++ include/unity/scopes/OnlineAccountClient.h 2014-10-08 10:59:41 +0000
@@ -55,7 +55,7 @@
55 */55 */
56 struct ServiceStatus56 struct ServiceStatus
57 {57 {
58 uint account_id; ///< A unique ID of the online account parenting this service.58 int account_id; ///< A unique ID of the online account parenting this service.
59 bool service_enabled; ///< True if this service is enabled.59 bool service_enabled; ///< True if this service is enabled.
60 bool service_authenticated; ///< True if this service is authenticated.60 bool service_authenticated; ///< True if this service is authenticated.
61 std::string client_id; ///< "ConsumerKey" / "ClientId" OAuth (1 / 2) parameter.61 std::string client_id; ///< "ConsumerKey" / "ClientId" OAuth (1 / 2) parameter.
@@ -68,16 +68,15 @@
68 /**68 /**
69 \brief Indicates whether an external main loop already exists, or one should be created internally.69 \brief Indicates whether an external main loop already exists, or one should be created internally.
7070
71 A running main loop is essential in order to receive service update callbacks. When in doubt, set71 A running main loop is essential in order to receive service updates from the online accounts
72 RunInExternalMainLoop and just use refresh_service_statuses() and get_service_statuses() to obtain72 backend. When in doubt, set to CreateInternalMainLoop.
73 service statuses.
74 */73 */
75 enum MainLoopSelect74 enum MainLoopSelect
76 {75 {
77 RunInExternalMainLoop, ///< An external main loop already exists or the service update76 RunInExternalMainLoop, ///< An external main loop already exists and is running.
78 /// callback is not required.77 CreateInternalMainLoop, ///< An external main loop does not exist.
79 CreateInternalMainLoop ///< An external main loop does not exist and the service update78 RunInExternalUiMainLoop, ///< An external UI main loop exists and is running (Intended for
80 /// callback is required.79 /// shell use only. A scope should not be running a UI main loop).
81 };80 };
8281
83 /**82 /**
@@ -91,7 +90,7 @@
91 OnlineAccountClient(std::string const& service_name,90 OnlineAccountClient(std::string const& service_name,
92 std::string const& service_type,91 std::string const& service_type,
93 std::string const& provider_name,92 std::string const& provider_name,
94 MainLoopSelect main_loop_select = RunInExternalMainLoop);93 MainLoopSelect main_loop_select = CreateInternalMainLoop);
9594
96 /// @cond95 /// @cond
97 ~OnlineAccountClient();96 ~OnlineAccountClient();
9897
=== modified file 'include/unity/scopes/internal/OnlineAccountClientImpl.h'
--- include/unity/scopes/internal/OnlineAccountClientImpl.h 2014-09-11 11:29:29 +0000
+++ include/unity/scopes/internal/OnlineAccountClientImpl.h 2014-10-08 10:59:41 +0000
@@ -19,6 +19,7 @@
19#ifndef UNITY_SCOPES_INTERNAL_ONLINEACCOUNTCLIENTIMPL_H19#ifndef UNITY_SCOPES_INTERNAL_ONLINEACCOUNTCLIENTIMPL_H
20#define UNITY_SCOPES_INTERNAL_ONLINEACCOUNTCLIENTIMPL_H20#define UNITY_SCOPES_INTERNAL_ONLINEACCOUNTCLIENTIMPL_H
2121
22#include <unity/scopes/internal/MiddlewareBase.h>
22#include <unity/scopes/OnlineAccountClient.h>23#include <unity/scopes/OnlineAccountClient.h>
2324
24#include <libaccounts-glib/accounts-glib.h>25#include <libaccounts-glib/accounts-glib.h>
@@ -78,42 +79,51 @@
78 OnlineAccountClient::PostLoginAction login_failed_action);79 OnlineAccountClient::PostLoginAction login_failed_action);
7980
80 // Methods used only by impl81 // Methods used only by impl
81 void flush_pending_sessions();82 void construct();
83 void tear_down();
84
85 void flush_pending_session(AgAccountId const& account_id, std::unique_lock<std::mutex>& lock);
8286
83 void main_loop_state_notify(bool is_running);87 void main_loop_state_notify(bool is_running);
8488
89 std::shared_ptr<AgManager> manager();
85 std::string service_name();90 std::string service_name();
86 void callback(OnlineAccountClient::ServiceStatus const& service_status);91 OnlineAccountClient::MainLoopSelect main_loop_select();
92
93 void callback(AccountInfo const* info, std::string const& error = "");
8794
88 bool has_account(AgAccountId const& account_id);95 bool has_account(AgAccountId const& account_id);
89 void add_account(AgAccountId const& account_id, std::shared_ptr<AccountInfo> account_info);96 void add_account(AgAccountId const& account_id, std::shared_ptr<AccountInfo> account_info);
90 void remove_account(AgAccountId const& account_id);97 void remove_account(AgAccountId const& account_id);
9198
92 bool inc_logins();
93 void dec_logins();
94
95private:99private:
96 std::string service_name_;100 std::string const service_name_;
97 std::string service_type_;101 std::string const service_type_;
98 std::string provider_name_;102 std::string const provider_name_;
103 OnlineAccountClient::MainLoopSelect const main_loop_select_;
104
105 std::mutex callback_mutex_;
99 OnlineAccountClient::ServiceUpdateCallback callback_;106 OnlineAccountClient::ServiceUpdateCallback callback_;
100 bool use_external_main_loop_;
101107
102 std::thread main_loop_thread_;
103 std::mutex callback_mutex_;
104 std::mutex mutex_;108 std::mutex mutex_;
105 std::condition_variable cond_;109 std::condition_variable cond_;
110 std::thread main_loop_thread_;
106 bool main_loop_is_running_;111 bool main_loop_is_running_;
107 bool client_stopping_;112 std::exception_ptr thread_exception_;
108 int logins_busy_;
109 gulong account_enabled_signal_id_;113 gulong account_enabled_signal_id_;
110 gulong account_deleted_signal_id_;114 gulong account_deleted_signal_id_;
111115
112 std::shared_ptr<GMainLoop> main_loop_;116 std::shared_ptr<GMainLoop> main_loop_;
117 std::shared_ptr<GMainContext> main_loop_context_;
113 std::shared_ptr<AgManager> manager_;118 std::shared_ptr<AgManager> manager_;
114 std::map<AgAccountId, std::shared_ptr<AccountInfo>> accounts_;119 std::map<AgAccountId, std::shared_ptr<AccountInfo>> accounts_;
115120
121 MiddlewareBase::SPtr mw_;
122 MWPublisher::UPtr auth_publisher_;
123 MWSubscriber::UPtr auth_subscriber_;
124
116 void main_loop_thread();125 void main_loop_thread();
126 void auth_callback(std::string const& details_json);
117};127};
118128
119} // namespace internal129} // namespace internal
120130
=== modified file 'src/scopes/internal/OnlineAccountClientImpl.cpp'
--- src/scopes/internal/OnlineAccountClientImpl.cpp 2014-09-15 07:06:51 +0000
+++ src/scopes/internal/OnlineAccountClientImpl.cpp 2014-10-08 10:59:41 +0000
@@ -17,7 +17,12 @@
17*/17*/
1818
19#include <unity/scopes/internal/OnlineAccountClientImpl.h>19#include <unity/scopes/internal/OnlineAccountClientImpl.h>
20#include <unity/util/ResourcePtr.h>20
21#include <unity/scopes/internal/JsonCppNode.h>
22#include <unity/scopes/internal/MiddlewareFactory.h>
23#include <unity/scopes/internal/RuntimeConfig.h>
24#include <unity/scopes/internal/RuntimeImpl.h>
25#include <unity/UnityExceptions.h>
2126
22#include <iostream>27#include <iostream>
2328
@@ -88,7 +93,41 @@
88 return service_status;93 return service_status;
89}94}
9095
91static void clear_session_info(AccountInfo* info)96static std::string details_to_json(OnlineAccountClient::ServiceStatus const& details)
97{
98 VariantMap vm;
99 vm["account_id"] = details.account_id;
100 vm["service_enabled"] = details.service_enabled;
101 vm["service_authenticated"] = details.service_authenticated;
102 vm["client_id"] = details.client_id;
103 vm["client_secret"] = details.client_secret;
104 vm["access_token"] = details.access_token;
105 vm["token_secret"] = details.token_secret;
106 vm["error"] = details.error;
107
108 Variant var(vm);
109 JsonCppNode node(var);
110 return node.to_json_string();
111}
112
113static OnlineAccountClient::ServiceStatus json_to_details(std::string const& json)
114{
115 OnlineAccountClient::ServiceStatus details;
116 JsonCppNode node(json);
117
118 details.account_id = node.get_node("account_id")->as_int();
119 details.service_enabled = node.get_node("service_enabled")->as_bool();
120 details.service_authenticated = node.get_node("service_authenticated")->as_bool();
121 details.client_id = node.get_node("client_id")->as_string();
122 details.client_secret = node.get_node("client_secret")->as_string();
123 details.access_token = node.get_node("access_token")->as_string();
124 details.token_secret = node.get_node("token_secret")->as_string();
125 details.error = node.get_node("error")->as_string();
126
127 return details;
128}
129
130static void clear_session(AccountInfo* info)
92{131{
93 if (info->session)132 if (info->session)
94 {133 {
@@ -96,8 +135,6 @@
96 signon_auth_session_cancel(info->session.get());135 signon_auth_session_cancel(info->session.get());
97 }136 }
98 info->session = nullptr;137 info->session = nullptr;
99 info->auth_params = nullptr;
100 info->session_data = nullptr;
101}138}
102139
103static gboolean main_loop_is_running_cb(void* user_data)140static gboolean main_loop_is_running_cb(void* user_data)
@@ -107,6 +144,13 @@
107 return G_SOURCE_REMOVE;144 return G_SOURCE_REMOVE;
108}145}
109146
147static gboolean main_loop_is_stopping_cb(void* user_data)
148{
149 OnlineAccountClientImpl* account_client = reinterpret_cast<OnlineAccountClientImpl*>(user_data);
150 account_client->main_loop_state_notify(false);
151 return G_SOURCE_REMOVE;
152}
153
110static gboolean wake_up_event_loop_cb(void* user_data)154static gboolean wake_up_event_loop_cb(void* user_data)
111{155{
112 GMainLoop* event_loop = reinterpret_cast<GMainLoop*>(user_data);156 GMainLoop* event_loop = reinterpret_cast<GMainLoop*>(user_data);
@@ -116,58 +160,75 @@
116160
117static void service_login_cb(GObject* source, GAsyncResult* result, void* user_data)161static void service_login_cb(GObject* source, GAsyncResult* result, void* user_data)
118{162{
119 GError* error = nullptr;
120 util::ResourcePtr<GError*, decltype(&g_error_free)> error_cleanup(error, free_error);
121
122 SignonAuthSession* session = reinterpret_cast<SignonAuthSession*>(source);163 SignonAuthSession* session = reinterpret_cast<SignonAuthSession*>(source);
123 AccountInfo* info = reinterpret_cast<AccountInfo*>(user_data);164 AccountInfo* info = reinterpret_cast<AccountInfo*>(user_data);
124165
125 // Get session data then send a notification with the login result166 // Get session data then send a notification with the login result
167 GError* error = nullptr;
126 info->session_data.reset(signon_auth_session_process_finish(session, result, &error), free_variant);168 info->session_data.reset(signon_auth_session_process_finish(session, result, &error), free_variant);
127 info->account_client->callback(info_to_details(info, error ? error->message : ""));169 std::shared_ptr<GError> error_cleanup(error, free_error);
128 info->account_client->dec_logins();170
171 info->account_client->callback(info, error ? error->message : "");
172
173 // Clear session info
174 clear_session(info);
129}175}
130176
131static void service_update_cb(AgAccountService* account_service, gboolean enabled, AccountInfo* info)177static void service_update_cb(AgAccountService* account_service, gboolean enabled, AccountInfo* info)
132{178{
133 GError* error = nullptr;
134 util::ResourcePtr<GError*, decltype(&g_error_free)> error_cleanup(error, free_error);
135
136 // Service state has updated, clear the old session data179 // Service state has updated, clear the old session data
137 info->service_enabled = enabled;180 info->service_enabled = enabled;
138 clear_session_info(info);181 clear_session(info);
139182
140 if (enabled)183 if (enabled)
141 {184 {
142 // Get authorization data then create a new authorization session185 // Get authorization data then create a new authorization session
143 std::shared_ptr<AgAuthData> auth_data(ag_account_service_get_auth_data(account_service), ag_auth_data_unref);186 std::shared_ptr<AgAuthData> auth_data(ag_account_service_get_auth_data(account_service), ag_auth_data_unref);
187
188 GError* error = nullptr;
144 info->session.reset(signon_auth_session_new(189 info->session.reset(signon_auth_session_new(
145 ag_auth_data_get_credentials_id(auth_data.get()), ag_auth_data_get_method(auth_data.get()), &error), g_object_unref);190 ag_auth_data_get_credentials_id(auth_data.get()), ag_auth_data_get_method(auth_data.get()), &error), g_object_unref);
191 std::shared_ptr<GError> error_cleanup(error, free_error);
192
146 if (error)193 if (error)
147 {194 {
148 // Send notification that the authorization session failed195 // Send notification that the authorization session failed
149 info->account_client->callback(info_to_details(info, error->message));196 info->account_client->callback(info, error->message);
150 return;197 return;
151 }198 }
152199
153 // Get authorization parameters then attempt to signon200 // Get authorization parameters then attempt to signon
201 GVariantBuilder builder;
202 g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
203
204 if (info->account_client->main_loop_select() == OnlineAccountClient::RunInExternalUiMainLoop)
205 {
206 g_variant_builder_add(&builder, "{sv}",
207 SIGNON_SESSION_DATA_UI_POLICY,
208 g_variant_new_int32(SIGNON_POLICY_DEFAULT));
209 }
210 else
211 {
212 g_variant_builder_add(&builder, "{sv}",
213 SIGNON_SESSION_DATA_UI_POLICY,
214 g_variant_new_int32(SIGNON_POLICY_NO_USER_INTERACTION));
215 }
216
154 info->auth_params.reset(217 info->auth_params.reset(
155 g_variant_ref_sink(ag_auth_data_get_login_parameters(auth_data.get(), nullptr)), free_variant);218 g_variant_ref_sink(ag_auth_data_get_login_parameters(auth_data.get(), g_variant_builder_end(&builder))), free_variant);
156219
157 if (info->account_client->inc_logins())220 // Start signon process
158 {221 signon_auth_session_process_async(info->session.get(),
159 signon_auth_session_process_async(info->session.get(),222 info->auth_params.get(),
160 info->auth_params.get(),223 ag_auth_data_get_mechanism(auth_data.get()),
161 ag_auth_data_get_mechanism(auth_data.get()),224 nullptr,
162 nullptr,225 service_login_cb,
163 service_login_cb,226 info);
164 info);
165 }
166 }227 }
167 else228 else
168 {229 {
169 // Send notification that account has been disabled230 // Send notification that account has been disabled
170 info->account_client->callback(info_to_details(info));231 info->account_client->callback(info);
171 }232 }
172}233}
173234
@@ -193,7 +254,7 @@
193 if (account_client->service_name() == ag_service_get_name(service))254 if (account_client->service_name() == ag_service_get_name(service))
194 {255 {
195 std::shared_ptr<AgAccountService> account_service(ag_account_service_new(account.get(), service), g_object_unref);256 std::shared_ptr<AgAccountService> account_service(ag_account_service_new(account.get(), service), g_object_unref);
196 std::shared_ptr<AccountInfo> info(new AccountInfo, [](AccountInfo* info){ info->account_client = nullptr; delete info; });257 std::shared_ptr<AccountInfo> info(new AccountInfo);
197 info->service_enabled = false;258 info->service_enabled = false;
198 info->account_client = account_client;259 info->account_client = account_client;
199 info->account_service.reset(reinterpret_cast<AgAccountService*>(g_object_ref(account_service.get())), g_object_unref);260 info->account_service.reset(reinterpret_cast<AgAccountService*>(g_object_ref(account_service.get())), g_object_unref);
@@ -226,51 +287,95 @@
226 : service_name_(service_name)287 : service_name_(service_name)
227 , service_type_(service_type)288 , service_type_(service_type)
228 , provider_name_(provider_name)289 , provider_name_(provider_name)
229 , use_external_main_loop_(main_loop_select == OnlineAccountClient::RunInExternalMainLoop)290 , main_loop_select_(main_loop_select)
230 , main_loop_is_running_(false)291 , main_loop_is_running_(main_loop_select != OnlineAccountClient::CreateInternalMainLoop)
231 , client_stopping_(false)
232 , logins_busy_(0)
233{292{
293 // Set up authentication pub/sub
294 std::string oa_id = provider_name + ":" + service_type + ":" + service_name;
295 RuntimeImpl::UPtr rt = RuntimeImpl::create(oa_id);
296 MiddlewareFactory mw_factory(rt.get());
297 RuntimeConfig rt_config("");
298
299 mw_ = mw_factory.create(oa_id, rt_config.default_middleware(), rt_config.default_middleware_configfile());
300 if (main_loop_select == OnlineAccountClient::RunInExternalUiMainLoop)
301 {
302 // If this client was created by the shell, set up as the publisher
303 auth_publisher_ = mw_->create_publisher(oa_id);
304 }
305 else
306 {
307 // If this client was created by a scope, set up as a subscriber
308 auth_subscriber_ = mw_->create_subscriber(oa_id);
309 auth_subscriber_->message_received().connect(std::bind(&OnlineAccountClientImpl::auth_callback, this, std::placeholders::_1));
310 }
311
234 // If we are responsible for the main loop312 // If we are responsible for the main loop
235 if (!use_external_main_loop_)313 if (main_loop_select_ == OnlineAccountClient::CreateInternalMainLoop)
236 {314 {
237 // Wait here until either the main loop begins running, or the thread exits315 // Wait here until either the main loop begins running, or the thread exits
238 std::unique_lock<std::mutex> lock(mutex_);316 {
239 main_loop_thread_ = std::thread(&OnlineAccountClientImpl::main_loop_thread, this);317 std::unique_lock<std::mutex> lock(mutex_);
240 cond_.wait(lock, [this] { return main_loop_is_running_; });318 main_loop_thread_ = std::thread(&OnlineAccountClientImpl::main_loop_thread, this);
241 }319 cond_.wait_for(lock, std::chrono::seconds(5), [this] { return main_loop_is_running_; });
242320 }
243 manager_.reset(ag_manager_new_for_service_type(service_type_.c_str()), g_object_unref);321 if (!main_loop_is_running_)
244322 {
245 // Watch for changes to accounts323 if (main_loop_)
246 account_enabled_signal_id_ = g_signal_connect(manager_.get(), "enabled-event", G_CALLBACK(account_enabled_cb), this);324 {
247 account_deleted_signal_id_ = g_signal_connect(manager_.get(), "account-deleted", G_CALLBACK(account_deleted_cb), this);325 // Quit the main loop, causing the thread to exit
248326 g_main_loop_quit(main_loop_.get());
249 // Now check initial state327 if (main_loop_thread_.joinable())
250 refresh_service_statuses();328 {
329 main_loop_thread_.join();
330 }
331 }
332 if (thread_exception_)
333 {
334 std::rethrow_exception(thread_exception_);
335 }
336 else
337 {
338 throw unity::ResourceException("OnlineAccountClientImpl(): main_loop_thread failed to start.");
339 }
340 }
341 }
342 else
343 {
344 construct();
345 }
251}346}
252347
253OnlineAccountClientImpl::~OnlineAccountClientImpl()348OnlineAccountClientImpl::~OnlineAccountClientImpl()
254{349{
255 // Disconnect all signal handlers350 {
256 g_signal_handler_disconnect(manager_.get(), account_enabled_signal_id_);351 std::lock_guard<std::mutex> lock(mutex_);
257 g_signal_handler_disconnect(manager_.get(), account_deleted_signal_id_);352 if (thread_exception_)
258353 {
259 for (auto info : accounts_)354 try
260 {355 {
261 g_signal_handler_disconnect(info.second->account_service.get(), info.second->service_update_signal_id_);356 std::rethrow_exception(thread_exception_);
262 }357 }
263358 catch (std::exception const& e)
264 // Wait until all currently running login sessions are done359 {
265 {360 std::cerr << "~OnlineAccountClientImpl(): main_loop_thread threw an exception: " << e.what() << std::endl;
266 std::unique_lock<std::mutex> lock(mutex_);361 }
267 client_stopping_ = true;362 catch (...)
268 }363 {
269 flush_pending_sessions();364 std::cerr << "~OnlineAccountClientImpl(): main_loop_thread threw an unknown exception" << std::endl;
270365 }
271 // If we are responsible for the main loop, quit it on destruction366 }
272 if (main_loop_)367 }
273 {368
369 // If we are responsible for the main loop
370 if (main_loop_select_ == OnlineAccountClient::CreateInternalMainLoop)
371 {
372 // Invoke tear_down() from within the main loop
373 {
374 std::unique_lock<std::mutex> lock(mutex_);
375 g_main_context_invoke(main_loop_context_.get(), main_loop_is_stopping_cb, this);
376 cond_.wait(lock, [this] { return !main_loop_is_running_; });
377 }
378
274 // Quit the main loop, causing the thread to exit379 // Quit the main loop, causing the thread to exit
275 g_main_loop_quit(main_loop_.get());380 g_main_loop_quit(main_loop_.get());
276 if (main_loop_thread_.joinable())381 if (main_loop_thread_.joinable())
@@ -278,16 +383,32 @@
278 main_loop_thread_.join();383 main_loop_thread_.join();
279 }384 }
280 }385 }
386 else
387 {
388 tear_down();
389 }
281}390}
282391
283void OnlineAccountClientImpl::set_service_update_callback(OnlineAccountClient::ServiceUpdateCallback callback)392void OnlineAccountClientImpl::set_service_update_callback(OnlineAccountClient::ServiceUpdateCallback callback)
284{393{
285 std::lock_guard<std::mutex> lock(callback_mutex_);394 std::lock_guard<std::mutex> lock(mutex_);
395 if (thread_exception_)
396 {
397 std::rethrow_exception(thread_exception_);
398 }
399
400 std::lock_guard<std::mutex> callback_lock(callback_mutex_);
286 callback_ = callback;401 callback_ = callback;
287}402}
288403
289void OnlineAccountClientImpl::refresh_service_statuses()404void OnlineAccountClientImpl::refresh_service_statuses()
290{405{
406 std::unique_lock<std::mutex> lock(mutex_);
407 if (thread_exception_)
408 {
409 std::rethrow_exception(thread_exception_);
410 }
411
291 std::shared_ptr<GList> enabled_accounts(ag_manager_list(manager_.get()), ag_manager_list_free);412 std::shared_ptr<GList> enabled_accounts(ag_manager_list(manager_.get()), ag_manager_list_free);
292 GList* it;413 GList* it;
293 for (it = enabled_accounts.get(); it; it = it->next)414 for (it = enabled_accounts.get(); it; it = it->next)
@@ -297,19 +418,25 @@
297 std::string provider_name = ag_account_get_provider_name(account.get());418 std::string provider_name = ag_account_get_provider_name(account.get());
298 if (provider_name == provider_name_)419 if (provider_name == provider_name_)
299 {420 {
421 lock.unlock();
300 account_enabled_cb(manager_.get(), account_id, this);422 account_enabled_cb(manager_.get(), account_id, this);
423 lock.lock();
301 }424 }
425 flush_pending_session(account_id, lock);
302 }426 }
303 flush_pending_sessions();
304}427}
305428
306std::vector<OnlineAccountClient::ServiceStatus> OnlineAccountClientImpl::get_service_statuses()429std::vector<OnlineAccountClient::ServiceStatus> OnlineAccountClientImpl::get_service_statuses()
307{430{
308 std::lock_guard<std::mutex> lock(mutex_);431 std::lock_guard<std::mutex> lock(mutex_);
432 if (thread_exception_)
433 {
434 std::rethrow_exception(thread_exception_);
435 }
309436
310 // Return all service statuses437 // Return all service statuses
311 std::vector<OnlineAccountClient::ServiceStatus> service_statuses;438 std::vector<OnlineAccountClient::ServiceStatus> service_statuses;
312 for (auto const info : accounts_)439 for (auto const& info : accounts_)
313 {440 {
314 service_statuses.push_back(info_to_details(info.second.get()));441 service_statuses.push_back(info_to_details(info.second.get()));
315 }442 }
@@ -352,42 +479,112 @@
352 widget.add_attribute_value("online_account_details", Variant(account_details_map));479 widget.add_attribute_value("online_account_details", Variant(account_details_map));
353}480}
354481
355void OnlineAccountClientImpl::flush_pending_sessions()482void OnlineAccountClientImpl::construct()
356{483{
484 manager_.reset(ag_manager_new_for_service_type(service_type_.c_str()), g_object_unref);
485
486 // Watch for changes to accounts
487 account_enabled_signal_id_ = g_signal_connect(manager_.get(), "enabled-event", G_CALLBACK(account_enabled_cb), this);
488 account_deleted_signal_id_ = g_signal_connect(manager_.get(), "account-deleted", G_CALLBACK(account_deleted_cb), this);
489
490 // Now check initial state
491 refresh_service_statuses();
492}
493
494void OnlineAccountClientImpl::tear_down()
495{
496 // Disconnect signal handlers
497 g_signal_handler_disconnect(manager_.get(), account_enabled_signal_id_);
498 g_signal_handler_disconnect(manager_.get(), account_deleted_signal_id_);
499
500 // Remove all accounts
501 {
502 std::unique_lock<std::mutex> lock(mutex_);
503 for (auto const& info : accounts_)
504 {
505 // Before we nuke the map, ensure that any pending sessions are done
506 g_signal_handler_disconnect(info.second->account_service.get(), info.second->service_update_signal_id_);
507 clear_session(info.second.get());
508 flush_pending_session(info.second->account_id, lock);
509 }
510 accounts_.clear();
511 }
512}
513
514void OnlineAccountClientImpl::flush_pending_session(AgAccountId const& account_id, std::unique_lock<std::mutex>& lock)
515{
516 // Get account info
517 auto info_it = accounts_.find(account_id);
518 if (info_it == accounts_.end())
519 {
520 return;
521 }
522 auto info = info_it->second;
523
357 // Wait until all currently running login sessions are done524 // Wait until all currently running login sessions are done
358 // (ensures that accounts_ is up to date)525 // (ensures that accounts_ is up to date)
359 std::unique_lock<std::mutex> lock(mutex_);526 std::shared_ptr<GMainLoop> event_loop;
527 event_loop.reset(g_main_loop_new(g_main_context_get_thread_default(), true), g_main_loop_unref);
528 while(info->session)
360 {529 {
361 std::shared_ptr<GMainLoop> event_loop;530 // We need to wait inside an event loop to allow for the main application loop to
362 event_loop.reset(g_main_loop_new(nullptr, true), g_main_loop_unref);531 // process its pending events
363 while(logins_busy_ > 0)532 std::shared_ptr<GSource> source;
364 {533 source.reset(g_timeout_source_new(10), g_source_unref);
365 // We need to wait inside an event loop to allow for the main application loop to534 g_source_set_callback(source.get(), wake_up_event_loop_cb, event_loop.get(), NULL);
366 // process its pending events535 g_source_attach(source.get(), g_main_context_get_thread_default());
367 g_timeout_add(100, wake_up_event_loop_cb, event_loop.get());536
368 lock.unlock();537 lock.unlock();
369 g_main_loop_run(event_loop.get());538 g_main_loop_run(event_loop.get());
370 lock.lock();539 lock.lock();
371 }
372 }540 }
373}541}
374542
375void OnlineAccountClientImpl::main_loop_state_notify(bool is_running)543void OnlineAccountClientImpl::main_loop_state_notify(bool is_running)
376{544{
545 bool was_running = false;
546 {
547 std::lock_guard<std::mutex> lock(mutex_);
548 was_running = main_loop_is_running_;
549 }
550 if (!was_running && is_running)
551 {
552 construct();
553 }
554 else if (was_running && !is_running)
555 {
556 tear_down();
557 }
558
377 std::lock_guard<std::mutex> lock(mutex_);559 std::lock_guard<std::mutex> lock(mutex_);
378 main_loop_is_running_ = is_running;560 main_loop_is_running_ = is_running;
379 cond_.notify_all();561 cond_.notify_all();
380}562}
381563
564std::shared_ptr<AgManager> OnlineAccountClientImpl::manager()
565{
566 return manager_;
567}
568
382std::string OnlineAccountClientImpl::service_name()569std::string OnlineAccountClientImpl::service_name()
383{570{
384 std::lock_guard<std::mutex> lock(mutex_);571 std::lock_guard<std::mutex> lock(mutex_);
385 return service_name_;572 return service_name_;
386}573}
387574
388void OnlineAccountClientImpl::callback(OnlineAccountClient::ServiceStatus const& service_status)575OnlineAccountClient::MainLoopSelect OnlineAccountClientImpl::main_loop_select()
576{
577 return main_loop_select_;
578}
579
580void OnlineAccountClientImpl::callback(AccountInfo const* info, std::string const& error)
389{581{
390 std::lock_guard<std::mutex> lock(callback_mutex_);582 std::lock_guard<std::mutex> lock(callback_mutex_);
583 auto service_status = info_to_details(info, error);
584 if (service_status.service_authenticated && auth_publisher_)
585 {
586 auth_publisher_->send_message(details_to_json(service_status));
587 }
391 if (callback_)588 if (callback_)
392 {589 {
393 callback_(service_status);590 callback_(service_status);
@@ -408,42 +605,80 @@
408605
409void OnlineAccountClientImpl::remove_account(AgAccountId const& account_id)606void OnlineAccountClientImpl::remove_account(AgAccountId const& account_id)
410{607{
411 std::lock_guard<std::mutex> lock(mutex_);608 std::unique_lock<std::mutex> lock(mutex_);
609
610 // Get account info
611 auto info_it = accounts_.find(account_id);
612 if (info_it == accounts_.end())
613 {
614 return;
615 }
616 auto info = info_it->second;
617
618 // Before we nuke the pointer, ensure that any pending sessions are done
619 g_signal_handler_disconnect(info->account_service.get(), info->service_update_signal_id_);
620 clear_session(info.get());
621 flush_pending_session(account_id, lock);
622
623 // Remove account info from accounts_ map
412 accounts_.erase(account_id);624 accounts_.erase(account_id);
413}625}
414626
415bool OnlineAccountClientImpl::inc_logins()
416{
417 std::lock_guard<std::mutex> lock(mutex_);
418 if (!client_stopping_)
419 {
420 // Increment number of logins busy
421 ++logins_busy_;
422 return true;
423 }
424 // Client is stopping so don't increment
425 return false;
426}
427
428void OnlineAccountClientImpl::dec_logins()
429{
430 // Decrement number of logins busy
431 std::lock_guard<std::mutex> lock(mutex_);
432 --logins_busy_;
433}
434
435void OnlineAccountClientImpl::main_loop_thread()627void OnlineAccountClientImpl::main_loop_thread()
436{628{
437 // If something goes wrong causing the main loop not to run, the destruction of this pointer629 // If something goes wrong causing the thread to abort, the destruction of this pointer update the
438 // will break the constructor from its wait630 // main_loop_is_running_ state accordingly.
439 std::shared_ptr<void> thread_exit_notifier(nullptr, [this](void*){ main_loop_state_notify(true); });631 std::shared_ptr<void> thread_exit_notifier(nullptr, [this](void*){ main_loop_state_notify(false); });
440632
441 // Stick a method call into the main loop to notify the constructor when the main loop begins running633 try
442 g_idle_add(main_loop_is_running_cb, this);634 {
443635 main_loop_context_.reset(g_main_context_new(), g_main_context_unref);
444 // Run the main loop636 g_main_context_push_thread_default(main_loop_context_.get());
445 main_loop_.reset(g_main_loop_new(nullptr, true), g_main_loop_unref);637
446 g_main_loop_run(main_loop_.get());638 // Stick a method call into the main loop to notify the constructor when the main loop begins running
639 g_main_context_invoke(main_loop_context_.get(), main_loop_is_running_cb, this);
640
641 // Run the main loop
642 main_loop_.reset(g_main_loop_new(main_loop_context_.get(), true), g_main_loop_unref);
643 g_main_loop_run(main_loop_.get());
644 }
645 catch (std::exception const& e)
646 {
647 std::cerr << "OnlineAccountClientImpl::main_loop_thread(): Thread aborted: " << e.what() << std::endl;
648 std::lock_guard<std::mutex> lock(mutex_);
649 thread_exception_ = std::current_exception();
650 }
651 catch (...)
652 {
653 std::cerr << "OnlineAccountClientImpl::main_loop_thread(): Thread aborted: unknown exception" << std::endl;
654 std::lock_guard<std::mutex> lock(mutex_);
655 thread_exception_ = std::current_exception();
656 }
657}
658
659void OnlineAccountClientImpl::auth_callback(std::string const& details_json)
660{
661 OnlineAccountClient::ServiceStatus details = json_to_details(details_json);
662
663 // Update account info
664 {
665 std::lock_guard<std::mutex> lock(mutex_);
666 auto info_it = accounts_.find(details.account_id);
667 if (info_it == accounts_.end())
668 {
669 return;
670 }
671 auto info = info_it->second;
672
673 GVariantDict dict;
674 g_variant_dict_init(&dict, nullptr);
675 g_variant_dict_insert(&dict, "AccessToken", "s", details.access_token.c_str());
676 g_variant_dict_insert(&dict, "TokenSecret", "s", details.token_secret.c_str());
677 info->session_data.reset(g_variant_ref_sink(g_variant_dict_end(&dict)), free_variant);
678 }
679
680 std::lock_guard<std::mutex> lock(callback_mutex_);
681 callback_(details);
447}682}
448683
449} // namespace internal684} // namespace internal
450685
=== modified file 'src/scopes/internal/RegistryObject.cpp'
--- src/scopes/internal/RegistryObject.cpp 2014-09-15 18:33:08 +0000
+++ src/scopes/internal/RegistryObject.cpp 2014-10-08 10:59:41 +0000
@@ -33,6 +33,7 @@
33#include <cassert>33#include <cassert>
34#include <fstream>34#include <fstream>
35#include <wordexp.h>35#include <wordexp.h>
36#include <iostream>
3637
37using namespace std;38using namespace std;
3839
@@ -385,7 +386,16 @@
385 desktop_file << "Type=Application" << std::endl;386 desktop_file << "Type=Application" << std::endl;
386 desktop_file << "NoDisplay=true" << std::endl;387 desktop_file << "NoDisplay=true" << std::endl;
387 desktop_file << "Name=" << metadata.display_name() << std::endl;388 desktop_file << "Name=" << metadata.display_name() << std::endl;
388 desktop_file << "Icon=" << metadata.icon() << std::endl;389 desktop_file << "Icon=";
390 try
391 {
392 desktop_file << metadata.icon();
393 }
394 catch (NotFoundException const&)
395 {
396 // Icon is optional.
397 }
398 desktop_file << endl;
389 desktop_file.close();399 desktop_file.close();
390}400}
391401
392402
=== modified file 'test/gtest/scopes/OnlineAccountClient/OnlineAccountClient_test.cpp'
--- test/gtest/scopes/OnlineAccountClient/OnlineAccountClient_test.cpp 2014-09-10 08:57:42 +0000
+++ test/gtest/scopes/OnlineAccountClient/OnlineAccountClient_test.cpp 2014-10-08 10:59:41 +0000
@@ -25,6 +25,8 @@
2525
26TEST(OnlineAccountClient, basic)26TEST(OnlineAccountClient, basic)
27{27{
28 setenv("XDG_RUNTIME_DIR", "/tmp", true);
29
28 OnlineAccountClient oa_client("com.ubuntu.scopes.youtube_youtube", "sharing", "google");30 OnlineAccountClient oa_client("com.ubuntu.scopes.youtube_youtube", "sharing", "google");
29 auto service_statuses = oa_client.get_service_statuses();31 auto service_statuses = oa_client.get_service_statuses();
30}32}

Subscribers

People subscribed via source and target branches

to all changes: