1 /**
2 * Copyright (C) 2010 The Android Open Source Project
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 #include <v8.h>
18 #include <telephony/ril.h>
19
20 #include "ctrl_server.h"
21 #include "logging.h"
22 #include "node_buffer.h"
23 #include "node_object_wrap.h"
24 #include "node_util.h"
25 #include "protobuf_v8.h"
26 #include "responses.h"
27 #include "status.h"
28 #include "util.h"
29 #include "worker.h"
30 #include "worker_v8.h"
31
32 #include "js_support.h"
33
34 //#define JS_SUPPORT_DEBUG
35 #ifdef JS_SUPPORT_DEBUG
36
37 #define DBG(...) LOGD(__VA_ARGS__)
38
39 #else
40
41 #define DBG(...)
42
43 #endif
44
45 /**
46 * Current Radio state
47 */
48 RIL_RadioState gRadioState = RADIO_STATE_UNAVAILABLE;
49
RadioStateGetter(v8::Local<v8::String> property,const v8::AccessorInfo & info)50 v8::Handle<v8::Value> RadioStateGetter(v8::Local<v8::String> property,
51 const v8::AccessorInfo& info) {
52 return v8::Integer::New((int)gRadioState);
53 }
54
RadioStateSetter(v8::Local<v8::String> property,v8::Local<v8::Value> value,const v8::AccessorInfo & info)55 void RadioStateSetter(v8::Local<v8::String> property,
56 v8::Local<v8::Value> value, const v8::AccessorInfo& info) {
57 gRadioState = RIL_RadioState(value->Int32Value());
58 }
59
60 // A javascript sleep for a number of milli-seconds
MsSleep(const v8::Arguments & args)61 v8::Handle<v8::Value> MsSleep(const v8::Arguments& args) {
62 if (args.Length() != 1) {
63 DBG("MsSleep: expecting milli-seconds to sleep");
64 } else {
65 v8::Handle<v8::Value> v8MsValue(args[0]->ToObject());
66 int ms = int(v8MsValue->NumberValue());
67 v8::Unlocker unlocker;
68 usleep(ms * 1000);
69 v8::Locker locker;
70 }
71 return v8::Undefined();
72 }
73
74 // A javascript print function
Print(const v8::Arguments & args)75 v8::Handle<v8::Value> Print(const v8::Arguments& args) {
76 bool first = true;
77 const int str_size = 1000;
78 char* str = new char[str_size];
79 int offset = 0;
80 for (int i = 0; i < args.Length(); i++) {
81 v8::HandleScope handle_scope;
82 if (first) {
83 first = false;
84 } else {
85 offset += snprintf(&str[offset], str_size, " ");
86 }
87 v8::String::Utf8Value strUtf8(args[i]);
88 const char* cstr = ToCString(strUtf8);
89 offset += snprintf(&str[offset], str_size, "%s", cstr);
90 }
91 LOGD("%s", str);
92 delete [] str;
93 return v8::Undefined();
94 }
95
ReadFile(const char * fileName,char ** data,size_t * length)96 int ReadFile(const char *fileName, char** data, size_t *length) {
97 int status;
98 char* buffer = NULL;
99 size_t fileLength = 0;
100 FILE *f;
101
102 DBG("ReadFile E fileName=%s", fileName);
103
104 f = fopen(fileName, "rb");
105 if (f == NULL) {
106 DBG("Could not fopen '%s'", fileName);
107 status = STATUS_COULD_NOT_OPEN_FILE;
108 } else {
109 // Determine the length of the file
110 fseek(f, 0, SEEK_END);
111 fileLength = ftell(f);
112 DBG("fileLength=%d", fileLength);
113 rewind(f);
114
115 // Read file into a buffer
116 buffer = new char[fileLength+1];
117 size_t readLength = fread(buffer, 1, fileLength, f);
118 if (readLength != fileLength) {
119 DBG("Couldn't read entire file");
120 delete [] buffer;
121 buffer = NULL;
122 status = STATUS_COULD_NOT_READ_FILE;
123 } else {
124 DBG("File read");
125 buffer[fileLength] = 0;
126 status = STATUS_OK;
127 }
128 fclose(f);
129 }
130
131 if (length != NULL) {
132 *length = fileLength;
133 }
134 *data = buffer;
135 DBG("ReadFile X status=%d", status);
136 return status;
137 }
138
CreateFileName(const v8::Arguments & args)139 char *CreateFileName(const v8::Arguments& args) {
140 v8::String::Utf8Value fileNameUtf8Value(args[0]);
141 const char* fileName = ToCString(fileNameUtf8Value);
142 const char* directory = "/sdcard/data/";
143
144 int fullPathLength = strlen(directory) + strlen(fileName) + 1;
145 char * fullPath = new char[fullPathLength];
146 strncpy(fullPath, directory, fullPathLength);
147 strncat(fullPath, fileName, fullPathLength);
148 return fullPath;
149 }
150
151 // A javascript read file function arg[0] = filename
ReadFileToString(const v8::Arguments & args)152 v8::Handle<v8::Value> ReadFileToString(const v8::Arguments& args) {
153 DBG("ReadFileToString E");
154 v8::HandleScope handle_scope;
155 v8::Handle<v8::Value> retValue;
156
157 if (args.Length() < 1) {
158 // No file name return Undefined
159 DBG("ReadFile X no argumens");
160 return v8::Undefined();
161 } else {
162 char *fileName = CreateFileName(args);
163
164 char *buffer;
165 int status = ReadFile(fileName, &buffer);
166 if (status == 0) {
167 retValue = v8::String::New(buffer);
168 } else {
169 retValue = v8::Undefined();
170 }
171 }
172 DBG("ReadFileToString X");
173 return retValue;
174 }
175
176 // A javascript read file function arg[0] = filename
ReadFileToBuffer(const v8::Arguments & args)177 v8::Handle<v8::Value> ReadFileToBuffer(const v8::Arguments& args) {
178 DBG("ReadFileToBuffer E");
179 v8::HandleScope handle_scope;
180 v8::Handle<v8::Value> retValue;
181
182 if (args.Length() < 1) {
183 // No file name return Undefined
184 DBG("ReadFileToBuffer X no argumens");
185 return v8::Undefined();
186 } else {
187 char *fileName = CreateFileName(args);
188
189 char *buffer;
190 size_t length;
191 int status = ReadFile(fileName, &buffer, &length);
192 if (status == 0) {
193 Buffer *buf = Buffer::New(length);
194 memmove(buf->data(), buffer, length);
195 retValue = buf->handle_;
196 } else {
197 retValue = v8::Undefined();
198 }
199 }
200 DBG("ReadFileToBuffer X");
201 return retValue;
202 }
203
ErrorCallback(v8::Handle<v8::Message> message,v8::Handle<v8::Value> data)204 void ErrorCallback(v8::Handle<v8::Message> message,
205 v8::Handle<v8::Value> data) {
206 LogErrorMessage(message, "");
207 }
208
209 // Read, compile and run a javascript file
Include(const v8::Arguments & args)210 v8::Handle<v8::Value> Include(const v8::Arguments& args) {
211 DBG("Include E");
212 v8::HandleScope handle_scope;
213 v8::Handle<v8::Value> retValue;
214 v8::TryCatch try_catch;
215 try_catch.SetVerbose(true);
216
217 if (args.Length() < 1) {
218 // No file name return Undefined
219 DBG("Include X no argumens");
220 return v8::Undefined();
221 } else {
222 char *fileName = CreateFileName(args);
223
224 char *buffer;
225 int status = ReadFile(fileName, &buffer);
226 if (status == 0) {
227 runJs(v8::Context::GetCurrent(), &try_catch, fileName, buffer);
228 } else {
229 retValue = v8::Undefined();
230 }
231 }
232 DBG("Include X");
233 return retValue;
234 }
235
236
237 /**
238 * Create a JsContext, must be called within a HandleScope?
239 */
makeJsContext()240 v8::Persistent<v8::Context> makeJsContext() {
241 v8::HandleScope handle_scope;
242 v8::TryCatch try_catch;
243
244 // Add a Message listner to Catch errors as they occur
245 v8::V8::AddMessageListener(ErrorCallback);
246
247 // Create a template for the global object and
248 // add the function template for print to it.
249 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
250 global->SetAccessor(v8::String::New("gRadioState"),
251 RadioStateGetter, RadioStateSetter);
252 global->Set(v8::String::New("msSleep"), v8::FunctionTemplate::New(MsSleep));
253 global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print));
254 global->Set(v8::String::New("readFileToBuffer"),
255 v8::FunctionTemplate::New(ReadFileToBuffer));
256 global->Set(v8::String::New("readFileToString"),
257 v8::FunctionTemplate::New(ReadFileToString));
258 global->Set(v8::String::New("sendRilRequestComplete"),
259 v8::FunctionTemplate::New(SendRilRequestComplete));
260 global->Set(v8::String::New("sendRilUnsolicitedResponse"),
261 v8::FunctionTemplate::New(SendRilUnsolicitedResponse));
262 global->Set(v8::String::New("sendCtrlRequestComplete"),
263 v8::FunctionTemplate::New(SendCtrlRequestComplete));
264 global->Set(v8::String::New("include"), v8::FunctionTemplate::New(Include));
265 WorkerV8ObjectTemplateInit(global);
266 SchemaObjectTemplateInit(global);
267 Buffer::InitializeObjectTemplate(global);
268
269 // Create context with our globals and make it the current scope
270 v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
271
272
273 if (try_catch.HasCaught()) {
274 DBG("makeJsContext: Exception making the context");
275 ReportException(&try_catch);
276 try_catch.ReThrow();
277 }
278
279 return context;
280 }
281
282 /**
283 * Run some javascript code.
284 */
runJs(v8::Handle<v8::Context> context,v8::TryCatch * try_catch,const char * fileName,const char * code)285 void runJs(v8::Handle<v8::Context> context, v8::TryCatch *try_catch,
286 const char *fileName, const char *code) {
287 v8::HandleScope handle_scope;
288
289 // Compile the source
290 v8::Handle<v8::Script> script = v8::Script::Compile(
291 v8::String::New(code), v8::String::New(fileName));
292 if (try_catch->HasCaught()) {
293 LOGE("-- Compiling the source failed");
294 } else {
295 // Run the resulting script
296 v8::Handle<v8::Value> result = script->Run();
297 if (try_catch->HasCaught()) {
298 LOGE("-- Running the script failed");
299 }
300 }
301 }
302
testRadioState(v8::Handle<v8::Context> context)303 void testRadioState(v8::Handle<v8::Context> context) {
304 LOGD("testRadioState E:");
305 v8::HandleScope handle_scope;
306
307 v8::TryCatch try_catch;
308 try_catch.SetVerbose(true);
309
310 runJs(context, &try_catch, "local-string",
311 "for(i = 0; i < 10; i++) {\n"
312 " gRadioState = i;\n"
313 " print('gRadioState=' + gRadioState);\n"
314 "}\n"
315 "gRadioState = 1;\n"
316 "print('last gRadioState=' + gRadioState);\n");
317 LOGD("testRadioState X:");
318 }
319
testMsSleep(v8::Handle<v8::Context> context)320 void testMsSleep(v8::Handle<v8::Context> context) {
321 LOGD("testMsSleep E:");
322 v8::HandleScope handle_scope;
323
324 v8::TryCatch try_catch;
325 try_catch.SetVerbose(true);
326
327 runJs(context, &try_catch, "local-string",
328 "for(i = 0; i < 10; i++) {\n"
329 " sleeptime = i * 200\n"
330 " print('msSleep ' + sleeptime);\n"
331 " msSleep(sleeptime);\n"
332 "}\n");
333 LOGD("testMsSleep X:");
334 }
335
testPrint(v8::Handle<v8::Context> context)336 void testPrint(v8::Handle<v8::Context> context) {
337 LOGD("testPrint E:");
338 v8::HandleScope handle_scope;
339
340 v8::TryCatch try_catch;
341 try_catch.SetVerbose(true);
342
343 runJs(context, &try_catch, "local-string", "print(\"Hello\")");
344 LOGD("testPrint X:");
345 }
346
testCompileError(v8::Handle<v8::Context> context)347 void testCompileError(v8::Handle<v8::Context> context) {
348 LOGD("testCompileError E:");
349 v8::HandleScope handle_scope;
350
351 v8::TryCatch try_catch;
352 try_catch.SetVerbose(true);
353
354 // +++ generate a compile time error
355 runJs(context, &try_catch, "local-string", "+++");
356 LOGD("testCompileError X:");
357 }
358
testRuntimeError(v8::Handle<v8::Context> context)359 void testRuntimeError(v8::Handle<v8::Context> context) {
360 LOGD("testRuntimeError E:");
361 v8::HandleScope handle_scope;
362
363 v8::TryCatch try_catch;
364 try_catch.SetVerbose(true);
365
366 // Runtime error
367 runJs(context, &try_catch, "local-string",
368 "function hello() {\n"
369 " print(\"Hi there\");\n"
370 "}\n"
371 "helloo()");
372 LOGD("testRuntimeError X:");
373 }
374
testReadFile()375 void testReadFile() {
376 char *buffer;
377 size_t length;
378 int status;
379
380 LOGD("testReadFile E:");
381
382 status = ReadFile("/sdcard/data/no-file", &buffer, &length);
383 LOGD("testReadFile expect status != 0, status=%d, buffer=%p, length=%d",
384 status, buffer, length);
385
386 LOGD("testReadFile X:");
387 }
388
389
testReadFileToStringBuffer(v8::Handle<v8::Context> context)390 void testReadFileToStringBuffer(v8::Handle<v8::Context> context) {
391 LOGD("testReadFileToStringBuffer E:");
392 v8::HandleScope handle_scope;
393
394 v8::TryCatch try_catch;
395 try_catch.SetVerbose(true);
396
397 runJs(context, &try_catch, "local-string",
398 "fileContents = readFileToString(\"mock_ril.js\");\n"
399 "print(\"fileContents:\\n\" + fileContents);\n"
400 "buffer = readFileToBuffer(\"ril.desc\");\n"
401 "print(\"buffer.length=\" + buffer.length);\n");
402 LOGD("testReadFileToStringBuffer X:");
403 }
404
testJsSupport(v8::Handle<v8::Context> context)405 void testJsSupport(v8::Handle<v8::Context> context) {
406 LOGD("testJsSupport E: ********");
407 testRadioState(context);
408 testMsSleep(context);
409 testPrint(context);
410 testCompileError(context);
411 testRuntimeError(context);
412 testReadFile();
413 testReadFileToStringBuffer(context);
414 LOGD("testJsSupport X: ********\n");
415 }
416