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