Merge lp:~mterry/ubuntu-app-launch/warn-on-xapp into lp:ubuntu-app-launch/16.04

Proposed by Ted Gould
Status: Rejected
Rejected by: dobey
Proposed branch: lp:~mterry/ubuntu-app-launch/warn-on-xapp
Merge into: lp:ubuntu-app-launch/16.04
Prerequisite: lp:~mterry/ubuntu-app-launch/fix-ftbfs
Diff against target: 1337 lines (+760/-123)
16 files modified
CMakeLists.txt (+4/-0)
data/com.canonical.UbuntuAppLaunch.xml (+4/-0)
debian/changelog (+8/-0)
debian/libubuntu-app-launch2.symbols (+3/-0)
helpers.c (+133/-22)
helpers.h (+7/-2)
libubuntu-app-launch/click-exec.c (+0/-13)
libubuntu-app-launch/desktop-exec.c (+0/-13)
libubuntu-app-launch/ubuntu-app-launch-trace.tp (+18/-24)
libubuntu-app-launch/ubuntu-app-launch.c (+124/-26)
libubuntu-app-launch/ubuntu-app-launch.h (+41/-2)
tests/CMakeLists.txt (+1/-1)
tests/exec-util-test.cc (+2/-1)
tests/helper-handshake-test.cc (+263/-13)
tests/libual-test.cc (+151/-6)
tools/ubuntu-app-watch.c (+1/-0)
To merge this branch: bzr merge lp:~mterry/ubuntu-app-launch/warn-on-xapp
Reviewer Review Type Date Requested Status
dobey (community) Needs Resubmitting
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+279323@code.launchpad.net

This proposal supersedes a proposal from 2015-11-24.

Commit message

Allow Unity to prevent an app from starting and give it as long as it wants to figure that out.

Description of the change

Allow Unity to prevent an app from starting and give it as long as it wants to figure that out.

This branch gives Unity the option to prevent an app from launching by blocking on its answer before actually starting the app and then acting on that answer.

Related branches:
 https://code.launchpad.net/~mterry/unity-api/warn-on-xapp/+merge/277922
 https://code.launchpad.net/~mterry/qtmir/warn-on-xapp/+merge/279172
 https://code.launchpad.net/~mterry/unity8/warn-on-xapp/+merge/277915

== The existing design ==

The current code goes through pains to avoid any knowledge of Unity (despite using the name Unity in several signals). It doesn't connect to it on the bus. And it doesn't restrict who can answer its "I am starting an app" signal.

I'm not sure why it does this, but it does. So the way it communicates is via broadcast signals on the bus. Which gets answered by "someone" (presumably Unity). If there are multiple signal observers on the bus, we continue launching the app as soon as we hear the first response.

It also waits 1s for an answer; if no answer is received, it just launches the app anyway. The signal is more of a courtesy to Unity than a requirement. So if Unity isn't running, exits before it can respond, or is taking too long, UAL just continues.

== New design ==

The new paradigm is that Unity is an approver of which apps can launch. The proximate reason for this MP is that we want to be able to stop legacy apps from launching when not in Desktop mode. But I could imagine other scenarios (parental controls on some apps, or admin privileges needed for some apps, etc). [1]

I also wanted to allow Unity to take as long as it wanted to decide. Because maybe it is taking user input (authenticating as parent maybe) or in the case of a legacy app on the phone, waiting for user to either cancel the app launch or dock the phone (in which case we'd like to continue the app launch).

If Unity is not running, I've assumed we don't want the app to launch (which is a behavior change).

== Changes ==

- So we want to change the API for observers of the "starting" signal to allow for providing an "approved or not" answer. And we want to make it easy to answer in an async manner. Unfortunately, the current API has no state (no objects on which the API acts) to match up callbacks to their answers. So rather than redo the API, I've just added a new API call that app-starting-observers can use to answer any app-starting message: ubuntu_app_launch_observer_finish_app_starting(). It just broadcasts the answer on the bus. If that new method is not called, the app will never finish launching. The only current user of this API should be unity8, for which I have a related branch to use this new API.

- We want to know if Unity is even running (if not, we should reject the app). So I've kept the immediate signal response UnityStartingSignal, so that UAL knows someone is listening and handling the request. If it doesn't hear that signal within 5s, we reject the app. (I think it would be nicer to call com.canonical.Unity.RequestAppStart or similar, which would mean that only Unity could handle the request, would immediately tell us if Unity is running, and would give us a nice error instead of silence on the bus if something is wrong. But I didn't want to change the design of UAL that much, so I've kept the signal dance.)

- As noted above, I've bumped the timeout from 1s to 5s. This is because now we reject by default if the timeout happens, so it's more important that we give unity8 time. And secondly, we're adding an async API, so increasing the potential blocking time shouldn't be as much of a burden.

- We only support listening to the first responder we hear on the bus. This would be easy to extend to support listening to all responders we hear and waiting for all of them to weigh in. But I didn't need that feature yet, so I didn't write it.

- Since we now wait as long as Unity wants, we have to be sensitive to Unity quitting on us. So once we hear UnityStartingSignal, we also listen for NameOwnerChanges on the bus.

- Eventually, Unity gets back to us with a UnityStartingApproved signal that tells us to continue or not.

- Consolidating the handshake logic out of click-exec.c and desktop-exec.c has the side effect of fixing a bug where task_setup failed but we already emitted a UnityStartingBroadcast. And we had no way to cancel the handshake. So Unity would think an app was starting even though it wasn't. Now all the handshaking happens right in a row, without the ability to fail due to desktop parsing or similar.

- I've added an async version of ubuntu_app_launch_start_application so that callers don't have to block on this whole process (even without this branch, there was blocking occurring, waiting up to 5s on signals -- this method should definitively have existed before anyway).

== Footnotes ==

[1] ubuntu_app_launch_start_application is only for unconfined apps, right? Specifically, Touch apps or scopes can't start something via upstart directly themselves, bypassing the above scheme? I *believe* they always have to go the URLDispatcher route. Otherwise, this paradigm wouldn't really work for parental controls.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
216. By Michael Terry

Fix tests to still use old smaller timeout for convenience

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
dobey (dobey) wrote :

If this is still relevant, it needs to be rebuilt/resubmitted against current trunk. Also, a bug describing the problem it's meant to solve would be nice.

review: Needs Resubmitting

Unmerged revisions

216. By Michael Terry

Fix tests to still use old smaller timeout for convenience

215. By Michael Terry

Bump timeout to 5s

214. By Michael Terry

Add async versions of start_application calls

213. By Michael Terry

fix tabs and revert some unnecessary wording changes

212. By Michael Terry

Wait for unity to get back to us

211. By Michael Terry

Allow server-side of handshake to be async by adding ubuntu_app_launch_observer_finish_app_starting

210. By Michael Terry

Add 'approved' bool parameter to UnityStartingSignal return signal, so that unity could prevent an app from opening

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2015-08-11 02:45:49 +0000
+++ CMakeLists.txt 2015-12-02 18:30:50 +0000
@@ -90,6 +90,10 @@
90add_library(helpers STATIC helpers.c helpers-shared.c libubuntu-app-launch/recoverable-problem.c)90add_library(helpers STATIC helpers.c helpers-shared.c libubuntu-app-launch/recoverable-problem.c)
91target_link_libraries(helpers ${GIO2_LIBRARIES} ${JSONGLIB_LIBRARIES} ${CLICK_LIBRARIES})91target_link_libraries(helpers ${GIO2_LIBRARIES} ${JSONGLIB_LIBRARIES} ${CLICK_LIBRARIES})
9292
93add_library(helpers-test STATIC helpers.c helpers-shared.c libubuntu-app-launch/recoverable-problem.c)
94set_target_properties(helpers-test PROPERTIES COMPILE_DEFINITIONS "UAL_TEST_MODE")
95target_link_libraries(helpers-test ${GIO2_LIBRARIES} ${JSONGLIB_LIBRARIES} ${CLICK_LIBRARIES})
96
93####################97####################
94# desktop-hook98# desktop-hook
95####################99####################
96100
=== modified file 'data/com.canonical.UbuntuAppLaunch.xml'
--- data/com.canonical.UbuntuAppLaunch.xml 2015-03-04 14:26:35 +0000
+++ data/com.canonical.UbuntuAppLaunch.xml 2015-12-02 18:30:50 +0000
@@ -16,6 +16,10 @@
16 <signal name="UnityStartingSignal">16 <signal name="UnityStartingSignal">
17 <arg type="s" name="appid" />17 <arg type="s" name="appid" />
18 </signal>18 </signal>
19 <signal name="UnityStartingApproved">
20 <arg type="s" name="appid" />
21 <arg type="b" name="approved" />
22 </signal>
19 <signal name="ApplicationFailed">23 <signal name="ApplicationFailed">
20 <arg type="s" name="appid" />24 <arg type="s" name="appid" />
21 <arg type="s" name="stage" />25 <arg type="s" name="stage" />
2226
=== modified file 'debian/changelog'
--- debian/changelog 2015-08-17 21:38:34 +0000
+++ debian/changelog 2015-12-02 18:30:50 +0000
@@ -1,3 +1,11 @@
1ubuntu-app-launch (0.6) xenial; urgency=medium
2
3 * Bump version because the expectations for callers of
4 ubuntu_app_launch_observer_add_app_starting has changed (to expect
5 a further call to ubuntu_app_launch_observer_finish_app_starting)
6
7 -- Michael Terry <mterry@ubuntu.com> Mon, 23 Nov 2015 16:25:15 -0500
8
1ubuntu-app-launch (0.5+15.10.20150817-0ubuntu1) wily; urgency=medium9ubuntu-app-launch (0.5+15.10.20150817-0ubuntu1) wily; urgency=medium
210
3 [ CI Train Bot ]11 [ CI Train Bot ]
412
=== modified file 'debian/libubuntu-app-launch2.symbols'
--- debian/libubuntu-app-launch2.symbols 2015-08-17 21:38:34 +0000
+++ debian/libubuntu-app-launch2.symbols 2015-12-02 18:30:50 +0000
@@ -27,10 +27,13 @@
27 ubuntu_app_launch_observer_delete_app_stop@Base 0.427 ubuntu_app_launch_observer_delete_app_stop@Base 0.4
28 ubuntu_app_launch_observer_delete_helper_started@Base 0.428 ubuntu_app_launch_observer_delete_helper_started@Base 0.4
29 ubuntu_app_launch_observer_delete_helper_stop@Base 0.429 ubuntu_app_launch_observer_delete_helper_stop@Base 0.4
30 ubuntu_app_launch_observer_finish_app_starting@Base 0.6
30 ubuntu_app_launch_pause_application@Base 0.4+14.10.20140915.331 ubuntu_app_launch_pause_application@Base 0.4+14.10.20140915.3
31 ubuntu_app_launch_pid_in_app_id@Base 0.432 ubuntu_app_launch_pid_in_app_id@Base 0.4
32 ubuntu_app_launch_resume_application@Base 0.4+14.10.20140915.333 ubuntu_app_launch_resume_application@Base 0.4+14.10.20140915.3
33 ubuntu_app_launch_start_application@Base 0.434 ubuntu_app_launch_start_application@Base 0.4
35 ubuntu_app_launch_start_application_async@Base 0.6
36 ubuntu_app_launch_start_application_async_test@Base 0.6
34 ubuntu_app_launch_start_application_test@Base 0.437 ubuntu_app_launch_start_application_test@Base 0.4
35 ubuntu_app_launch_start_helper@Base 0.438 ubuntu_app_launch_start_helper@Base 0.4
36 ubuntu_app_launch_start_multiple_helper@Base 0.439 ubuntu_app_launch_start_multiple_helper@Base 0.4
3740
=== modified file 'helpers.c'
--- helpers.c 2014-08-08 19:50:53 +0000
+++ helpers.c 2015-12-02 18:30:50 +0000
@@ -473,42 +473,146 @@
473 return;473 return;
474}474}
475475
476static void
477unity_signal_cb (GDBusConnection * con, const gchar * sender, const gchar * path, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
478{
479 GMainLoop * mainloop = (GMainLoop *)user_data;
480 g_main_loop_quit(mainloop);
481}
482
483struct _handshake_t {476struct _handshake_t {
484 GDBusConnection * con;477 GDBusConnection * con;
485 GMainLoop * mainloop;478 GMainLoop * mainloop;
486 guint signal_subscribe;479 guint signal_subscribe;
480 guint approved_subscribe;
481 guint name_subscribe;
487 guint timeout;482 guint timeout;
483 gboolean received;
484 gchar *receiver;
485 HandshakeCallback callback;
486 gpointer user_data;
487 GDestroyNotify user_data_free;
488};488};
489489
490static void
491handshake_free(handshake_t * handshake)
492{
493 if (handshake->timeout != 0) {
494 g_source_remove(handshake->timeout);
495 }
496 if (handshake->mainloop != NULL) {
497 g_main_loop_unref(handshake->mainloop);
498 }
499 if (handshake->signal_subscribe != 0) {
500 g_dbus_connection_signal_unsubscribe(handshake->con, handshake->signal_subscribe);
501 }
502 if (handshake->approved_subscribe != 0) {
503 g_dbus_connection_signal_unsubscribe(handshake->con, handshake->approved_subscribe);
504 }
505 if (handshake->name_subscribe != 0) {
506 g_dbus_connection_signal_unsubscribe(handshake->con, handshake->name_subscribe);
507 }
508 g_free(handshake->receiver);
509 g_object_unref(handshake->con);
510
511 if (handshake->user_data_free != NULL && handshake->user_data != NULL) {
512 handshake->user_data_free(handshake->user_data);
513 }
514
515 g_free(handshake);
516}
517
518static void
519handshake_stop(handshake_t * handshake, gboolean approved)
520{
521 if (approved) {
522 handshake->callback(handshake->user_data);
523 }
524
525 if (handshake->mainloop != NULL) {
526 g_main_loop_quit(handshake->mainloop);
527 }
528
529 handshake_free(handshake);
530}
531
532static void
533unity_name_cb (GDBusConnection * con, const gchar * sender, const gchar * path, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
534{
535 handshake_t * handshake = (handshake_t *)user_data;
536
537 if (g_variant_check_format_string(params, "(sss)", FALSE)) {
538 const gchar *new_name = NULL;
539 g_variant_get(params, "(ss&s)", NULL, NULL, &new_name);
540
541 if (new_name == NULL || new_name[0] == 0) {
542 // The process we were talking to exited! Let's stop waiting.
543 handshake_stop(handshake, FALSE);
544 }
545 }
546}
547
548static void
549unity_signal_cb (GDBusConnection * con, const gchar * sender, const gchar * path, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
550{
551 handshake_t * handshake = (handshake_t *)user_data;
552
553 if (!handshake->received) { // only allow the first responder
554 handshake->receiver = g_strdup(sender);
555 handshake->received = TRUE;
556
557 handshake->name_subscribe = g_dbus_connection_signal_subscribe(handshake->con,
558#ifdef UAL_TEST_MODE
559 NULL, /* accept from any sender, making it easier for tests to fake this signal */
560#else
561 "org.freedesktop.DBus", /* sender */
562#endif
563 "org.freedesktop.DBus", /* interface */
564 "NameOwnerChanged", /* signal */
565 "/org/freedesktop/DBus", /* path */
566 sender, /* arg0 */
567 G_DBUS_SIGNAL_FLAGS_NONE,
568 unity_name_cb, handshake,
569 NULL); /* user data destroy */
570 }
571}
572
573static void
574unity_approved_cb (GDBusConnection * con, const gchar * sender, const gchar * path, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
575{
576 handshake_t * handshake = (handshake_t *)user_data;
577
578 if (!handshake->received || g_strcmp0(sender, handshake->receiver) != 0) {
579 return;
580 }
581
582 gboolean approved = FALSE;
583 if (g_variant_check_format_string(params, "(sb)", FALSE)) {
584 g_variant_get(params, "(sb)", NULL, &approved);
585 }
586
587 handshake_stop(handshake, approved);
588}
589
490static gboolean590static gboolean
491unity_too_slow_cb (gpointer user_data)591unity_too_slow_cb (gpointer user_data)
492{592{
493 handshake_t * handshake = (handshake_t *)user_data;593 handshake_t * handshake = (handshake_t *)user_data;
494 g_main_loop_quit(handshake->mainloop);
495 handshake->timeout = 0;594 handshake->timeout = 0;
595 if (!handshake->received) {
596 handshake_stop(handshake, FALSE);
597 }
496 return G_SOURCE_REMOVE;598 return G_SOURCE_REMOVE;
497}599}
498600
499handshake_t *601handshake_t *
500starting_handshake_start (const gchar * app_id)602starting_handshake_start (const gchar * app_id, HandshakeCallback callback, gpointer user_data, GDestroyNotify user_data_free)
501{603{
502 GError * error = NULL;604 GError * error = NULL;
503 handshake_t * handshake = g_new0(handshake_t, 1);605 handshake_t * handshake = g_new0(handshake_t, 1);
504606
505 handshake->mainloop = g_main_loop_new(NULL, FALSE);607 handshake->callback = callback;
608 handshake->user_data = user_data;
609 handshake->user_data_free = user_data_free;
610
506 handshake->con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);611 handshake->con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
507
508 if (error != NULL) {612 if (error != NULL) {
509 g_critical("Unable to connect to session bus: %s", error->message);613 g_critical("Unable to connect to session bus: %s", error->message);
510 g_error_free(error);614 g_error_free(error);
511 g_free(handshake);615 handshake_free(handshake);
512 return NULL;616 return NULL;
513 }617 }
514618
@@ -520,7 +624,17 @@
520 "/", /* path */624 "/", /* path */
521 app_id, /* arg0 */625 app_id, /* arg0 */
522 G_DBUS_SIGNAL_FLAGS_NONE,626 G_DBUS_SIGNAL_FLAGS_NONE,
523 unity_signal_cb, handshake->mainloop,627 unity_signal_cb, handshake,
628 NULL); /* user data destroy */
629
630 handshake->approved_subscribe = g_dbus_connection_signal_subscribe(handshake->con,
631 NULL, /* sender */
632 "com.canonical.UbuntuAppLaunch", /* interface */
633 "UnityStartingApproved", /* signal */
634 "/", /* path */
635 app_id, /* arg0 */
636 G_DBUS_SIGNAL_FLAGS_NONE,
637 unity_approved_cb, handshake,
524 NULL); /* user data destroy */638 NULL); /* user data destroy */
525639
526 /* Send unfreeze to to Unity */640 /* Send unfreeze to to Unity */
@@ -532,8 +646,12 @@
532 g_variant_new("(s)", app_id),646 g_variant_new("(s)", app_id),
533 &error);647 &error);
534648
535 /* Really, Unity? */649 /* In case Unity is either not running or taking way too long, we early exit after 5s */
650#ifdef UAL_TEST_MODE
536 handshake->timeout = g_timeout_add_seconds(1, unity_too_slow_cb, handshake);651 handshake->timeout = g_timeout_add_seconds(1, unity_too_slow_cb, handshake);
652#else
653 handshake->timeout = g_timeout_add_seconds(5, unity_too_slow_cb, handshake);
654#endif
537655
538 return handshake;656 return handshake;
539}657}
@@ -544,15 +662,8 @@
544 if (handshake == NULL)662 if (handshake == NULL)
545 return;663 return;
546664
665 handshake->mainloop = g_main_loop_new(NULL, FALSE);
547 g_main_loop_run(handshake->mainloop);666 g_main_loop_run(handshake->mainloop);
548
549 if (handshake->timeout != 0)
550 g_source_remove(handshake->timeout);
551 g_main_loop_unref(handshake->mainloop);
552 g_dbus_connection_signal_unsubscribe(handshake->con, handshake->signal_subscribe);
553 g_object_unref(handshake->con);
554
555 g_free(handshake);
556}667}
557668
558EnvHandle *669EnvHandle *
559670
=== modified file 'helpers.h'
--- helpers.h 2015-07-15 02:32:54 +0000
+++ helpers.h 2015-12-02 18:30:50 +0000
@@ -21,6 +21,8 @@
2121
22typedef struct _EnvHandle EnvHandle;22typedef struct _EnvHandle EnvHandle;
2323
24typedef void (*HandshakeCallback) (gpointer user_data);
25
24gboolean app_id_to_triplet (const gchar * app_id,26gboolean app_id_to_triplet (const gchar * app_id,
25 gchar ** package,27 gchar ** package,
26 gchar ** application,28 gchar ** application,
@@ -45,8 +47,11 @@
45void env_handle_finish (EnvHandle * handle);47void env_handle_finish (EnvHandle * handle);
4648
47typedef struct _handshake_t handshake_t;49typedef struct _handshake_t handshake_t;
48handshake_t * starting_handshake_start (const gchar * app_id);50handshake_t * starting_handshake_start (const gchar * app_id,
49void starting_handshake_wait (handshake_t * handshake);51 HandshakeCallback callback,
52 gpointer user_data,
53 GDestroyNotify user_data_free);
54void starting_handshake_wait (handshake_t * handshake);
5055
51GDBusConnection * cgroup_manager_connection (void);56GDBusConnection * cgroup_manager_connection (void);
52void cgroup_manager_unref (GDBusConnection * cgroup_manager);57void cgroup_manager_unref (GDBusConnection * cgroup_manager);
5358
=== modified file 'libubuntu-app-launch/click-exec.c'
--- libubuntu-app-launch/click-exec.c 2014-09-17 14:11:59 +0000
+++ libubuntu-app-launch/click-exec.c 2015-12-02 18:30:50 +0000
@@ -51,13 +51,6 @@
5151
52 GError * error = NULL;52 GError * error = NULL;
5353
54 handshake_t * handshake = starting_handshake_start(app_id);
55 if (handshake == NULL) {
56 g_warning("Unable to setup starting handshake");
57 }
58
59 ual_tracepoint(click_starting_sent, app_id);
60
61 gchar * package = NULL;54 gchar * package = NULL;
62 /* 'Parse' the App ID */55 /* 'Parse' the App ID */
63 if (!app_id_to_triplet(app_id, &package, NULL, NULL)) {56 if (!app_id_to_triplet(app_id, &package, NULL, NULL)) {
@@ -160,11 +153,5 @@
160 g_key_file_unref(keyfile);153 g_key_file_unref(keyfile);
161 g_free(desktopfile);154 g_free(desktopfile);
162155
163 ual_tracepoint(handshake_wait, app_id);
164
165 starting_handshake_wait(handshake);
166
167 ual_tracepoint(handshake_complete, app_id);
168
169 return TRUE;156 return TRUE;
170}157}
171158
=== modified file 'libubuntu-app-launch/desktop-exec.c'
--- libubuntu-app-launch/desktop-exec.c 2015-08-12 02:52:05 +0000
+++ libubuntu-app-launch/desktop-exec.c 2015-12-02 18:30:50 +0000
@@ -126,13 +126,6 @@
126126
127 ual_tracepoint(desktop_start, app_id);127 ual_tracepoint(desktop_start, app_id);
128128
129 handshake_t * handshake = starting_handshake_start(app_id);
130 if (handshake == NULL) {
131 g_warning("Unable to setup starting handshake");
132 }
133
134 ual_tracepoint(desktop_starting_sent, app_id);
135
136 gchar * desktopfilename = NULL;129 gchar * desktopfilename = NULL;
137 GKeyFile * keyfile = NULL;130 GKeyFile * keyfile = NULL;
138 gchar * libertinecontainer = NULL;131 gchar * libertinecontainer = NULL;
@@ -208,11 +201,5 @@
208201
209 g_key_file_free(keyfile);202 g_key_file_free(keyfile);
210203
211 ual_tracepoint(handshake_wait, app_id);
212
213 starting_handshake_wait(handshake);
214
215 ual_tracepoint(handshake_complete, app_id);
216
217 return TRUE;204 return TRUE;
218}205}
219206
=== modified file 'libubuntu-app-launch/ubuntu-app-launch-trace.tp'
--- libubuntu-app-launch/ubuntu-app-launch-trace.tp 2015-07-15 01:42:22 +0000
+++ libubuntu-app-launch/ubuntu-app-launch-trace.tp 2015-12-02 18:30:50 +0000
@@ -44,6 +44,24 @@
44 ctf_string(appid, appid)44 ctf_string(appid, appid)
45 )45 )
46)46)
47TRACEPOINT_EVENT(ubuntu_app_launch, libual_handshake_start,
48 TP_ARGS(const char *, appid),
49 TP_FIELDS(
50 ctf_string(appid, appid)
51 )
52)
53TRACEPOINT_EVENT(ubuntu_app_launch, libual_handshake_wait,
54 TP_ARGS(const char *, appid),
55 TP_FIELDS(
56 ctf_string(appid, appid)
57 )
58)
59TRACEPOINT_EVENT(ubuntu_app_launch, libual_handshake_complete,
60 TP_ARGS(const char *, appid),
61 TP_FIELDS(
62 ctf_string(appid, appid)
63 )
64)
4765
48/*******************************66/*******************************
49 LibUAL observers67 LibUAL observers
@@ -211,12 +229,6 @@
211 ctf_string(appid, appid)229 ctf_string(appid, appid)
212 )230 )
213)231)
214TRACEPOINT_EVENT(ubuntu_app_launch, click_starting_sent,
215 TP_ARGS(const char *, appid),
216 TP_FIELDS(
217 ctf_string(appid, appid)
218 )
219)
220TRACEPOINT_EVENT(ubuntu_app_launch, click_found_pkgdir,232TRACEPOINT_EVENT(ubuntu_app_launch, click_found_pkgdir,
221 TP_ARGS(const char *, appid),233 TP_ARGS(const char *, appid),
222 TP_FIELDS(234 TP_FIELDS(
@@ -247,18 +259,6 @@
247 ctf_string(appid, appid)259 ctf_string(appid, appid)
248 )260 )
249)261)
250TRACEPOINT_EVENT(ubuntu_app_launch, handshake_wait,
251 TP_ARGS(const char *, appid),
252 TP_FIELDS(
253 ctf_string(appid, appid)
254 )
255)
256TRACEPOINT_EVENT(ubuntu_app_launch, handshake_complete,
257 TP_ARGS(const char *, appid),
258 TP_FIELDS(
259 ctf_string(appid, appid)
260 )
261)
262262
263/*******************************263/*******************************
264 Desktop Exec264 Desktop Exec
@@ -270,12 +270,6 @@
270 ctf_string(appid, appid)270 ctf_string(appid, appid)
271 )271 )
272)272)
273TRACEPOINT_EVENT(ubuntu_app_launch, desktop_starting_sent,
274 TP_ARGS(const char *, appid),
275 TP_FIELDS(
276 ctf_string(appid, appid)
277 )
278)
279TRACEPOINT_EVENT(ubuntu_app_launch, desktop_found,273TRACEPOINT_EVENT(ubuntu_app_launch, desktop_found,
280 TP_ARGS(const char *, appid),274 TP_ARGS(const char *, appid),
281 TP_FIELDS(275 TP_FIELDS(
282276
=== modified file 'libubuntu-app-launch/ubuntu-app-launch.c'
--- libubuntu-app-launch/ubuntu-app-launch.c 2015-12-02 18:30:50 +0000
+++ libubuntu-app-launch/ubuntu-app-launch.c 2015-12-02 18:30:50 +0000
@@ -71,6 +71,14 @@
71} app_start_t;71} app_start_t;
7272
73static void73static void
74app_start_data_free (app_start_t *data)
75{
76 g_free(data->appid);
77 g_free(data->uris);
78 g_free(data);
79}
80
81static void
74application_start_cb (GObject * obj, GAsyncResult * res, gpointer user_data)82application_start_cb (GObject * obj, GAsyncResult * res, gpointer user_data)
75{83{
76 app_start_t * data = (app_start_t *)user_data;84 app_start_t * data = (app_start_t *)user_data;
@@ -101,9 +109,7 @@
101 g_error_free(error);109 g_error_free(error);
102 }110 }
103111
104 g_free(data->appid);112 app_start_data_free(data);
105 g_free(data->uris);
106 g_free(data);
107}113}
108114
109/* Get the path of the job from Upstart, if we've got it already, we'll just115/* Get the path of the job from Upstart, if we've got it already, we'll just
@@ -220,8 +226,55 @@
220 }226 }
221}227}
222228
229typedef struct {
230 gchar * jobpath;
231 GVariant * params;
232 app_start_t * app_start_data;
233} handshake_data_t;
234
235static void
236handshake_data_free (handshake_data_t *data)
237{
238 g_free(data->jobpath);
239 g_variant_unref(data->params);
240 app_start_data_free(data->app_start_data);
241 g_free(data);
242}
243
244static void
245handshake_cb (gpointer user_data)
246{
247 handshake_data_t *data = (handshake_data_t *) user_data;
248
249 GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
250 g_return_if_fail(con != NULL);
251
252 /* Copy app start data, so it can be freed by application_start_cb */
253 app_start_t * app_data_copy = g_new0(app_start_t, 1);
254 app_data_copy->appid = g_strdup(data->app_start_data->appid);
255 app_data_copy->uris = g_strdup(data->app_start_data->uris);
256
257 /* Call the job start function */
258 g_dbus_connection_call(con,
259 DBUS_SERVICE_UPSTART,
260 data->jobpath,
261 DBUS_INTERFACE_UPSTART_JOB,
262 "Start",
263 data->params,
264 NULL,
265 G_DBUS_CALL_FLAGS_NONE,
266 -1,
267 NULL, /* cancelable */
268 application_start_cb,
269 app_data_copy);
270
271 ual_tracepoint(libual_start_message_sent, data->app_start_data->appid);
272
273 g_object_unref(con);
274}
275
223static gboolean276static gboolean
224start_application_core (const gchar * appid, const gchar * const * uris, gboolean test)277start_application_core (const gchar * appid, const gchar * const * uris, gboolean async, gboolean test)
225{278{
226 ual_tracepoint(libual_start, appid);279 ual_tracepoint(libual_start, appid);
227280
@@ -300,24 +353,29 @@
300 if (setup_complete) {353 if (setup_complete) {
301 g_variant_builder_close(&builder);354 g_variant_builder_close(&builder);
302 g_variant_builder_add_value(&builder, g_variant_new_boolean(TRUE));355 g_variant_builder_add_value(&builder, g_variant_new_boolean(TRUE));
303 356
304 /* Call the job start function */357 ual_tracepoint(libual_handshake_start, appid);
305 g_dbus_connection_call(con,358
306 DBUS_SERVICE_UPSTART,359 handshake_data_t *handshake_data = g_new0(handshake_data_t, 1);
307 jobpath,360 handshake_data->jobpath = g_strdup(jobpath);
308 DBUS_INTERFACE_UPSTART_JOB,361 handshake_data->params = g_variant_ref_sink(g_variant_builder_end(&builder));
309 "Start",362 handshake_data->app_start_data = app_start_data;
310 g_variant_builder_end(&builder),363
311 NULL,364 handshake_t * handshake = starting_handshake_start(appid, handshake_cb, handshake_data, (GDestroyNotify)handshake_data_free);
312 G_DBUS_CALL_FLAGS_NONE,365
313 -1,366 if (handshake == NULL) {
314 NULL, /* cancelable */367 g_warning("Unable to setup starting handshake");
315 application_start_cb,368 setup_complete = FALSE;
316 app_start_data);369 } else if (!async) {
317370 ual_tracepoint(libual_handshake_wait, appid);
318 ual_tracepoint(libual_start_message_sent, appid);371
372 starting_handshake_wait(handshake);
373 }
374
375 ual_tracepoint(libual_handshake_complete, appid);
319 } else {376 } else {
320 g_variant_builder_clear(&builder);377 g_variant_builder_clear(&builder);
378 app_start_data_free(app_start_data);
321 }379 }
322380
323 g_object_unref(con);381 g_object_unref(con);
@@ -328,13 +386,25 @@
328gboolean386gboolean
329ubuntu_app_launch_start_application (const gchar * appid, const gchar * const * uris)387ubuntu_app_launch_start_application (const gchar * appid, const gchar * const * uris)
330{388{
331 return start_application_core(appid, uris, FALSE);389 return start_application_core(appid, uris, FALSE, FALSE);
332}390}
333391
334gboolean392gboolean
335ubuntu_app_launch_start_application_test (const gchar * appid, const gchar * const * uris)393ubuntu_app_launch_start_application_test (const gchar * appid, const gchar * const * uris)
336{394{
337 return start_application_core(appid, uris, TRUE);395 return start_application_core(appid, uris, FALSE, TRUE);
396}
397
398void
399ubuntu_app_launch_start_application_async (const gchar * appid, const gchar * const * uris)
400{
401 start_application_core(appid, uris, TRUE, FALSE);
402}
403
404void
405ubuntu_app_launch_start_application_async_test (const gchar * appid, const gchar * const * uris)
406{
407 start_application_core(appid, uris, TRUE, TRUE);
338}408}
339409
340static void410static void
@@ -953,7 +1023,7 @@
953 observer_t * observer = (observer_t *)user_data;1023 observer_t * observer = (observer_t *)user_data;
954 const gchar * appid = NULL;1024 const gchar * appid = NULL;
9551025
956 if (observer->func != NULL) {1026 if (observer->func != NULL && g_variant_check_format_string(params, "(s)", FALSE)) {
957 g_variant_get(params, "(&s)", &appid);1027 g_variant_get(params, "(&s)", &appid);
958 observer->func(appid, observer->user_data);1028 observer->func(appid, observer->user_data);
959 }1029 }
@@ -1007,14 +1077,13 @@
1007 return add_session_generic(observer, user_data, "UnityResumeRequest", &resume_array, resume_signal_cb);1077 return add_session_generic(observer, user_data, "UnityResumeRequest", &resume_array, resume_signal_cb);
1008}1078}
10091079
1010/* Handle the starting signal when it occurs, call the observer, then send a signal back when we're done */1080/* Handle the starting signal when it occurs and call the observer,
1081 which will send a signal back when it's done via finish_app_starting */
1011static void1082static void
1012starting_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)1083starting_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
1013{1084{
1014 ual_tracepoint(observer_start, "starting");1085 ual_tracepoint(observer_start, "starting");
10151086
1016 generic_signal_cb(conn, sender, object, interface, signal, params, user_data);
1017
1018 GError * error = NULL;1087 GError * error = NULL;
1019 g_dbus_connection_emit_signal(conn,1088 g_dbus_connection_emit_signal(conn,
1020 sender, /* destination */1089 sender, /* destination */
@@ -1027,6 +1096,8 @@
1027 if (error != NULL) {1096 if (error != NULL) {
1028 g_warning("Unable to emit response signal: %s", error->message);1097 g_warning("Unable to emit response signal: %s", error->message);
1029 g_error_free(error);1098 g_error_free(error);
1099 } else {
1100 generic_signal_cb(conn, sender, object, interface, signal, params, user_data);
1030 }1101 }
10311102
1032 ual_tracepoint(observer_finish, "starting");1103 ual_tracepoint(observer_finish, "starting");
@@ -1038,6 +1109,33 @@
1038 return add_session_generic(observer, user_data, "UnityStartingBroadcast", &starting_array, starting_signal_cb);1109 return add_session_generic(observer, user_data, "UnityStartingBroadcast", &starting_array, starting_signal_cb);
1039}1110}
10401111
1112gboolean
1113ubuntu_app_launch_observer_finish_app_starting (const gchar *appid, gboolean approved)
1114{
1115 GDBusConnection * conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
1116 if (conn == NULL) {
1117 return FALSE;
1118 }
1119
1120 GError * error = NULL;
1121 g_dbus_connection_emit_signal(conn,
1122 NULL, /* destination */
1123 "/", /* path */
1124 "com.canonical.UbuntuAppLaunch", /* interface */
1125 "UnityStartingApproved", /* signal */
1126 g_variant_new("(sb)", appid, approved), /* params */
1127 &error);
1128 g_object_unref(conn);
1129
1130 if (error != NULL) {
1131 g_warning("Unable to emit response signal: %s", error->message);
1132 g_error_free(error);
1133 return FALSE;
1134 }
1135
1136 return TRUE;
1137}
1138
1041/* Handle the failed signal when it occurs, call the observer */1139/* Handle the failed signal when it occurs, call the observer */
1042static void1140static void
1043failed_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)1141failed_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
10441142
=== modified file 'libubuntu-app-launch/ubuntu-app-launch.h'
--- libubuntu-app-launch/ubuntu-app-launch.h 2015-08-10 14:19:05 +0000
+++ libubuntu-app-launch/ubuntu-app-launch.h 2015-12-02 18:30:50 +0000
@@ -97,6 +97,31 @@
97 const gchar * const * uris);97 const gchar * const * uris);
9898
99/**99/**
100 * ubuntu_app_launch_start_application_async:
101 * @appid: ID of the application to launch
102 * @uris: (allow-none) (array zero-terminated=1) (element-type utf8) (transfer none): A NULL terminated list of URIs to send to the application
103 *
104 * Asks upstart to launch an application, but does not block. To discover
105 * whether it successfully started or not, register an observer. For example,
106 * call ubuntu_app_launch_observer_add_app_started.
107 */
108void ubuntu_app_launch_start_application_async (const gchar * appid,
109 const gchar * const * uris);
110
111/**
112 * ubuntu_app_launch_start_application_async_test:
113 * @appid: ID of the application to launch
114 * @uris: (allow-none) (array zero-terminated=1) (element-type utf8) (transfer none): A NULL terminated list of URIs to send to the application
115 *
116 * Asks upstart to launch an application with environment variables set
117 * to enable testing, but does not block. To discover whether it successfully
118 * started or not, register an observer. For example, call
119 * ubuntu_app_launch_observer_add_app_started. Should only be used in testing.
120 */
121void ubuntu_app_launch_start_application_async_test (const gchar * appid,
122 const gchar * const * uris);
123
124/**
100 * ubuntu_app_launch_stop_application:125 * ubuntu_app_launch_stop_application:
101 * @appid: ID of the application to launch126 * @appid: ID of the application to launch
102 *127 *
@@ -163,14 +188,28 @@
163 * @user_data: (closure) (allow-none): Data to pass to the observer188 * @user_data: (closure) (allow-none): Data to pass to the observer
164 *189 *
165 * Sets up a callback to get called each time an application190 * Sets up a callback to get called each time an application
166 * is about to start. The application will not start until the191 * is about to start. The application will not start until
167 * function returns.192 * ubuntu_app_launch_observer_finish_app_starting is called after
193 * the callback is called.
168 *194 *
169 * Return value: Whether adding the observer was successful.195 * Return value: Whether adding the observer was successful.
170 */196 */
171gboolean ubuntu_app_launch_observer_add_app_starting (UbuntuAppLaunchAppObserver observer,197gboolean ubuntu_app_launch_observer_add_app_starting (UbuntuAppLaunchAppObserver observer,
172 gpointer user_data);198 gpointer user_data);
173/**199/**
200 * ubuntu_app_launch_observer_finish_app_starting:
201 * @appid: ID of the application being started
202 * @approved: Whether the app can continue starting
203 *
204 * Finishes handling an app that is starting up via the callback
205 * registered with ubuntu_app_launch_observer_add_app_starting.
206 * If the app is allowed to continue starting, pass TRUE.
207 *
208 * Return value: Whether finish app startup was successful.
209 */
210gboolean ubuntu_app_launch_observer_finish_app_starting (const gchar *appid,
211 gboolean approved);
212/**
174 * ubuntu_app_launch_observer_add_app_started:213 * ubuntu_app_launch_observer_add_app_started:
175 * @observer: (scope notified): Callback when an application started214 * @observer: (scope notified): Callback when an application started
176 * @user_data: (closure) (allow-none): Data to pass to the observer215 * @user_data: (closure) (allow-none): Data to pass to the observer
177216
=== modified file 'tests/CMakeLists.txt'
--- tests/CMakeLists.txt 2015-06-26 17:26:50 +0000
+++ tests/CMakeLists.txt 2015-12-02 18:30:50 +0000
@@ -24,7 +24,7 @@
24# Helper test24# Helper test
2525
26add_executable (helper-handshake-test helper-handshake-test.cc)26add_executable (helper-handshake-test helper-handshake-test.cc)
27target_link_libraries (helper-handshake-test helpers gtest ${GTEST_LIBS})27target_link_libraries (helper-handshake-test helpers-test gtest ${GTEST_LIBS})
2828
29add_test (helper-handshake-test helper-handshake-test)29add_test (helper-handshake-test helper-handshake-test)
3030
3131
=== modified file 'tests/exec-util-test.cc'
--- tests/exec-util-test.cc 2015-08-11 19:11:15 +0000
+++ tests/exec-util-test.cc 2015-12-02 18:30:50 +0000
@@ -34,7 +34,8 @@
3434
35 protected:35 protected:
36 static void starting_cb (const gchar * appid, gpointer user_data) {36 static void starting_cb (const gchar * appid, gpointer user_data) {
37 g_debug("I'm too sexy to callback");37 g_debug("Starting app %s", appid);
38 ASSERT_TRUE(ubuntu_app_launch_observer_finish_app_starting(appid, TRUE));
38 }39 }
3940
40 virtual void SetUp() {41 virtual void SetUp() {
4142
=== modified file 'tests/helper-handshake-test.cc'
--- tests/helper-handshake-test.cc 2014-04-30 16:18:29 +0000
+++ tests/helper-handshake-test.cc 2015-12-02 18:30:50 +0000
@@ -72,12 +72,20 @@
72 return static_cast<HelperHandshakeTest *>(user_data)->FilterFunc(conn, message, incomming);72 return static_cast<HelperHandshakeTest *>(user_data)->FilterFunc(conn, message, incomming);
73}73}
7474
75static void
76handshake_cb (gpointer user_data)
77{
78 bool * reached = static_cast<bool *>(user_data);
79 *reached = true;
80}
81
75TEST_F(HelperHandshakeTest, BaseHandshake)82TEST_F(HelperHandshakeTest, BaseHandshake)
76{83{
84 bool handshake_succeeded = false;
77 GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);85 GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
78 guint filter = g_dbus_connection_add_filter(con, filter_func, this, NULL);86 guint filter = g_dbus_connection_add_filter(con, filter_func, this, NULL);
7987
80 handshake_t * handshake = starting_handshake_start("fooapp");88 handshake_t * handshake = starting_handshake_start("fooapp", handshake_cb, &handshake_succeeded, NULL);
8189
82 g_main_loop_run(mainloop);90 g_main_loop_run(mainloop);
8391
@@ -91,8 +99,89 @@
91 g_variant_new("(s)", "fooapp"), /* params, the same */99 g_variant_new("(s)", "fooapp"), /* params, the same */
92 NULL);100 NULL);
93101
94 starting_handshake_wait(handshake);102 g_dbus_connection_emit_signal(con,
95103 g_dbus_connection_get_unique_name(con), /* destination */
104 "/", /* path */
105 "com.canonical.UbuntuAppLaunch", /* interface */
106 "UnityStartingApproved", /* signal */
107 g_variant_new("(sb)", "fooapp", TRUE), /* params */
108 NULL);
109
110 starting_handshake_wait(handshake);
111
112 ASSERT_TRUE(handshake_succeeded);
113 g_object_unref(con);
114
115 return;
116}
117
118TEST_F(HelperHandshakeTest, UnapprovedHandshake)
119{
120 bool handshake_succeeded = false;
121 GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
122 guint filter = g_dbus_connection_add_filter(con, filter_func, this, NULL);
123
124 handshake_t * handshake = starting_handshake_start("fooapp", handshake_cb, &handshake_succeeded, NULL);
125
126 g_main_loop_run(mainloop);
127
128 g_dbus_connection_remove_filter(con, filter);
129
130 g_dbus_connection_emit_signal(con,
131 g_dbus_connection_get_unique_name(con), /* destination */
132 "/", /* path */
133 "com.canonical.UbuntuAppLaunch", /* interface */
134 "UnityStartingSignal", /* signal */
135 g_variant_new("(s)", "fooapp"), /* params */
136 NULL);
137
138 g_dbus_connection_emit_signal(con,
139 g_dbus_connection_get_unique_name(con), /* destination */
140 "/", /* path */
141 "com.canonical.UbuntuAppLaunch", /* interface */
142 "UnityStartingApproved", /* signal */
143 g_variant_new("(sb)", "fooapp", FALSE), /* params */
144 NULL);
145
146 starting_handshake_wait(handshake);
147
148 ASSERT_FALSE(handshake_succeeded);
149 g_object_unref(con);
150
151 return;
152}
153
154TEST_F(HelperHandshakeTest, InvalidHandshake)
155{
156 bool handshake_succeeded = false;
157 GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
158 guint filter = g_dbus_connection_add_filter(con, filter_func, this, NULL);
159
160 handshake_t * handshake = starting_handshake_start("fooapp", handshake_cb, &handshake_succeeded, NULL);
161
162 g_main_loop_run(mainloop);
163
164 g_dbus_connection_remove_filter(con, filter);
165
166 g_dbus_connection_emit_signal(con,
167 g_dbus_connection_get_unique_name(con), /* destination */
168 "/", /* path */
169 "com.canonical.UbuntuAppLaunch", /* interface */
170 "UnityStartingSignal", /* signal */
171 g_variant_new("(s)", "fooapp"), /* params */
172 NULL);
173
174 g_dbus_connection_emit_signal(con,
175 g_dbus_connection_get_unique_name(con), /* destination */
176 "/", /* path */
177 "com.canonical.UbuntuAppLaunch", /* interface */
178 "UnityStartingApproved", /* signal */
179 g_variant_new("(ss)", "fooapp", "true"), /* bad params */
180 NULL);
181
182 starting_handshake_wait(handshake);
183
184 ASSERT_FALSE(handshake_succeeded);
96 g_object_unref(con);185 g_object_unref(con);
97186
98 return;187 return;
@@ -103,18 +192,179 @@
103{192{
104 bool * reached = static_cast<bool *>(user_data);193 bool * reached = static_cast<bool *>(user_data);
105 *reached = true;194 *reached = true;
195 return G_SOURCE_REMOVE;
106}196}
107197
108TEST_F(HelperHandshakeTest, HandshakeTimeout)198TEST_F(HelperHandshakeTest, HandshakeTimeout)
109{199{
110 bool timeout_reached = false;200 bool timeout_reached = false;
111 handshake_t * handshake = starting_handshake_start("fooapp");201 bool handshake_succeeded = false;
112202 handshake_t * handshake = starting_handshake_start("fooapp", handshake_cb, &handshake_succeeded, NULL);
113 guint outertimeout = g_timeout_add_seconds(2, two_second_reached, &timeout_reached);203
114204 guint outertimeout = g_timeout_add_seconds(2, two_second_reached, &timeout_reached);
115 starting_handshake_wait(handshake);205
116206 starting_handshake_wait(handshake);
117 ASSERT_FALSE(timeout_reached);207
118208 ASSERT_FALSE(timeout_reached);
119 return;209 ASSERT_FALSE(handshake_succeeded);
120}210 g_source_remove(outertimeout);
211
212 return;
213}
214
215TEST_F(HelperHandshakeTest, HandshakeTimeoutWithOnlyApprovedSignal)
216{
217 bool timeout_reached = false;
218 bool handshake_succeeded = false;
219 GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
220
221 guint outertimeout = g_timeout_add_seconds(2, two_second_reached, &timeout_reached);
222
223 guint filter = g_dbus_connection_add_filter(con, filter_func, this, NULL);
224 handshake_t * handshake = starting_handshake_start("fooapp", handshake_cb, &handshake_succeeded, NULL);
225 g_main_loop_run(mainloop);
226 g_dbus_connection_remove_filter(con, filter);
227
228 g_dbus_connection_emit_signal(con,
229 g_dbus_connection_get_unique_name(con), /* destination */
230 "/", /* path */
231 "com.canonical.UbuntuAppLaunch", /* interface */
232 "UnityStartingApproved", /* signal */
233 g_variant_new("(sb)", "fooapp", true), /* bad params */
234 NULL);
235
236 starting_handshake_wait(handshake);
237
238 ASSERT_FALSE(timeout_reached);
239 ASSERT_FALSE(handshake_succeeded);
240 g_source_remove(outertimeout);
241 g_object_unref(con);
242
243 return;
244}
245
246static gboolean
247emit_approval (gpointer user_data)
248{
249 GDBusConnection * con = (GDBusConnection *) user_data;
250
251 g_dbus_connection_emit_signal(con,
252 g_dbus_connection_get_unique_name(con), /* destination */
253 "/", /* path */
254 "com.canonical.UbuntuAppLaunch", /* interface */
255 "UnityStartingApproved", /* signal */
256 g_variant_new("(sb)", "fooapp", true), /* params */
257 NULL);
258
259 return G_SOURCE_REMOVE;
260}
261
262TEST_F(HelperHandshakeTest, HandshakeNoTimeoutWithReceivedSignal)
263{
264 bool timeout_reached = false;
265 bool handshake_succeeded = false;
266 GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
267
268 g_timeout_add_seconds(2, two_second_reached, &timeout_reached);
269 g_timeout_add_seconds(2, emit_approval, con);
270
271 guint filter = g_dbus_connection_add_filter(con, filter_func, this, NULL);
272 handshake_t * handshake = starting_handshake_start("fooapp", handshake_cb, &handshake_succeeded, NULL);
273 g_main_loop_run(mainloop);
274 g_dbus_connection_remove_filter(con, filter);
275
276 g_dbus_connection_emit_signal(con,
277 g_dbus_connection_get_unique_name(con), /* destination */
278 "/", /* path */
279 "com.canonical.UbuntuAppLaunch", /* interface */
280 "UnityStartingSignal", /* signal */
281 g_variant_new("(s)", "fooapp"), /* params */
282 NULL);
283
284 starting_handshake_wait(handshake);
285
286 ASSERT_TRUE(timeout_reached);
287 ASSERT_TRUE(handshake_succeeded);
288 g_object_unref(con);
289
290 return;
291}
292
293TEST_F(HelperHandshakeTest, AsyncHandshake)
294{
295 GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
296
297 g_timeout_add_seconds(2, emit_approval, con);
298
299 guint filter = g_dbus_connection_add_filter(con, filter_func, this, NULL);
300 handshake_t * handshake = starting_handshake_start("fooapp", (HandshakeCallback)g_main_loop_quit, mainloop, NULL);
301 g_main_loop_run(mainloop);
302 g_dbus_connection_remove_filter(con, filter);
303
304 g_dbus_connection_emit_signal(con,
305 g_dbus_connection_get_unique_name(con), /* destination */
306 "/", /* path */
307 "com.canonical.UbuntuAppLaunch", /* interface */
308 "UnityStartingSignal", /* signal */
309 g_variant_new("(s)", "fooapp"), /* params, the same */
310 NULL);
311
312 g_main_loop_run(mainloop);
313
314 g_object_unref(con);
315
316 return;
317}
318
319static gboolean
320emit_name_change (gpointer user_data)
321{
322 GDBusConnection * con = (GDBusConnection *) user_data;
323 const gchar *unique_name = g_dbus_connection_get_unique_name(con);
324
325 // We are allowed to emit this (instead of DBus) because we link against
326 // helpers-test, a special version of the helper code that listens
327 // for this signal from anyone.
328 g_dbus_connection_emit_signal(con,
329 unique_name, /* destination */
330 "/org/freedesktop/DBus", /* path */
331 "org.freedesktop.DBus", /* interface */
332 "NameOwnerChanged", /* signal */
333 g_variant_new("(sss)", unique_name, unique_name, ""), /* params */
334 NULL);
335
336 return G_SOURCE_REMOVE;
337}
338
339TEST_F(HelperHandshakeTest, StopsWaitingIfUnityExits)
340{
341 bool timeout_reached = false;
342 bool handshake_succeeded = false;
343 GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
344
345 g_timeout_add_seconds(1, emit_name_change, con);
346 guint outertimeout = g_timeout_add_seconds(2, two_second_reached, &timeout_reached);
347
348 guint filter = g_dbus_connection_add_filter(con, filter_func, this, NULL);
349 handshake_t * handshake = starting_handshake_start("fooapp", handshake_cb, &handshake_succeeded, NULL);
350 g_main_loop_run(mainloop);
351 g_dbus_connection_remove_filter(con, filter);
352
353 g_dbus_connection_emit_signal(con,
354 g_dbus_connection_get_unique_name(con), /* destination */
355 "/", /* path */
356 "com.canonical.UbuntuAppLaunch", /* interface */
357 "UnityStartingSignal", /* signal */
358 g_variant_new("(s)", "fooapp"), /* params */
359 NULL);
360
361 starting_handshake_wait(handshake);
362
363 ASSERT_FALSE(timeout_reached);
364 ASSERT_FALSE(handshake_succeeded);
365 g_source_remove(outertimeout);
366 g_object_unref(con);
367
368 return;
369}
370
121371
=== modified file 'tests/libual-test.cc'
--- tests/libual-test.cc 2015-08-12 02:45:40 +0000
+++ tests/libual-test.cc 2015-12-02 18:30:50 +0000
@@ -38,11 +38,19 @@
38 DbusTestDbusMock * mock = NULL;38 DbusTestDbusMock * mock = NULL;
39 DbusTestDbusMock * cgmock = NULL;39 DbusTestDbusMock * cgmock = NULL;
40 GDBusConnection * bus = NULL;40 GDBusConnection * bus = NULL;
41 std::string last_starting_appid;
41 std::string last_focus_appid;42 std::string last_focus_appid;
42 std::string last_resume_appid;43 std::string last_resume_appid;
43 guint resume_timeout = 0;44 guint resume_timeout = 0;
4445
45 private:46 private:
47 static void starting_cb (const gchar * appid, gpointer user_data) {
48 g_debug("Starting Callback: %s", appid);
49 LibUAL * _this = static_cast<LibUAL *>(user_data);
50 _this->last_starting_appid = appid;
51 ASSERT_TRUE(ubuntu_app_launch_observer_finish_app_starting(appid, TRUE));
52 }
53
46 static void focus_cb (const gchar * appid, gpointer user_data) {54 static void focus_cb (const gchar * appid, gpointer user_data) {
47 g_debug("Focus Callback: %s", appid);55 g_debug("Focus Callback: %s", appid);
48 LibUAL * _this = static_cast<LibUAL *>(user_data);56 LibUAL * _this = static_cast<LibUAL *>(user_data);
@@ -258,11 +266,13 @@
258 /* Make sure we pretend the CG manager is just on our bus */266 /* Make sure we pretend the CG manager is just on our bus */
259 g_setenv("UBUNTU_APP_LAUNCH_CG_MANAGER_SESSION_BUS", "YES", TRUE);267 g_setenv("UBUNTU_APP_LAUNCH_CG_MANAGER_SESSION_BUS", "YES", TRUE);
260268
269 ASSERT_TRUE(ubuntu_app_launch_observer_add_app_starting(starting_cb, this));
261 ASSERT_TRUE(ubuntu_app_launch_observer_add_app_focus(focus_cb, this));270 ASSERT_TRUE(ubuntu_app_launch_observer_add_app_focus(focus_cb, this));
262 ASSERT_TRUE(ubuntu_app_launch_observer_add_app_resume(resume_cb, this));271 ASSERT_TRUE(ubuntu_app_launch_observer_add_app_resume(resume_cb, this));
263 }272 }
264273
265 virtual void TearDown() {274 virtual void TearDown() {
275 ubuntu_app_launch_observer_delete_app_starting(starting_cb, this);
266 ubuntu_app_launch_observer_delete_app_focus(focus_cb, this);276 ubuntu_app_launch_observer_delete_app_focus(focus_cb, this);
267 ubuntu_app_launch_observer_delete_app_resume(resume_cb, this);277 ubuntu_app_launch_observer_delete_app_resume(resume_cb, this);
268278
@@ -426,6 +436,140 @@
426 g_variant_unref(env);436 g_variant_unref(env);
427}437}
428438
439typedef struct {
440 int count;
441 GMainLoop *loop;
442} async_data_t;
443
444static void
445async_test_app_focused_cb (const gchar * appid, gpointer user_data)
446{
447 async_data_t * data = (async_data_t *)user_data;
448 data->count++;
449 g_main_loop_quit(data->loop);
450}
451
452static gboolean
453two_second_reached (gpointer user_data)
454{
455 GMainLoop * loop = (GMainLoop *)user_data;
456 g_main_loop_quit(loop);
457 return G_SOURCE_REMOVE;
458}
459
460TEST_F(LibUAL, StartApplicationAsync)
461{
462 DbusTestDbusMockObject * obj = dbus_test_dbus_mock_get_object(mock, "/com/test/application_click", "com.ubuntu.Upstart0_6.Job", NULL);
463
464 async_data_t data = {
465 .count = 0,
466 .loop = g_main_loop_new(NULL, FALSE)
467 };
468
469 ASSERT_TRUE(ubuntu_app_launch_observer_add_app_focus(async_test_app_focused_cb, &data));
470
471 /* Basic make sure we can send the event */
472 guint outertimeout = g_timeout_add_seconds(2, two_second_reached, data.loop);
473 ubuntu_app_launch_start_application_async("com.test.good_application_1.2.3", NULL);
474 g_main_loop_run(data.loop);
475 g_source_remove(outertimeout);
476 ASSERT_EQ(1, data.count);
477
478 EXPECT_EQ(1, dbus_test_dbus_mock_object_check_method_call(mock, obj, "Start", NULL, NULL));
479 ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL));
480
481 /* Now look at the details of the call */
482 outertimeout = g_timeout_add_seconds(2, two_second_reached, data.loop);
483 ubuntu_app_launch_start_application_async("com.test.good_application_1.2.3", NULL);
484 g_main_loop_run(data.loop);
485 g_source_remove(outertimeout);
486 ASSERT_EQ(2, data.count);
487
488 guint len = 0;
489 const DbusTestDbusMockCall * calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL);
490 EXPECT_NE(nullptr, calls);
491 EXPECT_EQ(1, len);
492
493 EXPECT_STREQ("Start", calls->name);
494 EXPECT_EQ(2, g_variant_n_children(calls->params));
495
496 GVariant * block = g_variant_get_child_value(calls->params, 1);
497 EXPECT_TRUE(g_variant_get_boolean(block));
498 g_variant_unref(block);
499
500 GVariant * env = g_variant_get_child_value(calls->params, 0);
501 EXPECT_TRUE(check_env(env, "APP_ID", "com.test.good_application_1.2.3"));
502 g_variant_unref(env);
503
504 ASSERT_TRUE(dbus_test_dbus_mock_object_clear_method_calls(mock, obj, NULL));
505
506 /* Let's pass some URLs */
507 const gchar * urls[] = {
508 "http://ubuntu.com/",
509 "https://ubuntu.com/",
510 "file:///home/phablet/test.txt",
511 NULL
512 };
513 outertimeout = g_timeout_add_seconds(2, two_second_reached, data.loop);
514 ubuntu_app_launch_start_application("com.test.good_application_1.2.3", urls);
515 g_main_loop_run(data.loop);
516 g_source_remove(outertimeout);
517 ASSERT_EQ(3, data.count);
518
519 len = 0;
520 calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL);
521 EXPECT_NE(nullptr, calls);
522 EXPECT_EQ(1, len);
523
524 env = g_variant_get_child_value(calls->params, 0);
525 EXPECT_TRUE(check_env(env, "APP_ID", "com.test.good_application_1.2.3"));
526 EXPECT_TRUE(check_env(env, "APP_URIS", "'http://ubuntu.com/' 'https://ubuntu.com/' 'file:///home/phablet/test.txt'"));
527 g_variant_unref(env);
528
529 ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_focus(async_test_app_focused_cb, &data));
530 g_main_loop_unref(data.loop);
531
532 return;
533}
534
535TEST_F(LibUAL, StartApplicationAsyncTest)
536{
537 DbusTestDbusMockObject * obj = dbus_test_dbus_mock_get_object(mock, "/com/test/application_click", "com.ubuntu.Upstart0_6.Job", NULL);
538
539 async_data_t data = {
540 .count = 0,
541 .loop = g_main_loop_new(NULL, FALSE)
542 };
543
544 ASSERT_TRUE(ubuntu_app_launch_observer_add_app_focus(async_test_app_focused_cb, &data));
545
546 guint outertimeout = g_timeout_add_seconds(2, two_second_reached, data.loop);
547 ubuntu_app_launch_start_application_async_test("com.test.good_application_1.2.3", NULL);
548 g_main_loop_run(data.loop);
549 g_source_remove(outertimeout);
550 ASSERT_EQ(1, data.count);
551
552 guint len = 0;
553 const DbusTestDbusMockCall * calls = dbus_test_dbus_mock_object_get_method_calls(mock, obj, "Start", &len, NULL);
554 EXPECT_NE(nullptr, calls);
555 EXPECT_EQ(1, len);
556
557 EXPECT_STREQ("Start", calls->name);
558 EXPECT_EQ(2, g_variant_n_children(calls->params));
559
560 GVariant * block = g_variant_get_child_value(calls->params, 1);
561 EXPECT_TRUE(g_variant_get_boolean(block));
562 g_variant_unref(block);
563
564 GVariant * env = g_variant_get_child_value(calls->params, 0);
565 EXPECT_TRUE(check_env(env, "APP_ID", "com.test.good_application_1.2.3"));
566 EXPECT_TRUE(check_env(env, "QT_LOAD_TESTABILITY", "1"));
567 g_variant_unref(env);
568
569 ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_focus(async_test_app_focused_cb, &data));
570 g_main_loop_unref(data.loop);
571}
572
429TEST_F(LibUAL, StopApplication)573TEST_F(LibUAL, StopApplication)
430{574{
431 DbusTestDbusMockObject * obj = dbus_test_dbus_mock_get_object(mock, "/com/test/application_click", "com.ubuntu.Upstart0_6.Job", NULL);575 DbusTestDbusMockObject * obj = dbus_test_dbus_mock_get_object(mock, "/com/test/application_click", "com.ubuntu.Upstart0_6.Job", NULL);
@@ -748,7 +892,6 @@
748892
749TEST_F(LibUAL, StartingResponses)893TEST_F(LibUAL, StartingResponses)
750{894{
751 std::string last_observer;
752 unsigned int starting_count = 0;895 unsigned int starting_count = 0;
753 GDBusConnection * session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);896 GDBusConnection * session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
754 guint filter = g_dbus_connection_add_filter(session,897 guint filter = g_dbus_connection_add_filter(session,
@@ -756,8 +899,6 @@
756 &starting_count,899 &starting_count,
757 NULL);900 NULL);
758901
759 EXPECT_TRUE(ubuntu_app_launch_observer_add_app_starting(starting_observer, &last_observer));
760
761 g_dbus_connection_emit_signal(session,902 g_dbus_connection_emit_signal(session,
762 NULL, /* destination */903 NULL, /* destination */
763 "/", /* path */904 "/", /* path */
@@ -768,11 +909,9 @@
768909
769 pause(100);910 pause(100);
770911
771 EXPECT_EQ("com.test.good_application_1.2.3", last_observer);912 EXPECT_EQ("com.test.good_application_1.2.3", this->last_starting_appid);
772 EXPECT_EQ(1, starting_count);913 EXPECT_EQ(1, starting_count);
773914
774 EXPECT_TRUE(ubuntu_app_launch_observer_delete_app_starting(starting_observer, &last_observer));
775
776 g_dbus_connection_remove_filter(session, filter);915 g_dbus_connection_remove_filter(session, filter);
777 g_object_unref(session);916 g_object_unref(session);
778}917}
@@ -781,6 +920,7 @@
781{920{
782 ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.good_application_1.2.3", NULL));921 ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.good_application_1.2.3", NULL));
783 pause(50); /* Ensure all the events come through */922 pause(50); /* Ensure all the events come through */
923 EXPECT_EQ("com.test.good_application_1.2.3", this->last_starting_appid);
784 EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid);924 EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid);
785 EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid);925 EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid);
786}926}
@@ -817,6 +957,7 @@
817 ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.good_application_1.2.3", uris));957 ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.good_application_1.2.3", uris));
818 pause(100); /* Ensure all the events come through */958 pause(100); /* Ensure all the events come through */
819959
960 EXPECT_EQ("com.test.good_application_1.2.3", this->last_starting_appid);
820 EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid);961 EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid);
821 EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid);962 EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid);
822963
@@ -851,6 +992,7 @@
851 ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.good_application_1.2.3", uris));992 ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.good_application_1.2.3", uris));
852 pause(100); /* Ensure all the events come through */993 pause(100); /* Ensure all the events come through */
853994
995 EXPECT_EQ("com.test.good_application_1.2.3", this->last_starting_appid);
854 EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid);996 EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid);
855 EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid);997 EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid);
856}998}
@@ -861,6 +1003,7 @@
8611003
862 ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.good_application_1.2.3", NULL));1004 ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.good_application_1.2.3", NULL));
863 pause(1000); /* Ensure all the events come through */1005 pause(1000); /* Ensure all the events come through */
1006 EXPECT_EQ("com.test.good_application_1.2.3", this->last_starting_appid);
864 EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid);1007 EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid);
865 EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid);1008 EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid);
866}1009}
@@ -876,6 +1019,7 @@
8761019
877 ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.good_application_1.2.3", uris));1020 ASSERT_TRUE(ubuntu_app_launch_start_application("com.test.good_application_1.2.3", uris));
878 pause(1000); /* Ensure all the events come through */1021 pause(1000); /* Ensure all the events come through */
1022 EXPECT_EQ("com.test.good_application_1.2.3", this->last_starting_appid);
879 EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid);1023 EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid);
880 EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid);1024 EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid);
881}1025}
@@ -915,6 +1059,7 @@
9151059
916 pause(1000); /* Ensure all the events come through */1060 pause(1000); /* Ensure all the events come through */
9171061
1062 EXPECT_EQ("com.test.good_application_1.2.3", this->last_starting_appid);
918 EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid);1063 EXPECT_EQ("com.test.good_application_1.2.3", this->last_focus_appid);
919 EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid);1064 EXPECT_EQ("com.test.good_application_1.2.3", this->last_resume_appid);
9201065
9211066
=== modified file 'tools/ubuntu-app-watch.c'
--- tools/ubuntu-app-watch.c 2015-02-25 22:50:24 +0000
+++ tools/ubuntu-app-watch.c 2015-12-02 18:30:50 +0000
@@ -23,6 +23,7 @@
23starting (const gchar * appid, gpointer user_data)23starting (const gchar * appid, gpointer user_data)
24{24{
25 g_print("Starting %s\n", appid);25 g_print("Starting %s\n", appid);
26 ubuntu_app_launch_observer_finish_app_starting(appid, TRUE);
26 return;27 return;
27}28}
2829

Subscribers

People subscribed via source and target branches