-const Module = require('module');
-import { TimerManager } from '../service/timer_manager';
-import * as XWalkExtension from './wrt_xwalk_extension';
-import * as vm from 'vm';
+import { Worker, isMainThread } from 'worker_threads';
import { wrt } from '../browser/wrt';
-import { DeviceAPIRouter } from '../service/device_api_router';
-interface ContextMap {
- [id: string]: vm.Context;
+interface WorkerMap {
+ [id: string]: any;
}
+let workers: WorkerMap = {};
+let serviceType: string = wrt.getServiceModel();;
+let runner: any;
-interface ContextOption {
- [key: string]: any;
+function isStandalone() {
+ return serviceType === 'STANDALONE';
}
-let sandbox: ContextMap = {};
-let internal_handler: ContextOption = {};
-let service_type: string = wrt.getServiceModel?.() ?? 'UI';
-
-function requestStopService(id: string) {
- console.log(`${id} will be closed`);
- setTimeout(() => wrt.stopService(id), 500);
-}
-
-function callFunctionInContext(name: string, id: string) {
- try {
- const script = `if (typeof ${name} === 'function') { ${name}(); }`;
- vm.runInContext(script, sandbox[id]);
- } catch (e) {
- console.log(`${name} has exception: ${e}`);
- if (wrt.tv) {
- requestStopService(id);
+export function startService(id: string, filename: string) {
+ console.log(`startService - ${id}`);
+ if (isStandalone()) {
+ runner = require('../common/service_runner');
+ runner.start(id, filename);
+ } else {
+ if (isMainThread) {
+ let startService = __dirname + '/service_runner.js';
+ workers[id] = new Worker(startService, { workerData: { id: id, filename: filename } });
}
}
}
-export function startService(id: string, filename?: string) {
- if (sandbox[id] === undefined) {
- XWalkExtension.initialize();
- XWalkExtension.setRuntimeMessageHandler((type, data) => {
- if (type === 'tizen://exit') {
- requestStopService(id);
- }
- });
- sandbox[id] = {
- console: console,
- module: new Module,
- require: require,
- tizen: global.tizen,
- webapis: wrt.tv ? global.webapis : global.webapis = {},
- };
- sandbox[id].module.exports.onStop = () => {
- callFunctionInContext('module.exports.onExit', id);
- };
- let ids = id.split(':');
- let caller_app_id = ids[1] ?? '';
- sandbox[id].webapis.getCallerAppId = () => {
- return caller_app_id;
- }
- let service_id = ids[0];
- sandbox[id].webapis.getServiceId = () => {
- return service_id;
- }
- sandbox[id].webapis.getPackageId = () => {
- let app_info = global.tizen.application.getAppInfo(service_id);
- if (app_info)
- return app_info.packageId;
- return ids[0].split('.')[0];
- }
-
- if (service_type !== 'UI') {
- const permissions = wrt.getPrivileges(id);
- console.log(`permissions : ${permissions}`);
- const AccessControlManager = require('../service/access_control_manager');
- AccessControlManager.initialize(permissions, sandbox[id]);
- }
- for (let key in global)
- sandbox[id][key] = global[key];
-
- internal_handler[id] = {};
- internal_handler[id].timer_manager = new TimerManager();
- const timer_api = internal_handler[id].timer_manager.getTimerAPI();
- for (let key in timer_api)
- sandbox[id][key] = timer_api[key];
-
- let object_list = [ 'Error', 'EvalError', 'RangeError', 'ReferenceError',
- 'SyntaxError', 'TypeError', 'URIError', 'Number', 'BigInt', 'Math', 'Date',
- 'String', 'RegExp', 'Array', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray',
- 'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array', 'Float32Array',
- 'Float64Array', 'BigInt64Array', 'BigUint64Array', 'Map', 'Set', 'WeakMap',
- 'WeakSet', 'ArrayBuffer', 'DataView', 'JSON', 'Promise', 'Reflect', 'Proxy',
- 'Intl', 'WebAssembly', 'Boolean', 'Function', 'Object', 'Symbol' ];
- for (let prop of object_list)
- sandbox[id][prop] = global[prop];
-
- let options: ContextOption = {};
- let code;
- if (service_type !== 'UI') {
- options.filename = id;
- if (wrt.tv) {
- let extension_resolver = function (module: any, file_path: string) {
- console.log(`resolved path: ${file_path}`);
- let content = (wrt.tv as NativeWRTjs.TVExtension).decryptFile(id, file_path);
- if (content) {
- // Remove BOM
- if (content.charCodeAt(0) === 0xFEFF)
- content = content.slice(1);
- module._compile(content, file_path);
- }
- };
- sandbox[id].require.extensions['.js.spm'] = extension_resolver;
- sandbox[id].require.extensions['.spm'] = extension_resolver;
- }
- filename = wrt.getStartServiceFile(id);
- console.log(`start global service file: ${filename}`);
- }
- code = `const app = require('${filename}')`;
- if (service_type === 'DAEMON') {
- internal_handler[id].deivce_api_router = new DeviceAPIRouter(sandbox[id]);
+export function stopService(id: string) {
+ console.log(`stopService - ${id}`);
+ if (isStandalone()) {
+ if (runner) {
+ runner.stop(id);
}
- vm.runInNewContext(code, sandbox[id], options);
- }
-
- if (sandbox[id]['started'] === undefined) {
- sandbox[id]['started'] = true;
- sandbox[id]['stopped'] = undefined;
- callFunctionInContext('app.onStart', id);
- if (service_type !== 'UI')
- wrt.finishStartingService(id);
} else {
- console.log(id + ' service has been started.');
+ workers[id].postMessage('stopService');
}
- callFunctionInContext('app.onRequest', id);
-}
-
-export function stopService(id: string) {
- console.log('stopService')
- if (sandbox[id]['stopped']) {
- console.log(id + ' service has been already stopped.');
- return;
- }
-
- sandbox[id]['stopped'] = true;
- sandbox[id]['started'] = undefined;
- callFunctionInContext('app.onStop', id);
-
- internal_handler[id].timer_manager.releaseRemainingTimers();
- for (let key in sandbox[id])
- delete sandbox[id][key];
- delete sandbox[id];
- for (let key in internal_handler[id])
- delete internal_handler[id][key];
- delete internal_handler[id];
-
- if (Object.keys(sandbox).length === 0)
- XWalkExtension.cleanup();
}
--- /dev/null
+import './init';
+import * as XWalkExtension from './wrt_xwalk_extension';
+import { DeviceAPIRouter } from '../service/device_api_router';
+import { isMainThread, parentPort, workerData } from 'worker_threads';
+import { wrt } from '../browser/wrt';
+
+let serviceType: string = wrt.getServiceModel();
+
+function isServiceApplication() {
+ return serviceType !== 'UI';
+}
+
+function isGloablService() {
+ return serviceType === 'DAEMON';
+}
+
+function registerExtensionResolver(id: string) {
+ if (wrt.tv) {
+ let extensionResolver = (module: any, file_path: string) => {
+ console.log(`resolved path: ${file_path}`);
+ let content = (wrt.tv as NativeWRTjs.TVExtension).decryptFile(id, file_path);
+ if (content) {
+ // Remove BOM
+ if (content.charCodeAt(0) === 0xFEFF)
+ content = content.slice(1);
+ module._compile(content, file_path);
+ }
+ };
+ require.extensions['.js.spm'] = extensionResolver;
+ require.extensions['.spm'] = extensionResolver;
+ }
+}
+
+let app: any = null;
+export function start(id: string, filename: string) {
+ XWalkExtension.initialize();
+ XWalkExtension.setRuntimeMessageHandler((type, data) => {
+ if (type === 'tizen://exit') {
+ console.log(`${id} will be closed by ${type}`);
+ setTimeout(() => wrt.stopService(id), 500);
+ }
+ });
+
+ console.log('serviceType : '+serviceType)
+ new DeviceAPIRouter(id, isGloablService());
+
+ if (isServiceApplication()) {
+ registerExtensionResolver(id);
+ filename = wrt.getStartServiceFile(id);
+ console.log(`start global service file: ${filename}`);
+ }
+
+ // FIXME: this is for awaking up uv loop.
+ // uv loop is sleeping for a few second with tizen webapis's aync callback
+ setInterval(() => {}, 100);
+ try {
+ app = require(filename);
+ if (app.onStart !== undefined) {
+ app.onStart();
+ }
+ if (app.onRequest !== undefined) {
+ app.onRequest();
+ }
+ if (isGloablService()) {
+ wrt.finishStartingService(id);
+ }
+ } catch (e) {
+ console.log(`exception on start: ${e}`);
+ setTimeout(() => wrt.stopService(id), 500);
+ }
+}
+
+export function stop(id: string) {
+ try {
+ if (app.onStop !== undefined) {
+ app.onStop();
+ } else if (app.onExit !== undefined) {
+ app.onExit();
+ }
+ } catch (e) {
+ console.log(`exception on stop: ${e}`);
+ }
+ setTimeout(() => process.exit(), 500);
+}
+
+function run() {
+ let id = workerData.id;
+ let filename = workerData.filename;
+ start(id, filename);
+
+ if (!parentPort)
+ return;
+ parentPort.on('message', (msg) => {
+ console.log(`message received : ${msg}`);
+ if (msg === 'stopService') {
+ stop(id);
+ }
+ });
+}
+
+if (!isMainThread) {
+ run();
+}
-import * as vm from 'vm';
function checkSystemInfoApiPrivilege(func: any, permissions: string[]) {
let override_func = func;
}
}
-export function initialize(permissions: string[], sandbox: vm.Context) {
- let tizen = sandbox.tizen;
+export function initialize(permissions: string[]) {
+ let tizen = global.tizen;
if (!permissions.includes("http://tizen.org/privilege/alarm")) {
tizen.alarm.add =
tizen.alarm.remove =
export class DeviceAPIRouter {
currentApplication: any;
- sandbox: any;
funcCurrentApplication: any;
funcRequestedAppcontrol: any;
funcGetAppInfo: any;
funcGetSharedUri: any;
funcGetMetadata: any;
funcGetPackageInfo: any;
- funcPathResolve: any;
- constructor(sandbox: any) {
- this.sandbox = sandbox;
- this.RefineApplicationApis();
- this.RefinePackageApis();
- this.RefineFilesystemApis()
+ id: string;
+ serviceId: string;
+ packageId: string;
+ callerAppId: string;
+
+ constructor(id: string, isGlobal: boolean) {
+ this.id = id;
+ let ids = id.split(':');
+ this.serviceId = ids[0];
+ this.callerAppId = ids[1] ?? '';
+ this.packageId = this.serviceId.split('.')[0];
+
+ this.initWebapis();
+ if (isGlobal) {
+ this.refineApplicationApis();
+ this.refinePackageApis();
+ this.refineFilesystemApis()
+ this.initAccessControlManager();
+ }
+ }
+
+ initWebapis() {
+ global.webapis = global.webapis ?? {};
+
+ global.webapis.getCallerAppId = () => {
+ return this.callerAppId;
+ }
+ global.webapis.getServiceId = () => {
+ return this.serviceId;
+ }
+ let app_info = global.tizen.application.getAppInfo(this.serviceId);
+ if (app_info) {
+ this.packageId = app_info.packageId;
+ }
+ global.webapis.getPackageId = () => {
+ return this.packageId;
+ }
}
- GetServiceId() {
- return this.sandbox.webapis.getServiceId();
+ initAccessControlManager() {
+ const permissions = wrt.getPrivileges(this.id);
+ console.log(`permissions : ${permissions}`);
+ const AccessControlManager = require('./access_control_manager');
+ AccessControlManager.initialize(permissions);
}
- GetPackageId() {
- return this.sandbox.webapis.getPackageId();
+ getServiceId() {
+ return global.webapis.getServiceId();
}
- RefineApplicationApis() {
+ getPackageId() {
+ return global.webapis.getPackageId();
+ }
+
+ refineApplicationApis() {
// tizen.application.getCurrentApplication()
this.funcCurrentApplication = global.tizen.application.getCurrentApplication;
global.tizen.application.getCurrentApplication = () => {
- console.log(`Routing - getCurrentApplication() : ${this.GetServiceId()}`);
+ console.log(`Routing - getCurrentApplication() : ${this.getServiceId()}`);
if (this.currentApplication)
return this.currentApplication;
this.currentApplication = this.funcCurrentApplication();
// tizen.application.getCurrentApplication().getRequestedAppControl()
this.funcRequestedAppcontrol = this.currentApplication.getRequestedAppControl;
this.currentApplication.getRequestedAppControl = () => {
- console.log(`Routing - getRequestedAppControl() : ${this.GetServiceId()}`);
+ console.log(`Routing - getRequestedAppControl() : ${this.getServiceId()}`);
if (wrt.tv)
- wrt.tv.setCurrentApplication(this.GetServiceId());
+ wrt.tv.setCurrentApplication(this.getServiceId());
return this.funcRequestedAppcontrol();
}
return this.currentApplication;
global.tizen.application.getAppInfo = (app_id?: string) => {
console.log(`Routing - getAppInfo()`);
if (!app_id)
- app_id = this.GetServiceId();
+ app_id = this.getServiceId();
return this.funcGetAppInfo(app_id);
}
// tizen.application.getAppCerts()
global.tizen.application.getAppCerts = (app_id?: string) => {
console.log(`Routing - getAppCerts()`);
if (!app_id)
- app_id = this.GetServiceId();
+ app_id = this.getServiceId();
return this.funcGetAppcerts(app_id);
}
// tizen.application.getAppSharedURI()
global.tizen.application.getAppSharedURI = (app_id?: string) => {
console.log(`Routing - getAppSharedURI()`);
if (!app_id)
- app_id = this.GetServiceId();
+ app_id = this.getServiceId();
return this.funcGetSharedUri(app_id);
}
// tizen.application.getAppMetaData()
global.tizen.application.getAppMetaData = (app_id?: string) => {
console.log(`Routing - getAppMetaData()`);
if (!app_id)
- app_id = this.GetServiceId();
+ app_id = this.getServiceId();
return this.funcGetMetadata(app_id);
}
}
- RefinePackageApis() {
+ refinePackageApis() {
// tizen.package.getPackageInfo()
this.funcGetPackageInfo = global.tizen.package.getPackageInfo;
global.tizen.package.getPackageInfo = (package_id?: string) => {
console.log(`Routing - getPackageInfo()`);
if (!package_id)
- package_id = this.GetPackageId();
+ package_id = this.getPackageId();
return this.funcGetPackageInfo(package_id);
}
}
- RefineFilesystemApis() {
- // tizen.filesystem.resolve
- this.funcPathResolve = global.tizen.filesystem.resolve;
- global.tizen.filesystem.resolve = (location: string, onSuccess: Function,
- onError?: Function, mode?: string) => {
- console.log(`Routing - resolve(${location})`);
- let service_id = this.GetServiceId();
- location = wrt.resolveVirtualRoot(service_id, location);
- this.funcPathResolve(location, onSuccess, onError, mode ?? 'rw');
+ injectVirtualRootResolver(func: Function) {
+ return (...args: any[]) => {
+ console.log(args);
+ args[0] = wrt.resolveVirtualRoot(this.getServiceId(), args[0]);
+ console.log(args[0]);
+ func.apply(global.tizen.filesystem, args);
}
}
+
+ refineFilesystemApis() {
+ global.tizen.filesystem.resolve = this.injectVirtualRootResolver(global.tizen.filesystem.resolve);
+ global.tizen.filesystem.listDirectory = this.injectVirtualRootResolver(global.tizen.filesystem.listDirectory);
+ global.tizen.filesystem.createDirectory = this.injectVirtualRootResolver(global.tizen.filesystem.createDirectory);
+ global.tizen.filesystem.createDirectory = this.injectVirtualRootResolver(global.tizen.filesystem.createDirectory);
+ global.tizen.filesystem.deleteDirectory = this.injectVirtualRootResolver(global.tizen.filesystem.deleteDirectory);
+ global.tizen.filesystem.openFile = this.injectVirtualRootResolver(global.tizen.filesystem.openFile);
+ global.tizen.filesystem.deleteFile = this.injectVirtualRootResolver(global.tizen.filesystem.deleteFile);
+ global.tizen.filesystem.moveFile = this.injectVirtualRootResolver(global.tizen.filesystem.moveFile);
+ global.tizen.filesystem.copyFile = this.injectVirtualRootResolver(global.tizen.filesystem.copyFile);
+ global.tizen.filesystem.isFile = this.injectVirtualRootResolver(global.tizen.filesystem.isFile);
+ global.tizen.filesystem.toURI = this.injectVirtualRootResolver(global.tizen.filesystem.toURI);
+ global.tizen.filesystem.isDirectory = this.injectVirtualRootResolver(global.tizen.filesystem.isDirectory);
+ global.tizen.filesystem.pathExists = this.injectVirtualRootResolver(global.tizen.filesystem.pathExists);
+ }
}
wrt.on('start-service', (event: any, internal_id: string) => {
console.log('start service app : ' + internal_id);
- ServiceManager.startService(internal_id);
+ ServiceManager.startService(internal_id, '');
});
wrt.on('stop-service', (event: any, internal_id: string) => {