Home | History | Annotate | Download | only in android
      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 
     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 
     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 
    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 
    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 
    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 
    215 void TestProcess::cleanup (void)
    216 {
    217 	DBG_PRINT(("TestProcess::cleanup()"));
    218 
    219 	terminate();
    220 	m_logReader.stop();
    221 }
    222 
    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 
    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 
    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 
    277 int	TestProcess::getExitCode (void) const
    278 {
    279 	return 0;
    280 }
    281 
    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 
    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 
    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 
    305 ConnectionHandler::ConnectionHandler (JavaVM* vm, xs::ExecutionServer* server, de::Socket* socket)
    306 	: xs::ExecutionRequestHandler	(server, socket)
    307 	, m_vm							(vm)
    308 {
    309 }
    310 
    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 
    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 
    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 
    345 void ServerThread::stop (void)
    346 {
    347 	m_server.stopServer();
    348 	join();
    349 }
    350 
    351 // ExecService
    352 
    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 
    359 ExecService::~ExecService (void)
    360 {
    361 }
    362 
    363 void ExecService::start (void)
    364 {
    365 	m_thread.start();
    366 }
    367 
    368 void ExecService::stop (void)
    369 {
    370 	m_thread.stop();
    371 }
    372 
    373 } // Android
    374 } // tcu
    375