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