Merge "[Service] Apply sendSyncMessageWith* API" into tizen
[platform/framework/web/wrtjs.git] / wrt_app / service / service_runner.ts
1 import '../common/init';
2 import * as XWalkExtension from '../common/wrt_xwalk_extension';
3 import { DeviceAPIRouter } from './device_api_router';
4 import { isMainThread, parentPort, workerData } from 'worker_threads';
5 import { wrt } from '../browser/wrt';
6
7 Object.defineProperty(global, 'serviceType', {
8   value: wrt.getServiceModel(),
9   writable: false
10 });
11
12 function isServiceApplication() {
13   return global['serviceType'] !== 'UI';
14 }
15
16 function isGlobalService() {
17   return global['serviceType'] === 'GLOBAL';
18 }
19 function printAppControlData(id: string)  {
20   let reqAppControl = global.tizen.application.getCurrentApplication().getRequestedAppControl();
21   if (reqAppControl) {
22     console.debug(`id: ${id}, appControlData operation: ${reqAppControl.appControl.operation}`);
23     let appControlData = reqAppControl.appControl.data;
24     for (let dataIndex in appControlData) {
25       for (let valueIndex in appControlData[dataIndex].value)
26         console.debug(`data[${dataIndex}][${valueIndex}]: ${appControlData[dataIndex].value[valueIndex]}`);
27     }
28   }
29 }
30
31 function registerExtensionResolver(id: string) {
32   if (wrt.tv) {
33     let extensionResolver = (module: any, file_path: string) => {
34       console.debug(`resolved path: ${file_path}`);
35       let content = (wrt.tv as NativeWRTjs.TVExtension).decryptFile(id, file_path);
36       if (content) {
37         // Remove BOM
38         if (content.charCodeAt(0) === 0xFEFF)
39           content = content.slice(1);
40         module._compile(content, file_path);
41       }
42     };
43     require.extensions['.js.spm'] = extensionResolver;
44     require.extensions['.spm'] = extensionResolver;
45   }
46 }
47
48 let requestStopService = (id: string) => {
49   requestStopService = (id: string) => {};
50   setTimeout(() => wrt.stopService(id), 500);
51 }
52
53 let app: any = null;
54 let dummyTimer: any;
55 let periodLauncherAlive = 20; // 2s
56
57 let checkLauncherAlive = (id: string) => {
58   periodLauncherAlive--;
59   if (!periodLauncherAlive) {
60     periodLauncherAlive = 20;
61     if (!wrt.checkLauncherAlive(id)) {
62       console.debug(`${id} launcher was killed.`)
63       requestStopService(id);
64       checkLauncherAlive = () => {};
65     }
66   }
67 }
68
69 export function start(id: string, filename: string) {
70   wrt.setServiceAppId(id);
71   XWalkExtension.initialize();
72   XWalkExtension.setRuntimeMessageHandler((type, data) => {
73     if (type === 'tizen://exit') {
74       console.debug(`${id} will be closed by ${type}`);
75       requestStopService(id);
76     }
77   });
78
79   console.debug(`serviceType : ${global['serviceType']}`)
80   new DeviceAPIRouter(id, isGlobalService());
81
82   // this is workaround solution to make webapis singleton worker
83   // ahead of dropThreadPrivilege()
84   global.tizen.systeminfo.getPropertyValue("CPU", () => { }, () => { });
85
86   // This is for awaking up uv loop.
87   if (isGlobalService()) {
88     dummyTimer = setInterval(() => {
89       checkLauncherAlive(id);
90     }, 100);
91   }
92
93   if (isServiceApplication()) {
94     registerExtensionResolver(id);
95     filename = wrt.getStartServiceFile(id);
96     console.debug(`start global service file: ${filename}`);
97   }
98
99   try {
100     let ids = id.split(':');
101     let serviceId = ids[0];
102     let packageId = global.webapis.getPackageId();
103     wrt.security?.dropThreadPrivilege(packageId, serviceId);
104     printAppControlData(id);
105
106     app = require(filename);
107     if (app.onStart !== undefined) {
108       app.onStart();
109     }
110     if (app.onRequest !== undefined) {
111       app.onRequest();
112     }
113     if (isGlobalService()) {
114       wrt.finishStartingService(id);
115     }
116   } catch (e) {
117     console.debug(`exception on start: ${e}`);
118     requestStopService(id);
119   }
120 }
121
122 export function stop(id: string) {
123   if (dummyTimer)
124     clearInterval(dummyTimer);
125   try {
126     if (app.onStop !== undefined) {
127       app.onStop();
128     } else if (app.onExit !== undefined) {
129       app.onExit();
130     }
131   } catch (e) {
132     console.debug(`exception on stop: ${e}`);
133   }
134 }
135
136 function run() {
137   let id = workerData.id;
138   Object.defineProperty(global, 'internalId', {
139     value: id,
140     writable: false
141   });
142
143   // FIXME: this should be 'wrt.tv?.serviceMount(id)' after Tizen 6.5 release
144   (wrt.tv as any)?.serviceMount(id);
145
146   let filename = workerData.filename;
147   start(id, filename);
148
149   if (!parentPort)
150     return;
151   parentPort.on('message', (message) => {
152     console.debug(`Received message type : ${message.type}`);
153     if (message.type === 'wake') {
154       app?.onRequest();
155     } else if (message.type === 'stop') {
156       stop(id);
157       setTimeout(() => {
158         XWalkExtension.cleanup();
159         parentPort?.postMessage("will-terminate");
160         (wrt.tv as any)?.serviceUmount(id);
161       }, message.delay);
162     }
163   });
164 }
165
166 if (!isMainThread) {
167   run();
168 }