On the way to adding attach service surface
[apps/agl-service-windowmanager-2017.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 <stdlib.h>
22 #include <vector>
23 #include "window_manager.hpp"
24 #include "json_helper.hpp"
25
26 extern "C"
27 {
28 #include <afb/afb-binding.h>
29 #include <systemd/sd-event.h>
30 }
31
32 typedef struct WMClientCtxt
33 {
34     std::string name;
35     std::string role;
36     WMClientCtxt(const char *appName, const char* appRole)
37     {
38         name = appName;
39         role = appRole;
40     }
41 } WMClientCtxt;
42
43 struct afb_instance
44 {
45     wm::WindowManager wmgr;
46
47     afb_instance() : wmgr() {}
48     ~afb_instance() = default;
49
50     int init();
51 };
52
53 struct afb_instance *g_afb_instance;
54 std::mutex binding_m;
55
56 int afb_instance::init()
57 {
58     return this->wmgr.init();
59 }
60
61 int _binding_init()
62 {
63     HMI_NOTICE("WinMan ver. %s", WINMAN_VERSION_STRING);
64
65     g_afb_instance = new afb_instance;
66
67     if (g_afb_instance->init() == -1)
68     {
69         HMI_ERROR("Could not connect to compositor");
70         goto error;
71     }
72
73     atexit([] { delete g_afb_instance; });
74
75     return 0;
76
77 error:
78     delete g_afb_instance;
79     g_afb_instance = nullptr;
80     return -1;
81 }
82
83 int binding_init() noexcept
84 {
85     try
86     {
87         return _binding_init();
88     }
89     catch (std::exception &e)
90     {
91         HMI_ERROR("Uncaught exception in binding_init(): %s", e.what());
92     }
93     return -1;
94 }
95
96 static void cbRemoveClientCtxt(void *data)
97 {
98     WMClientCtxt *ctxt = (WMClientCtxt *)data;
99     if (ctxt == nullptr)
100     {
101         return;
102     }
103     HMI_DEBUG("remove app %s", ctxt->name.c_str());
104
105     // Policy Manager does not know this app was killed,
106     // so notify it by deactivate request.
107     g_afb_instance->wmgr.api_deactivate_surface(
108         ctxt->name.c_str(), ctxt->role.c_str(),
109         [](const char *) {});
110
111     g_afb_instance->wmgr.removeClient(ctxt->name);
112     delete ctxt;
113 }
114
115 static void createSecurityContext(afb_req req, const char* appid, const char* role)
116 {
117     WMClientCtxt *ctxt = (WMClientCtxt *)afb_req_context_get(req);
118     if (!ctxt)
119     {
120         // Create Security Context at first time
121         const char *new_role = g_afb_instance->wmgr.convertRoleOldToNew(role);
122         WMClientCtxt *ctxt = new WMClientCtxt(appid, new_role);
123         HMI_DEBUG("create session for %s", ctxt->name.c_str());
124         afb_req_session_set_LOA(req, 1);
125         afb_req_context_set(req, ctxt, cbRemoveClientCtxt);
126     }
127 }
128
129 void windowmanager_requestsurface(afb_req req) noexcept
130 {
131     std::lock_guard<std::mutex> guard(binding_m);
132
133     if (g_afb_instance == nullptr)
134     {
135         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
136         return;
137     }
138
139     try
140     {
141         const char *a_drawing_name = afb_req_value(req, "drawing_name");
142         if (!a_drawing_name)
143         {
144             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
145             return;
146         }
147
148         char *appid = afb_req_get_application_id(req);
149         if(appid)
150         {
151             auto ret = g_afb_instance->wmgr.api_request_surface(
152                 appid, a_drawing_name);
153             if (ret.is_err())
154             {
155                 afb_req_fail(req, "failed", ret.unwrap_err());
156             }
157             else
158             {
159                 createSecurityContext(req, appid, a_drawing_name);
160                 afb_req_success(req, json_object_new_int(ret.unwrap()), "success");
161             }
162             free(appid);
163         }
164         else
165         {
166             afb_req_fail(req, "failed", nullptr);
167         }
168     }
169     catch (std::exception &e)
170     {
171         afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurface: %s", e.what());
172         return;
173     }
174 }
175
176 void windowmanager_requestsurfacexdg(afb_req req) noexcept
177 {
178     std::lock_guard<std::mutex> guard(binding_m);
179
180     if (g_afb_instance == nullptr)
181     {
182         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
183         return;
184     }
185
186     try
187     {
188         json_object *jreq = afb_req_json(req);
189
190         json_object *j_drawing_name = nullptr;
191         if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name))
192         {
193             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
194             return;
195         }
196         char const *a_drawing_name = json_object_get_string(j_drawing_name);
197
198         json_object *j_ivi_id = nullptr;
199         if (!json_object_object_get_ex(jreq, "ivi_id", &j_ivi_id))
200         {
201             afb_req_fail(req, "failed", "Need char const* argument ivi_id");
202             return;
203         }
204         char const *a_ivi_id = json_object_get_string(j_ivi_id);
205         char *appid = afb_req_get_application_id(req);
206         if(appid)
207         {
208             auto ret = g_afb_instance->wmgr.api_request_surface(
209                 appid, a_drawing_name, a_ivi_id);
210
211             if (ret != nullptr)
212             {
213                 afb_req_fail(req, "failed", ret);
214             }
215             else
216             {
217                 createSecurityContext(req, appid, a_drawing_name);
218                 afb_req_success(req, NULL, "success");
219             }
220             free(appid);
221         }
222     }
223     catch (std::exception &e)
224     {
225         afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurfacexdg: %s", e.what());
226         return;
227     }
228 }
229
230 void windowmanager_setrole(afb_req req) noexcept
231 {
232     std::lock_guard<std::mutex> guard(binding_m);
233     if (g_afb_instance == nullptr)
234     {
235         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
236         return;
237     }
238     try
239     {
240         json_object *jreq = afb_req_json(req);
241
242         json_object *j_role = nullptr;
243         if (!json_object_object_get_ex(jreq, "role", &j_role))
244         {
245             afb_req_fail(req, "failed", "Need char const* argument role");
246             return;
247         }
248         char const *a_role = json_object_get_string(j_role);
249         char *appid = afb_req_get_application_id(req);
250
251         if(appid)
252         {
253             auto ret = g_afb_instance->wmgr.api_set_role(appid, a_role);
254             if (!ret)
255             {
256                 afb_req_fail(req, "failed", "Couldn't register");
257             }
258             else
259             {
260                 createSecurityContext(req, appid, a_role);
261                 afb_req_success(req, NULL, "success");
262             }
263             free(appid);
264         }
265     }
266     catch (std::exception &e)
267     {
268         afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurfacexdg: %s", e.what());
269         return;
270     }
271 }
272
273 void windowmanager_activatewindow(afb_req req) noexcept
274 {
275     std::lock_guard<std::mutex> guard(binding_m);
276
277     if (g_afb_instance == nullptr)
278     {
279         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
280         return;
281     }
282
283     try
284     {
285         const char *a_drawing_name = afb_req_value(req, "drawing_name");
286         if (!a_drawing_name)
287         {
288             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
289             return;
290         }
291
292         const char *a_drawing_area = afb_req_value(req, "drawing_area");
293         if (!a_drawing_area)
294         {
295             afb_req_fail(req, "failed", "Need char const* argument drawing_area");
296             return;
297         }
298
299         char* appid = afb_req_get_application_id(req);
300         if(appid)
301         {
302             g_afb_instance->wmgr.api_activate_surface(
303                 appid, a_drawing_name, a_drawing_area,
304                 [&req](const char *errmsg) {
305                     if (errmsg != nullptr)
306                     {
307                         HMI_ERROR(errmsg);
308                         afb_req_fail(req, "failed", errmsg);
309                         return;
310                     }
311                     afb_req_success(req, NULL, "success");
312                 });
313             free(appid);
314         }
315     }
316     catch (std::exception &e)
317     {
318         HMI_WARNING("failed: Uncaught exception while calling activatesurface: %s", e.what());
319         g_afb_instance->wmgr.exceptionProcessForTransition();
320         return;
321     }
322 }
323
324 void windowmanager_deactivatewindow(afb_req req) noexcept
325 {
326     std::lock_guard<std::mutex> guard(binding_m);
327
328     if (g_afb_instance == nullptr)
329     {
330         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
331         return;
332     }
333
334     try
335     {
336         const char *a_drawing_name = afb_req_value(req, "drawing_name");
337         if (!a_drawing_name)
338         {
339             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
340             return;
341         }
342
343         char* appid = afb_req_get_application_id(req);
344         if(appid)
345         {
346             g_afb_instance->wmgr.api_deactivate_surface(
347                 appid, a_drawing_name,
348                 [&req](const char *errmsg) {
349                     if (errmsg != nullptr)
350                     {
351                         HMI_ERROR(errmsg);
352                         afb_req_fail(req, "failed", errmsg);
353                         return;
354                     }
355                     afb_req_success(req, NULL, "success");
356                 });
357             free(appid);
358         }
359     }
360     catch (std::exception &e)
361     {
362         HMI_WARNING("failed: Uncaught exception while calling deactivatesurface: %s", e.what());
363         g_afb_instance->wmgr.exceptionProcessForTransition();
364         return;
365     }
366 }
367
368 void windowmanager_enddraw(afb_req req) noexcept
369 {
370     std::lock_guard<std::mutex> guard(binding_m);
371
372     if (g_afb_instance == nullptr)
373     {
374         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
375         return;
376     }
377
378     try
379     {
380         const char *a_drawing_name = afb_req_value(req, "drawing_name");
381         if (!a_drawing_name)
382         {
383             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
384             return;
385         }
386         afb_req_success(req, NULL, "success");
387
388         char* appid = afb_req_get_application_id(req);
389         if(appid)
390         {
391             g_afb_instance->wmgr.api_enddraw(appid, a_drawing_name);
392             free(appid);
393         }
394     }
395     catch (std::exception &e)
396     {
397         HMI_WARNING("failed: Uncaught exception while calling enddraw: %s", e.what());
398         g_afb_instance->wmgr.exceptionProcessForTransition();
399         return;
400     }
401 }
402
403 void windowmanager_getdisplayinfo_thunk(afb_req req) noexcept
404 {
405     std::lock_guard<std::mutex> guard(binding_m);
406
407     if (g_afb_instance == nullptr)
408     {
409         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
410         return;
411     }
412
413     try
414     {
415         auto ret = g_afb_instance->wmgr.api_get_display_info();
416         if (ret.is_err())
417         {
418             afb_req_fail(req, "failed", ret.unwrap_err());
419             return;
420         }
421
422         afb_req_success(req, ret.unwrap(), "success");
423     }
424     catch (std::exception &e)
425     {
426         afb_req_fail_f(req, "failed", "Uncaught exception while calling getdisplayinfo: %s", e.what());
427         return;
428     }
429 }
430
431 void windowmanager_getareainfo_thunk(afb_req req) noexcept
432 {
433     std::lock_guard<std::mutex> guard(binding_m);
434
435     if (g_afb_instance == nullptr)
436     {
437         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
438         return;
439     }
440
441     try
442     {
443         json_object *jreq = afb_req_json(req);
444
445         json_object *j_drawing_name = nullptr;
446         if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name))
447         {
448             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
449             return;
450         }
451         char const *a_drawing_name = json_object_get_string(j_drawing_name);
452
453         auto ret = g_afb_instance->wmgr.api_get_area_info(a_drawing_name);
454         if (ret.is_err())
455         {
456             afb_req_fail(req, "failed", ret.unwrap_err());
457             return;
458         }
459
460         afb_req_success(req, ret.unwrap(), "success");
461     }
462     catch (std::exception &e)
463     {
464         afb_req_fail_f(req, "failed", "Uncaught exception while calling getareainfo: %s", e.what());
465         return;
466     }
467 }
468
469 void windowmanager_set_render_order(afb_req req) noexcept
470 {
471     std::lock_guard<std::mutex> guard(binding_m);
472     if (g_afb_instance == nullptr)
473     {
474         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
475         return;
476     }
477
478     char* appid = afb_req_get_application_id(req);
479     if(appid)
480     {
481         json_object *jreq = afb_req_json(req);
482         json_object *j_ro; // Do not free this. binder frees jreq, then free j_ro
483         if (json_object_object_get_ex(jreq, "render_order", &j_ro))
484         {
485             int size = json_object_array_length(j_ro);
486             std::vector<std::string> ro(size);
487             for(int i = 0; i < size; i++)
488             {
489                 ro[i] = json_object_get_string(json_object_array_get_idx(j_ro, i));
490             }
491
492             auto ret = g_afb_instance->wmgr.api_client_set_render_order(appid, ro);
493             if (!ret)
494             {
495                 afb_req_fail(req, "failed", nullptr);
496             }
497             else
498             {
499                 afb_req_success(req, nullptr, nullptr);
500             }
501         }
502         free(appid);
503     }
504     else
505     {
506         afb_req_fail(req, "failed", nullptr);
507     }
508 }
509
510 void windowmanager_attach_app(afb_req req) noexcept
511 {
512     std::lock_guard<std::mutex> guard(binding_m);
513     if (g_afb_instance == nullptr)
514     {
515         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
516         return;
517     }
518
519     char* appid = afb_req_get_application_id(req);
520     if(appid)
521     {
522         json_object *jreq = afb_req_json(req);
523         json_object *j_dest, *j_id; // Do not free this. binder frees jreq, then free j_ro
524         if (json_object_object_get_ex(jreq, "destination", &j_dest) &&
525             json_object_object_get_ex(jreq, "service_surface", &j_id))
526         {
527             const char* dest_app = json_object_get_string(j_dest);
528             const char* service = json_object_get_string(j_id);
529
530             std::string uuid = g_afb_instance->wmgr.api_client_attach_service_surface(appid, dest_app, service);
531             if (uuid.empty())
532             {
533                 afb_req_fail(req, "failed", nullptr);
534             }
535             else
536             {
537                 json_object *resp = json_object_new_object();
538                 json_object_object_add(resp, "uuid", json_object_new_string(uuid.c_str()));
539                 afb_req_success(req, resp, nullptr);
540             }
541         }
542         free(appid);
543     }
544     else
545     {
546         afb_req_fail(req, "failed", nullptr);
547     }
548 }
549
550 void windowmanager_wm_subscribe(afb_req req) noexcept
551 {
552     std::lock_guard<std::mutex> guard(binding_m);
553
554     if (g_afb_instance == nullptr)
555     {
556         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
557         return;
558     }
559
560     try
561     {
562         json_object *jreq = afb_req_json(req);
563         json_object *j = nullptr;
564         if (!json_object_object_get_ex(jreq, "event", &j))
565         {
566             afb_req_fail(req, "failed", "Need char const* argument event");
567             return;
568         }
569         int event_type = json_object_get_int(j);
570         const char *event_name = g_afb_instance->wmgr.kListEventName[event_type];
571         struct afb_event event = g_afb_instance->wmgr.map_afb_event[event_name];
572         int ret = afb_req_subscribe(req, event);
573         if (ret)
574         {
575             afb_req_fail(req, "failed", "Error: afb_req_subscribe()");
576             return;
577         }
578         afb_req_success(req, NULL, "success");
579     }
580     catch (std::exception &e)
581     {
582         afb_req_fail_f(req, "failed", "Uncaught exception while calling wm_subscribe: %s", e.what());
583         return;
584     }
585 }
586
587 void windowmanager_list_drawing_names(afb_req req) noexcept
588 {
589     std::lock_guard<std::mutex> guard(binding_m);
590
591     /* if (g_afb_instance == nullptr)
592     {
593         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
594         return;
595     }
596
597     try
598     {
599
600         nlohmann::json j = g_afb_instance->wmgr.id_alloc.name2id;
601         auto ret = wm::Ok(json_tokener_parse(j.dump().c_str()));
602         if (ret.is_err())
603         {
604             afb_req_fail(req, "failed", ret.unwrap_err());
605             return;
606         }
607
608         afb_req_success(req, ret.unwrap(), "success");
609     }
610     catch (std::exception &e)
611     {
612         afb_req_fail_f(req, "failed", "Uncaught exception while calling list_drawing_names: %s", e.what());
613         return;
614     } */
615 }
616
617 void windowmanager_ping(afb_req req) noexcept
618 {
619     std::lock_guard<std::mutex> guard(binding_m);
620
621     if (g_afb_instance == nullptr)
622     {
623         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
624         return;
625     }
626     else
627     {
628         afb_req_success(req, NULL, "success");
629     }
630 }
631
632 void windowmanager_debug_status(afb_req req) noexcept
633 {
634     std::lock_guard<std::mutex> guard(binding_m);
635
636     /* if (g_afb_instance == nullptr)
637     {
638         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
639         return;
640     }
641
642     try
643     {
644
645         json_object *jr = json_object_new_object();
646         json_object_object_add(jr, "surfaces",
647                                to_json(g_afb_instance->wmgr.controller->sprops));
648         json_object_object_add(jr, "layers", to_json(g_afb_instance->wmgr.controller->lprops));
649
650         afb_req_success(req, jr, "success");
651     }
652     catch (std::exception &e)
653     {
654         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_status: %s", e.what());
655         return;
656     } */
657 }
658
659 void windowmanager_debug_layers(afb_req req) noexcept
660 {
661     std::lock_guard<std::mutex> guard(binding_m);
662
663     /* if (g_afb_instance == nullptr)
664     {
665         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
666         return;
667     }
668
669     try
670     {
671         auto ret = wm::Ok(json_tokener_parse(g_afb_instance->wmgr.layers.to_json().dump().c_str()));
672
673         afb_req_success(req, ret, "success");
674     }
675     catch (std::exception &e)
676     {
677         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_layers: %s", e.what());
678         return;
679     } */
680 }
681
682 void windowmanager_debug_surfaces(afb_req req) noexcept
683 {
684     std::lock_guard<std::mutex> guard(binding_m);
685
686     /* if (g_afb_instance == nullptr)
687     {
688         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
689         return;
690     }
691
692     try
693     {
694
695         auto ret = wm::Ok(to_json(g_afb_instance->wmgr.controller->sprops));
696         if (ret.is_err())
697         {
698             afb_req_fail(req, "failed", ret.unwrap_err());
699             return;
700         }
701
702         afb_req_success(req, ret.unwrap(), "success");
703     }
704     catch (std::exception &e)
705     {
706         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_surfaces: %s", e.what());
707         return;
708     } */
709 }
710
711 void windowmanager_debug_terminate(afb_req req) noexcept
712 {
713     std::lock_guard<std::mutex> guard(binding_m);
714
715     if (g_afb_instance == nullptr)
716     {
717         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
718         return;
719     }
720
721     try
722     {
723
724         if (getenv("WINMAN_DEBUG_TERMINATE") != nullptr)
725         {
726             raise(SIGKILL); // afb-daemon kills it's pgroup using TERM, which
727                             // doesn't play well with perf
728         }
729
730         afb_req_success(req, NULL, "success");
731     }
732     catch (std::exception &e)
733     {
734         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_terminate: %s", e.what());
735         return;
736     }
737 }
738
739 const struct afb_verb_v2 windowmanager_verbs[] = {
740     {"requestSurface", windowmanager_requestsurface, nullptr, nullptr, AFB_SESSION_NONE},
741     {"requestSurfaceXdg", windowmanager_requestsurfacexdg, nullptr, nullptr, AFB_SESSION_NONE},
742     {"setRole", windowmanager_setrole, nullptr, nullptr, AFB_SESSION_NONE},
743     {"activateWindow", windowmanager_activatewindow, nullptr, nullptr, AFB_SESSION_NONE},
744     {"deactivateWindow", windowmanager_deactivatewindow, nullptr, nullptr, AFB_SESSION_NONE},
745     {"endDraw", windowmanager_enddraw, nullptr, nullptr, AFB_SESSION_NONE},
746     {"getDisplayInfo", windowmanager_getdisplayinfo_thunk, nullptr, nullptr, AFB_SESSION_NONE},
747     {"getAreaInfo", windowmanager_getareainfo_thunk, nullptr, nullptr, AFB_SESSION_NONE},
748     {"setRenderOrder", windowmanager_set_render_order, nullptr, nullptr, AFB_SESSION_NONE},
749     {"attachApp", windowmanager_attach_app, nullptr, nullptr, AFB_SESSION_NONE},
750     {"wm_subscribe", windowmanager_wm_subscribe, nullptr, nullptr, AFB_SESSION_NONE},
751     {"list_drawing_names", windowmanager_list_drawing_names, nullptr, nullptr, AFB_SESSION_NONE},
752     {"ping", windowmanager_ping, nullptr, nullptr, AFB_SESSION_NONE},
753     {"debug_status", windowmanager_debug_status, nullptr, nullptr, AFB_SESSION_NONE},
754     {"debug_layers", windowmanager_debug_layers, nullptr, nullptr, AFB_SESSION_NONE},
755     {"debug_surfaces", windowmanager_debug_surfaces, nullptr, nullptr, AFB_SESSION_NONE},
756     {"debug_terminate", windowmanager_debug_terminate, nullptr, nullptr, AFB_SESSION_NONE},
757     {}};
758
759 extern "C" const struct afb_binding_v2 afbBindingV2 = {
760     "windowmanager", nullptr, nullptr, windowmanager_verbs, nullptr, binding_init, nullptr, 0};