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, deSocketFamily family, int port) 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