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) \
58 do \
59 { \
60 checkJniException(env, __FILE__, __LINE__); \
61 TCU_CHECK_INTERNAL(EXPR); \
62 } while (false)
63
64 // TestProcess
65
TestProcess(JavaVM * vm,jobject context)66 TestProcess::TestProcess(JavaVM *vm, jobject context)
67 : m_vm(vm)
68 , m_remoteCls(0)
69 , m_remote(0)
70 , m_start(0)
71 , m_kill(0)
72 , m_isRunning(0)
73 , m_launchTime(0)
74 , m_lastQueryTime(0)
75 , m_lastRunningStatus(false)
76 , m_logReader(xs::LOG_BUFFER_BLOCK_SIZE, xs::LOG_BUFFER_NUM_BLOCKS)
77 {
78 DBG_PRINT(("TestProcess::TestProcess(%p, %p)", vm, context));
79
80 JNIEnv *env = getCurrentThreadEnv();
81 jobject remote = 0;
82 jstring logFileName = 0;
83
84 try
85 {
86 jclass remoteCls = 0;
87 jmethodID ctorId = 0;
88
89 remoteCls = env->FindClass("com/drawelements/deqp/testercore/RemoteAPI");
90 JNI_CHECK(remoteCls);
91
92 // Acquire global reference to RemoteAPI class.
93 m_remoteCls = reinterpret_cast<jclass>(env->NewGlobalRef(remoteCls));
94 JNI_CHECK(m_remoteCls);
95 env->DeleteLocalRef(remoteCls);
96 remoteCls = 0;
97
98 ctorId = env->GetMethodID(m_remoteCls, "<init>", "(Landroid/content/Context;Ljava/lang/String;)V");
99 JNI_CHECK(ctorId);
100
101 logFileName = env->NewStringUTF(LOG_FILE_NAME);
102 JNI_CHECK(logFileName);
103
104 // Create RemoteAPI instance.
105 remote = env->NewObject(m_remoteCls, ctorId, context, logFileName);
106 JNI_CHECK(remote);
107
108 env->DeleteLocalRef(logFileName);
109 logFileName = 0;
110
111 // Acquire global reference to remote.
112 m_remote = env->NewGlobalRef(remote);
113 JNI_CHECK(m_remote);
114 env->DeleteLocalRef(remote);
115 remote = 0;
116
117 m_start = env->GetMethodID(m_remoteCls, "start", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z");
118 JNI_CHECK(m_start);
119
120 m_kill = env->GetMethodID(m_remoteCls, "kill", "()Z");
121 JNI_CHECK(m_kill);
122
123 m_isRunning = env->GetMethodID(m_remoteCls, "isRunning", "()Z");
124 JNI_CHECK(m_isRunning);
125 }
126 catch (...)
127 {
128 if (logFileName)
129 env->DeleteLocalRef(logFileName);
130 if (remote)
131 env->DeleteLocalRef(remote);
132 if (m_remoteCls)
133 env->DeleteGlobalRef(reinterpret_cast<jobject>(m_remoteCls));
134 if (m_remote)
135 env->DeleteGlobalRef(m_remote);
136 throw;
137 }
138 }
139
~TestProcess(void)140 TestProcess::~TestProcess(void)
141 {
142 DBG_PRINT(("TestProcess::~TestProcess()"));
143
144 try
145 {
146 JNIEnv *env = getCurrentThreadEnv();
147 env->DeleteGlobalRef(m_remote);
148 env->DeleteGlobalRef(m_remoteCls);
149 }
150 catch (...)
151 {
152 }
153 }
154
start(const char * name,const char * params,const char * workingDir,const char * caseList)155 void TestProcess::start(const char *name, const char *params, const char *workingDir, const char *caseList)
156 {
157 DBG_PRINT(("TestProcess::start(%s, %s, %s, ...)", name, params, workingDir));
158
159 JNIEnv *env = getCurrentThreadEnv();
160 jstring nameStr = 0;
161 jstring paramsStr = 0;
162 jstring caseListStr = 0;
163
164 DE_UNREF(workingDir);
165
166 // Remove old log file if such exists.
167 if (deFileExists(LOG_FILE_NAME))
168 {
169 if (!deDeleteFile(LOG_FILE_NAME) || deFileExists(LOG_FILE_NAME))
170 throw xs::TestProcessException(std::string("Failed to remove '") + LOG_FILE_NAME + "'");
171 }
172
173 try
174 {
175 nameStr = env->NewStringUTF(name);
176 JNI_CHECK(nameStr);
177
178 paramsStr = env->NewStringUTF(params);
179 JNI_CHECK(paramsStr);
180
181 caseListStr = env->NewStringUTF(caseList);
182 JNI_CHECK(caseListStr);
183
184 jboolean res = env->CallBooleanMethod(m_remote, m_start, nameStr, paramsStr, caseListStr);
185 checkJniException(env, __FILE__, __LINE__);
186
187 if (res == JNI_FALSE)
188 throw xs::TestProcessException("Failed to launch activity");
189
190 m_launchTime = deGetMicroseconds();
191 m_lastQueryTime = m_launchTime;
192 m_lastRunningStatus = true;
193 }
194 catch (...)
195 {
196 if (nameStr)
197 env->DeleteLocalRef(nameStr);
198 if (paramsStr)
199 env->DeleteLocalRef(paramsStr);
200 if (caseListStr)
201 env->DeleteLocalRef(caseListStr);
202 throw;
203 }
204
205 env->DeleteLocalRef(nameStr);
206 env->DeleteLocalRef(paramsStr);
207 env->DeleteLocalRef(caseListStr);
208 }
209
terminate(void)210 void TestProcess::terminate(void)
211 {
212 DBG_PRINT(("TestProcess::terminate()"));
213
214 JNIEnv *env = getCurrentThreadEnv();
215 jboolean res = env->CallBooleanMethod(m_remote, m_kill);
216 checkJniException(env, __FILE__, __LINE__);
217 DE_UNREF(res); // Failure to kill process is ignored.
218 }
219
cleanup(void)220 void TestProcess::cleanup(void)
221 {
222 DBG_PRINT(("TestProcess::cleanup()"));
223
224 terminate();
225 m_logReader.stop();
226 }
227
isRunning(void)228 bool TestProcess::isRunning(void)
229 {
230 uint64_t curTime = deGetMicroseconds();
231
232 // On Android process launch is asynchronous so we don't want to poll for process until after some time.
233 if (curTime - m_launchTime < PROCESS_START_TIMEOUT || curTime - m_lastQueryTime < PROCESS_QUERY_INTERVAL)
234 return m_lastRunningStatus;
235
236 JNIEnv *env = getCurrentThreadEnv();
237 jboolean res = env->CallBooleanMethod(m_remote, m_isRunning);
238 checkJniException(env, __FILE__, __LINE__);
239
240 DBG_PRINT(("TestProcess::isRunning(): %s", res == JNI_TRUE ? "true" : "false"));
241 m_lastQueryTime = curTime;
242 m_lastRunningStatus = res == JNI_TRUE;
243
244 return m_lastRunningStatus;
245 }
246
getCurrentThreadEnv(void)247 JNIEnv *TestProcess::getCurrentThreadEnv(void)
248 {
249 JNIEnv *env = DE_NULL;
250 jint ret = m_vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
251
252 if (ret == JNI_OK)
253 return env;
254 else
255 throw InternalError("GetEnv() failed");
256 }
257
readTestLog(uint8_t * dst,int numBytes)258 int TestProcess::readTestLog(uint8_t *dst, int numBytes)
259 {
260 if (!m_logReader.isRunning())
261 {
262 if (deGetMicroseconds() - m_launchTime > xs::LOG_FILE_TIMEOUT * 1000)
263 {
264 // Timeout, kill process.
265 terminate();
266 DBG_PRINT(("TestProcess:readTestLog(): Log file timeout occurred!"));
267 return 0; // \todo [2013-08-13 pyry] Throw exception?
268 }
269
270 if (!deFileExists(LOG_FILE_NAME))
271 return 0;
272
273 // Start reader.
274 m_logReader.start(LOG_FILE_NAME);
275 }
276
277 DE_ASSERT(m_logReader.isRunning());
278 return m_logReader.read(dst, numBytes);
279 }
280
getExitCode(void) const281 int TestProcess::getExitCode(void) const
282 {
283 return 0;
284 }
285
readInfoLog(uint8_t * dst,int numBytes)286 int TestProcess::readInfoLog(uint8_t *dst, int numBytes)
287 {
288 // \todo [2012-11-12 pyry] Read device log.
289 DE_UNREF(dst && numBytes);
290 return 0;
291 }
292
293 // ExecutionServer
294
ExecutionServer(JavaVM * vm,xs::TestProcess * testProcess,deSocketFamily family,int port,RunMode runMode)295 ExecutionServer::ExecutionServer(JavaVM *vm, xs::TestProcess *testProcess, deSocketFamily family, int port,
296 RunMode runMode)
297 : xs::ExecutionServer(testProcess, family, port, runMode)
298 , m_vm(vm)
299 {
300 }
301
createHandler(de::Socket * socket,const de::SocketAddress & clientAddress)302 xs::ConnectionHandler *ExecutionServer::createHandler(de::Socket *socket, const de::SocketAddress &clientAddress)
303 {
304 DE_UNREF(clientAddress);
305 return new ConnectionHandler(m_vm, this, socket);
306 }
307
308 // ConnectionHandler
309
ConnectionHandler(JavaVM * vm,xs::ExecutionServer * server,de::Socket * socket)310 ConnectionHandler::ConnectionHandler(JavaVM *vm, xs::ExecutionServer *server, de::Socket *socket)
311 : xs::ExecutionRequestHandler(server, socket)
312 , m_vm(vm)
313 {
314 }
315
run(void)316 void ConnectionHandler::run(void)
317 {
318 JNIEnv *env = DE_NULL;
319 if (m_vm->AttachCurrentThread(&env, DE_NULL) != 0)
320 {
321 print("AttachCurrentThread() failed");
322 return;
323 }
324
325 xs::ExecutionRequestHandler::run();
326
327 if (m_vm->DetachCurrentThread() != 0)
328 print("DetachCurrentThread() failed");
329 }
330
331 // ServerThread
332
ServerThread(JavaVM * vm,xs::TestProcess * process,deSocketFamily family,int port)333 ServerThread::ServerThread(JavaVM *vm, xs::TestProcess *process, deSocketFamily family, int port)
334 : m_server(vm, process, family, port, xs::ExecutionServer::RUNMODE_FOREVER)
335 {
336 }
337
run(void)338 void ServerThread::run(void)
339 {
340 try
341 {
342 m_server.runServer();
343 }
344 catch (const std::exception &e)
345 {
346 die("ServerThread::run(): %s", e.what());
347 }
348 }
349
stop(void)350 void ServerThread::stop(void)
351 {
352 m_server.stopServer();
353 join();
354 }
355
356 // ExecService
357
ExecService(JavaVM * vm,jobject context,int port,deSocketFamily family)358 ExecService::ExecService(JavaVM *vm, jobject context, int port, deSocketFamily family)
359 : m_process(vm, context)
360 , m_thread(vm, &m_process, family, port)
361 {
362 }
363
~ExecService(void)364 ExecService::~ExecService(void)
365 {
366 }
367
start(void)368 void ExecService::start(void)
369 {
370 m_thread.start();
371 }
372
stop(void)373 void ExecService::stop(void)
374 {
375 m_thread.stop();
376 }
377
378 } // namespace Android
379 } // namespace tcu
380