2 * Copyright (c) 2018 TOYOTA MOTOR CORPORATION
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
23 #include <systemd/sd-event.h>
24 #include <json-c/json.h>
25 #include "policy_manager.hpp"
26 #include "hmi-debug.h"
30 #include "dummy_stm.h"
36 typedef std::unordered_map<std::string, std::string> AppAttribute;
37 typedef std::unordered_map<std::string, AppAttribute> AreasState;
38 typedef std::unordered_map<std::string, AreasState> LayoutState;
39 typedef std::unordered_map<std::string, LayoutState> LayersState;
41 struct sd_event* event_loop;
42 std::map<int, struct sd_event_source*> event_source_list;
43 PolicyManager::CallbackTable callback;
44 LayersState g_prv_layers;
45 LayersState g_crr_layers;
49 PolicyManager::PolicyManager() :
57 HMI_DEBUG("wm:pm", "Call");
60 int PolicyManager::initialize() {
61 HMI_DEBUG("wm:pm", "Call");
66 for (unsigned int i=0; i<STM_NUM_EVT; i++) {
67 HMI_DEBUG("wm:pm", "event name:%s no:%d", stm::gStmEventName[i], stm::gStmEventNo[i]);
68 this->eventname2no_[stm::gStmEventName[i]] = stm::gStmEventNo[i];
71 for (unsigned int i=0; i<STM_NUM_CTG; i++) {
72 HMI_DEBUG("wm:pm", "category name:%s no:%d", stm::gStmCategoryName[i], stm::gStmCategoryNo[i]);
73 this->categoryname2no_[stm::gStmCategoryName[i]] = stm::gStmCategoryNo[i];
76 for (unsigned int i=0; i<STM_NUM_ARA; i++) {
77 HMI_DEBUG("wm:pm", "area name:%s no:%d", stm::gStmAreaName[i], stm::gStmAreaNo[i]);
78 this->areaname2no_[stm::gStmAreaName[i]] = stm::gStmAreaNo[i];
82 ret = this->loadRoleDb();
84 HMI_ERROR("wm:pm", "Load role.db Error!!");
88 // Initialize current/previous state of layers
89 pm::AppAttribute init_app;
90 pm::AreasState init_area;
91 pm::LayoutState init_layout;
92 init_app["role"] = "none";
93 init_area["none"] = init_app;
94 init_layout["none"] = init_area;
96 pm::g_crr_layers["homescreen"] = init_layout;
97 pm::g_crr_layers["apps"] = init_layout;
98 pm::g_crr_layers["restriction"] = init_layout;
99 pm::g_crr_layers["on_screen"] = init_layout;
100 pm::g_prv_layers = pm::g_crr_layers;
102 // Initialize StateTransitioner
103 stm::stmInitialize();
105 // Initialize sd_event loop
106 ret = this->initializeSdEventLoop();
108 HMI_ERROR("wm:pm", "Failed to initializeSdEventLoop!!");
115 int PolicyManager::initializeSdEventLoop() {
116 // Get default event loop object
117 int ret = sd_event_new(&(pm::event_loop));
119 HMI_ERROR("wm:pm", "Faild to sd_event_default: errno:%d", ret);
123 // Create thread for sd_event and detach
124 std::thread sd_event_loop([this]() {
126 sd_event_run(pm::event_loop, 1000);
129 sd_event_loop.detach();
134 static void addStateToJson(
135 const char* key, int is_changed, const char* state, json_object** json_out) {
136 if ((nullptr == key) || (nullptr == state) || (nullptr == json_out)) {
137 HMI_ERROR("wm:pm", "Argument is nullptr!!!");
141 json_object* json_obj = json_object_new_object();
142 json_object_object_add(json_obj, "is_changed", json_object_new_boolean(is_changed));
144 HMI_DEBUG("wm:pm", "%s: state changed (%s)", key, state);
145 json_object_object_add(json_obj, "state", json_object_new_string(state));
147 json_object_object_add(*json_out, key, json_obj);
150 static int checkPolicyEntry(int event, uint64_t delay_ms);
151 static int checkPolicy(sd_event_source *source, void *data) {
152 HMI_DEBUG("wm:pm", "Call");
153 HMI_DEBUG("wm:pm", ">>>>>>>>>> START CHECK POLICY");
155 int event = *((int*)data);
157 int event_no, category_no, area_no;
158 event_no = event & STM_MSK_EVT_NO;
159 category_no = event & STM_MSK_CTG_NO;
160 area_no = event & STM_MSK_ARA_NO;
161 HMI_DEBUG("wm:pm", ">>>>>>>>>> event:%s category:%s area:%s",
162 stm::gStmEventName[event_no - 1],
163 stm::gStmCategoryName[(category_no >> 8) - 1],
164 stm::gStmAreaName[(area_no >> 16) - 1]);
167 stm::stm_state_t crr_state;
168 int ret = stm::stmTransitionState(event, &crr_state);
170 HMI_ERROR("wm:pm", "Error!!");
174 HMI_DEBUG("wm:pm", "parking brake state (is_changed:%d state:%d:%s)",
175 crr_state.parking_brake.is_changed,
176 crr_state.parking_brake.state,
177 stm::gStmParkingBrakeStateNo2Name[crr_state.parking_brake.state]);
178 HMI_DEBUG("wm:pm", "accelerator pedal state (is_changed:%d state:%d:%s)",
179 crr_state.accel_pedal.is_changed,
180 crr_state.accel_pedal.state,
181 stm::gStmAccelPedalStateNo2Name[crr_state.accel_pedal.state]);
182 HMI_DEBUG("wm:pm", "lightstatus brake state (is_changed:%d state:%d:%s)",
183 crr_state.lightstatus_brake.is_changed,
184 crr_state.lightstatus_brake.state,
185 stm::gStmLightstatusBrakeStateNo2Name[crr_state.lightstatus_brake.state]);
186 HMI_DEBUG("wm:pm", "car state (is_changed:%d state:%d:%s)",
187 crr_state.car.is_changed,
189 stm::gStmCarStateNo2Name[crr_state.car.state]);
190 HMI_DEBUG("wm:pm", "lamp state (is_changed:%d state:%d:%s)",
191 crr_state.lamp.is_changed,
192 crr_state.lamp.state,
193 stm::gStmLampStateNo2Name[crr_state.lamp.state]);
194 HMI_DEBUG("wm:pm", "restriction mode state (is_changed:%d state:%d:%s)",
195 crr_state.restriction_mode.is_changed,
196 crr_state.restriction_mode.state,
197 stm::gStmRestrictionModeStateNo2Name[crr_state.restriction_mode.state]);
198 HMI_DEBUG("wm:pm", "homescreen state (is_changed:%d state:%d:%s)",
199 crr_state.layer[stm::gStmLayerNoHomescreen].is_changed,
200 crr_state.layer[stm::gStmLayerNoHomescreen].state,
201 stm::gStmLayoutNo2Name[crr_state.layer[stm::gStmLayerNoHomescreen].state]);
202 HMI_DEBUG("wm:pm", "apps state (is_changed:%d state:%d:%s)",
203 crr_state.layer[stm::gStmLayerNoApps].is_changed,
204 crr_state.layer[stm::gStmLayerNoApps].state,
205 stm::gStmLayoutNo2Name[crr_state.layer[stm::gStmLayerNoApps].state]);
206 HMI_DEBUG("wm:pm", "restriction state (is_changed:%d state:%d:%s)",
207 crr_state.layer[stm::gStmLayerNoRestriction].is_changed,
208 crr_state.layer[stm::gStmLayerNoRestriction].state,
209 stm::gStmLayoutNo2Name[crr_state.layer[stm::gStmLayerNoRestriction].state]);
210 HMI_DEBUG("wm:pm", "on_screen state (is_changed:%d state:%d:%s)",
211 crr_state.layer[stm::gStmLayerNoOnScreen].is_changed,
212 crr_state.layer[stm::gStmLayerNoOnScreen].state,
213 stm::gStmLayoutNo2Name[crr_state.layer[stm::gStmLayerNoOnScreen].state]);
215 #if 0 // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
216 // Store previous layers
217 pm::g_prv_layers = pm::g_crr_layers;
219 std::string layer_name = "homescreen";
222 if (crr_state.layer[stm::gStmLayerNoHomescreen].is_changed) {
223 // Get previous layout name of this layer
224 pm::LayoutState prv_layout_state = pm::g_prv_layers[layer_name];
225 std::string prv_layout_name = prv_homescreen_layout_state.first();
227 // Get current layout name of this layer
228 std::string crr_layout_name = std::string(stm::gStmLayoutNo2Name[crr_state.layer[stm::gStmLayerNoHomescreen].state]);
230 // Compare layout name
231 pm::LayoutState crr_layout_state;
232 if ("none" == crr_layout_name) {
233 // If current layout is "none",
234 // current areas is set with "none"
235 HMI_DEBUG("wm:pm", "Current layout is \"none\"");
236 HMI_DEBUG("wm:lm", "Current layout is \"none\"");
237 pm::AppAttribute crr_app_attribute;
238 pm::AreasState crr_areas_state;
239 crr_app_attribute["role"] = "none";
240 crr_areas_state["none"] = "none";
241 crr_layout_state["none"] = crr_areas_state;
244 if (prv_layout_name == crr_layout_name) {
245 // If previous layout is same with current,
246 // previous areas are copied to current
247 crr_layout_state[crr_layout_name] = pm::g_prv_layers[layer_name][crr_layout_name];
250 // If previous layout is same with current,
251 // previous areas are copied to current
252 crr_layout_state[crr_layout_name] = this->default_layout_state[crr_layout_name];
255 // Update role in new area
257 if (crr_state.restriction_mode.is_changed) {
258 // Updating role is not necessary
259 // because new_role is not specified
260 // when restriction mode is changed
261 HMI_DEBUG("wm:lm", "Updating role is not necessary because new_role is not specified when restriction mode is changed");
263 if (crr_state.car.is_changed) {
264 // Updating role is not necessary
265 // because new_role is not specified
266 // when car state is changed
267 HMI_DEBUG("wm:lm", "Updating role is not necessary because new_role is not specified when car state is changed");
271 HMI_DEBUG("wm:lm", "Get new_area for new role");
272 // Get new_area for new role
273 std::string new_area = this->getAreaName(this->layout_define_[crr_layout_name],
276 if ("none" == new_area) {
277 HMI_DEBUG("wm:lm", "It is not necessary to update role of areas in this layer, because new_role is not specified for this layer");
280 // Is there new_area?
281 // if there is new_area, set new role there
283 // if NOT, find same category of new_role
284 // pop old role and shift area
285 // push new role and set area
288 // Update role in new area
289 // because new_role is specified for this layer
291 crr_role["role"] = std::string(new_role);
292 crr_layout[crr_layout_name][new_area] = crr_role;
298 // Update current layout of this layer
299 pm::g_crr_layers[layer_name] = crr_layout_state;
302 #endif // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
304 json_object* json_out = json_object_new_object();
308 // "parking_brake": {
309 // "is_changed": <bool>,
310 // "state": <const char*>
312 addStateToJson("parking_brake",
313 crr_state.parking_brake.is_changed,
314 stm::gStmParkingBrakeStateNo2Name[crr_state.parking_brake.state],
318 // "is_changed": <bool>,
319 // "state": <const char*>
321 addStateToJson("accel_pedal",
322 crr_state.accel_pedal.is_changed,
323 stm::gStmAccelPedalStateNo2Name[crr_state.accel_pedal.state],
326 // "lightstatus_brake": {
327 // "is_changed": <bool>,
328 // "state": <const char*>
330 addStateToJson("lightstatus_brake",
331 crr_state.lightstatus_brake.is_changed,
332 stm::gStmLightstatusBrakeStateNo2Name[crr_state.lightstatus_brake.state],
336 // "is_changed": <bool>,
337 // "state": <const char*>
339 addStateToJson("car",
340 crr_state.car.is_changed,
341 stm::gStmCarStateNo2Name[crr_state.car.state],
345 // "is_changed": <bool>,
346 // "state": <const char*>
348 addStateToJson("lamp",
349 crr_state.lamp.is_changed,
350 stm::gStmLampStateNo2Name[crr_state.lamp.state],
353 // "restriction_mode": {
354 // "is_changed": <bool>,
355 // "state": <const char*>
357 addStateToJson("restriction_mode",
358 crr_state.restriction_mode.is_changed,
359 stm::gStmRestrictionModeStateNo2Name[crr_state.restriction_mode.state],
363 json_object* json_layer = json_object_new_array();
364 json_object* json_tmp;
368 // "is_changed": <bool>,
369 // "state": <const char*>
374 json_tmp = json_object_new_object();
375 addStateToJson("homescreen",
376 crr_state.layer[stm::gStmLayerNoHomescreen].is_changed,
377 stm::gStmLayoutNo2Name[crr_state.layer[stm::gStmLayerNoHomescreen].state],
379 json_object_array_add(json_layer, json_tmp);
383 // "is_changed": <bool>,
384 // "state": <const char*>
387 json_tmp = json_object_new_object();
388 addStateToJson("apps",
389 crr_state.layer[stm::gStmLayerNoApps].is_changed,
390 stm::gStmLayoutNo2Name[crr_state.layer[stm::gStmLayerNoApps].state],
392 json_object_array_add(json_layer, json_tmp);
396 // "is_changed": <bool>,
397 // "state": <const char*>
400 json_tmp = json_object_new_object();
401 addStateToJson("restriction",
402 crr_state.layer[stm::gStmLayerNoRestriction].is_changed,
403 stm::gStmLayoutNo2Name[crr_state.layer[stm::gStmLayerNoRestriction].state],
405 json_object_array_add(json_layer, json_tmp);
409 // "is_changed": <bool>,
410 // "state": <const char*>
413 json_tmp = json_object_new_object();
414 addStateToJson("on_screen",
415 crr_state.layer[stm::gStmLayerNoOnScreen].is_changed,
416 stm::gStmLayoutNo2Name[crr_state.layer[stm::gStmLayerNoOnScreen].state],
418 json_object_array_add(json_layer, json_tmp);
420 // Add json array of layer
421 json_object_object_add(json_out, "layers", json_layer);
423 // Notify state is changed
424 if (nullptr != pm::callback.onStateTransitioned) {
425 pm::callback.onStateTransitioned(json_out);
428 if (crr_state.car.is_changed) {
429 if (stm::gStmCarStateNoRun == crr_state.car.state) {
430 // Set delay event(restriction mode on)
431 checkPolicyEntry(STM_EVT_NO_RESTRICTION_MODE_ON, 3000);
433 else if (stm::gStmCarStateNoStop == crr_state.car.state) {
434 // Set event(restriction mode off)
435 checkPolicyEntry(STM_EVT_NO_RESTRICTION_MODE_OFF, 0);
437 // Stop timer for restriction on event
438 if (pm::event_source_list.find(STM_EVT_NO_RESTRICTION_MODE_ON)
439 != pm::event_source_list.end()) {
440 HMI_DEBUG("wm:pm", "Stop timer for restriction on");
441 sd_event_source *event_source
442 = pm::event_source_list[STM_EVT_NO_RESTRICTION_MODE_ON];
443 int ret = sd_event_source_set_enabled(event_source, SD_EVENT_OFF);
445 HMI_ERROR("wm:pm", "Failed to stop timer");
451 // Release json_object
452 json_object_put(json_out);
457 // Destroy sd_event_source object
458 sd_event_source_unref(source);
460 // Remove event source from list
461 if (pm::event_source_list.find(event) != pm::event_source_list.end()) {
462 pm::event_source_list.erase(event);
465 HMI_DEBUG("wm:pm", ">>>>>>>>>> FINISH CHECK POLICY");
469 static int timerEvent(sd_event_source *source, uint64_t usec, void *data) {
470 checkPolicy(source, data);
473 static int checkPolicyEntry(int event, uint64_t delay_ms)
475 HMI_DEBUG("wm:pm", "Call");
476 HMI_DEBUG("wm:pm", "event:0x%x", event);
479 int ret = sd_event_add_defer(pm::event_loop, NULL,
480 &checkPolicy, new int(event));
482 HMI_ERROR("wm:pm", "Faild to sd_event_add_defer: errno:%d", ret);
488 struct timespec time_spec;
489 clock_gettime(CLOCK_MONOTONIC, &time_spec);
491 // Calculate timer fired time
492 uint64_t usec = (time_spec.tv_sec * 1000000)
493 + (time_spec.tv_nsec / 1000)
497 struct sd_event_source* event_source;
498 int ret = sd_event_add_time(pm::event_loop, &event_source, CLOCK_MONOTONIC, usec, 1,
499 &timerEvent, new int(event));
501 HMI_ERROR("wm:pm", "Faild to sd_event_add_time: errno:%d", ret);
505 // Store event source
506 pm::event_source_list[event] = event_source;
512 void PolicyManager::registerCallback(CallbackTable callback) {
513 pm::callback.onStateTransitioned = callback.onStateTransitioned;
514 pm::callback.onError = callback.onError;
517 int PolicyManager::inputEvent(json_object* json_in) {
518 HMI_DEBUG("wm:pm", "Call");
521 if (nullptr == json_in) {
522 HMI_ERROR("wm:pm", "Argument is NULL!!");
526 // Get event from json_object
527 const char* event = this->getStringFromJson(json_in, "event");
529 if (nullptr != event) {
530 // Convert name to number
531 event_no = this->eventname2no_[event];
532 HMI_DEBUG("wm:pm", "event(%s:%d)", event, event_no);
535 // Get role from json_object
536 const char* role = this->getStringFromJson(json_in, "role");
538 if (nullptr != role) {
539 HMI_DEBUG("wm:pm", "role(%s)", role);
541 // Convert role to category
542 const char* category = this->role2category_[role].c_str();
543 if (0 == strcmp("", category)) {
544 HMI_ERROR("wm:pm", "Error!!");
547 HMI_DEBUG("wm:pm", "category(%s)", category);
549 // Convert name to number
550 category_no = categoryname2no_[category];
551 HMI_DEBUG("wm:pm", "role(%s), category(%s:%d)", role, category, category_no);
554 // Get areat from json_object
555 const char* area = this->getStringFromJson(json_in, "area");
557 if (nullptr != area) {
558 // Convert name to number
559 area_no = areaname2no_[area];
560 HMI_DEBUG("wm:pm", "area(%s:%d)", area, area_no);
564 checkPolicyEntry((event_no | category_no | area_no), 0);
569 std::string PolicyManager::roleToCategory(const char* role) {
570 return this->role2category_[role];
573 extern const char* kDefaultRoleDb;
574 int PolicyManager::loadRoleDb() {
575 HMI_DEBUG("wm:pm", "Call");
577 std::string file_name;
579 // Get afm application installed dir
580 char const *afm_app_install_dir = getenv("AFM_APP_INSTALL_DIR");
581 HMI_DEBUG("wm:pm", "afm_app_install_dir:%s", afm_app_install_dir);
583 if (!afm_app_install_dir) {
584 HMI_ERROR("wm:pm", "AFM_APP_INSTALL_DIR is not defined");
587 file_name = std::string(afm_app_install_dir) + std::string("/etc/role.db");
591 json_object* json_obj;
592 int ret = this->inputJsonFilie(file_name.c_str(), &json_obj);
594 HMI_ERROR("wm:pm", "Could not open role.db, so use default role information");
595 json_obj = json_tokener_parse(kDefaultRoleDb);
597 HMI_DEBUG("wm:pm", "json_obj dump:%s", json_object_get_string(json_obj));
599 json_object* json_roles;
600 if (!json_object_object_get_ex(json_obj, "roles", &json_roles)) {
601 HMI_ERROR("wm:pm", "Parse Error!!");
605 int len = json_object_array_length(json_roles);
606 HMI_DEBUG("wm:pm", "json_cfg len:%d", len);
607 HMI_DEBUG("wm:pm", "json_cfg dump:%s", json_object_get_string(json_roles));
609 json_object* json_tmp;
610 const char* category;
613 for (int i=0; i<len; i++) {
614 json_tmp = json_object_array_get_idx(json_roles, i);
616 category = this->getStringFromJson(json_tmp, "category");
617 roles = this->getStringFromJson(json_tmp, "role");
618 areas = this->getStringFromJson(json_tmp, "area");
620 if ((nullptr == category) || (nullptr == roles) || (nullptr == areas)) {
621 HMI_ERROR("wm:pm", "Parse Error!!");
625 // Parse roles by '|'
626 std::vector<std::string> vct_roles;
627 vct_roles = this->parseString(std::string(roles), '|');
629 // Parse areas by '|'
630 std::vector<std::string> vct_areas;
631 vct_areas = this->parseString(std::string(areas), '|');
633 // Set role, category, default area
634 for (auto itr = vct_roles.begin(); itr != vct_roles.end(); ++itr) {
635 // Delete space from role and area name
636 std::string role = this->deleteSpace(*itr);
637 std::string area = this->deleteSpace(vct_areas[0]);
639 this->role2category_[role] = std::string(category);
640 this->role2defaultarea_[role] = area;
643 this->category2role_[std::string(category)] = std::string(roles);
647 HMI_DEBUG("wm:pm", "Check role2category_");
648 for (auto& x:this->role2category_){
649 HMI_DEBUG("wm:pm", "key:%s, val:%s", x.first.c_str(), x.second.c_str());
652 HMI_DEBUG("wm:pm", "Check role2defaultarea_");
653 for (auto& x:this->role2defaultarea_){
654 HMI_DEBUG("wm:pm", "key:%s, val:%s", x.first.c_str(), x.second.c_str());
657 HMI_DEBUG("wm:pm", "Check category2role_");
658 for (auto& x:this->category2role_){
659 HMI_DEBUG("wm:pm", "key:%s, val:%s", x.first.c_str(), x.second.c_str());
666 // This function will be removed because json_helper has same function.
667 // json_helper should be library.
668 const char* PolicyManager::getStringFromJson(json_object* obj, const char* key) {
669 if ((nullptr == obj) || (nullptr == key)) {
670 HMI_ERROR("wm:pm", "Argument is nullptr!!!");
675 if (!json_object_object_get_ex(obj, key, &tmp)) {
676 HMI_DEBUG("wm:pm", "Not found key \"%s\"", key);
680 return json_object_get_string(tmp);
684 // This function will be removed because json_helper has same function.
685 // json_helper should be library.
686 int PolicyManager::inputJsonFilie(const char* file, json_object** obj) {
687 const int input_size = 128;
690 if ((nullptr == file) || (nullptr == obj)) {
691 HMI_ERROR("wm:jh", "Argument is nullptr!!!");
695 HMI_DEBUG("wm:jh", "Input file: %s", file);
698 FILE *fp = fopen(file, "rb");
700 HMI_ERROR("wm:jh", "Could not open file");
705 struct json_tokener *tokener = json_tokener_new();
706 enum json_tokener_error json_error;
707 char buffer[input_size];
710 size_t len = fread(buffer, sizeof(char), input_size, fp);
711 *obj = json_tokener_parse_ex(tokener, buffer, len);
712 if (nullptr != *obj) {
713 HMI_DEBUG("wm:jh", "File input is success");
718 json_error = json_tokener_get_error(tokener);
719 if ((json_tokener_continue != json_error)
720 || (input_size > len)) {
721 HMI_ERROR("wm:jh", "Failed to parse file (byte:%d err:%s)",
722 (input_size * block_cnt), json_tokener_error_desc(json_error));
723 HMI_ERROR("wm:jh", "\n%s", buffer);
734 json_tokener_free(tokener);
739 std::vector<std::string> PolicyManager::parseString(std::string str, char delimiter) {
740 // Parse string by delimiter
741 std::vector<std::string> vct;
742 std::stringstream ss{str};
744 while (std::getline(ss, buf, delimiter)) {
752 std::string PolicyManager::deleteSpace(std::string str) {
753 std::string ret = str;
755 while ((pos = ret.find_first_of(" ")) != std::string::npos) {
761 const char* kDefaultRoleDb = "{ \
764 \"category\": \"homescreen\", \
765 \"role\": \"homescreen\", \
766 \"area\": \"full\", \
769 \"category\": \"map\", \
771 \"area\": \"full | normal | split.main\", \
774 \"category\": \"general\", \
775 \"role\": \"poi | music | video | browser | sdl | settings | mixer | radio | hvac | dashboard | debug\", \
776 \"area\": \"normal\", \
779 \"category\": \"phone\", \
780 \"role\": \"phone\", \
781 \"area\": \"normal\", \
784 \"category\": \"splitable\", \
785 \"role\": \"splitable1 | splitable2\", \
786 \"area\": \"normal | split.main | split.sub\", \
789 \"category\": \"popup\", \
790 \"role\": \"popup\", \
791 \"area\": \"on_screen\", \
794 \"category\": \"system_alert\", \
795 \"role\": \"system_alert\", \
796 \"area\": \"on_screen\", \
799 \"category\": \"tbt\", \