Launcher_Android.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  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 <Launcher.h>
  9. #include <../Common/UnixLike/Launcher_UnixLike.h>
  10. #include <AzCore/Android/AndroidEnv.h>
  11. #include <AzCore/Android/Utils.h>
  12. #include <AzCore/Android/JNI/JNI.h>
  13. #include <AzCore/Android/JNI/Object.h>
  14. #include <AzCore/Android/JNI/scoped_ref.h>
  15. #include <AzFramework/API/ApplicationAPI_Platform.h>
  16. #include <AzFramework/Input/Buses/Notifications/RawInputNotificationBus_Platform.h>
  17. #include <AzGameFramework/Application/GameApplication.h>
  18. #include <IConsole.h>
  19. #include <android/asset_manager_jni.h>
  20. #include <android/log.h>
  21. #include <android/native_activity.h>
  22. #include <android/native_window.h>
  23. #include <android_native_app_glue.h>
  24. #include <sys/resource.h>
  25. #include <sys/types.h>
  26. #if defined(AZ_ENABLE_TRACING) || defined(RELEASE_LOGGING)
  27. #define ENABLE_LOGGING
  28. #endif // defined(AZ_ENABLE_TRACING) || defined(RELEASE_LOGGING)
  29. #if defined(ENABLE_LOGGING)
  30. #define LOG_TAG "LMBR"
  31. #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
  32. #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
  33. #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
  34. struct COutputPrintSink
  35. : public IOutputPrintSink
  36. {
  37. virtual void Print(const char* message)
  38. {
  39. LOGI("%s", message);
  40. }
  41. };
  42. COutputPrintSink g_androidPrintSink;
  43. #else
  44. #define LOGI(...)
  45. #define LOGE(...)
  46. #endif // !defined(_RELEASE)
  47. #define MAIN_EXIT_FAILURE(_appState, ...) \
  48. LOGE("****************************************************************"); \
  49. LOGE("STARTUP FAILURE - EXITING"); \
  50. LOGE("REASON:"); \
  51. LOGE(__VA_ARGS__); \
  52. LOGE("****************************************************************"); \
  53. _appState->userData = nullptr; \
  54. ANativeActivity_finish(_appState->activity); \
  55. while (_appState->destroyRequested == 0) { \
  56. g_eventDispatcher.PumpAllEvents(); \
  57. } \
  58. return;
  59. namespace
  60. {
  61. class NativeEventDispatcher
  62. : public AzFramework::AndroidEventDispatcher
  63. {
  64. public:
  65. NativeEventDispatcher()
  66. : m_appState(nullptr)
  67. {
  68. }
  69. ~NativeEventDispatcher() = default;
  70. void PumpAllEvents() override
  71. {
  72. bool continueRunning = true;
  73. while (continueRunning)
  74. {
  75. continueRunning = PumpEvents(&ALooper_pollAll);
  76. }
  77. }
  78. void PumpEventLoopOnce() override
  79. {
  80. PumpEvents(&ALooper_pollOnce);
  81. }
  82. void SetAppState(android_app* appState)
  83. {
  84. m_appState = appState;
  85. }
  86. private:
  87. // signature of ALooper_pollOnce and ALooper_pollAll -> int timeoutMillis, int* outFd, int* outEvents, void** outData
  88. typedef int (*EventPumpFunc)(int, int*, int*, void**);
  89. bool PumpEvents(EventPumpFunc looperFunc)
  90. {
  91. if (!m_appState)
  92. {
  93. return false;
  94. }
  95. int events = 0;
  96. android_poll_source* source = nullptr;
  97. const AZ::Android::AndroidEnv* androidEnv = AZ::Android::AndroidEnv::Get();
  98. // when timeout is negative, the function will block until an event is received
  99. const int result = looperFunc(androidEnv->IsRunning() ? 0 : -1, nullptr, &events, reinterpret_cast<void**>(&source));
  100. // the value returned from the looper poll func is either:
  101. // 1. the identifier associated with the event source (>= 0) and has event data that needs to be processed manually
  102. // 2. an ALOOPER_POLL_* enum (< 0) indicating there is no data to be processed due to error or callback(s) registered
  103. // with the event source were called
  104. const bool validIdentifier = (result >= 0);
  105. if (validIdentifier && source)
  106. {
  107. source->process(m_appState, source);
  108. }
  109. const bool destroyRequested = (m_appState->destroyRequested != 0);
  110. if (destroyRequested)
  111. {
  112. AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::ExitMainLoop);
  113. }
  114. return (validIdentifier && !destroyRequested);
  115. }
  116. android_app* m_appState;
  117. };
  118. NativeEventDispatcher g_eventDispatcher;
  119. bool g_windowInitialized = false;
  120. void OnPostAppStart()
  121. {
  122. // set the event dispatcher with the application framework
  123. AzFramework::AndroidAppRequests::Bus::Broadcast(&AzFramework::AndroidAppRequests::SetEventDispatcher, &g_eventDispatcher);
  124. // queue the dismissal of the system splash screen in case the engine splash is disabled
  125. AZ::TickBus::QueueFunction([](){
  126. AZ::Android::Utils::DismissSplashScreen();
  127. });
  128. }
  129. int32_t HandleInputEvents(android_app* app, AInputEvent* event)
  130. {
  131. AzFramework::RawInputNotificationBusAndroid::Broadcast(&AzFramework::RawInputNotificationsAndroid::OnRawInputEvent, event);
  132. return 0;
  133. }
  134. void HandleApplicationLifecycleEvents(android_app* appState, int32_t command)
  135. {
  136. #if defined(ENABLE_LOGGING)
  137. const char* commandNames[] = {
  138. "APP_CMD_INPUT_CHANGED",
  139. "APP_CMD_INIT_WINDOW",
  140. "APP_CMD_TERM_WINDOW",
  141. "APP_CMD_WINDOW_RESIZED",
  142. "APP_CMD_WINDOW_REDRAW_NEEDED",
  143. "APP_CMD_CONTENT_RECT_CHANGED",
  144. "APP_CMD_GAINED_FOCUS",
  145. "APP_CMD_LOST_FOCUS",
  146. "APP_CMD_CONFIG_CHANGED",
  147. "APP_CMD_LOW_MEMORY",
  148. "APP_CMD_START",
  149. "APP_CMD_RESUME",
  150. "APP_CMD_SAVE_STATE",
  151. "APP_CMD_PAUSE",
  152. "APP_CMD_STOP",
  153. "APP_CMD_DESTROY",
  154. };
  155. if (command >= 0 && command < sizeof(commandNames))
  156. {
  157. LOGI("Engine command received: %s", commandNames[command]);
  158. }
  159. else
  160. {
  161. LOGW("Unknown engine command received: %d", command);
  162. }
  163. #endif
  164. AZ::Android::AndroidEnv* androidEnv = static_cast<AZ::Android::AndroidEnv*>(appState->userData);
  165. if (!androidEnv)
  166. {
  167. return;
  168. }
  169. switch (command)
  170. {
  171. case APP_CMD_GAINED_FOCUS:
  172. {
  173. AzFramework::AndroidLifecycleEvents::Bus::Broadcast(
  174. &AzFramework::AndroidLifecycleEvents::Bus::Events::OnGainedFocus);
  175. }
  176. break;
  177. case APP_CMD_LOST_FOCUS:
  178. {
  179. AzFramework::AndroidLifecycleEvents::Bus::Broadcast(
  180. &AzFramework::AndroidLifecycleEvents::Bus::Events::OnLostFocus);
  181. }
  182. break;
  183. case APP_CMD_PAUSE:
  184. {
  185. AzFramework::AndroidLifecycleEvents::Bus::Broadcast(
  186. &AzFramework::AndroidLifecycleEvents::Bus::Events::OnPause);
  187. androidEnv->SetIsRunning(false);
  188. }
  189. break;
  190. case APP_CMD_RESUME:
  191. {
  192. androidEnv->SetIsRunning(true);
  193. AzFramework::AndroidLifecycleEvents::Bus::Broadcast(
  194. &AzFramework::AndroidLifecycleEvents::Bus::Events::OnResume);
  195. }
  196. break;
  197. case APP_CMD_DESTROY:
  198. {
  199. AzFramework::AndroidLifecycleEvents::Bus::Broadcast(
  200. &AzFramework::AndroidLifecycleEvents::Bus::Events::OnDestroy);
  201. }
  202. break;
  203. case APP_CMD_INIT_WINDOW:
  204. {
  205. g_windowInitialized = true;
  206. androidEnv->SetWindow(appState->window);
  207. AzFramework::AndroidLifecycleEvents::Bus::Broadcast(
  208. &AzFramework::AndroidLifecycleEvents::Bus::Events::OnWindowInit);
  209. }
  210. break;
  211. case APP_CMD_TERM_WINDOW:
  212. {
  213. AzFramework::AndroidLifecycleEvents::Bus::Broadcast(
  214. &AzFramework::AndroidLifecycleEvents::Bus::Events::OnWindowDestroy);
  215. androidEnv->SetWindow(nullptr);
  216. }
  217. break;
  218. case APP_CMD_LOW_MEMORY:
  219. {
  220. AzFramework::AndroidLifecycleEvents::Bus::Broadcast(
  221. &AzFramework::AndroidLifecycleEvents::Bus::Events::OnLowMemory);
  222. }
  223. break;
  224. case APP_CMD_CONFIG_CHANGED:
  225. {
  226. androidEnv->UpdateConfiguration();
  227. }
  228. break;
  229. case APP_CMD_WINDOW_REDRAW_NEEDED:
  230. {
  231. AzFramework::AndroidLifecycleEvents::Bus::Broadcast(
  232. &AzFramework::AndroidLifecycleEvents::Bus::Events::OnWindowRedrawNeeded);
  233. }
  234. break;
  235. }
  236. }
  237. void OnWindowRedrawNeeded(ANativeActivity* activity, ANativeWindow* rect)
  238. {
  239. android_app* app = static_cast<android_app*>(activity->instance);
  240. int8_t cmd = APP_CMD_WINDOW_REDRAW_NEEDED;
  241. if (write(app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd))
  242. {
  243. LOGE("Failure writing android_app cmd: %s\n", strerror(errno));
  244. }
  245. }
  246. }
  247. // This is the main entry point of a native application that is using android_native_app_glue.
  248. // It runs in its own thread, with its own event loop for receiving input events
  249. void android_main(android_app* appState)
  250. {
  251. const AZ::Debug::Trace tracer;
  252. // Adding a start up banner so you can see when the game is starting up in amongst the logcat spam
  253. LOGI("****************************************************************");
  254. LOGI("* Launching Game... *");
  255. LOGI("****************************************************************");
  256. // setup the system command handler which are guaranteed to be called on the same
  257. // thread the events are pumped
  258. appState->onAppCmd = HandleApplicationLifecycleEvents;
  259. appState->onInputEvent = HandleInputEvents;
  260. g_eventDispatcher.SetAppState(appState);
  261. // This callback will notify us when the orientation of the device changes.
  262. // While Android does have an onNativeWindowResized callback, it is never called in android_native_app_glue when the window size changes.
  263. // 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.
  264. appState->activity->callbacks->onNativeWindowRedrawNeeded = OnWindowRedrawNeeded;
  265. // setup the android environment
  266. {
  267. AZ::Android::AndroidEnv::Descriptor descriptor;
  268. descriptor.m_jvm = appState->activity->vm;
  269. descriptor.m_activityRef = appState->activity->clazz;
  270. descriptor.m_assetManager = appState->activity->assetManager;
  271. descriptor.m_configuration = appState->config;
  272. descriptor.m_appPrivateStoragePath = appState->activity->internalDataPath;
  273. descriptor.m_appPublicStoragePath = appState->activity->externalDataPath;
  274. descriptor.m_obbStoragePath = appState->activity->obbPath;
  275. if (!AZ::Android::AndroidEnv::Create(descriptor))
  276. {
  277. AZ::Android::AndroidEnv::Destroy();
  278. MAIN_EXIT_FAILURE(appState, "Failed to create the AndroidEnv");
  279. }
  280. AZ::Android::AndroidEnv* androidEnv = AZ::Android::AndroidEnv::Get();
  281. appState->userData = androidEnv;
  282. androidEnv->SetIsRunning(true);
  283. }
  284. // sync the window creation
  285. while (!g_windowInitialized)
  286. {
  287. g_eventDispatcher.PumpAllEvents();
  288. }
  289. // Now that the window has been created we can show the java splash screen. We need
  290. // to do it here and not in the window init event because every time the app is
  291. // backgrounded/foregrounded the window is destroyed/created, respectively. So, we
  292. // don't want to show the splash screen when we resumed from a paused state.
  293. AZ::Android::Utils::ShowSplashScreen();
  294. // run the Lumberyard application
  295. using namespace O3DELauncher;
  296. PlatformMainInfo mainInfo;
  297. mainInfo.m_updateResourceLimits = IncreaseResourceLimits;
  298. mainInfo.m_onPostAppStart = OnPostAppStart;
  299. mainInfo.m_appResourcesPath = AZ::Android::Utils::FindAssetsDirectory();
  300. mainInfo.m_additionalVfsResolution = "\t- Make sure \'adb reverse\' is setup for the device when connecting to localhost";
  301. // Always add the app as the first arg to mimic the way other platforms start with the executable name.
  302. const char* packageName = AZ::Android::Utils::GetPackageName();
  303. if (packageName)
  304. {
  305. mainInfo.AddArgument(packageName);
  306. }
  307. // Get the string extras and pass them along as cmd line params
  308. AZ::Android::JNI::Internal::Object<AZ::OSAllocator> activityObject(AZ::Android::JNI::GetEnv()->GetObjectClass(appState->activity->clazz), appState->activity->clazz);
  309. activityObject.RegisterMethod("getIntent", "()Landroid/content/Intent;");
  310. jobject intent = activityObject.InvokeObjectMethod<jobject>("getIntent");
  311. AZ::Android::JNI::Internal::Object<AZ::OSAllocator> intentObject(AZ::Android::JNI::GetEnv()->GetObjectClass(intent), intent);
  312. intentObject.RegisterMethod("getStringExtra", "(Ljava/lang/String;)Ljava/lang/String;");
  313. intentObject.RegisterMethod("getExtras", "()Landroid/os/Bundle;");
  314. jobject extras = intentObject.InvokeObjectMethod<jobject>("getExtras");
  315. if (extras)
  316. {
  317. // Get the set of keys
  318. AZ::Android::JNI::Internal::Object<AZ::OSAllocator> extrasObject(AZ::Android::JNI::GetEnv()->GetObjectClass(extras), extras);
  319. extrasObject.RegisterMethod("keySet", "()Ljava/util/Set;");
  320. jobject extrasKeySet = extrasObject.InvokeObjectMethod<jobject>("keySet");
  321. // get the array of string objects
  322. AZ::Android::JNI::Internal::Object<AZ::OSAllocator> extrasKeySetObject(AZ::Android::JNI::GetEnv()->GetObjectClass(extrasKeySet), extrasKeySet);
  323. extrasKeySetObject.RegisterMethod("toArray", "()[Ljava/lang/Object;");
  324. jobjectArray extrasKeySetArray = extrasKeySetObject.InvokeObjectMethod<jobjectArray>("toArray");
  325. int extrasKeySetArraySize = AZ::Android::JNI::GetEnv()->GetArrayLength(extrasKeySetArray);
  326. for (int x = 0; x < extrasKeySetArraySize; x++)
  327. {
  328. jstring keyObject = static_cast<jstring>(AZ::Android::JNI::GetEnv()->GetObjectArrayElement(extrasKeySetArray, x));
  329. AZ::OSString value = intentObject.InvokeStringMethod("getStringExtra", keyObject);
  330. const char* keyChars = AZ::Android::JNI::GetEnv()->GetStringUTFChars(keyObject, 0);
  331. char argName[AZ_COMMAND_LINE_LEN] = { 0 };
  332. azsprintf(argName, "-%s", keyChars);
  333. mainInfo.AddArgument(argName);
  334. mainInfo.AddArgument(value.c_str());
  335. AZ::Android::JNI::GetEnv()->ReleaseStringUTFChars(keyObject, keyChars);
  336. }
  337. }
  338. #if defined(_RELEASE)
  339. mainInfo.m_appWriteStoragePath = AZ::Android::Utils::GetAppPrivateStoragePath();
  340. #else
  341. mainInfo.m_appWriteStoragePath = AZ::Android::Utils::GetAppPublicStoragePath();
  342. #endif // defined(_RELEASE)
  343. #if defined(ENABLE_LOGGING)
  344. mainInfo.m_printSink = &g_androidPrintSink;
  345. #endif // defined(ENABLE_LOGGING)
  346. ReturnCode status = Run(mainInfo);
  347. AZ::Android::AndroidEnv::Destroy();
  348. if (status != ReturnCode::Success)
  349. {
  350. MAIN_EXIT_FAILURE(appState, "%s", GetReturnCodeString(status));
  351. }
  352. }
  353. void CVar_OnViewportPosition([[maybe_unused]] const AZ::Vector2& value) {}