platform_android.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <atomic>
  9. #include <chrono>
  10. #include <thread>
  11. #include <AzTest/AzTest.h>
  12. #include <AzTest/Platform.h>
  13. #include <AzCore/IO/SystemFile.h> // for AZ_MAX_PATH_LEN
  14. #include <AzCore/StringFunc/StringFunc.h>
  15. #include <AzCore/Utils/Utils.h>
  16. #include <AzCore/std/functional.h>
  17. #include <AzCore/Android/AndroidEnv.h>
  18. #include <AzCore/Android/Utils.h>
  19. #include <AzCore/Android/JNI/JNI.h>
  20. #include <AzCore/Android/JNI/Object.h>
  21. #include <AzCore/Android/JNI/scoped_ref.h>
  22. #include <AzCore/Component/TickBus.h>
  23. #include <AzFramework/API/ApplicationAPI_Platform.h>
  24. #include <AzFramework/Input/Buses/Notifications/RawInputNotificationBus_Platform.h>
  25. #include "aztestrunner.h"
  26. #include <android/asset_manager_jni.h>
  27. #include <android/log.h>
  28. #include <android/native_activity.h>
  29. #include <android/native_window.h>
  30. #include <android_native_app_glue.h>
  31. #include <sys/resource.h>
  32. #include <sys/types.h>
  33. constexpr const char* s_logTag = "LMBR";
  34. #define MAIN_EXIT_FAILURE(_appState, ...) \
  35. __android_log_print(ANDROID_LOG_INFO, s_logTag, "****************************************************************"); \
  36. __android_log_print(ANDROID_LOG_INFO, s_logTag, "STARTUP FAILURE - EXITING"); \
  37. __android_log_print(ANDROID_LOG_INFO, s_logTag, "REASON:"); \
  38. __android_log_print(ANDROID_LOG_INFO, s_logTag, __VA_ARGS__); \
  39. __android_log_print(ANDROID_LOG_INFO, s_logTag, "****************************************************************"); \
  40. _appState->userData = nullptr; \
  41. ANativeActivity_finish(_appState->activity); \
  42. while (_appState->destroyRequested == 0) { \
  43. g_eventDispatcher.PumpAllEvents(); \
  44. } \
  45. return;
  46. namespace AzTestRunner
  47. {
  48. void set_quiet_mode()
  49. {
  50. }
  51. const char* get_current_working_directory()
  52. {
  53. static char cwd_buffer[AZ_MAX_PATH_LEN] = { '\0' };
  54. [[maybe_unused]] AZ::Utils::ExecutablePathResult result = AZ::Utils::GetExecutableDirectory(cwd_buffer, AZ_ARRAY_SIZE(cwd_buffer));
  55. AZ_Assert(result == AZ::Utils::ExecutablePathResult::Success, "Error retrieving executable path");
  56. return static_cast<const char*>(cwd_buffer);
  57. }
  58. void pause_on_completion()
  59. {
  60. }
  61. }
  62. namespace
  63. {
  64. class NativeEventDispatcher
  65. : public AzFramework::AndroidEventDispatcher
  66. {
  67. public:
  68. NativeEventDispatcher()
  69. : m_appState(nullptr)
  70. {
  71. }
  72. ~NativeEventDispatcher()
  73. {
  74. }
  75. void PumpAllEvents() override
  76. {
  77. bool continueRunning = true;
  78. while (continueRunning)
  79. {
  80. continueRunning = PumpEvents(&ALooper_pollOnce);
  81. }
  82. }
  83. void PumpEventLoopOnce() override
  84. {
  85. PumpEvents(&ALooper_pollOnce);
  86. }
  87. void SetAppState(android_app* appState)
  88. {
  89. m_appState = appState;
  90. }
  91. private:
  92. // signature of ALooper_pollOnce and ALooper_pollAll -> int timeoutMillis, int* outFd, int* outEvents, void** outData
  93. typedef int (*EventPumpFunc)(int, int*, int*, void**);
  94. bool PumpEvents(EventPumpFunc looperFunc)
  95. {
  96. if (!m_appState)
  97. {
  98. return false;
  99. }
  100. int events = 0;
  101. android_poll_source* source = nullptr;
  102. const AZ::Android::AndroidEnv* androidEnv = AZ::Android::AndroidEnv::Get();
  103. // when timeout is negative, the function will block until an event is received
  104. const int result = looperFunc(androidEnv->IsRunning() ? 0 : -1, nullptr, &events, reinterpret_cast<void**>(&source));
  105. // the value returned from the looper poll func is either:
  106. // 1. the identifier associated with the event source (>= 0) and has event data that needs to be processed manually
  107. // 2. an ALOOPER_POLL_* enum (< 0) indicating there is no data to be processed due to error or callback(s) registered
  108. // with the event source were called
  109. const bool validIdentifier = (result >= 0);
  110. if (validIdentifier && source)
  111. {
  112. source->process(m_appState, source);
  113. }
  114. const bool destroyRequested = (m_appState->destroyRequested != 0);
  115. return (validIdentifier && !destroyRequested);
  116. }
  117. android_app* m_appState;
  118. };
  119. NativeEventDispatcher g_eventDispatcher;
  120. bool g_windowInitialized = false;
  121. int32_t HandleInputEvents(android_app* app, AInputEvent* event)
  122. {
  123. AzFramework::RawInputNotificationBusAndroid::Broadcast(&AzFramework::RawInputNotificationsAndroid::OnRawInputEvent, event);
  124. return 0;
  125. }
  126. void HandleApplicationLifecycleEvents(android_app* appState, int32_t command)
  127. {
  128. AZ::Android::AndroidEnv* androidEnv = static_cast<AZ::Android::AndroidEnv*>(appState->userData);
  129. if (!androidEnv)
  130. {
  131. return;
  132. }
  133. switch (command)
  134. {
  135. case APP_CMD_GAINED_FOCUS:
  136. {
  137. AzFramework::AndroidLifecycleEvents::Bus::Broadcast(
  138. &AzFramework::AndroidLifecycleEvents::Bus::Events::OnGainedFocus);
  139. }
  140. break;
  141. case APP_CMD_LOST_FOCUS:
  142. {
  143. AzFramework::AndroidLifecycleEvents::Bus::Broadcast(
  144. &AzFramework::AndroidLifecycleEvents::Bus::Events::OnLostFocus);
  145. }
  146. break;
  147. case APP_CMD_PAUSE:
  148. {
  149. AzFramework::AndroidLifecycleEvents::Bus::Broadcast(
  150. &AzFramework::AndroidLifecycleEvents::Bus::Events::OnPause);
  151. androidEnv->SetIsRunning(false);
  152. }
  153. break;
  154. case APP_CMD_RESUME:
  155. {
  156. androidEnv->SetIsRunning(true);
  157. AzFramework::AndroidLifecycleEvents::Bus::Broadcast(
  158. &AzFramework::AndroidLifecycleEvents::Bus::Events::OnResume);
  159. }
  160. break;
  161. case APP_CMD_DESTROY:
  162. {
  163. AzFramework::AndroidLifecycleEvents::Bus::Broadcast(
  164. &AzFramework::AndroidLifecycleEvents::Bus::Events::OnDestroy);
  165. }
  166. break;
  167. case APP_CMD_INIT_WINDOW:
  168. {
  169. g_windowInitialized = true;
  170. androidEnv->SetWindow(appState->window);
  171. AzFramework::AndroidLifecycleEvents::Bus::Broadcast(
  172. &AzFramework::AndroidLifecycleEvents::Bus::Events::OnWindowInit);
  173. }
  174. break;
  175. case APP_CMD_TERM_WINDOW:
  176. {
  177. AzFramework::AndroidLifecycleEvents::Bus::Broadcast(
  178. &AzFramework::AndroidLifecycleEvents::Bus::Events::OnWindowDestroy);
  179. androidEnv->SetWindow(nullptr);
  180. }
  181. break;
  182. case APP_CMD_LOW_MEMORY:
  183. {
  184. AzFramework::AndroidLifecycleEvents::Bus::Broadcast(
  185. &AzFramework::AndroidLifecycleEvents::Bus::Events::OnLowMemory);
  186. }
  187. break;
  188. case APP_CMD_CONFIG_CHANGED:
  189. {
  190. androidEnv->UpdateConfiguration();
  191. }
  192. break;
  193. case APP_CMD_WINDOW_REDRAW_NEEDED:
  194. {
  195. AzFramework::AndroidLifecycleEvents::Bus::Broadcast(
  196. &AzFramework::AndroidLifecycleEvents::Bus::Events::OnWindowRedrawNeeded);
  197. }
  198. break;
  199. }
  200. }
  201. void OnWindowRedrawNeeded(ANativeActivity* activity, ANativeWindow* rect)
  202. {
  203. android_app* app = static_cast<android_app*>(activity->instance);
  204. int8_t cmd = APP_CMD_WINDOW_REDRAW_NEEDED;
  205. if (write(app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd))
  206. {
  207. __android_log_print(ANDROID_LOG_ERROR, s_logTag, "Failure writing android_app cmd: %s\n", strerror(errno));
  208. }
  209. }
  210. }
  211. // In order to read the logcat from adb, stdout and stderr needs to be redirected custom handles and we will need
  212. // to spawn a thread to read from the custom handles and pass the output of those streams through __android_log_print
  213. // to a specia 'LMBR' tag
  214. static int s_pfd[2];
  215. static std::atomic_bool s_testRunComplete { false };
  216. static void *thread_logger_func(void*)
  217. {
  218. ssize_t readSize;
  219. char logBuffer[256] = {'\0'};
  220. while (!s_testRunComplete)
  221. {
  222. readSize = read(s_pfd[0], logBuffer, sizeof(logBuffer)-1);
  223. if (readSize>0) {
  224. // Trim the tailing return character
  225. if (logBuffer[readSize - 1] == '\n') {
  226. --readSize;
  227. }
  228. logBuffer[readSize] = '\0';
  229. ((void) __android_log_print(ANDROID_LOG_INFO, s_logTag, "%s", logBuffer));
  230. }
  231. }
  232. return 0;
  233. }
  234. constexpr const char* s_defaultAppName = "AzTestRunner";
  235. constexpr size_t s_maxArgCount = 8;
  236. constexpr size_t s_maxArgLength = 64;
  237. void android_main(android_app* appState)
  238. {
  239. // Adding a start up banner so you can see when the test runner is starting up in amongst the logcat spam
  240. __android_log_print(ANDROID_LOG_INFO, s_logTag, "****************************************************************");
  241. __android_log_print(ANDROID_LOG_INFO, s_logTag, " Starting %s", s_defaultAppName);
  242. __android_log_print(ANDROID_LOG_INFO, s_logTag, "****************************************************************");
  243. // setup the system command handler which are guaranteed to be called on the same
  244. // thread the events are pumped
  245. appState->onAppCmd = HandleApplicationLifecycleEvents;
  246. appState->onInputEvent = HandleInputEvents;
  247. g_eventDispatcher.SetAppState(appState);
  248. // This callback will notify us when the orientation of the device changes.
  249. // While Android does have an onNativeWindowResized callback, it is never called in android_native_app_glue when the window size changes.
  250. // The onNativeConfigChanged callback is called too early(before the window size has changed), so we won't have the correct window size at that point.
  251. appState->activity->callbacks->onNativeWindowRedrawNeeded = OnWindowRedrawNeeded;
  252. {
  253. AZ::Android::AndroidEnv::Descriptor descriptor;
  254. descriptor.m_jvm = appState->activity->vm;
  255. descriptor.m_activityRef = appState->activity->clazz;
  256. descriptor.m_assetManager = appState->activity->assetManager;
  257. descriptor.m_configuration = appState->config;
  258. descriptor.m_appPrivateStoragePath = appState->activity->internalDataPath;
  259. descriptor.m_appPublicStoragePath = appState->activity->externalDataPath;
  260. descriptor.m_obbStoragePath = appState->activity->obbPath;
  261. if (!AZ::Android::AndroidEnv::Create(descriptor))
  262. {
  263. AZ::Android::AndroidEnv::Destroy();
  264. MAIN_EXIT_FAILURE(appState, "Failed to create the AndroidEnv");
  265. }
  266. AZ::Android::AndroidEnv* androidEnv = AZ::Android::AndroidEnv::Get();
  267. appState->userData = androidEnv;
  268. androidEnv->SetIsRunning(true);
  269. }
  270. g_eventDispatcher.PumpAllEvents();
  271. // Prepare the command line args to pass to main
  272. char commandLineArgs[s_maxArgCount][s_maxArgLength] = { {'\0'} };
  273. size_t currentCommandLineIndex = 0;
  274. // Always add the app as the first arg to mimic the way other platforms start with the executable name.
  275. const char* packageName = AZ::Android::Utils::GetPackageName();
  276. const char* appName = (packageName) ? packageName : s_defaultAppName;
  277. size_t appNameLen = strlen(appName);
  278. azstrncpy(commandLineArgs[currentCommandLineIndex++], s_maxArgLength, appName, appNameLen + 1);
  279. // "activityObject" and "intent" need to be destroyed before we call Destroy() on the allocator to ensure graceful shutdown.
  280. {
  281. // Get the string extras and pass them along as cmd line params
  282. AZ::Android::JNI::Internal::Object<AZ::OSAllocator> activityObject(AZ::Android::JNI::GetEnv()->GetObjectClass(appState->activity->clazz), appState->activity->clazz);
  283. activityObject.RegisterMethod("getIntent", "()Landroid/content/Intent;");
  284. jobject intent = activityObject.InvokeObjectMethod<jobject>("getIntent");
  285. AZ::Android::JNI::Internal::Object<AZ::OSAllocator> intentObject(AZ::Android::JNI::GetEnv()->GetObjectClass(intent), intent);
  286. intentObject.RegisterMethod("getStringExtra", "(Ljava/lang/String;)Ljava/lang/String;");
  287. intentObject.RegisterMethod("getExtras", "()Landroid/os/Bundle;");
  288. jobject extras = intentObject.InvokeObjectMethod<jobject>("getExtras");
  289. int start_delay = 0;
  290. if (extras)
  291. {
  292. // Get the set of keys
  293. AZ::Android::JNI::Internal::Object<AZ::OSAllocator> extrasObject(AZ::Android::JNI::GetEnv()->GetObjectClass(extras), extras);
  294. extrasObject.RegisterMethod("keySet", "()Ljava/util/Set;");
  295. jobject extrasKeySet = extrasObject.InvokeObjectMethod<jobject>("keySet");
  296. // get the array of string objects
  297. AZ::Android::JNI::Internal::Object<AZ::OSAllocator> extrasKeySetObject(AZ::Android::JNI::GetEnv()->GetObjectClass(extrasKeySet), extrasKeySet);
  298. extrasKeySetObject.RegisterMethod("toArray", "()[Ljava/lang/Object;");
  299. jobjectArray extrasKeySetArray = extrasKeySetObject.InvokeObjectMethod<jobjectArray>("toArray");
  300. int extrasKeySetArraySize = AZ::Android::JNI::GetEnv()->GetArrayLength(extrasKeySetArray);
  301. for (int x = 0; x < extrasKeySetArraySize; x++)
  302. {
  303. jstring keyObject = static_cast<jstring>(AZ::Android::JNI::GetEnv()->GetObjectArrayElement(extrasKeySetArray, x));
  304. AZ::OSString value = intentObject.InvokeStringMethod("getStringExtra", keyObject);
  305. const char* keyChars = AZ::Android::JNI::GetEnv()->GetStringUTFChars(keyObject, 0);
  306. if (azstricmp("startdelay", keyChars) == 0)
  307. {
  308. start_delay = strtol(value.c_str(), nullptr, 10);
  309. }
  310. else if (azstricmp("gtest_filter", keyChars) == 0)
  311. {
  312. azsnprintf(commandLineArgs[currentCommandLineIndex++], s_maxArgLength, "--gtest_filter=%.*s", aznumeric_cast<int>(value.size()), value.c_str());
  313. }
  314. else
  315. {
  316. azstrncpy(commandLineArgs[currentCommandLineIndex++], s_maxArgLength, keyChars, strlen(keyChars) + 1);
  317. azstrncpy(commandLineArgs[currentCommandLineIndex++], s_maxArgLength, value.c_str(), value.length() + 1);
  318. }
  319. AZ::Android::JNI::GetEnv()->ReleaseStringUTFChars(keyObject, keyChars);
  320. }
  321. }
  322. if (start_delay > 0)
  323. {
  324. std::this_thread::sleep_for(std::chrono::seconds(start_delay));
  325. }
  326. }
  327. char* argv[s_maxArgCount];
  328. for (size_t index = 0; index < s_maxArgCount; index++)
  329. {
  330. argv[index] = commandLineArgs[index];
  331. }
  332. // Redirect stdout and stderr to custom handles and prepare the thread to read from the custom handles
  333. pthread_t log_thread;
  334. setvbuf(stdout, 0, _IOLBF, 0);
  335. setvbuf(stderr, 0, _IONBF, 0);
  336. pipe(s_pfd);
  337. dup2(s_pfd[1], 1);
  338. dup2(s_pfd[1], 2);
  339. if (pthread_create(&log_thread, 0, thread_logger_func, 0) == -1)
  340. {
  341. ((void)__android_log_print(ANDROID_LOG_INFO, s_logTag, "[FAILURE] Unable to spawn logging thread"));
  342. return;
  343. }
  344. // Execute the unit test main
  345. int result = AzTestRunner::wrapped_main(aznumeric_cast<int>(currentCommandLineIndex), argv);
  346. g_eventDispatcher.PumpAllEvents();
  347. s_testRunComplete = true;
  348. std::this_thread::sleep_for(std::chrono::seconds(1));
  349. if (result == 0)
  350. {
  351. ((void)__android_log_print(ANDROID_LOG_INFO, s_logTag, "[SUCCESS]"));
  352. }
  353. else
  354. {
  355. ((void)__android_log_print(ANDROID_LOG_INFO, s_logTag, "[FAILURE]"));
  356. }
  357. AZ::Android::AndroidEnv::Destroy();
  358. }