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