[Service] Apply wrt.finalizeService()
[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
20 function printAppControlData(id: string)  {
21   let reqAppControl = global.tizen.application.getCurrentApplication().getRequestedAppControl();
22   if (reqAppControl) {
23     console.debug(`id: ${id}, appControlData operation: ${reqAppControl.appControl.operation}`);
24     let appControlData = reqAppControl.appControl.data;
25     for (let dataIndex in appControlData) {
26       for (let valueIndex in appControlData[dataIndex].value)
27         console.debug(`data[${dataIndex}][${valueIndex}]: ${appControlData[dataIndex].value[valueIndex]}`);
28     }
29   }
30 }
31
32 function registerExtensionResolver(id: string) {
33   if (wrt.tv) {
34     let extensionResolver = (module: any, file_path: string) => {
35       console.debug(`resolved path: ${file_path}`);
36       let content = (wrt.tv as NativeWRTjs.TVExtension).decryptFile(id, file_path);
37       if (content) {
38         // Remove BOM
39         if (content.charCodeAt(0) === 0xFEFF)
40           content = content.slice(1);
41         module._compile(content, file_path);
42       }
43     };
44     require.extensions['.js.spm'] = extensionResolver;
45     require.extensions['.spm'] = extensionResolver;
46   }
47 }
48
49 let requestStopService = (id: string) => {
50   requestStopService = (id: string) => {};
51   setTimeout(() => wrt.stopService(id), 500);
52 }
53
54 let app: any = null;
55 let dummyTimer: any;
56 let periodLauncherAlive = 20; // 2s
57
58 let checkLauncherAlive = (id: string) => {
59   periodLauncherAlive--;
60   if (!periodLauncherAlive) {
61     periodLauncherAlive = 20;
62     if (!wrt.checkLauncherAlive(id)) {
63       console.debug(`${id} launcher was killed.`)
64       requestStopService(id);
65       checkLauncherAlive = () => {};
66     }
67   }
68 }
69
70 export function start(id: string, filename: string) {
71   let ids = id.split(':');
72   let serviceId = ids[0];
73   let packageId = wrt.getPackageId(id);
74   if (!packageId) {
75     console.debug(`${id}'s pkgid is empty, so stop service`);
76     requestStopService(id);
77     return;
78   }
79   wrt.setServiceAppId(id);
80   wrt.security?.dropThreadPrivilege(packageId, serviceId);
81
82   XWalkExtension.initialize();
83   XWalkExtension.setRuntimeMessageHandler((type, data) => {
84     if (type === 'tizen://exit') {
85       console.debug(`${id} will be closed by ${type}`);
86       requestStopService(id);
87     }
88   });
89
90   console.debug(`serviceType : ${global['serviceType']}`)
91   new DeviceAPIRouter(id, isGlobalService());
92   printAppControlData(id);
93
94   // This is for awaking up uv loop.
95   if (isGlobalService()) {
96     dummyTimer = setInterval(() => {
97       checkLauncherAlive(id);
98     }, 100);
99   }
100
101   if (isServiceApplication()) {
102     registerExtensionResolver(id);
103     filename = wrt.getStartServiceFile(id);
104     console.debug(`start global service file: ${filename}`);
105   }
106
107   try {
108     app = require(filename);
109     if (app.onStart !== undefined) {
110       app.onStart();
111     }
112     if (app.onRequest !== undefined) {
113       app.onRequest();
114     }
115   } catch (e) {
116     console.debug(`exception on start: ${e}`);
117     requestStopService(id);
118   } finally {
119     if (isGlobalService()) {
120       wrt.finishStartingService(id);
121     }
122   }
123 }
124
125 export function stop(id: string) {
126   if (dummyTimer)
127     clearInterval(dummyTimer);
128   try {
129     if (app.onStop !== undefined) {
130       app.onStop();
131     } else if (app.onExit !== undefined) {
132       app.onExit();
133     }
134   } catch (e) {
135     console.debug(`exception on stop: ${e}`);
136   } finally {
137     if (isGlobalService())
138       wrt.finalizeService(id);
139   }
140 }
141
142 function run() {
143   let id = workerData.id;
144   if (!id) {
145     console.debug('workerData.id is empty!');
146     process.exit();
147   }
148
149   Object.defineProperty(global, 'internalId', {
150     value: id,
151     writable: false
152   });
153
154   wrt.tv?.serviceMount(id);
155   let filename = workerData.filename;
156   start(id, filename);
157
158   parentPort?.on('message', (message) => {
159     console.debug(`Received message type : ${message.type}`);
160     if (message.type === 'wake') {
161       app?.onRequest();
162     } else if (message.type === 'stop') {
163       stop(id);
164       setTimeout(() => {
165         XWalkExtension.cleanup();
166         parentPort?.postMessage("will-terminate");
167         parentPort?.close();
168         wrt.tv?.serviceUmount(id);
169       }, message.delay);
170     }
171   });
172 }
173
174 if (!isMainThread) {
175   run();
176 }