[Service] StopService when appid is empty
[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   let ids = id.split(':');
71   let serviceId = ids[0];
72   let packageId = wrt.getPackageId(id);
73   if (!packageId) {
74     console.debug(`${id}'s pkgid is empty, so stop service`);
75     requestStopService(id);
76     return;
77   }
78   wrt.setServiceAppId(id);
79   wrt.security?.dropThreadPrivilege(packageId, serviceId);
80
81   XWalkExtension.initialize();
82   XWalkExtension.setRuntimeMessageHandler((type, data) => {
83     if (type === 'tizen://exit') {
84       console.debug(`${id} will be closed by ${type}`);
85       requestStopService(id);
86     }
87   });
88
89   console.debug(`serviceType : ${global['serviceType']}`)
90   new DeviceAPIRouter(id, isGlobalService());
91   printAppControlData(id);
92
93   // This is for awaking up uv loop.
94   if (isGlobalService()) {
95     dummyTimer = setInterval(() => {
96       checkLauncherAlive(id);
97     }, 100);
98   }
99
100   if (isServiceApplication()) {
101     registerExtensionResolver(id);
102     filename = wrt.getStartServiceFile(id);
103     console.debug(`start global service file: ${filename}`);
104   }
105
106   try {
107     app = require(filename);
108     if (app.onStart !== undefined) {
109       app.onStart();
110     }
111     if (app.onRequest !== undefined) {
112       app.onRequest();
113     }
114   } catch (e) {
115     console.debug(`exception on start: ${e}`);
116     requestStopService(id);
117   } finally {
118     if (isGlobalService()) {
119       wrt.finishStartingService(id);
120     }
121   }
122 }
123
124 export function stop(id: string) {
125   if (dummyTimer)
126     clearInterval(dummyTimer);
127   try {
128     if (app.onStop !== undefined) {
129       app.onStop();
130     } else if (app.onExit !== undefined) {
131       app.onExit();
132     }
133   } catch (e) {
134     console.debug(`exception on stop: ${e}`);
135   }
136 }
137
138 function run() {
139   let id = workerData.id;
140   if (!id) {
141     console.debug('workerData.id is empty!');
142     process.exit();
143   }
144
145   Object.defineProperty(global, 'internalId', {
146     value: id,
147     writable: false
148   });
149
150   wrt.tv?.serviceMount(id);
151   let filename = workerData.filename;
152   start(id, filename);
153
154   parentPort?.on('message', (message) => {
155     console.debug(`Received message type : ${message.type}`);
156     if (message.type === 'wake') {
157       app?.onRequest();
158     } else if (message.type === 'stop') {
159       stop(id);
160       setTimeout(() => {
161         XWalkExtension.cleanup();
162         parentPort?.postMessage("will-terminate");
163         parentPort?.close();
164         wrt.tv?.serviceUmount(id);
165       }, message.delay);
166     }
167   });
168 }
169
170 if (!isMainThread) {
171   run();
172 }