loader.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. #include "loader.h"
  2. #include "loader_i.h"
  3. #include "loader_menu.h"
  4. #include <applications.h>
  5. #include <furi_hal.h>
  6. #define TAG "Loader"
  7. #define LOADER_MAGIC_THREAD_VALUE 0xDEADBEEF
  8. // api
  9. LoaderStatus loader_start(Loader* loader, const char* name, const char* args) {
  10. LoaderMessage message;
  11. LoaderMessageLoaderStatusResult result;
  12. message.type = LoaderMessageTypeStartByName;
  13. message.start.name = name;
  14. message.start.args = args;
  15. message.api_lock = api_lock_alloc_locked();
  16. message.status_value = &result;
  17. furi_message_queue_put(loader->queue, &message, FuriWaitForever);
  18. api_lock_wait_unlock_and_free(message.api_lock);
  19. return result.value;
  20. }
  21. bool loader_lock(Loader* loader) {
  22. LoaderMessage message;
  23. LoaderMessageBoolResult result;
  24. message.type = LoaderMessageTypeLock;
  25. message.api_lock = api_lock_alloc_locked();
  26. message.bool_value = &result;
  27. furi_message_queue_put(loader->queue, &message, FuriWaitForever);
  28. api_lock_wait_unlock_and_free(message.api_lock);
  29. return result.value;
  30. }
  31. void loader_unlock(Loader* loader) {
  32. LoaderMessage message;
  33. message.type = LoaderMessageTypeUnlock;
  34. furi_message_queue_put(loader->queue, &message, FuriWaitForever);
  35. }
  36. bool loader_is_locked(Loader* loader) {
  37. LoaderMessage message;
  38. LoaderMessageBoolResult result;
  39. message.type = LoaderMessageTypeIsLocked;
  40. message.api_lock = api_lock_alloc_locked();
  41. message.bool_value = &result;
  42. furi_message_queue_put(loader->queue, &message, FuriWaitForever);
  43. api_lock_wait_unlock_and_free(message.api_lock);
  44. return result.value;
  45. }
  46. void loader_show_menu(Loader* loader) {
  47. LoaderMessage message;
  48. message.type = LoaderMessageTypeShowMenu;
  49. furi_message_queue_put(loader->queue, &message, FuriWaitForever);
  50. }
  51. FuriPubSub* loader_get_pubsub(Loader* loader) {
  52. furi_assert(loader);
  53. // it's safe to return pubsub without locking
  54. // because it's never freed and loader is never exited
  55. // also the loader instance cannot be obtained until the pubsub is created
  56. return loader->pubsub;
  57. }
  58. // callbacks
  59. static void loader_menu_closed_callback(void* context) {
  60. Loader* loader = context;
  61. LoaderMessage message;
  62. message.type = LoaderMessageTypeMenuClosed;
  63. furi_message_queue_put(loader->queue, &message, FuriWaitForever);
  64. }
  65. static void loader_menu_click_callback(const char* name, void* context) {
  66. Loader* loader = context;
  67. loader_start(loader, name, NULL);
  68. }
  69. static void loader_thread_state_callback(FuriThreadState thread_state, void* context) {
  70. furi_assert(context);
  71. Loader* loader = context;
  72. LoaderEvent event;
  73. if(thread_state == FuriThreadStateRunning) {
  74. event.type = LoaderEventTypeApplicationStarted;
  75. furi_pubsub_publish(loader->pubsub, &event);
  76. } else if(thread_state == FuriThreadStateStopped) {
  77. LoaderMessage message;
  78. message.type = LoaderMessageTypeAppClosed;
  79. furi_message_queue_put(loader->queue, &message, FuriWaitForever);
  80. event.type = LoaderEventTypeApplicationStopped;
  81. furi_pubsub_publish(loader->pubsub, &event);
  82. }
  83. }
  84. // implementation
  85. static Loader* loader_alloc() {
  86. Loader* loader = malloc(sizeof(Loader));
  87. loader->pubsub = furi_pubsub_alloc();
  88. loader->queue = furi_message_queue_alloc(1, sizeof(LoaderMessage));
  89. loader->loader_menu = NULL;
  90. loader->app.args = NULL;
  91. loader->app.name = NULL;
  92. loader->app.thread = NULL;
  93. loader->app.insomniac = false;
  94. return loader;
  95. }
  96. static FlipperApplication const* loader_find_application_by_name_in_list(
  97. const char* name,
  98. const FlipperApplication* list,
  99. const uint32_t n_apps) {
  100. for(size_t i = 0; i < n_apps; i++) {
  101. if(strcmp(name, list[i].name) == 0) {
  102. return &list[i];
  103. }
  104. }
  105. return NULL;
  106. }
  107. static const FlipperApplication* loader_find_application_by_name(const char* name) {
  108. const FlipperApplication* application = NULL;
  109. application = loader_find_application_by_name_in_list(name, FLIPPER_APPS, FLIPPER_APPS_COUNT);
  110. if(!application) {
  111. application = loader_find_application_by_name_in_list(
  112. name, FLIPPER_SETTINGS_APPS, FLIPPER_SETTINGS_APPS_COUNT);
  113. }
  114. if(!application) {
  115. application = loader_find_application_by_name_in_list(
  116. name, FLIPPER_SYSTEM_APPS, FLIPPER_SYSTEM_APPS_COUNT);
  117. }
  118. return application;
  119. }
  120. static void
  121. loader_start_internal_app(Loader* loader, const FlipperApplication* app, const char* args) {
  122. FURI_LOG_I(TAG, "Starting %s", app->name);
  123. // store args
  124. furi_assert(loader->app.args == NULL);
  125. if(args && strlen(args) > 0) {
  126. loader->app.args = strdup(args);
  127. }
  128. // store name
  129. furi_assert(loader->app.name == NULL);
  130. loader->app.name = strdup(app->name);
  131. // setup app thread
  132. loader->app.thread =
  133. furi_thread_alloc_ex(app->name, app->stack_size, app->app, loader->app.args);
  134. furi_thread_set_appid(loader->app.thread, app->appid);
  135. // setup heap trace
  136. FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode();
  137. if(mode > FuriHalRtcHeapTrackModeNone) {
  138. furi_thread_enable_heap_trace(loader->app.thread);
  139. } else {
  140. furi_thread_disable_heap_trace(loader->app.thread);
  141. }
  142. // setup insomnia
  143. if(!(app->flags & FlipperApplicationFlagInsomniaSafe)) {
  144. furi_hal_power_insomnia_enter();
  145. loader->app.insomniac = true;
  146. } else {
  147. loader->app.insomniac = false;
  148. }
  149. // setup app thread callbacks
  150. furi_thread_set_state_context(loader->app.thread, loader);
  151. furi_thread_set_state_callback(loader->app.thread, loader_thread_state_callback);
  152. // start app thread
  153. furi_thread_start(loader->app.thread);
  154. }
  155. // process messages
  156. static void loader_do_menu_show(Loader* loader) {
  157. if(!loader->loader_menu) {
  158. loader->loader_menu = loader_menu_alloc();
  159. loader_menu_set_closed_callback(loader->loader_menu, loader_menu_closed_callback, loader);
  160. loader_menu_set_click_callback(loader->loader_menu, loader_menu_click_callback, loader);
  161. loader_menu_start(loader->loader_menu);
  162. }
  163. }
  164. static void loader_do_menu_closed(Loader* loader) {
  165. if(loader->loader_menu) {
  166. loader_menu_stop(loader->loader_menu);
  167. loader_menu_free(loader->loader_menu);
  168. loader->loader_menu = NULL;
  169. }
  170. }
  171. static bool loader_do_is_locked(Loader* loader) {
  172. return loader->app.thread != NULL;
  173. }
  174. static LoaderStatus loader_do_start_by_name(Loader* loader, const char* name, const char* args) {
  175. if(loader_do_is_locked(loader)) {
  176. return LoaderStatusErrorAppStarted;
  177. }
  178. const FlipperApplication* app = loader_find_application_by_name(name);
  179. if(!app) {
  180. return LoaderStatusErrorUnknownApp;
  181. }
  182. loader_start_internal_app(loader, app, args);
  183. return LoaderStatusOk;
  184. }
  185. static bool loader_do_lock(Loader* loader) {
  186. if(loader->app.thread) {
  187. return false;
  188. }
  189. loader->app.thread = (FuriThread*)LOADER_MAGIC_THREAD_VALUE;
  190. return true;
  191. }
  192. static void loader_do_unlock(Loader* loader) {
  193. furi_assert(loader->app.thread == (FuriThread*)LOADER_MAGIC_THREAD_VALUE);
  194. loader->app.thread = NULL;
  195. }
  196. static void loader_do_app_closed(Loader* loader) {
  197. furi_assert(loader->app.thread);
  198. FURI_LOG_I(TAG, "Application stopped. Free heap: %zu", memmgr_get_free_heap());
  199. if(loader->app.args) {
  200. free(loader->app.args);
  201. loader->app.args = NULL;
  202. }
  203. if(loader->app.insomniac) {
  204. furi_hal_power_insomnia_exit();
  205. }
  206. free(loader->app.name);
  207. loader->app.name = NULL;
  208. furi_thread_join(loader->app.thread);
  209. furi_thread_free(loader->app.thread);
  210. loader->app.thread = NULL;
  211. }
  212. // app
  213. int32_t loader_srv(void* p) {
  214. UNUSED(p);
  215. Loader* loader = loader_alloc();
  216. furi_record_create(RECORD_LOADER, loader);
  217. FURI_LOG_I(TAG, "Executing system start hooks");
  218. for(size_t i = 0; i < FLIPPER_ON_SYSTEM_START_COUNT; i++) {
  219. FLIPPER_ON_SYSTEM_START[i]();
  220. }
  221. if(FLIPPER_AUTORUN_APP_NAME && strlen(FLIPPER_AUTORUN_APP_NAME)) {
  222. loader_do_start_by_name(loader, FLIPPER_AUTORUN_APP_NAME, NULL);
  223. }
  224. LoaderMessage message;
  225. while(true) {
  226. if(furi_message_queue_get(loader->queue, &message, FuriWaitForever) == FuriStatusOk) {
  227. switch(message.type) {
  228. case LoaderMessageTypeStartByName:
  229. message.status_value->value =
  230. loader_do_start_by_name(loader, message.start.name, message.start.args);
  231. api_lock_unlock(message.api_lock);
  232. break;
  233. case LoaderMessageTypeShowMenu:
  234. loader_do_menu_show(loader);
  235. break;
  236. case LoaderMessageTypeMenuClosed:
  237. loader_do_menu_closed(loader);
  238. break;
  239. case LoaderMessageTypeIsLocked:
  240. message.bool_value->value = loader_do_is_locked(loader);
  241. api_lock_unlock(message.api_lock);
  242. break;
  243. case LoaderMessageTypeAppClosed:
  244. loader_do_app_closed(loader);
  245. break;
  246. case LoaderMessageTypeLock:
  247. message.bool_value->value = loader_do_lock(loader);
  248. api_lock_unlock(message.api_lock);
  249. break;
  250. case LoaderMessageTypeUnlock:
  251. loader_do_unlock(loader);
  252. }
  253. }
  254. }
  255. return 0;
  256. }