Use sd_event_add_defer() for dispatching checkPolicy
[apps/agl-service-windowmanager.git] / src / policy_manager / policy_manager.cpp
1 /*
2  * Copyright (c) 2018 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
18 #include <fstream>
19 #include <sstream>
20 #include <istream>
21 #include <thread>
22 #include <map>
23 #include <systemd/sd-event.h>
24 #include <json-c/json.h>
25 #include "policy_manager.hpp"
26 #include "hmi-debug.h"
27
28 namespace stm {
29 extern "C" {
30 #include "dummy_stm.h"
31 }
32 } // namespace stm
33
34
35 namespace pm {
36
37 struct sd_event* event_loop;
38 struct sd_event_source* event_source;
39
40 struct EventData {
41     PolicyManager *ctx;
42     int event;
43     PolicyManager::Handler handler;
44 };
45
46 std::map<int, EventData> event_data_list;
47
48 }  // namespace pm
49
50 PolicyManager::PolicyManager() :
51   eventname2no_(),
52   categoryname2no_(),
53   areaname2no_(),
54   role2category_(),
55   category2role_(),
56   role2defaultarea_()
57 {
58     HMI_DEBUG("wm:pm", "Call");
59 }
60
61 int PolicyManager::initialize() {
62     HMI_DEBUG("wm:pm", "Call");
63
64     int ret = 0;
65
66     // Create convert map
67     for (unsigned int i=0; i<STM_NUM_EVT; i++) {
68         HMI_DEBUG("wm:pm", "event name:%s no:%d", stm::gStmEventName[i], stm::gStmEventNo[i]);
69         this->eventname2no_[stm::gStmEventName[i]] = stm::gStmEventNo[i];
70     }
71
72     for (unsigned int i=0; i<STM_NUM_CTG; i++) {
73         HMI_DEBUG("wm:pm", "category name:%s no:%d", stm::gStmCategoryName[i], stm::gStmCategoryNo[i]);
74         this->categoryname2no_[stm::gStmCategoryName[i]] = stm::gStmCategoryNo[i];
75     }
76
77     for (unsigned int i=0; i<STM_NUM_ARA; i++) {
78         HMI_DEBUG("wm:pm", "area name:%s no:%d", stm::gStmAreaName[i], stm::gStmAreaNo[i]);
79         this->areaname2no_[stm::gStmAreaName[i]] = stm::gStmAreaNo[i];
80     }
81
82     // Load role.db
83     ret = this->loadRoleDb();
84     if (0 > ret) {
85         HMI_ERROR("wm:pm", "Load role.db Error!!");
86         return ret;
87     }
88
89     // Initialize StateTransitioner
90     stm::stmInitialize();
91
92     // Initialize sd_event loop
93     ret = this->initializeSdEventLoop();
94     if (0 > ret) {
95         HMI_ERROR("wm:pm", "Failed to initializeSdEventLoop!!");
96         return ret;
97     }
98
99     return ret;
100 }
101
102 int PolicyManager::initializeSdEventLoop() {
103     // Get default event loop object
104     int ret = sd_event_new(&(pm::event_loop));
105     if (0 > ret) {
106         HMI_ERROR("wm:pm", "Faild to sd_event_default: errno:%d", ret);
107         return -1;
108     }
109
110     // Create thread for sd_event and detach
111     std::thread sd_event_loop([this]() {
112         while (1) {
113             sd_event_run(pm::event_loop, 1000);
114         }
115     });
116     sd_event_loop.detach();
117
118     return 0;
119 }
120
121 static void addStateToJson(
122   const char* key, int is_changed, const char* state, json_object** json_out) {
123     if ((nullptr == key) || (nullptr == state) || (nullptr == json_out)) {
124         HMI_ERROR("wm:pm", "Argument is nullptr!!!");
125         return;
126     }
127
128     json_object* json_obj = json_object_new_object();
129     json_object_object_add(json_obj, "is_changed", json_object_new_boolean(is_changed));
130     if (is_changed) {
131         HMI_DEBUG("wm:pm", "%s: state changed (%s)", key, state);
132         json_object_object_add(json_obj, "state", json_object_new_string(state));
133     }
134     json_object_object_add(*json_out, key, json_obj);
135 }
136
137 static int checkPolicyEntry(PolicyManager* ctx, int event, uint64_t delay_ms,
138                             PolicyManager::Handler handler);
139 static int checkPolicy(sd_event_source *source, void *data) {
140     HMI_DEBUG("wm:pm", "Call");
141     HMI_DEBUG("wm:pm", ">>>>>>>>>> START CHECK POLICY");
142
143     int event = *((int*)data);
144
145     pm::EventData event_data;
146     if (pm::event_data_list.find(event) != pm::event_data_list.end()) {
147         event_data = pm::event_data_list[event];
148
149         int event_no, category_no, area_no;
150         event_no    = event & STM_MSK_EVT_NO;
151         category_no = event & STM_MSK_CTG_NO;
152         area_no     = event & STM_MSK_ARA_NO;
153         HMI_DEBUG("wm:pm", ">>>>>>>>>> event:%s category:%s area:%s",
154                   stm::gStmEventName[event_no - 1],
155                   stm::gStmCategoryName[(category_no >> 8) - 1],
156                   stm::gStmAreaName[(area_no >> 16) - 1]);
157
158         // Transition state
159         stm::stm_state_t crr_state;
160         int ret = stm::stmTransitionState(event_data.event, &crr_state);
161         if (0 > ret) {
162             HMI_ERROR("wm:pm", "Error!!");
163             return -1;
164         }
165
166         HMI_DEBUG("wm:pm", "parking brake state     (is_changed:%d state:%d:%s)",
167                   crr_state.parking_brake.is_changed,
168                   crr_state.parking_brake.state,
169                   stm::gStmParkingBrakeStateNo2Name[crr_state.parking_brake.state]);
170         HMI_DEBUG("wm:pm", "accelerator pedal state (is_changed:%d state:%d:%s)",
171                   crr_state.accel_pedal.is_changed,
172                   crr_state.accel_pedal.state,
173                   stm::gStmAccelPedalStateNo2Name[crr_state.accel_pedal.state]);
174         HMI_DEBUG("wm:pm", "lightstatus brake state (is_changed:%d state:%d:%s)",
175                   crr_state.lightstatus_brake.is_changed,
176                   crr_state.lightstatus_brake.state,
177                   stm::gStmLightstatusBrakeStateNo2Name[crr_state.lightstatus_brake.state]);
178         HMI_DEBUG("wm:pm", "car state               (is_changed:%d state:%d:%s)",
179                   crr_state.car.is_changed,
180                   crr_state.car.state,
181                   stm::gStmCarStateNo2Name[crr_state.car.state]);
182         HMI_DEBUG("wm:pm", "lamp state              (is_changed:%d state:%d:%s)",
183                   crr_state.lamp.is_changed,
184                   crr_state.lamp.state,
185                   stm::gStmLampStateNo2Name[crr_state.lamp.state]);
186         HMI_DEBUG("wm:pm", "restriction mode state  (is_changed:%d state:%d:%s)",
187                   crr_state.restriction_mode.is_changed,
188                   crr_state.restriction_mode.state,
189                   stm::gStmRestrictionModeStateNo2Name[crr_state.restriction_mode.state]);
190         HMI_DEBUG("wm:pm", "homescreen state        (is_changed:%d state:%d:%s)",
191                   crr_state.layer.homescreen.is_changed,
192                   crr_state.layer.homescreen.state,
193                   stm::gStmLayoutNo2Name[crr_state.layer.homescreen.state]);
194         HMI_DEBUG("wm:pm", "apps state              (is_changed:%d state:%d:%s)",
195                   crr_state.layer.apps.is_changed,
196                   crr_state.layer.apps.state,
197                   stm::gStmLayoutNo2Name[crr_state.layer.apps.state]);
198         HMI_DEBUG("wm:pm", "restriction state       (is_changed:%d state:%d:%s)",
199                   crr_state.layer.restriction.is_changed,
200                   crr_state.layer.restriction.state,
201                   stm::gStmLayoutNo2Name[crr_state.layer.restriction.state]);
202         HMI_DEBUG("wm:pm", "on_screen state         (is_changed:%d state:%d:%s)",
203                   crr_state.layer.on_screen.is_changed,
204                   crr_state.layer.on_screen.state,
205                   stm::gStmLayoutNo2Name[crr_state.layer.on_screen.state]);
206
207         json_object* json_out = json_object_new_object();
208
209         // Create result
210         // {
211         //     "parking_brake": {
212         //         "is_changed": <bool>,
213         //         "state": <const char*>
214         //     },
215         addStateToJson("parking_brake",
216                        crr_state.parking_brake.is_changed,
217                        stm::gStmParkingBrakeStateNo2Name[crr_state.parking_brake.state],
218                        &json_out);
219
220         //     "accel_pedal": {
221         //         "is_changed": <bool>,
222         //         "state": <const char*>
223         //     },
224         addStateToJson("accel_pedal",
225                        crr_state.accel_pedal.is_changed,
226                        stm::gStmAccelPedalStateNo2Name[crr_state.accel_pedal.state],
227                        &json_out);
228
229         //     "lightstatus_brake": {
230         //         "is_changed": <bool>,
231         //         "state": <const char*>
232         //     },
233         addStateToJson("lightstatus_brake",
234                        crr_state.lightstatus_brake.is_changed,
235                        stm::gStmLightstatusBrakeStateNo2Name[crr_state.lightstatus_brake.state],
236                        &json_out);
237
238         //     "car": {
239         //         "is_changed": <bool>,
240         //         "state": <const char*>
241         //     },
242         addStateToJson("car",
243                        crr_state.car.is_changed,
244                        stm::gStmCarStateNo2Name[crr_state.car.state],
245                        &json_out);
246
247         //     "lamp": {
248         //         "is_changed": <bool>,
249         //         "state": <const char*>
250         //     },
251         addStateToJson("lamp",
252                        crr_state.lamp.is_changed,
253                        stm::gStmLampStateNo2Name[crr_state.lamp.state],
254                        &json_out);
255
256         //     "restriction_mode": {
257         //         "is_changed": <bool>,
258         //         "state": <const char*>
259         //     },
260         addStateToJson("restriction_mode",
261                        crr_state.restriction_mode.is_changed,
262                        stm::gStmRestrictionModeStateNo2Name[crr_state.restriction_mode.state],
263                        &json_out);
264
265         //     "layers": [
266         json_object* json_layer = json_object_new_array();
267         json_object* json_tmp;
268
269         //         {
270         //             "homescreen": {
271         //                 "is_changed": <bool>,
272         //                 "state": <const char*>
273         //             }
274         //         },
275         //     ]
276         // }
277         json_tmp = json_object_new_object();
278         addStateToJson("homescreen",
279                        crr_state.layer.homescreen.is_changed,
280                        stm::gStmLayoutNo2Name[crr_state.layer.homescreen.state],
281                        &json_tmp);
282         json_object_array_add(json_layer, json_tmp);
283
284         //         {
285         //             "apps": {
286         //                 "is_changed": <bool>,
287         //                 "state": <const char*>
288         //             }
289         //         },
290         json_tmp = json_object_new_object();
291         addStateToJson("apps",
292                        crr_state.layer.apps.is_changed,
293                        stm::gStmLayoutNo2Name[crr_state.layer.apps.state],
294                        &json_tmp);
295         json_object_array_add(json_layer, json_tmp);
296
297         //         {
298         //             "restriction": {
299         //                 "is_changed": <bool>,
300         //                 "state": <const char*>
301         //             }
302         //         },
303         json_tmp = json_object_new_object();
304         addStateToJson("restriction",
305                        crr_state.layer.restriction.is_changed,
306                        stm::gStmLayoutNo2Name[crr_state.layer.restriction.state],
307                        &json_tmp);
308         json_object_array_add(json_layer, json_tmp);
309
310         //         {
311         //             "on_screen": {
312         //                 "is_changed": <bool>,
313         //                 "state": <const char*>
314         //             }
315         //         },
316         json_tmp = json_object_new_object();
317         addStateToJson("on_screen",
318                        crr_state.layer.on_screen.is_changed,
319                        stm::gStmLayoutNo2Name[crr_state.layer.on_screen.state],
320                        &json_tmp);
321         json_object_array_add(json_layer, json_tmp);
322
323         // Add json array of layer
324         json_object_object_add(json_out, "layers", json_layer);
325
326         // Call event handler
327         event_data.handler(json_out);
328
329         if (crr_state.car.is_changed) {
330             // Set delay event(restriction mode on) when car state is chaged stop -> run 
331             if (stm::gStmCarStateNoRun == crr_state.car.state) {
332                 checkPolicyEntry(event_data.ctx, STM_EVT_NO_RESTRICTION_MODE_ON,
333                                  3000, event_data.handler);
334             }
335             else if (stm::gStmCarStateNoStop == crr_state.car.state) {
336                 checkPolicyEntry(event_data.ctx, STM_EVT_NO_RESTRICTION_MODE_OFF,
337                                  0, event_data.handler);
338             }
339         }
340
341         // Release json_object
342         json_object_put(json_out);
343
344     }
345     else {
346         HMI_DEBUG("wm:pm", "Request for event:%d is removed", event);
347     }
348
349     // Release data
350     delete (int*)data;
351
352     // Destroy sd_event_soutce object
353     sd_event_source_unref(source);
354
355     HMI_DEBUG("wm:pm", ">>>>>>>>>> FINISH CHECK POLICY");
356     return 0;
357 }
358
359 static int timerEvent(sd_event_source *source, uint64_t usec, void *data) {
360     checkPolicy(source, data);
361 };
362
363 static int checkPolicyEntry(PolicyManager* ctx, int event, uint64_t delay_ms,
364                             PolicyManager::Handler handler)
365 {
366     HMI_DEBUG("wm:pm", "Call");
367     HMI_DEBUG("wm:pm", "event:0x%x", event);
368
369     // If event is restriction off and there is restriction on event,
370     // remove restriction on event
371     if ((STM_EVT_NO_RESTRICTION_MODE_OFF == event)
372       && (pm::event_data_list.find(event) != pm::event_data_list.end())) {
373         HMI_DEBUG("wm:pm", "Remove event: restriction on");
374         pm::event_data_list.erase(STM_EVT_NO_RESTRICTION_MODE_ON);
375     }
376
377     // Create event data
378     pm::EventData event_data;
379     event_data.ctx = ctx;
380     event_data.event = event;
381     event_data.handler = handler;
382     pm::event_data_list[event] = event_data;
383
384     if (0 == delay_ms) {
385         int ret = sd_event_add_defer(pm::event_loop, NULL,
386                                      &checkPolicy, new int(event));
387         if (0 > ret) {
388             HMI_ERROR("wm:pm", "Faild to sd_event_add_defer: errno:%d", ret);
389             return -1;
390         }
391     }
392     else {
393         // Get current time
394         struct timespec time_spec;
395         clock_gettime(CLOCK_MONOTONIC, &time_spec);
396
397         // Calculate timer fired time
398         uint64_t usec = (time_spec.tv_sec * 1000000)
399             + (time_spec.tv_nsec / 1000)
400             + (delay_ms * 1000);
401
402         // Set timer
403         int ret = sd_event_add_time(pm::event_loop, NULL, CLOCK_MONOTONIC, usec, 1,
404                                     &timerEvent, new int(event));
405         if (0 > ret) {
406             HMI_ERROR("wm:pm", "Faild to sd_event_add_time: errno:%d", ret);
407             return -1;
408         }
409     }
410
411     return 0;
412 }
413
414 int PolicyManager::inputEvent(json_object* json_in, PolicyManager::Handler notify_state) {
415     HMI_DEBUG("wm:pm", "Call");
416
417     // Check arguments
418     if (nullptr == json_in) {
419         HMI_ERROR("wm:pm", "Argument is NULL!!");
420         return -1;
421     }
422
423     // Get event from json_object
424     const char* event = this->getStringFromJson(json_in, "event");
425     int event_no = 0;
426     if (nullptr != event) {
427         // Convert name to number
428         event_no = this->eventname2no_[event];
429         HMI_DEBUG("wm:pm", "event(%s:%d)", event, event_no);
430     }
431
432     // Get role from json_object
433     const char* role = this->getStringFromJson(json_in, "role");
434     int category_no = 0;
435     if (nullptr != role) {
436         HMI_DEBUG("wm:pm", "role(%s)", role);
437
438         // Convert role to category
439         const char* category = this->role2category_[role].c_str();
440         if (0 == strcmp("", category)) {
441             HMI_ERROR("wm:pm", "Error!!");
442             return -1;
443         }
444         HMI_DEBUG("wm:pm", "category(%s)", category);
445
446         // Convert name to number
447         category_no = categoryname2no_[category];
448         HMI_DEBUG("wm:pm", "role(%s), category(%s:%d)", role, category, category_no);
449     }
450
451     // Get areat from json_object
452     const char* area = this->getStringFromJson(json_in, "area");
453     int area_no = 0;
454     if (nullptr != area) {
455         // Convert name to number
456         area_no = areaname2no_[area];
457         HMI_DEBUG("wm:pm", "area(%s:%d)", area, area_no);
458     }
459
460     // Check policy
461     checkPolicyEntry(this, (event_no | category_no | area_no), 0, notify_state);
462
463     return 0;
464 }
465
466 std::string PolicyManager::roleToCategory(const char* role) {
467     return this->role2category_[role];
468 }
469
470 extern const char* kDefaultRoleDb;
471 int PolicyManager::loadRoleDb() {
472     HMI_DEBUG("wm:pm", "Call");
473
474     std::string file_name;
475
476     // Get afm application installed dir
477     char const *afm_app_install_dir = getenv("AFM_APP_INSTALL_DIR");
478     HMI_DEBUG("wm:pm", "afm_app_install_dir:%s", afm_app_install_dir);
479
480     if (!afm_app_install_dir) {
481         HMI_ERROR("wm:pm", "AFM_APP_INSTALL_DIR is not defined");
482     }
483     else {
484         file_name = std::string(afm_app_install_dir) + std::string("/etc/role.db");
485     }
486
487     // Load role.db
488     json_object* json_obj;
489     int ret = this->inputJsonFilie(file_name.c_str(), &json_obj);
490     if (0 > ret) {
491         HMI_ERROR("wm:pm", "Could not open role.db, so use default role information");
492         json_obj = json_tokener_parse(kDefaultRoleDb);
493     }
494     HMI_DEBUG("wm:pm", "json_obj dump:%s", json_object_get_string(json_obj));
495
496     json_object* json_roles;
497     if (!json_object_object_get_ex(json_obj, "roles", &json_roles)) {
498         HMI_ERROR("wm:pm", "Parse Error!!");
499         return -1;
500     }
501
502     int len = json_object_array_length(json_roles);
503     HMI_DEBUG("wm:pm", "json_cfg len:%d", len);
504     HMI_DEBUG("wm:pm", "json_cfg dump:%s", json_object_get_string(json_roles));
505
506     json_object* json_tmp;
507     const char* category;
508     const char* roles;
509     const char* areas;
510     for (int i=0; i<len; i++) {
511         json_tmp = json_object_array_get_idx(json_roles, i);
512
513         category = this->getStringFromJson(json_tmp, "category");
514         roles =  this->getStringFromJson(json_tmp, "role");
515         areas =  this->getStringFromJson(json_tmp, "area");
516
517         if ((nullptr == category) || (nullptr == roles) || (nullptr == areas)) {
518             HMI_ERROR("wm:pm", "Parse Error!!");
519             return -1;
520         }
521
522         // Parse roles by '|'
523         std::vector<std::string> vct_roles;
524         vct_roles = this->parseString(std::string(roles), '|');
525
526         // Parse areas by '|'
527         std::vector<std::string> vct_areas;
528         vct_areas = this->parseString(std::string(areas), '|');
529
530         // Set role, category, default area
531         for (auto itr = vct_roles.begin(); itr != vct_roles.end(); ++itr) {
532             // Delete space from role and area name
533             std::string role = this->deleteSpace(*itr);
534             std::string area = this->deleteSpace(vct_areas[0]);
535
536             this->role2category_[role] = std::string(category);
537             this->role2defaultarea_[role] = area;
538         }
539
540         this->category2role_[std::string(category)] = std::string(roles);
541     }
542
543     // Check
544     HMI_DEBUG("wm:pm", "Check role2category_");
545     for (auto& x:this->role2category_){
546         HMI_DEBUG("wm:pm", "key:%s, val:%s", x.first.c_str(), x.second.c_str());
547     }
548
549     HMI_DEBUG("wm:pm", "Check role2defaultarea_");
550     for (auto& x:this->role2defaultarea_){
551         HMI_DEBUG("wm:pm", "key:%s, val:%s", x.first.c_str(), x.second.c_str());
552     }
553
554     HMI_DEBUG("wm:pm", "Check category2role_");
555     for (auto& x:this->category2role_){
556         HMI_DEBUG("wm:pm", "key:%s, val:%s", x.first.c_str(), x.second.c_str());
557     }
558
559     return 0;
560 }
561
562 // TODO:
563 // This function will be removed because json_helper has same function.
564 // json_helper should be library.
565 const char* PolicyManager::getStringFromJson(json_object* obj, const char* key) {
566     if ((nullptr == obj) || (nullptr == key)) {
567         HMI_ERROR("wm:pm", "Argument is nullptr!!!");
568         return nullptr;
569     }
570
571     json_object* tmp;
572     if (!json_object_object_get_ex(obj, key, &tmp)) {
573         HMI_DEBUG("wm:pm", "Not found key \"%s\"", key);
574         return nullptr;
575     }
576
577     return json_object_get_string(tmp);
578 }
579
580 // TODO:
581 // This function will be removed because json_helper has same function.
582 // json_helper should be library.
583 int PolicyManager::inputJsonFilie(const char* file, json_object** obj) {
584     const int input_size = 128;
585     int ret = -1;
586
587     if ((nullptr == file) || (nullptr == obj)) {
588         HMI_ERROR("wm:jh", "Argument is nullptr!!!");
589         return ret;
590     }
591
592     HMI_DEBUG("wm:jh", "Input file: %s", file);
593
594     // Open json file
595     FILE *fp = fopen(file, "rb");
596     if (nullptr == fp) {
597         HMI_ERROR("wm:jh", "Could not open file");
598         return ret;
599     }
600
601     // Parse file data
602     struct json_tokener *tokener = json_tokener_new();
603     enum json_tokener_error json_error;
604     char buffer[input_size];
605     int block_cnt = 1;
606     while (1) {
607         size_t len = fread(buffer, sizeof(char), input_size, fp);
608         *obj = json_tokener_parse_ex(tokener, buffer, len);
609         if (nullptr != *obj) {
610             HMI_DEBUG("wm:jh", "File input is success");
611             ret = 0;
612             break;
613         }
614
615         json_error = json_tokener_get_error(tokener);
616         if ((json_tokener_continue != json_error)
617             || (input_size > len)) {
618             HMI_ERROR("wm:jh", "Failed to parse file (byte:%d err:%s)",
619                       (input_size * block_cnt), json_tokener_error_desc(json_error));
620             HMI_ERROR("wm:jh", "\n%s", buffer);
621             *obj = nullptr;
622             break;
623         }
624         block_cnt++;
625     }
626
627     // Close json file
628     fclose(fp);
629
630     // Free json_tokener
631     json_tokener_free(tokener);
632
633     return ret;
634 }
635
636 std::vector<std::string> PolicyManager::parseString(std::string str, char delimiter) {
637     // Parse string by delimiter
638     std::vector<std::string> vct;
639     std::stringstream ss{str};
640     std::string buf;
641     while (std::getline(ss, buf, delimiter)) {
642       if (!buf.empty()) {
643         vct.push_back(buf);
644       }
645     }
646     return vct;
647 }
648
649 std::string PolicyManager::deleteSpace(std::string str) {
650     std::string ret = str;
651     size_t pos;
652     while ((pos = ret.find_first_of(" ")) != std::string::npos) {
653       ret.erase(pos, 1);
654     }
655     return ret;
656 }
657
658 const char* kDefaultRoleDb = "{ \
659     \"roles\":[ \
660     { \
661         \"category\": \"homescreen\", \
662         \"role\": \"homescreen\", \
663         \"area\": \"full\", \
664     }, \
665     { \
666         \"category\": \"map\", \
667         \"role\": \"map\", \
668         \"area\": \"full | normal | split.main\", \
669     }, \
670     { \
671         \"category\": \"general\", \
672         \"role\": \"poi | music | video | browser | sdl | settings | mixer | radio | hvac | dashboard | debug\", \
673         \"area\": \"normal\", \
674     }, \
675     { \
676         \"category\": \"phone\", \
677         \"role\": \"phone\", \
678         \"area\": \"normal\", \
679     }, \
680     { \
681         \"category\": \"splitable\", \
682         \"role\": \"splitable1 | splitable2\", \
683         \"area\": \"normal | split.main | split.sub\", \
684     }, \
685     { \
686         \"category\": \"popup\", \
687         \"role\": \"popup\", \
688         \"area\": \"on_screen\", \
689     }, \
690     { \
691         \"category\": \"system_alert\", \
692         \"role\": \"system_alert\", \
693         \"area\": \"on_screen\", \
694     }, \
695     { \
696         \"category\": \"tbt\", \
697         \"role\": \"tbt\", \
698         \"area\": \"hud\", \
699     } \
700     ] \
701 }";