Add new addStateToJson()
[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 typedef struct AreaState {
37     std::string name;
38     std::string category;
39     std::string role;
40 } AreaState;
41
42 typedef std::vector<AreaState> AreaList;
43 typedef struct LayoutState {
44     std::string name;
45     std::map<std::string, int> category_num;
46 //    int category_num[stm::gStmCategoryNoNum];
47     AreaList area_list;
48     std::map<std::string, std::vector<std::string>> role_history;
49 } LayoutState;
50
51 typedef struct LayerState {
52     std::string name;
53     LayoutState layout_state;
54 } LayerState;
55
56 struct sd_event* event_loop;
57 std::map<int, struct sd_event_source*> event_source_list;
58 std::map<int, std::string> g_event_info_list;
59 PolicyManager::CallbackTable callback;
60
61 std::unordered_map<std::string, LayerState> g_prv_layers;
62 std::unordered_map<std::string, LayerState> g_crr_layers;
63 std::unordered_map<std::string, LayerState> g_prv_layers_car_stop;
64 std::unordered_map<std::string, LayoutState> g_default_layouts;
65 }  // namespace pm
66
67
68 PolicyManager::PolicyManager() :
69   eventname2no_(),
70   categoryname2no_(),
71   areaname2no_(),
72   role2category_(),
73   category2role_(),
74   role2defaultarea_()
75 {
76     HMI_DEBUG("wm:pm", "Call");
77 }
78
79 int PolicyManager::initialize() {
80     HMI_DEBUG("wm:pm", "Call");
81
82     int ret = 0;
83
84     // Create convert map
85     for (unsigned int i=0; i<STM_NUM_EVT; i++) {
86         HMI_DEBUG("wm:pm", "event name:%s no:%d", stm::gStmEventName[i], stm::gStmEventNo[i]);
87         this->eventname2no_[stm::gStmEventName[i]] = stm::gStmEventNo[i];
88     }
89
90     for (int i = stm::gStmCategoryNoMin; i <= stm::gStmCategoryNoMax; i++) {
91         HMI_DEBUG("wm:pm", "category name:%s no:%d", stm::gStmCategoryName[i], stm::gStmCategoryNo[i]);
92         this->categoryname2no_[stm::gStmCategoryName[i]] = stm::gStmCategoryNo[i];
93     }
94
95     for (unsigned int i=0; i<STM_NUM_ARA; i++) {
96         HMI_DEBUG("wm:pm", "area name:%s no:%d", stm::gStmAreaName[i], stm::gStmAreaNo[i]);
97         this->areaname2no_[stm::gStmAreaName[i]] = stm::gStmAreaNo[i];
98     }
99
100     // Load role.db
101     ret = this->loadRoleDb();
102     if (0 > ret) {
103         HMI_ERROR("wm:pm", "Load role.db Error!!");
104         return ret;
105     }
106
107     // Load layout.db
108     ret = this->loadLayoutDb();
109     if (0 > ret) {
110         HMI_ERROR("wm:pm", "Load layout.db Error!!");
111         return ret;
112     }
113
114     // Initialize current/previous state of layers
115     pm::AreaState init_area;
116     pm::LayoutState init_layout;
117     init_area.name     = "none";
118     init_area.category = "none";
119     init_area.role     = "none";
120     init_layout.area_list.push_back(init_area);
121
122     for (int i = stm::gStmLayerNoMin; i <= stm::gStmLayerNoMax; i++) {
123         const char* layer_name = stm::gStmLayerName[i];
124         pm::g_crr_layers[layer_name].name          = layer_name;
125         pm::g_crr_layers[layer_name].layout_state  = init_layout;
126     }
127
128     pm::g_prv_layers = pm::g_crr_layers;
129
130     // Initialize StateTransitioner
131     stm::stmInitialize();
132
133     // Initialize sd_event loop
134     ret = this->initializeSdEventLoop();
135     if (0 > ret) {
136         HMI_ERROR("wm:pm", "Failed to initializeSdEventLoop!!");
137         return ret;
138     }
139
140     return ret;
141 }
142
143 int PolicyManager::initializeSdEventLoop() {
144     // Get default event loop object
145     int ret = sd_event_new(&(pm::event_loop));
146     if (0 > ret) {
147         HMI_ERROR("wm:pm", "Faild to sd_event_default: errno:%d", ret);
148         return -1;
149     }
150
151     // Create thread for sd_event and detach
152     std::thread sd_event_loop([this]() {
153         while (1) {
154             sd_event_run(pm::event_loop, 1000);
155         }
156     });
157     sd_event_loop.detach();
158
159     return 0;
160 }
161
162 static void addStateToJson(
163   const char* key, int is_changed, const char* state, json_object** json_out) {
164     if ((nullptr == key) || (nullptr == state) || (nullptr == json_out)) {
165         HMI_ERROR("wm:pm", "Argument is nullptr!!!");
166         return;
167     }
168
169     json_object* json_obj = json_object_new_object();
170     json_object_object_add(json_obj, "is_changed", json_object_new_boolean(is_changed));
171     if (is_changed) {
172         HMI_DEBUG("wm:pm", "%s: state changed (%s)", key, state);
173         json_object_object_add(json_obj, "state", json_object_new_string(state));
174     }
175     json_object_object_add(*json_out, key, json_obj);
176 }
177
178 static void addStateToJson(const char* layer_name, unsigned int changed,
179                            pm::AreaList area_list, json_object** json_out) {
180     if ((nullptr == layer_name) || (1 < changed) || (nullptr == json_out)) {
181         HMI_ERROR("wm:pm", "Invalid argument!!!");
182         return;
183     }
184
185     json_object* json_areas = json_object_new_array();
186     json_object* json_tmp;
187     for (pm::AreaState as : area_list) {
188         json_tmp = json_object_new_object();
189         json_object_object_add(json_tmp, "name", json_object_new_string(as.name.c_str()));
190         json_object_object_add(json_tmp, "role", json_object_new_string(as.role.c_str()));
191         json_object_array_add(json_areas, json_tmp);
192     }
193
194     json_object_object_add(*json_out, "name", json_object_new_string(layer_name));
195     json_object_object_add(*json_out, "changed", json_object_new_boolean(changed));
196     json_object_object_add(*json_out, "areas", json_areas);
197 }
198
199
200 static int checkPolicyEntry(int event, uint64_t delay_ms, const char* role);
201 static int checkPolicy(sd_event_source *source, void *data) {
202     HMI_DEBUG("wm:pm", "Call");
203     HMI_DEBUG("wm:pm", ">>>>>>>>>> START CHECK POLICY");
204
205     int event_data = *((int*)data);
206
207     int event_no, category_no, area_no;
208     event_no    = (event_data & STM_MSK_EVT_NO) - 1;
209     category_no = ((event_data & STM_MSK_CTG_NO) >> 8) - 1;
210     area_no     = ((event_data & STM_MSK_ARA_NO) >> 16) - 1;
211     HMI_DEBUG("wm:pm", ">>>>>>>>>> event:%s category:%s area:%s",
212               stm::gStmEventName[event_no],
213               stm::gStmCategoryName[category_no],
214               stm::gStmAreaName[area_no]);
215
216     // Transition state
217     stm::stm_state_t crr_state;
218     int ret = stm::stmTransitionState(event_data, &crr_state);
219     if (0 > ret) {
220         HMI_ERROR("wm:pm", "Error!!");
221         return -1;
222     }
223
224     HMI_DEBUG("wm:pm", "parking brake state     (is_changed:%d state:%d:%s)",
225               crr_state.parking_brake.is_changed,
226               crr_state.parking_brake.state,
227               stm::gStmParkingBrakeStateNo2Name[crr_state.parking_brake.state]);
228     HMI_DEBUG("wm:pm", "accelerator pedal state (is_changed:%d state:%d:%s)",
229               crr_state.accel_pedal.is_changed,
230               crr_state.accel_pedal.state,
231               stm::gStmAccelPedalStateNo2Name[crr_state.accel_pedal.state]);
232     HMI_DEBUG("wm:pm", "lightstatus brake state (is_changed:%d state:%d:%s)",
233               crr_state.lightstatus_brake.is_changed,
234               crr_state.lightstatus_brake.state,
235               stm::gStmLightstatusBrakeStateNo2Name[crr_state.lightstatus_brake.state]);
236     HMI_DEBUG("wm:pm", "car state               (is_changed:%d state:%d:%s)",
237               crr_state.car.is_changed,
238               crr_state.car.state,
239               stm::gStmCarStateNo2Name[crr_state.car.state]);
240     HMI_DEBUG("wm:pm", "lamp state              (is_changed:%d state:%d:%s)",
241               crr_state.lamp.is_changed,
242               crr_state.lamp.state,
243               stm::gStmLampStateNo2Name[crr_state.lamp.state]);
244     HMI_DEBUG("wm:pm", "restriction mode state  (is_changed:%d state:%d:%s)",
245               crr_state.restriction_mode.is_changed,
246               crr_state.restriction_mode.state,
247               stm::gStmRestrictionModeStateNo2Name[crr_state.restriction_mode.state]);
248     HMI_DEBUG("wm:pm", "homescreen state        (is_changed:%d state:%d:%s)",
249               crr_state.layer[stm::gStmLayerNoHomescreen].is_changed,
250               crr_state.layer[stm::gStmLayerNoHomescreen].state,
251               stm::gStmLayoutNo2Name[crr_state.layer[stm::gStmLayerNoHomescreen].state]);
252     HMI_DEBUG("wm:pm", "apps state              (is_changed:%d state:%d:%s)",
253               crr_state.layer[stm::gStmLayerNoApps].is_changed,
254               crr_state.layer[stm::gStmLayerNoApps].state,
255               stm::gStmLayoutNo2Name[crr_state.layer[stm::gStmLayerNoApps].state]);
256     HMI_DEBUG("wm:pm", "restriction state       (is_changed:%d state:%d:%s)",
257               crr_state.layer[stm::gStmLayerNoRestriction].is_changed,
258               crr_state.layer[stm::gStmLayerNoRestriction].state,
259               stm::gStmLayoutNo2Name[crr_state.layer[stm::gStmLayerNoRestriction].state]);
260     HMI_DEBUG("wm:pm", "on_screen state         (is_changed:%d state:%d:%s)",
261               crr_state.layer[stm::gStmLayerNoOnScreen].is_changed,
262               crr_state.layer[stm::gStmLayerNoOnScreen].state,
263               stm::gStmLayoutNo2Name[crr_state.layer[stm::gStmLayerNoOnScreen].state]);
264
265 #if 1 // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
266     // Store previous layers
267     pm::g_prv_layers = pm::g_crr_layers;
268
269     std::string req_role = pm::g_event_info_list[event_data];
270     std::string req_evt = std::string(stm::gStmEventName[event_no]);
271     std::string req_ctg = std::string(stm::gStmCategoryName[category_no]);
272     std::string req_area = std::string(stm::gStmAreaName[area_no]);
273     HMI_DEBUG("wm:pm", "REQ: event:%s role%s category:%s area:%s",
274         req_evt.c_str(), req_role.c_str(), req_ctg.c_str(), req_area.c_str());
275
276     // Update layers
277     for (int layer_no = stm::gStmLayerNoMin;
278          layer_no <= stm::gStmLayerNoMax; layer_no++) {
279         const char* layer_name = stm::gStmLayerName[layer_no];
280         HMI_DEBUG("wm:pm", "LAYER:%s", layer_name);
281
282 #if 1
283         // If restriction mode is changed off -> on,
284         // store current state for state of restriction mode off
285         if ((crr_state.restriction_mode.is_changed)
286             && (stm::gStmRestrictionModeStateNoOn == crr_state.restriction_mode.state)) {
287             HMI_DEBUG("wm:lm", "Store current state for state of restriction mode off");
288             pm::g_prv_layers_car_stop[layer_name] = pm::g_crr_layers[layer_name];
289         }
290 #else
291         // If car state is changed car_stop -> car_run,
292         // store current state for state of car stop
293         if ((crr_state.car.is_changed)
294             && (stm::gStmCarStateNoRun == crr_state.car.state)) {
295             HMI_DEBUG("wm:lm", "Store current state for state of car stop");
296             pm::g_prv_layers_car_stop[layer_name] = pm::g_crr_layers[layer_name];
297         }
298 #endif
299
300
301         // This layer is changed?
302         if (crr_state.layer[layer_no].is_changed) {
303             // Get previous layout name of this layer
304             pm::LayoutState prv_layout_state =  pm::g_prv_layers[layer_name].layout_state;
305             std::string prv_layout_name = prv_layout_state.name;
306
307             // Get current layout name of this layer
308             int crr_layout_state_no =  crr_state.layer[layer_no].state;
309             std::string crr_layout_name = std::string(stm::gStmLayoutNo2Name[crr_layout_state_no]);
310
311             pm::LayoutState crr_layout_state;
312 #if 1
313             if ((crr_state.restriction_mode.is_changed)
314                 && (stm::gStmRestrictionModeStateNoOff == crr_state.restriction_mode.state)) {
315                 // If restriction mode is changed on -> off,
316                 // restore state of restriction mode off
317                 HMI_DEBUG("wm:lm", "Restriction mode is changed on -> off, so restore state of restriction mode off");
318                 crr_layout_state = pm::g_prv_layers_car_stop[layer_name].layout_state;
319 #else
320             if ((crr_state.car.is_changed)
321                 && (stm::gStmCarStateNoStop == crr_state.car.state)) {
322                 // If car state is changed car_run -> car_stop,
323                 // restore state of car stop
324                 HMI_DEBUG("wm:lm", "Car state is changed car_run -> car_stop, so restore state of car stop");
325                 crr_layout_state = pm::g_prv_layers_car_stop[layer_name].layout_state;
326 #endif
327             }
328             else {
329                 // Copy previous layout state for current
330                 crr_layout_state = prv_layout_state;
331
332                 if (prv_layout_name == crr_layout_name) {
333                     HMI_DEBUG("wm:lm", "Previous layout is same with current");
334                 }
335                 else {
336                     // If previous layout is NOT same with current,
337                     // current areas is set with default value
338                     HMI_DEBUG("wm:lm", "Previous layout is NOT same with current");
339                     crr_layout_state.name         = pm::g_default_layouts[crr_layout_name].name;
340                     crr_layout_state.category_num = pm::g_default_layouts[crr_layout_name].category_num;
341                     crr_layout_state.area_list    = pm::g_default_layouts[crr_layout_name].area_list;
342                 }
343
344                 // Create candidate list
345                 std::map<std::string, pm::AreaList> cand_list;
346                 for (int ctg_no=stm::gStmCategoryNoMin;
347                      ctg_no<=stm::gStmCategoryNoMax; ctg_no++) {
348                     const char* ctg = stm::gStmCategoryName[ctg_no];
349                     HMI_DEBUG("wm:pm", "ctg:%s", ctg);
350
351                     // Create candidate list for category from the previous displayed categories
352                     pm::AreaList tmp_cand_list;
353                     for (pm::AreaState area_state : prv_layout_state.area_list) {
354                         if (std::string(ctg) == area_state.category) {
355                             // If there is the category which is same with new category in previous layout,
356                             // push it to list
357                             HMI_DEBUG("wm:pm", "Push to candidate list category:%s role:%s",
358                                       area_state.category.c_str(), area_state.role.c_str());
359                             tmp_cand_list.push_back(area_state);
360                         }
361                     }
362
363                     int candidate_num = prv_layout_state.category_num[ctg];
364                     int blank_num = crr_layout_state.category_num[ctg];
365                     HMI_DEBUG("wm:pm", "blank_num:%d candidate_num:%d", blank_num, candidate_num);
366
367                     // If requested event is "activate"
368                     // and there are requested category and area,
369                     // update area with requested role in current layout.
370                     bool request_for_this_layer = false;
371                     bool updated = false;
372                     if ((ctg == req_ctg) && ("activate" == req_evt) ) {
373                         HMI_DEBUG("wm:pm", "requested event is activate");
374                         for (pm::AreaState &as : crr_layout_state.area_list) {
375                             if (as.category == req_ctg) {
376                                 request_for_this_layer = true;
377
378                                 if (as.name == req_area) {
379                                     HMI_DEBUG("wm:pm", "Update current layout: area:%s category:%s role:%s",
380                                               as.name.c_str(), as.category.c_str(), as.role.c_str());
381                                     as.role = req_role;
382                                     blank_num--;
383                                     updated = true;
384                                     break;
385                                 }
386                             }
387                         }
388
389                         // If NOT updated: there is not requested area in new layout, 
390                         // so push requested role to candidate list
391                         if (request_for_this_layer && (!updated)) {
392                             HMI_DEBUG("wm:pm", "Push request to candidate list");
393                             pm::AreaState area_state;
394                             area_state.name = req_area;
395                             area_state.category = req_ctg;
396                             area_state.role = req_role;
397                             tmp_cand_list.push_back(area_state);
398                         }
399                     }
400
401                     // Compare number of candidate/blank,
402                     // And remove role in order of the oldest as necessary
403                     if (candidate_num < blank_num) {
404                         // Refer history stack
405                         // and add to the top of tmp_cand_list in order to the newest
406                         while (candidate_num != blank_num) {
407                             pm::AreaState area_state;
408                             area_state.name = "";
409                             area_state.category = ctg;
410                             if (0 != crr_layout_state.role_history[ctg].size()) {
411                                 HMI_ERROR("wm:pm", "Use role in history stack:%s",
412                                           crr_layout_state.role_history[ctg].back().c_str());
413                                 area_state.role = crr_layout_state.role_history[ctg].back();
414                                 crr_layout_state.role_history[ctg].pop_back();
415                             }
416                             else {
417                                 HMI_ERROR("wm:pm", "There is no role in history stack!!");
418                                 area_state.role = "";
419                             }
420                             tmp_cand_list.push_back(area_state);
421                             candidate_num++;
422                         }
423                     }
424                     else if (candidate_num > blank_num) {
425                         HMI_DEBUG("wm:pm", "candidate_num > blank_num");
426
427                         // Remove the oldest role from candidate list
428                         while (candidate_num != blank_num) {
429                             std::string removed_role = tmp_cand_list.begin()->role;
430                             HMI_DEBUG("wm:pm", "Remove the oldest data(role:%s) from tmp_cand_list",
431                                       removed_role.c_str());
432                             tmp_cand_list.erase(tmp_cand_list.begin());
433                             candidate_num--;
434
435                             // Push removed data to history stack
436                             crr_layout_state.role_history[ctg].push_back(removed_role);
437                         }
438                     }
439                     else {  // (candidate_num == blank_num)
440                         // nop
441                     }
442
443                     cand_list[ctg] = tmp_cand_list;
444                 }
445
446                 // Update areas
447                 for (pm::AreaState &as : crr_layout_state.area_list) {
448                     HMI_DEBUG("wm:pm", "Current area info area:%s category:%s",
449                               as.name.c_str(), as.category.c_str());
450                     if ("" == as.role) {
451                         HMI_DEBUG("wm:pm", "Update this area with role:%s",
452                                   cand_list[as.category].begin()->role.c_str());
453                         as.role = cand_list[as.category].begin()->role;
454                         cand_list[as.category].erase(cand_list[as.category].begin());
455                     }
456                 }
457             }
458             // Update current layout of this layer
459             pm::g_crr_layers[layer_name].layout_state = crr_layout_state;
460         }
461     }
462
463     // Check
464     for (auto itr : pm::g_crr_layers) {
465         pm::LayerState ls = itr.second;
466         HMI_DEBUG("wm:pm", ">>> LAYER:%s",ls.name.c_str());
467         HMI_DEBUG("wm:pm", ">>> >>> LAYOUT:%s", ls.layout_state.name.c_str());
468
469         for (pm::AreaState as : ls.layout_state.area_list) {
470             HMI_DEBUG("wm:pm", ">>> >>> >>> AREA:%s", as.name.c_str());
471             HMI_DEBUG("wm:pm", ">>> >>> >>> >>> CTG:%s", as.category.c_str());
472             HMI_DEBUG("wm:pm", ">>> >>> >>> >>> ROLE:%s", as.role.c_str());
473         }
474     }
475 #endif // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
476
477     json_object* json_out = json_object_new_object();
478
479     // Create result
480     // {
481     //     "parking_brake": {
482     //         "is_changed": <bool>,
483     //         "state": <const char*>
484     //     },
485     addStateToJson("parking_brake",
486                    crr_state.parking_brake.is_changed,
487                    stm::gStmParkingBrakeStateNo2Name[crr_state.parking_brake.state],
488                    &json_out);
489
490     //     "accel_pedal": {
491     //         "is_changed": <bool>,
492     //         "state": <const char*>
493     //     },
494     addStateToJson("accel_pedal",
495                    crr_state.accel_pedal.is_changed,
496                    stm::gStmAccelPedalStateNo2Name[crr_state.accel_pedal.state],
497                    &json_out);
498
499     //     "lightstatus_brake": {
500     //         "is_changed": <bool>,
501     //         "state": <const char*>
502     //     },
503     addStateToJson("lightstatus_brake",
504                    crr_state.lightstatus_brake.is_changed,
505                    stm::gStmLightstatusBrakeStateNo2Name[crr_state.lightstatus_brake.state],
506                    &json_out);
507
508     //     "car": {
509     //         "is_changed": <bool>,
510     //         "state": <const char*>
511     //     },
512     addStateToJson("car",
513                    crr_state.car.is_changed,
514                    stm::gStmCarStateNo2Name[crr_state.car.state],
515                    &json_out);
516
517     //     "lamp": {
518     //         "is_changed": <bool>,
519     //         "state": <const char*>
520     //     },
521     addStateToJson("lamp",
522                    crr_state.lamp.is_changed,
523                    stm::gStmLampStateNo2Name[crr_state.lamp.state],
524                    &json_out);
525
526     //     "restriction_mode": {
527     //         "is_changed": <bool>,
528     //         "state": <const char*>
529     //     },
530     addStateToJson("restriction_mode",
531                    crr_state.restriction_mode.is_changed,
532                    stm::gStmRestrictionModeStateNo2Name[crr_state.restriction_mode.state],
533                    &json_out);
534
535     //     "layers": [
536     json_object* json_layer = json_object_new_array();
537     json_object* json_tmp;
538
539     //         {
540     //             "homescreen": {
541     //                 "changed": <bool>,
542     //                 "areas": [
543     //                 {
544     //                     "name":<const char*>,
545     //                     "role":<const char*>
546     //                 }.
547     //                 ...
548     //                 ]
549     //             }
550     //         },
551     json_tmp = json_object_new_object();
552     addStateToJson("homescreen",
553                    crr_state.layer[stm::gStmLayerNoHomescreen].is_changed,
554 #if 1
555                    pm::g_crr_layers["homescreen"].layout_state.name.c_str(),
556 #else
557                    stm::gStmLayoutNo2Name[crr_state.layer[stm::gStmLayerNoHomescreen].state],
558 #endif
559                    &json_tmp);
560     json_object_array_add(json_layer, json_tmp);
561
562     //         {
563     //             "apps": {
564     //                 "is_changed": <bool>,
565     //                 "state": <const char*>
566     //             }
567     //         },
568     json_tmp = json_object_new_object();
569     addStateToJson("apps",
570                    crr_state.layer[stm::gStmLayerNoApps].is_changed,
571 #if 1
572                    pm::g_crr_layers["apps"].layout_state.name.c_str(),
573 #else
574                    stm::gStmLayoutNo2Name[crr_state.layer[stm::gStmLayerNoApps].state],
575 #endif
576                    &json_tmp);
577     json_object_array_add(json_layer, json_tmp);
578
579     //         {
580     //             "restriction": {
581     //                 "is_changed": <bool>,
582     //                 "state": <const char*>
583     //             }
584     //         },
585     json_tmp = json_object_new_object();
586     addStateToJson("restriction",
587                    crr_state.layer[stm::gStmLayerNoRestriction].is_changed,
588 #if 1
589                    pm::g_crr_layers["restriction"].layout_state.name.c_str(),
590 #else
591                    stm::gStmLayoutNo2Name[crr_state.layer[stm::gStmLayerNoRestriction].state],
592 #endif
593                    &json_tmp);
594     json_object_array_add(json_layer, json_tmp);
595
596     //         {
597     //             "on_screen": {
598     //                 "is_changed": <bool>,
599     //                 "state": <const char*>
600     //             }
601     //         },
602     json_tmp = json_object_new_object();
603     addStateToJson("on_screen",
604                    crr_state.layer[stm::gStmLayerNoOnScreen].is_changed,
605 #if 1
606                    pm::g_crr_layers["on_screen"].layout_state.name.c_str(),
607 #else
608                    stm::gStmLayoutNo2Name[crr_state.layer[stm::gStmLayerNoOnScreen].state],
609 #endif
610                    &json_tmp);
611     json_object_array_add(json_layer, json_tmp);
612
613     // Add json array of layer
614     json_object_object_add(json_out, "layers", json_layer);
615
616     // Notify state is changed
617     if (nullptr != pm::callback.onStateTransitioned) {
618         pm::callback.onStateTransitioned(json_out);
619     }
620
621     if (crr_state.car.is_changed) {
622         if (stm::gStmCarStateNoRun == crr_state.car.state) {
623             // Set delay event(restriction mode on)
624             checkPolicyEntry(STM_EVT_NO_RESTRICTION_MODE_ON, 3000, nullptr);
625         }
626         else if (stm::gStmCarStateNoStop == crr_state.car.state) {
627             // Set event(restriction mode off)
628             checkPolicyEntry(STM_EVT_NO_RESTRICTION_MODE_OFF, 0, nullptr);
629
630             // Stop timer for restriction on event
631             if (pm::event_source_list.find(STM_EVT_NO_RESTRICTION_MODE_ON)
632               != pm::event_source_list.end()) {
633                 HMI_DEBUG("wm:pm", "Stop timer for restriction on");
634                 sd_event_source *event_source
635                     = pm::event_source_list[STM_EVT_NO_RESTRICTION_MODE_ON];
636                 int ret = sd_event_source_set_enabled(event_source, SD_EVENT_OFF);
637                 if (0 > ret) {
638                     HMI_ERROR("wm:pm", "Failed to stop timer");
639                 }
640             }
641         }
642     }
643
644     // Release json_object
645     json_object_put(json_out);
646
647     // Release data
648     delete (int*)data;
649
650     // Destroy sd_event_source object
651     sd_event_source_unref(source);
652
653     // Remove event source from list
654     if (pm::event_source_list.find(event_data) != pm::event_source_list.end()) {
655         pm::event_source_list.erase(event_data);
656     }
657
658     HMI_DEBUG("wm:pm", ">>>>>>>>>> FINISH CHECK POLICY");
659     return 0;
660 }
661
662 static int timerEvent(sd_event_source *source, uint64_t usec, void *data) {
663     int ret = checkPolicy(source, data);
664     return ret;
665 };
666
667 static int checkPolicyEntry(int event, uint64_t delay_ms, const char* role)
668 {
669     HMI_DEBUG("wm:pm", "Call");
670     HMI_DEBUG("wm:pm", "event:0x%x", event);
671
672     // Store event info
673     if (nullptr == role) {
674         pm::g_event_info_list[event] = std::string("");
675     }
676     else {
677         pm::g_event_info_list[event] = std::string(role);
678     }
679
680     if (0 == delay_ms) {
681         int ret = sd_event_add_defer(pm::event_loop, NULL,
682                                      &checkPolicy, new int(event));
683         if (0 > ret) {
684             HMI_ERROR("wm:pm", "Faild to sd_event_add_defer: errno:%d", ret);
685             pm::g_event_info_list.erase(event);
686             return -1;
687         }
688     }
689     else {
690         // Get current time
691         struct timespec time_spec;
692         clock_gettime(CLOCK_MONOTONIC, &time_spec);
693
694         // Calculate timer fired time
695         uint64_t usec = (time_spec.tv_sec * 1000000)
696             + (time_spec.tv_nsec / 1000)
697             + (delay_ms * 1000);
698
699         // Set timer
700         struct sd_event_source* event_source;
701         int ret = sd_event_add_time(pm::event_loop, &event_source, CLOCK_MONOTONIC, usec, 1,
702                                     &timerEvent, new int(event));
703         if (0 > ret) {
704             HMI_ERROR("wm:pm", "Faild to sd_event_add_time: errno:%d", ret);
705             pm::g_event_info_list.erase(event);
706             return -1;
707         }
708
709         // Store event source
710         pm::event_source_list[event] = event_source;
711     }
712
713     return 0;
714 }
715
716 void PolicyManager::registerCallback(CallbackTable callback) {
717     pm::callback.onStateTransitioned = callback.onStateTransitioned;
718     pm::callback.onError             = callback.onError;
719 }
720
721 int PolicyManager::inputEvent(json_object* json_in) {
722     HMI_DEBUG("wm:pm", "Call");
723
724     // Check arguments
725     if (nullptr == json_in) {
726         HMI_ERROR("wm:pm", "Argument is NULL!!");
727         return -1;
728     }
729
730     // Get event from json_object
731     const char* event = this->getStringFromJson(json_in, "event");
732     int event_no = 0;
733     if (nullptr != event) {
734         // Convert name to number
735         event_no = this->eventname2no_[event];
736         HMI_DEBUG("wm:pm", "event(%s:%d)", event, event_no);
737     }
738
739     // Get role from json_object
740     const char* role = this->getStringFromJson(json_in, "role");
741     int category_no = 0;
742     if (nullptr != role) {
743         HMI_DEBUG("wm:pm", "role(%s)", role);
744
745         // Convert role to category
746         const char* category = this->role2category_[role].c_str();
747         if (0 == strcmp("", category)) {
748             HMI_ERROR("wm:pm", "Error!!");
749             return -1;
750         }
751         HMI_DEBUG("wm:pm", "category(%s)", category);
752
753         // Convert name to number
754         category_no = categoryname2no_[category];
755         HMI_DEBUG("wm:pm", "role(%s), category(%s:%d)", role, category, category_no);
756     }
757
758     // Get areat from json_object
759     const char* area = this->getStringFromJson(json_in, "area");
760     int area_no = 0;
761     if (nullptr != area) {
762         // Convert name to number
763         area_no = areaname2no_[area];
764         HMI_DEBUG("wm:pm", "area(%s:%d)", area, area_no);
765     }
766
767     // Check policy
768     checkPolicyEntry((event_no | category_no | area_no), 0, role);
769
770     return 0;
771 }
772
773 std::string PolicyManager::roleToCategory(const char* role) {
774     return this->role2category_[role];
775 }
776
777 extern const char* kDefaultRoleDb;
778 int PolicyManager::loadRoleDb() {
779     HMI_DEBUG("wm:pm", "Call");
780
781     std::string file_name;
782
783     // Get afm application installed dir
784     char const *afm_app_install_dir = getenv("AFM_APP_INSTALL_DIR");
785     HMI_DEBUG("wm:pm", "afm_app_install_dir:%s", afm_app_install_dir);
786
787     if (!afm_app_install_dir) {
788         HMI_ERROR("wm:pm", "AFM_APP_INSTALL_DIR is not defined");
789     }
790     else {
791         file_name = std::string(afm_app_install_dir) + std::string("/etc/role.db");
792     }
793
794     // Load role.db
795     json_object* json_obj;
796     int ret = this->inputJsonFilie(file_name.c_str(), &json_obj);
797     if (0 > ret) {
798         HMI_ERROR("wm:pm", "Could not open role.db, so use default role information");
799         json_obj = json_tokener_parse(kDefaultRoleDb);
800     }
801     HMI_DEBUG("wm:pm", "json_obj dump:%s", json_object_get_string(json_obj));
802
803     json_object* json_roles;
804     if (!json_object_object_get_ex(json_obj, "roles", &json_roles)) {
805         HMI_ERROR("wm:pm", "Parse Error!!");
806         return -1;
807     }
808
809     int len = json_object_array_length(json_roles);
810     HMI_DEBUG("wm:pm", "json_cfg len:%d", len);
811     HMI_DEBUG("wm:pm", "json_cfg dump:%s", json_object_get_string(json_roles));
812
813     json_object* json_tmp;
814     const char* category;
815     const char* roles;
816     const char* areas;
817     for (int i=0; i<len; i++) {
818         json_tmp = json_object_array_get_idx(json_roles, i);
819
820         category = this->getStringFromJson(json_tmp, "category");
821         roles =  this->getStringFromJson(json_tmp, "role");
822         areas =  this->getStringFromJson(json_tmp, "area");
823
824         if ((nullptr == category) || (nullptr == roles) || (nullptr == areas)) {
825             HMI_ERROR("wm:pm", "Parse Error!!");
826             return -1;
827         }
828
829         // Parse roles by '|'
830         std::vector<std::string> vct_roles;
831         vct_roles = this->parseString(std::string(roles), '|');
832
833         // Parse areas by '|'
834         std::vector<std::string> vct_areas;
835         vct_areas = this->parseString(std::string(areas), '|');
836
837         // Set role, category, default area
838         for (auto itr = vct_roles.begin(); itr != vct_roles.end(); ++itr) {
839             // Delete space from role and area name
840             std::string role = this->deleteSpace(*itr);
841             std::string area = this->deleteSpace(vct_areas[0]);
842
843             this->role2category_[role] = std::string(category);
844             this->role2defaultarea_[role] = area;
845         }
846
847         this->category2role_[std::string(category)] = std::string(roles);
848     }
849
850     // Check
851     HMI_DEBUG("wm:pm", "Check role2category_");
852     for (auto& x:this->role2category_){
853         HMI_DEBUG("wm:pm", "key:%s, val:%s", x.first.c_str(), x.second.c_str());
854     }
855
856     HMI_DEBUG("wm:pm", "Check role2defaultarea_");
857     for (auto& x:this->role2defaultarea_){
858         HMI_DEBUG("wm:pm", "key:%s, val:%s", x.first.c_str(), x.second.c_str());
859     }
860
861     HMI_DEBUG("wm:pm", "Check category2role_");
862     for (auto& x:this->category2role_){
863         HMI_DEBUG("wm:pm", "key:%s, val:%s", x.first.c_str(), x.second.c_str());
864     }
865
866     return 0;
867 }
868
869 extern const char* kDefaultLayoutDb;
870 int PolicyManager::loadLayoutDb() {
871     HMI_DEBUG("wm:lm", "Call");
872
873     // Get afm application installed dir
874     char const *afm_app_install_dir = getenv("AFM_APP_INSTALL_DIR");
875     HMI_DEBUG("wm:pm", "afm_app_install_dir:%s", afm_app_install_dir);
876
877     std::string file_name;
878     if (!afm_app_install_dir) {
879         HMI_ERROR("wm:pm", "AFM_APP_INSTALL_DIR is not defined");
880     }
881     else {
882         file_name = std::string(afm_app_install_dir) + std::string("/etc/layout.db");
883     }
884
885     // Load layout.db
886     json_object* json_obj;
887     int ret = this->inputJsonFilie(file_name.c_str(), &json_obj);
888     if (0 > ret) {
889         HMI_DEBUG("wm:pm", "Could not open layout.db, so use default layout information");
890         json_obj = json_tokener_parse(kDefaultLayoutDb);
891     }
892     HMI_DEBUG("wm:pm", "json_obj dump:%s", json_object_get_string(json_obj));
893
894     // Perse layouts
895     HMI_DEBUG("wm:pm", "Perse layouts");
896     json_object* json_cfg;
897     if (!json_object_object_get_ex(json_obj, "layouts", &json_cfg)) {
898         HMI_ERROR("wm:pm", "Parse Error!!");
899         return -1;
900     }
901
902     int len = json_object_array_length(json_cfg);
903     HMI_DEBUG("wm:pm", "json_cfg len:%d", len);
904     HMI_DEBUG("wm:pm", "json_cfg dump:%s", json_object_get_string(json_cfg));
905
906     const char* layout;
907     const char* role;
908     const char* category;
909     for (int i=0; i<len; i++) {
910         json_object* json_tmp = json_object_array_get_idx(json_cfg, i);
911
912         layout = this->getStringFromJson(json_tmp, "name");
913         if (nullptr == layout) {
914             HMI_ERROR("wm:pm", "Parse Error!!");
915             return -1;
916         }
917         HMI_DEBUG("wm:pm", "> layout:%s", layout);
918
919         json_object* json_area_array;
920         if (!json_object_object_get_ex(json_tmp, "areas", &json_area_array)) {
921           HMI_ERROR("wm:pm", "Parse Error!!");
922           return -1;
923         }
924
925         int len_area = json_object_array_length(json_area_array);
926         HMI_DEBUG("wm:pm", "json_area_array len:%d", len_area);
927         HMI_DEBUG("wm:pm", "json_area_array dump:%s", json_object_get_string(json_area_array));
928
929         pm::LayoutState layout_state;
930         pm::AreaState area_state;
931         std::map<std::string, int> category_num;
932         for (int ctg_no = stm::gStmCategoryNoMin;
933              ctg_no <= stm::gStmCategoryNoMax; ctg_no++) {
934             const char* ctg_name = stm::gStmCategoryName[ctg_no];
935             category_num[ctg_name] = 0;
936         }
937
938         for (int j=0; j<len_area; j++) {
939             json_object* json_area = json_object_array_get_idx(json_area_array, j);
940
941             // Get area name
942             const char* area = this->getStringFromJson(json_area, "name");
943             if (nullptr == area) {
944               HMI_ERROR("wm:pm", "Parse Error!!");
945               return -1;
946             }
947             area_state.name = std::string(area);
948             HMI_DEBUG("wm:pm", ">> area:%s", area);
949
950             // Get app attribute of the area
951             category = this->getStringFromJson(json_area, "category");
952             if (nullptr == category) {
953                 HMI_ERROR("wm:pm", "Parse Error!!");
954                 return -1;
955             }
956             area_state.category = std::string(category);
957             category_num[category]++;
958             HMI_DEBUG("wm:pm", ">>> category:%s", category);
959
960             role = this->getStringFromJson(json_area, "role");
961             if (nullptr != role) {
962                 // Role is NOT essential here
963                 area_state.role = std::string(role);
964             }
965             else {
966                 area_state.role = std::string("");
967
968             }
969             HMI_DEBUG("wm:pm", ">>> role:%s", role);
970
971             layout_state.area_list.push_back(area_state);
972
973         }
974
975         layout_state.name = layout;
976         layout_state.category_num = category_num;
977         pm::g_default_layouts[layout] = layout_state;
978     }
979
980     // initialize for none layout
981     pm::LayoutState none_layout_state;
982     memset(&none_layout_state, 0, sizeof(none_layout_state));
983     none_layout_state.name                 = "none";
984     pm::g_default_layouts["none"] = none_layout_state;
985
986     // Check
987     for(auto itr_layout = pm::g_default_layouts.begin();
988       itr_layout != pm::g_default_layouts.end(); ++itr_layout) {
989         HMI_DEBUG("wm:pm", ">>> layout:%s", itr_layout->first.c_str());
990
991         for (auto itr_area = itr_layout->second.area_list.begin();
992           itr_area != itr_layout->second.area_list.end(); ++itr_area) {
993             HMI_DEBUG("wm:pm", ">>> >>> area    :%s", itr_area->name.c_str());
994             HMI_DEBUG("wm:pm", ">>> >>> category:%s", itr_area->category.c_str());
995             HMI_DEBUG("wm:pm", ">>> >>> role    :%s", itr_area->role.c_str());
996 #if 0
997             for (auto itr_role = itr_area->second.begin();
998               itr_role != itr_area->second.end(); ++itr_role) {
999                 HMI_DEBUG("wm:pm", ">>> >>> >>> attribute:%s, name:%s",
1000                           itr_role->first.c_str(), itr_role->second.c_str());
1001             }
1002 #endif
1003         }
1004     }
1005
1006     // Release json_object
1007     json_object_put(json_obj);
1008
1009     return 0;
1010 }
1011
1012 // TODO:
1013 // This function will be removed because json_helper has same function.
1014 // json_helper should be library.
1015 const char* PolicyManager::getStringFromJson(json_object* obj, const char* key) {
1016     if ((nullptr == obj) || (nullptr == key)) {
1017         HMI_ERROR("wm:pm", "Argument is nullptr!!!");
1018         return nullptr;
1019     }
1020
1021     json_object* tmp;
1022     if (!json_object_object_get_ex(obj, key, &tmp)) {
1023         HMI_DEBUG("wm:pm", "Not found key \"%s\"", key);
1024         return nullptr;
1025     }
1026
1027     return json_object_get_string(tmp);
1028 }
1029
1030 // TODO:
1031 // This function will be removed because json_helper has same function.
1032 // json_helper should be library.
1033 int PolicyManager::inputJsonFilie(const char* file, json_object** obj) {
1034     const int input_size = 128;
1035     int ret = -1;
1036
1037     if ((nullptr == file) || (nullptr == obj)) {
1038         HMI_ERROR("wm:jh", "Argument is nullptr!!!");
1039         return ret;
1040     }
1041
1042     HMI_DEBUG("wm:jh", "Input file: %s", file);
1043
1044     // Open json file
1045     FILE *fp = fopen(file, "rb");
1046     if (nullptr == fp) {
1047         HMI_ERROR("wm:jh", "Could not open file");
1048         return ret;
1049     }
1050
1051     // Parse file data
1052     struct json_tokener *tokener = json_tokener_new();
1053     enum json_tokener_error json_error;
1054     char buffer[input_size];
1055     int block_cnt = 1;
1056     while (1) {
1057         size_t len = fread(buffer, sizeof(char), input_size, fp);
1058         *obj = json_tokener_parse_ex(tokener, buffer, len);
1059         if (nullptr != *obj) {
1060             HMI_DEBUG("wm:jh", "File input is success");
1061             ret = 0;
1062             break;
1063         }
1064
1065         json_error = json_tokener_get_error(tokener);
1066         if ((json_tokener_continue != json_error)
1067             || (input_size > len)) {
1068             HMI_ERROR("wm:jh", "Failed to parse file (byte:%d err:%s)",
1069                       (input_size * block_cnt), json_tokener_error_desc(json_error));
1070             HMI_ERROR("wm:jh", "\n%s", buffer);
1071             *obj = nullptr;
1072             break;
1073         }
1074         block_cnt++;
1075     }
1076
1077     // Close json file
1078     fclose(fp);
1079
1080     // Free json_tokener
1081     json_tokener_free(tokener);
1082
1083     return ret;
1084 }
1085
1086 std::vector<std::string> PolicyManager::parseString(std::string str, char delimiter) {
1087     // Parse string by delimiter
1088     std::vector<std::string> vct;
1089     std::stringstream ss{str};
1090     std::string buf;
1091     while (std::getline(ss, buf, delimiter)) {
1092       if (!buf.empty()) {
1093         vct.push_back(buf);
1094       }
1095     }
1096     return vct;
1097 }
1098
1099 std::string PolicyManager::deleteSpace(std::string str) {
1100     std::string ret = str;
1101     size_t pos;
1102     while ((pos = ret.find_first_of(" ")) != std::string::npos) {
1103       ret.erase(pos, 1);
1104     }
1105     return ret;
1106 }
1107
1108 const char* kDefaultRoleDb = "{ \
1109     \"roles\":[ \
1110     { \
1111         \"category\": \"homescreen\", \
1112         \"role\": \"homescreen\", \
1113         \"area\": \"full\", \
1114     }, \
1115     { \
1116         \"category\": \"map\", \
1117         \"role\": \"map\", \
1118         \"area\": \"full | normal | split.main\", \
1119     }, \
1120     { \
1121         \"category\": \"general\", \
1122         \"role\": \"poi | music | video | browser | sdl | settings | mixer | radio | hvac | dashboard | debug\", \
1123         \"area\": \"normal\", \
1124     }, \
1125     { \
1126         \"category\": \"phone\", \
1127         \"role\": \"phone\", \
1128         \"area\": \"normal\", \
1129     }, \
1130     { \
1131         \"category\": \"splitable\", \
1132         \"role\": \"splitable1 | splitable2\", \
1133         \"area\": \"normal | split.main | split.sub\", \
1134     }, \
1135     { \
1136         \"category\": \"popup\", \
1137         \"role\": \"popup\", \
1138         \"area\": \"on_screen\", \
1139     }, \
1140     { \
1141         \"category\": \"system_alert\", \
1142         \"role\": \"system_alert\", \
1143         \"area\": \"on_screen\", \
1144     }, \
1145     { \
1146         \"category\": \"tbt\", \
1147         \"role\": \"tbt\", \
1148         \"area\": \"hud\", \
1149     } \
1150     ] \
1151 }";
1152
1153
1154 const char* kDefaultLayoutDb = "{ \
1155     \"layouts\": [ \
1156         { \
1157             \"name\": \"pu\", \
1158             \"layer\": \"on_screen\", \
1159             \"areas\": [ \
1160                 { \
1161                     \"name\": \"pop_up\", \
1162                     \"role\": \"incomming_call\" \
1163                 } \
1164             ] \
1165         }, \
1166         { \
1167             \"name\": \"sa\", \
1168             \"layer\": \"on_screen\", \
1169             \"areas\": [ \
1170                 { \
1171                     \"name\": \"system_alert\", \
1172                     \"role\": \"system_alert\" \
1173                 } \
1174             ] \
1175         }, \
1176         { \
1177             \"name\": \"m1\", \
1178             \"layer\": \"apps\", \
1179             \"areas\": [ \
1180                 { \
1181                     \"name\": \"normal\", \
1182                     \"role\": \"map\" \
1183                 } \
1184             ] \
1185         }, \
1186         { \
1187             \"name\": \"m2\", \
1188             \"layer\": \"apps\", \
1189             \"areas\": [ \
1190                 { \
1191                     \"name\": \"split.main\", \
1192                     \"role\": \"map\" \
1193                 }, \
1194                 { \
1195                     \"name\": \"split.sub\", \
1196                     \"category\": \"hvac\" \
1197                 } \
1198             ] \
1199         }, \
1200         { \
1201             \"name\": \"mf\", \
1202             \"layer\": \"apps\", \
1203             \"areas\": [ \
1204                 { \
1205                     \"name\": \"full\", \
1206                     \"role\": \"map\" \
1207                 } \
1208             ] \
1209         }, \
1210         { \
1211             \"name\": \"s1\", \
1212             \"layer\": \"apps\", \
1213             \"areas\": [ \
1214                 { \
1215                     \"name\": \"normal\", \
1216                     \"category\": \"splitable\" \
1217                 } \
1218             ] \
1219         }, \
1220         { \
1221             \"name\": \"s2\", \
1222             \"layer\": \"apps\", \
1223             \"areas\": [ \
1224                 { \
1225                     \"name\": \"split.main\", \
1226                     \"category\": \"splitable\" \
1227                 }, \
1228                 { \
1229                     \"name\": \"split.sub\", \
1230                     \"category\": \"splitable\" \
1231                 } \
1232             ] \
1233         }, \
1234         { \
1235             \"name\": \"g\", \
1236             \"layer\": \"apps\", \
1237             \"areas\": [ \
1238                 { \
1239                     \"name\": \"normal\", \
1240                     \"category\": \"general\" \
1241                 } \
1242             ] \
1243         }, \
1244         { \
1245             \"name\": \"hs\", \
1246             \"layer\": \"homescreen\", \
1247             \"areas\": [ \
1248                 { \
1249                     \"name\": \"full\", \
1250                     \"role\": \"homescreen\" \
1251                 } \
1252             ] \
1253         } \
1254     ], \
1255     \"areas\": [ \
1256         { \
1257             \"name\": \"normal\", \
1258             \"rect\": { \
1259                 \"x\": 0, \
1260                 \"y\": 218, \
1261                 \"w\": 1080, \
1262                 \"h\": 1488 \
1263             } \
1264         }, \
1265         { \
1266             \"name\": \"split.main\", \
1267             \"rect\": { \
1268                 \"x\": 0, \
1269                 \"y\": 218, \
1270                 \"w\": 1080, \
1271                 \"h\": 744 \
1272             } \
1273         }, \
1274         { \
1275             \"name\": \"split.sub\", \
1276             \"rect\": { \
1277                 \"x\": 0, \
1278                 \"y\": 962, \
1279                 \"w\": 1080, \
1280                 \"h\": 744 \
1281             } \
1282         }, \
1283         { \
1284             \"name\": \"full\", \
1285             \"rect\": { \
1286                 \"x\": 0, \
1287                 \"y\": 0, \
1288                 \"w\": 1080, \
1289                 \"h\": 1920 \
1290             } \
1291         }, \
1292         { \
1293             \"name\": \"pop_up\", \
1294             \"rect\": { \
1295                 \"x\": 0, \
1296                 \"y\": 640, \
1297                 \"w\": 1080, \
1298                 \"h\": 640 \
1299             } \
1300         }, \
1301         { \
1302             \"name\": \"system_alert\", \
1303             \"rect\": { \
1304                 \"x\": 0, \
1305                 \"y\": 640, \
1306                 \"w\": 1080, \
1307                 \"h\": 640 \
1308             } \
1309         } \
1310     ] \
1311 }";