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
34namespace tcu
35{
36namespace Android
37{
38
39static const char* LOG_FILE_NAME = "/sdcard/dEQP-log.qpa";
40
41enum
42{
43	PROCESS_START_TIMEOUT	= 5000*1000,	//!< Timeout in usec.
44	PROCESS_QUERY_INTERVAL	= 1000*1000		//!< Running query interval limit in usec.
45};
46
47static 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
61TestProcess::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
135TestProcess::~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
150void 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
205void 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
215void TestProcess::cleanup (void)
216{
217	DBG_PRINT(("TestProcess::cleanup()"));
218
219	terminate();
220	m_logReader.stop();
221}
222
223bool 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
243JNIEnv* 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
254int 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
277int	TestProcess::getExitCode (void) const
278{
279	return 0;
280}
281
282int 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
291ExecutionServer::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
297xs::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
305ConnectionHandler::ConnectionHandler (JavaVM* vm, xs::ExecutionServer* server, de::Socket* socket)
306	: xs::ExecutionRequestHandler	(server, socket)
307	, m_vm							(vm)
308{
309}
310
311void 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
328ServerThread::ServerThread (JavaVM* vm, xs::TestProcess* process, deSocketFamily family, int port)
329	: m_server(vm, process, family, port, xs::ExecutionServer::RUNMODE_FOREVER)
330{
331}
332
333void 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
345void ServerThread::stop (void)
346{
347	m_server.stopServer();
348	join();
349}
350
351// ExecService
352
353ExecService::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
359ExecService::~ExecService (void)
360{
361}
362
363void ExecService::start (void)
364{
365	m_thread.start();
366}
367
368void ExecService::stop (void)
369{
370	m_thread.stop();
371}
372
373} // Android
374} // tcu
375