8e37d104153837b4ca826b455d6bf983d40c3a37
[apps/agl-service-windowmanager.git] / src / main.cpp
1 /*
2  * Copyright (c) 2017 TOYOTA MOTOR CORPORATION
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <unistd.h>
18 #include <algorithm>
19 #include <mutex>
20 #include <json.h>
21 #include "window_manager.hpp"
22 #include "json_helper.hpp"
23
24 extern "C"
25 {
26 #include <afb/afb-binding.h>
27 #include <systemd/sd-event.h>
28 }
29
30 typedef struct WMClientCtxt
31 {
32     std::string name;
33     std::string role;
34     WMClientCtxt(const char *appName, const char* appRole)
35     {
36         name = appName;
37         role = appRole;
38     }
39 } WMClientCtxt;
40
41 struct afb_instance
42 {
43     wm::WindowManager wmgr;
44
45     afb_instance() : wmgr() {}
46     ~afb_instance() = default;
47
48     int init();
49 };
50
51 struct afb_instance *g_afb_instance;
52 std::mutex binding_m;
53
54 int afb_instance::init()
55 {
56     return this->wmgr.init();
57 }
58
59 static int _binding_init()
60 {
61     HMI_NOTICE("WinMan ver. %s", WINMAN_VERSION_STRING);
62
63     g_afb_instance = new afb_instance;
64
65     if (g_afb_instance->init() == -1)
66     {
67         HMI_ERROR("Could not connect to compositor");
68         goto error;
69     }
70
71     atexit([] { delete g_afb_instance; });
72
73     return 0;
74
75 error:
76     delete g_afb_instance;
77     g_afb_instance = nullptr;
78     return -1;
79 }
80
81 static int binding_init (afb_api_t api) noexcept
82 {
83     try
84     {
85         return _binding_init();
86     }
87     catch (std::exception &e)
88     {
89         HMI_ERROR("Uncaught exception in binding_init(): %s", e.what());
90     }
91     return -1;
92 }
93
94 static void cbRemoveClientCtxt(void *data)
95 {
96     WMClientCtxt *ctxt = (WMClientCtxt *)data;
97     if (ctxt == nullptr)
98     {
99         return;
100     }
101     HMI_DEBUG("remove app %s", ctxt->name.c_str());
102
103     // Policy Manager does not know this app was killed,
104     // so notify it by deactivate request.
105     g_afb_instance->wmgr.api_deactivate_window(
106         ctxt->name.c_str(), ctxt->role.c_str(),
107         [](const char *) {});
108
109     g_afb_instance->wmgr.removeClient(ctxt->name);
110     delete ctxt;
111 }
112
113 static void createSecurityContext(afb_req_t req, const char* appid, const char* role)
114 {
115     WMClientCtxt *ctxt = (WMClientCtxt *)afb_req_context_get(req);
116     if (!ctxt)
117     {
118         // Create Security Context at first time
119         WMClientCtxt *ctxt = new WMClientCtxt(appid, role);
120         HMI_DEBUG("create session for %s", ctxt->name.c_str());
121         afb_req_session_set_LOA(req, 1);
122         afb_req_context_set(req, ctxt, cbRemoveClientCtxt);
123     }
124 }
125
126 void windowmanager_requestsurface(afb_req_t req) noexcept
127 {
128     std::lock_guard<std::mutex> guard(binding_m);
129     if (g_afb_instance == nullptr)
130     {
131         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
132         return;
133     }
134
135     try
136     {
137         const char *a_drawing_name = afb_req_value(req, "drawing_name");
138         if (!a_drawing_name)
139         {
140             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
141             return;
142         }
143
144         char *appid = afb_req_get_application_id(req);
145         if(appid)
146         {
147             auto ret = g_afb_instance->wmgr.api_request_surface(
148                 appid, a_drawing_name);
149             if (ret.is_err())
150             {
151                 afb_req_fail(req, "failed", ret.unwrap_err());
152             }
153             else
154             {
155                 createSecurityContext(req, appid, a_drawing_name);
156                 afb_req_success(req, json_object_new_int(ret.unwrap()), "success");
157             }
158             free(appid);
159         }
160         else
161         {
162             afb_req_fail(req, "failed", nullptr);
163         }
164     }
165     catch (std::exception &e)
166     {
167         afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurface: %s", e.what());
168         return;
169     }
170 }
171
172 void windowmanager_requestsurfacexdg(afb_req_t req) noexcept
173 {
174     std::lock_guard<std::mutex> guard(binding_m);
175     if (g_afb_instance == nullptr)
176     {
177         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
178         return;
179     }
180
181     try
182     {
183         json_object *jreq = afb_req_json(req);
184
185         json_object *j_drawing_name = nullptr;
186         if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name))
187         {
188             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
189             return;
190         }
191         char const *a_drawing_name = json_object_get_string(j_drawing_name);
192
193         json_object *j_ivi_id = nullptr;
194         if (!json_object_object_get_ex(jreq, "ivi_id", &j_ivi_id))
195         {
196             afb_req_fail(req, "failed", "Need char const* argument ivi_id");
197             return;
198         }
199         char const *a_ivi_id = json_object_get_string(j_ivi_id);
200         char *appid = afb_req_get_application_id(req);
201         if(appid)
202         {
203             auto ret = g_afb_instance->wmgr.api_request_surface(
204                 appid, a_drawing_name, a_ivi_id);
205
206             if (ret != nullptr)
207             {
208                 afb_req_fail(req, "failed", ret);
209             }
210             else
211             {
212                 createSecurityContext(req, appid, a_drawing_name);
213                 afb_req_success(req, NULL, "success");
214             }
215             free(appid);
216         }
217     }
218     catch (std::exception &e)
219     {
220         afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurfacexdg: %s", e.what());
221         return;
222     }
223 }
224
225 void windowmanager_activatewindow(afb_req_t req) noexcept
226 {
227     std::lock_guard<std::mutex> guard(binding_m);
228     if (g_afb_instance == nullptr)
229     {
230         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
231         return;
232     }
233
234     try
235     {
236         const char *a_drawing_name = afb_req_value(req, "drawing_name");
237         if (!a_drawing_name)
238         {
239             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
240             return;
241         }
242
243         const char *a_drawing_area = afb_req_value(req, "drawing_area");
244         if (!a_drawing_area)
245         {
246             afb_req_fail(req, "failed", "Need char const* argument drawing_area");
247             return;
248         }
249
250         char* appid = afb_req_get_application_id(req);
251         if(appid)
252         {
253             g_afb_instance->wmgr.api_activate_window(
254                 appid, a_drawing_name, a_drawing_area,
255                 [&req](const char *errmsg) {
256                     if (errmsg != nullptr)
257                     {
258                         HMI_ERROR(errmsg);
259                         afb_req_fail(req, "failed", errmsg);
260                         return;
261                     }
262                     afb_req_success(req, NULL, "success");
263                 });
264             free(appid);
265         }
266     }
267     catch (std::exception &e)
268     {
269         HMI_WARNING("failed: Uncaught exception while calling activatesurface: %s", e.what());
270         g_afb_instance->wmgr.exceptionProcessForTransition();
271         return;
272     }
273 }
274
275 void windowmanager_deactivatewindow(afb_req_t req) noexcept
276 {
277     std::lock_guard<std::mutex> guard(binding_m);
278     if (g_afb_instance == nullptr)
279     {
280         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
281         return;
282     }
283
284     try
285     {
286         const char *a_drawing_name = afb_req_value(req, "drawing_name");
287         if (!a_drawing_name)
288         {
289             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
290             return;
291         }
292
293         char* appid = afb_req_get_application_id(req);
294         if(appid)
295         {
296             g_afb_instance->wmgr.api_deactivate_window(
297                 appid, a_drawing_name,
298                 [&req](const char *errmsg) {
299                     if (errmsg != nullptr)
300                     {
301                         HMI_ERROR(errmsg);
302                         afb_req_fail(req, "failed", errmsg);
303                         return;
304                     }
305                     afb_req_success(req, NULL, "success");
306                 });
307             free(appid);
308         }
309     }
310     catch (std::exception &e)
311     {
312         HMI_WARNING("failed: Uncaught exception while calling deactivatesurface: %s", e.what());
313         g_afb_instance->wmgr.exceptionProcessForTransition();
314         return;
315     }
316 }
317
318 void windowmanager_enddraw(afb_req_t req) noexcept
319 {
320     std::lock_guard<std::mutex> guard(binding_m);
321     if (g_afb_instance == nullptr)
322     {
323         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
324         return;
325     }
326
327     try
328     {
329         const char *a_drawing_name = afb_req_value(req, "drawing_name");
330         if (!a_drawing_name)
331         {
332             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
333             return;
334         }
335         afb_req_success(req, NULL, "success");
336
337         char* appid = afb_req_get_application_id(req);
338         if(appid)
339         {
340             g_afb_instance->wmgr.api_enddraw(appid, a_drawing_name);
341             free(appid);
342         }
343     }
344     catch (std::exception &e)
345     {
346         HMI_WARNING("failed: Uncaught exception while calling enddraw: %s", e.what());
347         g_afb_instance->wmgr.exceptionProcessForTransition();
348         return;
349     }
350 }
351
352 void windowmanager_getdisplayinfo_thunk(afb_req_t req) noexcept
353 {
354     std::lock_guard<std::mutex> guard(binding_m);
355     if (g_afb_instance == nullptr)
356     {
357         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
358         return;
359     }
360
361     try
362     {
363         auto ret = g_afb_instance->wmgr.api_get_display_info();
364         if (ret.is_err())
365         {
366             afb_req_fail(req, "failed", ret.unwrap_err());
367             return;
368         }
369
370         afb_req_success(req, ret.unwrap(), "success");
371     }
372     catch (std::exception &e)
373     {
374         afb_req_fail_f(req, "failed", "Uncaught exception while calling getdisplayinfo: %s", e.what());
375         return;
376     }
377 }
378
379 void windowmanager_getareainfo_thunk(afb_req_t req) noexcept
380 {
381     std::lock_guard<std::mutex> guard(binding_m);
382     if (g_afb_instance == nullptr)
383     {
384         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
385         return;
386     }
387
388     try
389     {
390         json_object *jreq = afb_req_json(req);
391
392         json_object *j_drawing_name = nullptr;
393         if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name))
394         {
395             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
396             return;
397         }
398         char const *a_drawing_name = json_object_get_string(j_drawing_name);
399
400         auto ret = g_afb_instance->wmgr.api_get_area_info(a_drawing_name);
401         if (ret.is_err())
402         {
403             afb_req_fail(req, "failed", ret.unwrap_err());
404             return;
405         }
406
407         afb_req_success(req, ret.unwrap(), "success");
408     }
409     catch (std::exception &e)
410     {
411         afb_req_fail_f(req, "failed", "Uncaught exception while calling getareainfo: %s", e.what());
412         return;
413     }
414 }
415
416 void windowmanager_wm_subscribe(afb_req_t req) noexcept
417 {
418     std::lock_guard<std::mutex> guard(binding_m);
419     if (g_afb_instance == nullptr)
420     {
421         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
422         return;
423     }
424
425     try
426     {
427         json_object *jreq = afb_req_json(req);
428         json_object *j = nullptr;
429         if (!json_object_object_get_ex(jreq, "event", &j))
430         {
431             afb_req_fail(req, "failed", "Need char const* argument event");
432             return;
433         }
434         int event_id = json_object_get_int(j);
435         int ret = g_afb_instance->wmgr.api_subscribe(req, event_id);
436
437         if (ret)
438         {
439             afb_req_fail(req, "failed", "Error: afb_req_subscribe()");
440             return;
441         }
442         afb_req_success(req, NULL, "success");
443     }
444     catch (std::exception &e)
445     {
446         afb_req_fail_f(req, "failed", "Uncaught exception while calling wm_subscribe: %s", e.what());
447         return;
448     }
449 }
450
451 void windowmanager_ping(afb_req_t req) noexcept
452 {
453     std::lock_guard<std::mutex> guard(binding_m);
454
455     if (g_afb_instance == nullptr)
456     {
457         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
458         return;
459     }
460     else
461     {
462         afb_req_success(req, NULL, "success");
463     }
464 }
465
466 void windowmanager_debug_terminate(afb_req_t req) noexcept
467 {
468     std::lock_guard<std::mutex> guard(binding_m);
469     if (g_afb_instance == nullptr)
470     {
471         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
472         return;
473     }
474
475     try
476     {
477
478         if (getenv("WINMAN_DEBUG_TERMINATE") != nullptr)
479         {
480             raise(SIGKILL); // afb-daemon kills it's pgroup using TERM, which
481                             // doesn't play well with perf
482         }
483
484         afb_req_success(req, NULL, "success");
485     }
486     catch (std::exception &e)
487     {
488         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_terminate: %s", e.what());
489         return;
490     }
491 }
492
493 /* AGL Lifecycle Management API */
494 static void lcm_register_activity_observer (afb_req_t req)
495 {
496   std::lock_guard<std::mutex> guard(binding_m);
497   if (g_afb_instance == nullptr) {
498     afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
499     return;
500   }
501
502   // register new activity observer
503   // {
504   //      id: "app id of target to observe",
505   //    type: "type of lifecycle, APP, HMI or GUI to observe",
506   //  filter: { states which observer wants to know }
507   // }
508   g_afb_instance->wmgr.amgr.api_register_activity_observer(req);
509
510   afb_req_success(req, NULL, "success");
511 }
512
513 static void lcm_unregister_activity_observer (afb_req_t req)
514 {
515   std::lock_guard<std::mutex> guard(binding_m);
516   if (g_afb_instance == nullptr)
517   {
518     afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
519     return;
520   }
521
522   // unregister activity observer
523   // {
524   //    id: "uniq id of observer"
525   // }
526   g_afb_instance->wmgr.amgr.api_unregister_activity_observer(req);
527
528   afb_req_success(req, NULL, "success");
529 }
530
531 static void lcm_get_activity_status(afb_req_t req)
532 {
533   std::lock_guard<std::mutex> guard(binding_m);
534
535   if (g_afb_instance == nullptr) {
536     afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
537   } else {
538     json_object *object = afb_req_json(req);
539     json_object *j_target = nullptr;
540     if (!json_object_object_get_ex(object, "target", &j_target)) {
541       afb_req_fail(req, "failed", "Need const char* argument target");
542       return;
543     }
544
545     const char *target_id = json_object_get_string(j_target);
546
547     auto ret = g_afb_instance->wmgr.amgr.api_get_activity_status(target_id);
548
549     if (ret.is_err()) {
550       afb_req_fail(req, "failed", ret.unwrap_err());
551     } else {
552       afb_req_success(req, ret.unwrap(), "success");
553     }
554   }
555 }
556
557 const afb_verb_t windowmanager_verbs[] = {
558     { .verb = "requestSurface", .callback = windowmanager_requestsurface },
559     { .verb = "requestSurfaceXDG", .callback = windowmanager_requestsurfacexdg },
560     { .verb = "activateWindow", .callback = windowmanager_activatewindow },
561     { .verb = "deactivateWindow", .callback = windowmanager_deactivatewindow },
562     { .verb = "endDraw", .callback = windowmanager_enddraw },
563     { .verb = "getDisplayInfo", .callback = windowmanager_getdisplayinfo_thunk },
564     { .verb = "getAreaInfo", .callback = windowmanager_getareainfo_thunk },
565     { .verb = "wm_subscribe", .callback = windowmanager_wm_subscribe },
566     { .verb = "ping", .callback = windowmanager_ping },
567     { .verb = "debug_terminate", .callback = windowmanager_debug_terminate },
568     /* AGL Lifecycle Management API */
569     { .verb = "registerActivityObserver", .callback = lcm_register_activity_observer },
570     { .verb = "unregisterActivityObserver", .callback = lcm_unregister_activity_observer },
571     { .verb = "getActivityStatus", .callback = lcm_get_activity_status },
572     {} };
573
574 extern "C" const afb_binding_t afbBindingExport = {
575   .api = "windowmanager",
576   .specification = "windowmanager",
577   .info = "windowmanager",
578   .verbs = windowmanager_verbs,
579   .preinit = nullptr,
580   .init = binding_init,
581   .onevent = nullptr,
582   .userdata = nullptr,
583   .provide_class = nullptr,
584   .require_class = nullptr,
585   .require_api = nullptr,
586   .noconcurrency = 0
587 };