diff --git a/applications/services/applications.h b/applications/services/applications.h index 85f736742..45e0be2e7 100644 --- a/applications/services/applications.h +++ b/applications/services/applications.h @@ -15,6 +15,7 @@ typedef struct { const size_t stack_size; const Icon* icon; const FlipperApplicationFlag flags; + void* preload; } FlipperApplication; typedef void (*FlipperOnStartHook)(void); @@ -24,32 +25,32 @@ extern const char* FLIPPER_AUTORUN_APP_NAME; /* Services list * Spawned on startup */ -extern const FlipperApplication FLIPPER_SERVICES[]; +extern FlipperApplication FLIPPER_SERVICES[]; extern const size_t FLIPPER_SERVICES_COUNT; /* Apps list * Spawned by loader */ -extern const FlipperApplication FLIPPER_APPS[]; +extern FlipperApplication FLIPPER_APPS[]; extern const size_t FLIPPER_APPS_COUNT; /* On system start hooks * Called by loader, after OS initialization complete */ -extern const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[]; +extern FlipperOnStartHook FLIPPER_ON_SYSTEM_START[]; extern const size_t FLIPPER_ON_SYSTEM_START_COUNT; /* System apps * Can only be spawned by loader by name */ -extern const FlipperApplication FLIPPER_SYSTEM_APPS[]; +extern FlipperApplication FLIPPER_SYSTEM_APPS[]; extern const size_t FLIPPER_SYSTEM_APPS_COUNT; /* Separate scene app holder * Spawned by loader */ extern const FlipperApplication FLIPPER_SCENE; -extern const FlipperApplication FLIPPER_SCENE_APPS[]; +extern FlipperApplication FLIPPER_SCENE_APPS[]; extern const size_t FLIPPER_SCENE_APPS_COUNT; extern const FlipperApplication FLIPPER_ARCHIVE; @@ -57,5 +58,5 @@ extern const FlipperApplication FLIPPER_ARCHIVE; /* Settings list * Spawned by loader */ -extern const FlipperApplication FLIPPER_SETTINGS_APPS[]; +extern FlipperApplication FLIPPER_SETTINGS_APPS[]; extern const size_t FLIPPER_SETTINGS_APPS_COUNT; diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 62dc746c8..4ea4c6aab 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -1,6 +1,7 @@ #include "loader.h" #include "loader_i.h" #include "loader_menu.h" +#include "loader_preload.h" #include #include #include @@ -147,29 +148,38 @@ static Loader* loader_alloc() { loader->app.insomniac = false; ExtMainAppList_init(loader->ext_main_apps); - Storage* storage = furi_record_open(RECORD_STORAGE); - FuriString* path = furi_string_alloc(); - FuriString* name = furi_string_alloc(); - Stream* stream = file_stream_alloc(storage); - if(file_stream_open(stream, XTREME_APPS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { - while(stream_read_line(stream, path)) { - furi_string_replace_all(path, "\r", ""); - furi_string_replace_all(path, "\n", ""); - const Icon* icon; - if(!loader_menu_load_fap_meta(storage, path, name, &icon)) continue; - ExtMainAppList_push_back( - loader->ext_main_apps, - (ExtMainApp){ - .name = strdup(furi_string_get_cstr(name)), - .path = strdup(furi_string_get_cstr(path)), - .icon = icon}); + if(furi_hal_is_normal_boot()) { + Storage* storage = furi_record_open(RECORD_STORAGE); + for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { + if(FLIPPER_APPS[i].app != NULL) continue; + if(storage_common_exists(storage, FLIPPER_APPS[i].appid)) { + void* preload = loader_preload(storage, FLIPPER_APPS[i].appid); + FLIPPER_APPS[i].preload = preload; + } } + FuriString* path = furi_string_alloc(); + FuriString* name = furi_string_alloc(); + Stream* stream = file_stream_alloc(storage); + if(file_stream_open(stream, XTREME_APPS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + while(stream_read_line(stream, path)) { + furi_string_replace_all(path, "\r", ""); + furi_string_replace_all(path, "\n", ""); + const Icon* icon; + if(!loader_menu_load_fap_meta(storage, path, name, &icon)) continue; + ExtMainAppList_push_back( + loader->ext_main_apps, + (ExtMainApp){ + .name = strdup(furi_string_get_cstr(name)), + .path = strdup(furi_string_get_cstr(path)), + .icon = icon}); + } + } + file_stream_close(stream); + stream_free(stream); + furi_string_free(name); + furi_string_free(path); + furi_record_close(RECORD_STORAGE); } - file_stream_close(stream); - stream_free(stream); - furi_string_free(name); - furi_string_free(path); - furi_record_close(RECORD_STORAGE); return loader; } @@ -214,6 +224,10 @@ static void FURI_LOG_I(TAG, "Starting %s", app->name); if(app->app == NULL) { + if(app->preload != NULL) { + loader_preload_start(app->preload, app->appid); + return; + } args = app->appid; app = loader_find_application_by_name_in_list( FAP_LOADER_APP_NAME, FLIPPER_APPS, FLIPPER_APPS_COUNT); diff --git a/applications/services/loader/loader_preload.c b/applications/services/loader/loader_preload.c new file mode 100644 index 000000000..21e3770b7 --- /dev/null +++ b/applications/services/loader/loader_preload.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include + +#define TAG "Preload" + +void* loader_preload(Storage* storage, const char* path) { + FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface); + size_t start = furi_get_tick(); + + FURI_LOG_I(TAG, "Loading %s", path); + + FlipperApplicationPreloadStatus preload_res = flipper_application_preload(app, path); + if(preload_res != FlipperApplicationPreloadStatusSuccess) { + return NULL; + } + + FURI_LOG_I(TAG, "Mapping"); + FlipperApplicationLoadStatus load_status = flipper_application_map_to_memory(app); + if(load_status != FlipperApplicationLoadStatusSuccess) { + const char* err_msg = flipper_application_load_status_to_string(load_status); + FURI_LOG_E(TAG, "Failed to map to memory %s: %s", path, err_msg); + return NULL; + } + + FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start)); + return app; +} + +void loader_preload_start(void* _app, const char* path) { + FlipperApplication* app = _app; + FURI_LOG_I(TAG, "Starting app"); + + FuriThread* thread = flipper_application_spawn(app, NULL); + + /* This flag is set by the debugger - to break on app start */ + if(furi_hal_debug_is_gdb_session_active()) { + FURI_LOG_W(TAG, "Triggering BP for debugger"); + /* After hitting this, you can set breakpoints in your .fap's code + * Note that you have to toggle breakpoints that were set before */ + __asm volatile("bkpt 0"); + } + + FuriString* app_name = furi_string_alloc(); + path_extract_filename_no_ext(path, app_name); + furi_thread_set_appid(thread, furi_string_get_cstr(app_name)); + furi_string_free(app_name); + + furi_thread_start(thread); + furi_thread_join(thread); + + flipper_application_despawn(app); +} diff --git a/applications/services/loader/loader_preload.h b/applications/services/loader/loader_preload.h new file mode 100644 index 000000000..1257c9890 --- /dev/null +++ b/applications/services/loader/loader_preload.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +void* loader_preload(Storage* storage, const char* path); +void loader_preload_start(void* app, const char* path); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index ad365f862..e1b9af856 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -945,6 +945,7 @@ Function,-,finitel,int,long double Function,-,fiprintf,int,"FILE*, const char*, ..." Function,-,fiscanf,int,"FILE*, const char*, ..." Function,+,flipper_application_alloc,FlipperApplication*,"Storage*, const ElfApiInterface*" +Function,+,flipper_application_despawn,void,FlipperApplication* Function,+,flipper_application_free,void,FlipperApplication* Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,FlipperApplication* Function,+,flipper_application_is_plugin,_Bool,FlipperApplication* diff --git a/lib/flipper_application/flipper_application.c b/lib/flipper_application/flipper_application.c index 1b4f56814..9272b9093 100644 --- a/lib/flipper_application/flipper_application.c +++ b/lib/flipper_application/flipper_application.c @@ -236,6 +236,15 @@ FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) { return app->thread; } +void flipper_application_despawn(FlipperApplication* app) { + furi_check(app->thread != NULL); + furi_check(!flipper_application_is_plugin(app)); + + furi_thread_join(app->thread); + furi_thread_free(app->thread); + app->thread = NULL; +} + static const char* preload_status_strings[] = { [FlipperApplicationPreloadStatusSuccess] = "Success", [FlipperApplicationPreloadStatusUnspecifiedError] = "Unknown error", @@ -289,4 +298,4 @@ const FlipperAppPluginDescriptor* lib_descriptor->ep_api_version); return lib_descriptor; -} \ No newline at end of file +} diff --git a/lib/flipper_application/flipper_application.h b/lib/flipper_application/flipper_application.h index 519cc3971..42665080b 100644 --- a/lib/flipper_application/flipper_application.h +++ b/lib/flipper_application/flipper_application.h @@ -107,14 +107,19 @@ FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplicatio /** * @brief Create application thread at entry point address, using app name and - * stack size from metadata. Returned thread isn't started yet. - * Can be only called once for application instance. + * stack size from metadata. Returned thread isn't started yet. * @param app Applicaiton pointer * @param args Object to pass to app's entry point * @return Created thread */ FuriThread* flipper_application_spawn(FlipperApplication* app, void* args); +/** + * @brief Cleanup application in order to re-spawn later. + * @param app Applicaiton pointer + */ +void flipper_application_despawn(FlipperApplication* app); + /** * @brief Check if application is a plugin (not a runnable standalone app) * @param app Application pointer diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index 395482005..c938026bc 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -395,7 +395,7 @@ class ApplicationsCGenerator: map(self.get_app_ep_forward, self.buildset.get_apps_of_type(apptype)) ) entry_type, entry_block = self.APP_TYPE_MAP[apptype] - contents.append(f"const {entry_type} {entry_block}[] = {{") + contents.append(f"{entry_type} {entry_block}[] = {{") apps = self.buildset.get_apps_of_type(apptype) if apptype is FlipperAppType.APP: apps += self.buildset.get_apps_of_type(FlipperAppType.EXTMAINAPP)