1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program Tester Core
3 * ----------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Android ExecServer.
22 *//*--------------------------------------------------------------------*/
23
24 #include "tcuAndroidExecService.hpp"
25 #include "deFile.h"
26 #include "deClock.h"
27
28 #if 0
29 # define DBG_PRINT(ARGS) print ARGS
30 #else
31 # define DBG_PRINT(ARGS)
32 #endif
33
34 namespace tcu
35 {
36 namespace Android
37 {
38
39 static const char* LOG_FILE_NAME = "/sdcard/dEQP-log.qpa";
40
41 enum
42 {
43 PROCESS_START_TIMEOUT = 5000*1000, //!< Timeout in usec.
44 PROCESS_QUERY_INTERVAL = 1000*1000 //!< Running query interval limit in usec.
45 };
46
checkJniException(JNIEnv * env,const char * file,int line)47 static void checkJniException (JNIEnv* env, const char* file, int line)
48 {
49 if (env->ExceptionOccurred())
50 {
51 env->ExceptionDescribe();
52 env->ExceptionClear();
53 throw InternalError("JNI Exception", DE_NULL, file, line);
54 }
55 }
56
57 #define JNI_CHECK(EXPR) do { checkJniException(env, __FILE__, __LINE__); TCU_CHECK_INTERNAL(EXPR); } while (deGetFalse())
58
59 // TestProcess
60
TestProcess(JavaVM * vm,jobject context)61 TestProcess::TestProcess (JavaVM* vm, jobject context)
62 : m_vm (vm)
63 , m_remoteCls (0)
64 , m_remote (0)
65 , m_start (0)
66 , m_kill (0)
67 , m_isRunning (0)
68 , m_launchTime (0)
69 , m_lastQueryTime (0)
70 , m_lastRunningStatus (false)
71 , m_logReader (xs::LOG_BUFFER_BLOCK_SIZE, xs::LOG_BUFFER_NUM_BLOCKS)
72 {
73 DBG_PRINT(("TestProcess::TestProcess(%p, %p)", vm, context));
74
75 JNIEnv* env = getCurrentThreadEnv();
76 jobject remote = 0;
77 jstring logFileName = 0;
78
79 try
80 {
81 jclass remoteCls = 0;
82 jmethodID ctorId = 0;
83
84 remoteCls = env->FindClass("com/drawelements/deqp/testercore/RemoteAPI");
85 JNI_CHECK(remoteCls);
86
87 // Acquire global reference to RemoteAPI class.
88 m_remoteCls = reinterpret_cast<jclass>(env->NewGlobalRef(remoteCls));
89 JNI_CHECK(m_remoteCls);
90 env->DeleteLocalRef(remoteCls);
91 remoteCls = 0;
92
93 ctorId = env->GetMethodID(m_remoteCls, "<init>", "(Landroid/content/Context;Ljava/lang/String;)V");
94 JNI_CHECK(ctorId);
95
96 logFileName = env->NewStringUTF(LOG_FILE_NAME);
97 JNI_CHECK(logFileName);
98
99 // Create RemoteAPI instance.
100 remote = env->NewObject(m_remoteCls, ctorId, context, logFileName);
101 JNI_CHECK(remote);
102
103 env->DeleteLocalRef(logFileName);
104 logFileName = 0;
105
106 // Acquire global reference to remote.
107 m_remote = env->NewGlobalRef(remote);
108 JNI_CHECK(m_remote);
109 env->DeleteLocalRef(remote);
110 remote = 0;
111
112 m_start = env->GetMethodID(m_remoteCls, "start", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z");
113 JNI_CHECK(m_start);
114
115 m_kill = env->GetMethodID(m_remoteCls, "kill", "()Z");
116 JNI_CHECK(m_kill);
117
118 m_isRunning = env->GetMethodID(m_remoteCls, "isRunning", "()Z");
119 JNI_CHECK(m_isRunning);
120 }
121 catch (...)
122 {
123 if (logFileName)
124 env->DeleteLocalRef(logFileName);
125 if (remote)
126 env->DeleteLocalRef(remote);
127 if (m_remoteCls)
128 env->DeleteGlobalRef(reinterpret_cast<jobject>(m_remoteCls));
129 if (m_remote)
130 env->DeleteGlobalRef(m_remote);
131 throw;
132 }
133 }
134
~TestProcess(void)135 TestProcess::~TestProcess (void)
136 {
137 DBG_PRINT(("TestProcess::~TestProcess()"));
138
139 try
140 {
141 JNIEnv* env = getCurrentThreadEnv();
142 env->DeleteGlobalRef(m_remote);
143 env->DeleteGlobalRef(m_remoteCls);
144 }
145 catch (...)
146 {
147 }
148 }
149
start(const char * name,const char * params,const char * workingDir,const char * caseList)150 void TestProcess::start (const char* name, const char* params, const char* workingDir, const char* caseList)
151 {
152 DBG_PRINT(("TestProcess::start(%s, %s, %s, ...)", name, params, workingDir));
153
154 JNIEnv* env = getCurrentThreadEnv();
155 jstring nameStr = 0;
156 jstring paramsStr = 0;
157 jstring caseListStr = 0;
158
159 DE_UNREF(workingDir);
160
161 // Remove old log file if such exists.
162 if (deFileExists(LOG_FILE_NAME))
163 {
164 if (!deDeleteFile(LOG_FILE_NAME) || deFileExists(LOG_FILE_NAME))
165 throw xs::TestProcessException(std::string("Failed to remove '") + LOG_FILE_NAME + "'");
166 }
167
168 try
169 {
170 nameStr = env->NewStringUTF(name);
171 JNI_CHECK(nameStr);
172
173 paramsStr = env->NewStringUTF(params);
174 JNI_CHECK(paramsStr);
175
176 caseListStr = env->NewStringUTF(caseList);
177 JNI_CHECK(caseListStr);
178
179 jboolean res = env->CallBooleanMethod(m_remote, m_start, nameStr, paramsStr, caseListStr);
180 checkJniException(env, __FILE__, __LINE__);
181
182 if (res == JNI_FALSE)
183 throw xs::TestProcessException("Failed to launch activity");
184
185 m_launchTime = deGetMicroseconds();
186 m_lastQueryTime = m_launchTime;
187 m_lastRunningStatus = true;
188 }
189 catch (...)
190 {
191 if (nameStr)
192 env->DeleteLocalRef(nameStr);
193 if (paramsStr)
194 env->DeleteLocalRef(paramsStr);
195 if (caseListStr)
196 env->DeleteLocalRef(caseListStr);
197 throw;
198 }
199
200 env->DeleteLocalRef(nameStr);
201 env->DeleteLocalRef(paramsStr);
202 env->DeleteLocalRef(caseListStr);
203 }
204
terminate(void)205 void TestProcess::terminate (void)
206 {
207 DBG_PRINT(("TestProcess::terminate()"));
208
209 JNIEnv* env = getCurrentThreadEnv();
210 jboolean res = env->CallBooleanMethod(m_remote, m_kill);
211 checkJniException(env, __FILE__, __LINE__);
212 DE_UNREF(res); // Failure to kill process is ignored.
213 }
214
cleanup(void)215 void TestProcess::cleanup (void)
216 {
217 DBG_PRINT(("TestProcess::cleanup()"));
218
219 terminate();
220 m_logReader.stop();
221 }
222
isRunning(void)223 bool TestProcess::isRunning (void)
224 {
225 deUint64 curTime = deGetMicroseconds();
226
227 // On Android process launch is asynchronous so we don't want to poll for process until after some time.
228 if (curTime-m_launchTime < PROCESS_START_TIMEOUT ||
229 curTime-m_lastQueryTime < PROCESS_QUERY_INTERVAL)
230 return m_lastRunningStatus;
231
232 JNIEnv* env = getCurrentThreadEnv();
233 jboolean res = env->CallBooleanMethod(m_remote, m_isRunning);
234 checkJniException(env, __FILE__, __LINE__);
235
236 DBG_PRINT(("TestProcess::isRunning(): %s", res == JNI_TRUE ? "true" : "false"));
237 m_lastQueryTime = curTime;
238 m_lastRunningStatus = res == JNI_TRUE;
239
240 return m_lastRunningStatus;
241 }
242
getCurrentThreadEnv(void)243 JNIEnv* TestProcess::getCurrentThreadEnv (void)
244 {
245 JNIEnv* env = DE_NULL;
246 jint ret = m_vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
247
248 if (ret == JNI_OK)
249 return env;
250 else
251 throw InternalError("GetEnv() failed");
252 }
253
readTestLog(deUint8 * dst,int numBytes)254 int TestProcess::readTestLog (deUint8* dst, int numBytes)
255 {
256 if (!m_logReader.isRunning())
257 {
258 if (deGetMicroseconds() - m_launchTime > xs::LOG_FILE_TIMEOUT*1000)
259 {
260 // Timeout, kill process.
261 terminate();
262 DBG_PRINT(("TestProcess:readTestLog(): Log file timeout occurred!"));
263 return 0; // \todo [2013-08-13 pyry] Throw exception?
264 }
265
266 if (!deFileExists(LOG_FILE_NAME))
267 return 0;
268
269 // Start reader.
270 m_logReader.start(LOG_FILE_NAME);
271 }
272
273 DE_ASSERT(m_logReader.isRunning());
274 return m_logReader.read(dst, numBytes);
275 }
276
getExitCode(void) const277 int TestProcess::getExitCode (void) const
278 {
279 return 0;
280 }
281
readInfoLog(deUint8 * dst,int numBytes)282 int TestProcess::readInfoLog (deUint8* dst, int numBytes)
283 {
284 // \todo [2012-11-12 pyry] Read device log.
285 DE_UNREF(dst && numBytes);
286 return 0;
287 }
288
289 // ExecutionServer
290
ExecutionServer(JavaVM * vm,xs::TestProcess * testProcess,deSocketFamily family,int port,RunMode runMode)291 ExecutionServer::ExecutionServer (JavaVM* vm, xs::TestProcess* testProcess, deSocketFamily family, int port, RunMode runMode)
292 : xs::ExecutionServer (testProcess, family, port, runMode)
293 , m_vm (vm)
294 {
295 }
296
createHandler(de::Socket * socket,const de::SocketAddress & clientAddress)297 xs::ConnectionHandler* ExecutionServer::createHandler (de::Socket* socket, const de::SocketAddress& clientAddress)
298 {
299 DE_UNREF(clientAddress);
300 return new ConnectionHandler(m_vm, this, socket);
301 }
302
303 // ConnectionHandler
304
ConnectionHandler(JavaVM * vm,xs::ExecutionServer * server,de::Socket * socket)305 ConnectionHandler::ConnectionHandler (JavaVM* vm, xs::ExecutionServer* server, de::Socket* socket)
306 : xs::ExecutionRequestHandler (server, socket)
307 , m_vm (vm)
308 {
309 }
310
run(void)311 void ConnectionHandler::run (void)
312 {
313 JNIEnv* env = DE_NULL;
314 if (m_vm->AttachCurrentThread(&env, DE_NULL) != 0)
315 {
316 print("AttachCurrentThread() failed");
317 return;
318 }
319
320 xs::ExecutionRequestHandler::run();
321
322 if (m_vm->DetachCurrentThread() != 0)
323 print("DetachCurrentThread() failed");
324 }
325
326 // ServerThread
327
ServerThread(JavaVM * vm,xs::TestProcess * process,deSocketFamily family,int port)328 ServerThread::ServerThread (JavaVM* vm, xs::TestProcess* process, deSocketFamily family, int port)
329 : m_server(vm, process, family, port, xs::ExecutionServer::RUNMODE_FOREVER)
330 {
331 }
332
run(void)333 void ServerThread::run (void)
334 {
335 try
336 {
337 m_server.runServer();
338 }
339 catch (const std::exception& e)
340 {
341 die("ServerThread::run(): %s", e.what());
342 }
343 }
344
stop(void)345 void ServerThread::stop (void)
346 {
347 m_server.stopServer();
348 join();
349 }
350
351 // ExecService
352
ExecService(JavaVM * vm,jobject context,int port,deSocketFamily family)353 ExecService::ExecService (JavaVM* vm, jobject context, int port, deSocketFamily family)
354 : m_process (vm, context)
355 , m_thread (vm, &m_process, family, port)
356 {
357 }
358
~ExecService(void)359 ExecService::~ExecService (void)
360 {
361 }
362
start(void)363 void ExecService::start (void)
364 {
365 m_thread.start();
366 }
367
stop(void)368 void ExecService::stop (void)
369 {
370 m_thread.stop();
371 }
372
373 } // Android
374 } // tcu
375