From 49dcf81743859c17ee332f882e838500206f8039 Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Fri, 10 Nov 2023 10:22:34 +0300 Subject: [PATCH] [FL-3618] Infrared remote button index support (#3180) * Do not load all signals at once (Draft) * Minor cleanup * Refactor remote renaming * Improve function signatures * Rename infrared_remote functions * Optimise signal loading * Implement adding signals to remote * Add read_name() method * Deprecate a function * Partially implement deleting signals (draft) * Use m-array instead of m-list for signal name directory * Use plain C strings instead of furi_string * Implement deleting signals * Implement deleting signals via generalised callback * Implement renaming signals * Rename some types * Some more renaming * Remove unused type * Implement inserting signals (internal use) * Improve InfraredMoveView * Send an event to move a signal * Remove unused type * Implement moving signals * Implement creating new remotes with one signal * Un-deprecate and rename a function * Add InfraredRemote API docs * Add InfraredSignal API docs * Better error messages * Show progress pop-up when moving buttons in a remote * Copy labels to the InfraredMoveView to avoid pointer invalidation * Improve file selection scene * Show progress pop-up when renaming buttons in a remote * Refactor a scene * Show progress when deleting a button from remote * Use a random name for temp files * Add docs to infrared_brute_force.h * Rename Infrared type to InfraredApp * Add docs to infrared_app_i.h * Deliver event data via a callback * Bundle event data together with event type * Change DataExchange behaviour * Adapt RPC debug app to new API * Remove rogue output * Add Doxygen comments to rpc_app.h * Simplify rpc_app.c code * Remove superflous parameter * Do not allocate protobuf messages on the stack * Fix GetError response * Support for button indices * Comment out shallow submodules * Fix F18 api * Fix logical error and add more debug output * fbt: testing unshallow for protobuf * github: lint&checks: unshallow prior to checks * Fix a TODO * github: do not unshallow the unshallowed * fbt: assets: only attempt to unshallow if cannot describe * Do not use the name when loading a signal by index (duh) * Simplify loading infrared signals by name * Sync with protobuf release * Infrared: use compact furi_crash macros Co-authored-by: hedger Co-authored-by: hedger Co-authored-by: Aleksandr Kutuzov --- .../workflows/lint_and_submodule_check.yml | 2 +- .../debug/rpc_debug_app/rpc_debug_app.c | 39 +- ...pc_debug_app_scene_receive_data_exchange.c | 39 +- applications/main/ibutton/ibutton.c | 14 +- .../main/ibutton/ibutton_custom_event.h | 2 +- .../main/ibutton/scenes/ibutton_scene_rpc.c | 23 +- applications/main/infrared/infrared_app.c | 34 +- applications/main/infrared/infrared_app_i.h | 1 + .../main/infrared/infrared_brute_force.c | 2 +- .../main/infrared/infrared_custom_event.h | 5 +- applications/main/infrared/infrared_remote.c | 7 +- applications/main/infrared/infrared_signal.c | 36 +- applications/main/infrared/infrared_signal.h | 21 +- .../infrared/scenes/infrared_scene_remote.c | 6 +- .../main/infrared/scenes/infrared_scene_rpc.c | 45 +- applications/main/lfrfid/lfrfid.c | 12 +- .../main/lfrfid/scenes/lfrfid_scene_rpc.c | 8 +- .../main/nfc/helpers/nfc_custom_event.h | 2 +- .../protocol_support/nfc_protocol_support.c | 16 +- applications/main/nfc/nfc_app.c | 14 +- .../main/subghz/scenes/subghz_scene_rpc.c | 16 +- applications/main/subghz/subghz.c | 16 +- applications/services/rpc/rpc.c | 3 + applications/services/rpc/rpc_app.c | 465 +++++++++--------- applications/services/rpc/rpc_app.h | 202 +++++++- assets/protobuf | 2 +- scripts/fbt_tools/fbt_assets.py | 45 +- targets/f18/api_symbols.csv | 4 +- targets/f7/api_symbols.csv | 4 +- 29 files changed, 670 insertions(+), 415 deletions(-) diff --git a/.github/workflows/lint_and_submodule_check.yml b/.github/workflows/lint_and_submodule_check.yml index 51e2593eccb..3f4d8c79bd0 100644 --- a/.github/workflows/lint_and_submodule_check.yml +++ b/.github/workflows/lint_and_submodule_check.yml @@ -23,7 +23,7 @@ jobs: - name: 'Check protobuf branch' run: | - git submodule update --init + git submodule update --init; SUB_PATH="assets/protobuf"; SUB_BRANCH="dev"; SUB_COMMITS_MIN=40; diff --git a/applications/debug/rpc_debug_app/rpc_debug_app.c b/applications/debug/rpc_debug_app/rpc_debug_app.c index 314be5e7225..2a0756383c2 100644 --- a/applications/debug/rpc_debug_app/rpc_debug_app.c +++ b/applications/debug/rpc_debug_app/rpc_debug_app.c @@ -21,22 +21,51 @@ static void rpc_debug_app_tick_event_callback(void* context) { scene_manager_handle_tick_event(app->scene_manager); } -static void rpc_debug_app_rpc_command_callback(RpcAppSystemEvent event, void* context) { +static void + rpc_debug_app_format_hex(const uint8_t* data, size_t data_size, char* buf, size_t buf_size) { + if(data == NULL || data_size == 0) { + strncpy(buf, "", buf_size); + return; + } + + const size_t byte_width = 3; + const size_t line_width = 7; + + data_size = MIN(data_size, buf_size / (byte_width + 1)); + + for(size_t i = 0; i < data_size; ++i) { + char* p = buf + (i * byte_width); + char sep = !((i + 1) % line_width) ? '\n' : ' '; + snprintf(p, byte_width + 1, "%02X%c", data[i], sep); + } + + buf[buf_size - 1] = '\0'; +} + +static void rpc_debug_app_rpc_command_callback(const RpcAppSystemEvent* event, void* context) { furi_assert(context); RpcDebugApp* app = context; furi_assert(app->rpc); - if(event == RpcAppEventSessionClose) { + if(event->type == RpcAppEventTypeSessionClose) { scene_manager_stop(app->scene_manager); view_dispatcher_stop(app->view_dispatcher); rpc_system_app_set_callback(app->rpc, NULL, NULL); app->rpc = NULL; - } else if(event == RpcAppEventAppExit) { + } else if(event->type == RpcAppEventTypeAppExit) { scene_manager_stop(app->scene_manager); view_dispatcher_stop(app->view_dispatcher); - rpc_system_app_confirm(app->rpc, RpcAppEventAppExit, true); + rpc_system_app_confirm(app->rpc, true); + } else if(event->type == RpcAppEventTypeDataExchange) { + furi_assert(event->data.type == RpcAppSystemEventDataTypeBytes); + + rpc_debug_app_format_hex( + event->data.bytes.ptr, event->data.bytes.size, app->text_store, TEXT_STORE_SIZE); + + view_dispatcher_send_custom_event( + app->view_dispatcher, RpcDebugAppCustomEventRpcDataExchange); } else { - rpc_system_app_confirm(app->rpc, event, false); + rpc_system_app_confirm(app->rpc, false); } } diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_receive_data_exchange.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_receive_data_exchange.c index 98bafff8a7b..10d5e36f624 100644 --- a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_receive_data_exchange.c +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_receive_data_exchange.c @@ -1,40 +1,5 @@ #include "../rpc_debug_app.h" -static void rpc_debug_app_scene_start_format_hex( - const uint8_t* data, - size_t data_size, - char* buf, - size_t buf_size) { - furi_assert(data); - furi_assert(buf); - - const size_t byte_width = 3; - const size_t line_width = 7; - - data_size = MIN(data_size, buf_size / (byte_width + 1)); - - for(size_t i = 0; i < data_size; ++i) { - char* p = buf + (i * byte_width); - char sep = !((i + 1) % line_width) ? '\n' : ' '; - snprintf(p, byte_width + 1, "%02X%c", data[i], sep); - } - - buf[buf_size - 1] = '\0'; -} - -static void rpc_debug_app_scene_receive_data_exchange_callback( - const uint8_t* data, - size_t data_size, - void* context) { - RpcDebugApp* app = context; - if(data) { - rpc_debug_app_scene_start_format_hex(data, data_size, app->text_store, TEXT_STORE_SIZE); - } else { - strncpy(app->text_store, "", TEXT_STORE_SIZE); - } - view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventRpcDataExchange); -} - void rpc_debug_app_scene_receive_data_exchange_on_enter(void* context) { RpcDebugApp* app = context; strncpy(app->text_store, "Received data will appear here...", TEXT_STORE_SIZE); @@ -42,8 +7,6 @@ void rpc_debug_app_scene_receive_data_exchange_on_enter(void* context) { text_box_set_text(app->text_box, app->text_store); text_box_set_font(app->text_box, TextBoxFontHex); - rpc_system_app_set_data_exchange_callback( - app->rpc, rpc_debug_app_scene_receive_data_exchange_callback, app); view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextBox); } @@ -53,6 +16,7 @@ bool rpc_debug_app_scene_receive_data_exchange_on_event(void* context, SceneMana if(event.type == SceneManagerEventTypeCustom) { if(event.event == RpcDebugAppCustomEventRpcDataExchange) { + rpc_system_app_confirm(app->rpc, true); notification_message(app->notifications, &sequence_blink_cyan_100); notification_message(app->notifications, &sequence_display_backlight_on); text_box_set_text(app->text_box, app->text_store); @@ -66,5 +30,4 @@ bool rpc_debug_app_scene_receive_data_exchange_on_event(void* context, SceneMana void rpc_debug_app_scene_receive_data_exchange_on_exit(void* context) { RpcDebugApp* app = context; text_box_reset(app->text_box); - rpc_system_app_set_data_exchange_callback(app->rpc, NULL, NULL); } diff --git a/applications/main/ibutton/ibutton.c b/applications/main/ibutton/ibutton.c index 760918097f1..fb6d9dcb52a 100644 --- a/applications/main/ibutton/ibutton.c +++ b/applications/main/ibutton/ibutton.c @@ -39,21 +39,23 @@ static void ibutton_make_app_folder(iButton* ibutton) { furi_record_close(RECORD_STORAGE); } -static void ibutton_rpc_command_callback(RpcAppSystemEvent event, void* context) { +static void ibutton_rpc_command_callback(const RpcAppSystemEvent* event, void* context) { furi_assert(context); iButton* ibutton = context; - if(event == RpcAppEventSessionClose) { + if(event->type == RpcAppEventTypeSessionClose) { view_dispatcher_send_custom_event( ibutton->view_dispatcher, iButtonCustomEventRpcSessionClose); rpc_system_app_set_callback(ibutton->rpc, NULL, NULL); ibutton->rpc = NULL; - } else if(event == RpcAppEventAppExit) { + } else if(event->type == RpcAppEventTypeAppExit) { view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit); - } else if(event == RpcAppEventLoadFile) { - view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcLoad); + } else if(event->type == RpcAppEventTypeLoadFile) { + furi_assert(event->data.type == RpcAppSystemEventDataTypeString); + furi_string_set(ibutton->file_path, event->data.string); + view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcLoadFile); } else { - rpc_system_app_confirm(ibutton->rpc, event, false); + rpc_system_app_confirm(ibutton->rpc, false); } } diff --git a/applications/main/ibutton/ibutton_custom_event.h b/applications/main/ibutton/ibutton_custom_event.h index 1ac50ee3cf6..b0246d31094 100644 --- a/applications/main/ibutton/ibutton_custom_event.h +++ b/applications/main/ibutton/ibutton_custom_event.h @@ -15,7 +15,7 @@ typedef enum { iButtonCustomEventWorkerWriteNoDetect, iButtonCustomEventWorkerWriteCannotWrite, - iButtonCustomEventRpcLoad, + iButtonCustomEventRpcLoadFile, iButtonCustomEventRpcExit, iButtonCustomEventRpcSessionClose, } iButtonCustomEvent; diff --git a/applications/main/ibutton/scenes/ibutton_scene_rpc.c b/applications/main/ibutton/scenes/ibutton_scene_rpc.c index 9205eb4b46f..7106fefaa25 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_rpc.c +++ b/applications/main/ibutton/scenes/ibutton_scene_rpc.c @@ -23,28 +23,23 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { consumed = true; - if(event.event == iButtonCustomEventRpcLoad) { + if(event.event == iButtonCustomEventRpcLoadFile) { bool result = false; - const char* file_path = rpc_system_app_get_data(ibutton->rpc); - if(file_path && (furi_string_empty(ibutton->file_path))) { - furi_string_set(ibutton->file_path, file_path); + if(ibutton_load_key(ibutton)) { + popup_set_text(popup, ibutton->key_name, 82, 32, AlignCenter, AlignTop); + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup); - if(ibutton_load_key(ibutton)) { - popup_set_text(popup, ibutton->key_name, 82, 32, AlignCenter, AlignTop); - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup); + ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart); + ibutton_worker_emulate_start(ibutton->worker, ibutton->key); - ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart); - ibutton_worker_emulate_start(ibutton->worker, ibutton->key); - - result = true; - } + result = true; } - rpc_system_app_confirm(ibutton->rpc, RpcAppEventLoadFile, result); + rpc_system_app_confirm(ibutton->rpc, result); } else if(event.event == iButtonCustomEventRpcExit) { - rpc_system_app_confirm(ibutton->rpc, RpcAppEventAppExit, true); + rpc_system_app_confirm(ibutton->rpc, true); scene_manager_stop(ibutton->scene_manager); view_dispatcher_stop(ibutton->view_dispatcher); diff --git a/applications/main/infrared/infrared_app.c b/applications/main/infrared/infrared_app.c index 7abb4e4eb61..645659bbc5d 100644 --- a/applications/main/infrared/infrared_app.c +++ b/applications/main/infrared/infrared_app.c @@ -44,30 +44,42 @@ static void infrared_tick_event_callback(void* context) { scene_manager_handle_tick_event(infrared->scene_manager); } -static void infrared_rpc_command_callback(RpcAppSystemEvent event, void* context) { +static void infrared_rpc_command_callback(const RpcAppSystemEvent* event, void* context) { furi_assert(context); InfraredApp* infrared = context; furi_assert(infrared->rpc_ctx); - if(event == RpcAppEventSessionClose) { + if(event->type == RpcAppEventTypeSessionClose) { view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeRpcSessionClose); rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL); infrared->rpc_ctx = NULL; - } else if(event == RpcAppEventAppExit) { + } else if(event->type == RpcAppEventTypeAppExit) { view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeRpcExit); - } else if(event == RpcAppEventLoadFile) { + } else if(event->type == RpcAppEventTypeLoadFile) { + furi_assert(event->data.type == RpcAppSystemEventDataTypeString); + furi_string_set(infrared->file_path, event->data.string); view_dispatcher_send_custom_event( - infrared->view_dispatcher, InfraredCustomEventTypeRpcLoad); - } else if(event == RpcAppEventButtonPress) { - view_dispatcher_send_custom_event( - infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPress); - } else if(event == RpcAppEventButtonRelease) { + infrared->view_dispatcher, InfraredCustomEventTypeRpcLoadFile); + } else if(event->type == RpcAppEventTypeButtonPress) { + furi_assert( + event->data.type == RpcAppSystemEventDataTypeString || + event->data.type == RpcAppSystemEventDataTypeInt32); + if(event->data.type == RpcAppSystemEventDataTypeString) { + furi_string_set(infrared->button_name, event->data.string); + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPressName); + } else { + infrared->app_state.current_button_index = event->data.i32; + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPressIndex); + } + } else if(event->type == RpcAppEventTypeButtonRelease) { view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonRelease); } else { - rpc_system_app_confirm(infrared->rpc_ctx, event, false); + rpc_system_app_confirm(infrared->rpc_ctx, false); } } @@ -117,6 +129,7 @@ static InfraredApp* infrared_alloc() { InfraredApp* infrared = malloc(sizeof(InfraredApp)); infrared->file_path = furi_string_alloc(); + infrared->button_name = furi_string_alloc(); InfraredAppState* app_state = &infrared->app_state; app_state->is_learning_new_remote = false; @@ -247,6 +260,7 @@ static void infrared_free(InfraredApp* infrared) { infrared->gui = NULL; furi_string_free(infrared->file_path); + furi_string_free(infrared->button_name); free(infrared); } diff --git a/applications/main/infrared/infrared_app_i.h b/applications/main/infrared/infrared_app_i.h index c9dfe3ab865..c35d3fa410a 100644 --- a/applications/main/infrared/infrared_app_i.h +++ b/applications/main/infrared/infrared_app_i.h @@ -121,6 +121,7 @@ struct InfraredApp { InfraredProgressView* progress; /**< Custom view for showing brute force progress. */ FuriString* file_path; /**< Full path to the currently loaded file. */ + FuriString* button_name; /** Name of the button requested in RPC mode. */ /** Arbitrary text storage for various inputs. */ char text_store[INFRARED_TEXT_STORE_NUM][INFRARED_TEXT_STORE_SIZE + 1]; InfraredAppState app_state; /**< Application state. */ diff --git a/applications/main/infrared/infrared_brute_force.c b/applications/main/infrared/infrared_brute_force.c index bb7992ae61c..373c7270f58 100644 --- a/applications/main/infrared/infrared_brute_force.c +++ b/applications/main/infrared/infrared_brute_force.c @@ -128,7 +128,7 @@ void infrared_brute_force_stop(InfraredBruteForce* brute_force) { bool infrared_brute_force_send_next(InfraredBruteForce* brute_force) { furi_assert(brute_force->is_started); - const bool success = infrared_signal_search_and_read( + const bool success = infrared_signal_search_by_name_and_read( brute_force->current_signal, brute_force->ff, furi_string_get_cstr(brute_force->current_record_name)); diff --git a/applications/main/infrared/infrared_custom_event.h b/applications/main/infrared/infrared_custom_event.h index 09440ddec18..30bb0f729cd 100644 --- a/applications/main/infrared/infrared_custom_event.h +++ b/applications/main/infrared/infrared_custom_event.h @@ -15,9 +15,10 @@ enum InfraredCustomEventType { InfraredCustomEventTypeButtonSelected, InfraredCustomEventTypeBackPressed, - InfraredCustomEventTypeRpcLoad, + InfraredCustomEventTypeRpcLoadFile, InfraredCustomEventTypeRpcExit, - InfraredCustomEventTypeRpcButtonPress, + InfraredCustomEventTypeRpcButtonPressName, + InfraredCustomEventTypeRpcButtonPressIndex, InfraredCustomEventTypeRpcButtonRelease, InfraredCustomEventTypeRpcSessionClose, }; diff --git a/applications/main/infrared/infrared_remote.c b/applications/main/infrared/infrared_remote.c index 5b241fe9f60..cab241c1258 100644 --- a/applications/main/infrared/infrared_remote.c +++ b/applications/main/infrared/infrared_remote.c @@ -95,10 +95,9 @@ bool infrared_remote_load_signal( const char* path = furi_string_get_cstr(remote->path); if(!flipper_format_buffered_file_open_existing(ff, path)) break; - const char* name = infrared_remote_get_signal_name(remote, index); - - if(!infrared_signal_search_and_read(signal, ff, name)) { - FURI_LOG_E(TAG, "Failed to load signal '%s' from file '%s'", name, path); + if(!infrared_signal_search_by_index_and_read(signal, ff, index)) { + const char* signal_name = infrared_remote_get_signal_name(remote, index); + FURI_LOG_E(TAG, "Failed to load signal '%s' from file '%s'", signal_name, path); break; } diff --git a/applications/main/infrared/infrared_signal.c b/applications/main/infrared/infrared_signal.c index c73e4db98da..2b0d3479111 100644 --- a/applications/main/infrared/infrared_signal.c +++ b/applications/main/infrared/infrared_signal.c @@ -266,19 +266,37 @@ bool infrared_signal_read_name(FlipperFormat* ff, FuriString* name) { return flipper_format_read_string(ff, INFRARED_SIGNAL_NAME_KEY, name); } -bool infrared_signal_search_and_read(InfraredSignal* signal, FlipperFormat* ff, const char* name) { +bool infrared_signal_search_by_name_and_read( + InfraredSignal* signal, + FlipperFormat* ff, + const char* name) { bool success = false; FuriString* tmp = furi_string_alloc(); - do { - bool is_name_found = false; - while(!is_name_found && infrared_signal_read_name(ff, tmp)) { //-V560 - is_name_found = furi_string_equal(tmp, name); + while(infrared_signal_read_name(ff, tmp)) { + if(furi_string_equal(tmp, name)) { + success = infrared_signal_read_body(signal, ff); + break; } - if(!is_name_found) break; //-V547 - if(!infrared_signal_read_body(signal, ff)) break; //-V779 - success = true; - } while(false); + } + + furi_string_free(tmp); + return success; +} + +bool infrared_signal_search_by_index_and_read( + InfraredSignal* signal, + FlipperFormat* ff, + size_t index) { + bool success = false; + FuriString* tmp = furi_string_alloc(); + + for(uint32_t i = 0; infrared_signal_read_name(ff, tmp); ++i) { + if(i == index) { + success = infrared_signal_read_body(signal, ff); + break; + } + } furi_string_free(tmp); return success; diff --git a/applications/main/infrared/infrared_signal.h b/applications/main/infrared/infrared_signal.h index fcbb3c87396..cfa4cfa94e2 100644 --- a/applications/main/infrared/infrared_signal.h +++ b/applications/main/infrared/infrared_signal.h @@ -162,7 +162,26 @@ bool infrared_signal_read_name(FlipperFormat* ff, FuriString* name); * @param[in] name pointer to a zero-terminated string containing the requested signal name. * @returns true if a signal was found and successfully read, false otherwise (e.g. the signal was not found). */ -bool infrared_signal_search_and_read(InfraredSignal* signal, FlipperFormat* ff, const char* name); +bool infrared_signal_search_by_name_and_read( + InfraredSignal* signal, + FlipperFormat* ff, + const char* name); + +/** + * @brief Read a signal with a particular index from a FlipperFormat file into an InfraredSignal instance. + * + * This function will look for a signal with the given index and if found, attempt to read it. + * Same considerations apply as to infrared_signal_read(). + * + * @param[in,out] signal pointer to the instance to be read into. + * @param[in,out] ff pointer to the FlipperFormat file instance to read from. + * @param[in] index the requested signal index. + * @returns true if a signal was found and successfully read, false otherwise (e.g. the signal was not found). + */ +bool infrared_signal_search_by_index_and_read( + InfraredSignal* signal, + FlipperFormat* ff, + size_t index); /** * @brief Save a signal contained in an InfraredSignal instance to a FlipperFormat file. diff --git a/applications/main/infrared/scenes/infrared_scene_remote.c b/applications/main/infrared/scenes/infrared_scene_remote.c index 5974acbfecb..6c1d1ec4e39 100644 --- a/applications/main/infrared/scenes/infrared_scene_remote.c +++ b/applications/main/infrared/scenes/infrared_scene_remote.c @@ -1,7 +1,7 @@ #include "../infrared_app_i.h" typedef enum { - ButtonIndexPlus = -2, + ButtonIndexLearn = -2, ButtonIndexEdit = -1, ButtonIndexNA = 0, } ButtonIndex; @@ -44,7 +44,7 @@ void infrared_scene_remote_on_enter(void* context) { button_menu_add_item( button_menu, "+", - ButtonIndexPlus, + ButtonIndexLearn, infrared_scene_remote_button_menu_callback, ButtonMenuItemTypeControl, context); @@ -95,7 +95,7 @@ bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) { if(is_transmitter_idle) { scene_manager_set_scene_state( scene_manager, InfraredSceneRemote, (unsigned)button_index); - if(button_index == ButtonIndexPlus) { + if(button_index == ButtonIndexLearn) { infrared->app_state.is_learning_new_remote = false; scene_manager_next_scene(scene_manager, InfraredSceneLearn); consumed = true; diff --git a/applications/main/infrared/scenes/infrared_scene_rpc.c b/applications/main/infrared/scenes/infrared_scene_rpc.c index fa5a599afa7..f3408fba4dd 100644 --- a/applications/main/infrared/scenes/infrared_scene_rpc.c +++ b/applications/main/infrared/scenes/infrared_scene_rpc.c @@ -1,6 +1,8 @@ #include "../infrared_app_i.h" #include +#define TAG "InfraredApp" + typedef enum { InfraredRpcStateIdle, InfraredRpcStateLoaded, @@ -38,11 +40,9 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { view_dispatcher_stop(infrared->view_dispatcher); } else if(event.event == InfraredCustomEventTypePopupClosed) { view_dispatcher_stop(infrared->view_dispatcher); - } else if(event.event == InfraredCustomEventTypeRpcLoad) { + } else if(event.event == InfraredCustomEventTypeRpcLoadFile) { bool result = false; - const char* arg = rpc_system_app_get_data(infrared->rpc_ctx); - if(arg && (state == InfraredRpcStateIdle)) { - furi_string_set(infrared->file_path, arg); + if(state == InfraredRpcStateIdle) { result = infrared_remote_load( infrared->remote, furi_string_get_cstr(infrared->file_path)); if(result) { @@ -56,20 +56,35 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { popup_set_text( infrared->popup, infrared->text_store[0], 89, 44, AlignCenter, AlignTop); - rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventLoadFile, result); - } else if(event.event == InfraredCustomEventTypeRpcButtonPress) { + rpc_system_app_confirm(infrared->rpc_ctx, result); + } else if( + event.event == InfraredCustomEventTypeRpcButtonPressName || + event.event == InfraredCustomEventTypeRpcButtonPressIndex) { bool result = false; - const char* arg = rpc_system_app_get_data(infrared->rpc_ctx); - if(arg && (state == InfraredRpcStateLoaded)) { - size_t button_index = 0; - if(infrared_remote_get_signal_index(infrared->remote, arg, &button_index)) { - infrared_tx_start_button_index(infrared, button_index); - result = true; + if(state == InfraredRpcStateLoaded) { + if(event.event == InfraredCustomEventTypeRpcButtonPressName) { + const char* button_name = furi_string_get_cstr(infrared->button_name); + size_t index; + const bool index_found = + infrared_remote_get_signal_index(infrared->remote, button_name, &index); + infrared->app_state.current_button_index = + index_found ? (signed)index : InfraredButtonIndexNone; + FURI_LOG_D(TAG, "Sending signal with name \"%s\"", button_name); + } else { + FURI_LOG_D( + TAG, + "Sending signal with index \"%ld\"", + infrared->app_state.current_button_index); + } + if(infrared->app_state.current_button_index != InfraredButtonIndexNone) { + infrared_tx_start_button_index( + infrared, infrared->app_state.current_button_index); scene_manager_set_scene_state( infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateSending); + result = true; } } - rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, result); + rpc_system_app_confirm(infrared->rpc_ctx, result); } else if(event.event == InfraredCustomEventTypeRpcButtonRelease) { bool result = false; if(state == InfraredRpcStateSending) { @@ -78,11 +93,11 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state( infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded); } - rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, result); + rpc_system_app_confirm(infrared->rpc_ctx, result); } else if(event.event == InfraredCustomEventTypeRpcExit) { scene_manager_stop(infrared->scene_manager); view_dispatcher_stop(infrared->view_dispatcher); - rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventAppExit, true); + rpc_system_app_confirm(infrared->rpc_ctx, true); } else if(event.event == InfraredCustomEventTypeRpcSessionClose) { scene_manager_stop(infrared->scene_manager); view_dispatcher_stop(infrared->view_dispatcher); diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c index 2bef08df21f..cd0613861fe 100644 --- a/applications/main/lfrfid/lfrfid.c +++ b/applications/main/lfrfid/lfrfid.c @@ -13,21 +13,23 @@ static bool lfrfid_debug_back_event_callback(void* context) { return scene_manager_handle_back_event(app->scene_manager); } -static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) { +static void rpc_command_callback(const RpcAppSystemEvent* event, void* context) { furi_assert(context); LfRfid* app = (LfRfid*)context; - if(rpc_event == RpcAppEventSessionClose) { + if(event->type == RpcAppEventTypeSessionClose) { view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventRpcSessionClose); // Detach RPC rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL); app->rpc_ctx = NULL; - } else if(rpc_event == RpcAppEventAppExit) { + } else if(event->type == RpcAppEventTypeAppExit) { view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventExit); - } else if(rpc_event == RpcAppEventLoadFile) { + } else if(event->type == RpcAppEventTypeLoadFile) { + furi_assert(event->data.type == RpcAppSystemEventDataTypeString); + furi_string_set(app->file_path, event->data.string); view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventRpcLoadFile); } else { - rpc_system_app_confirm(app->rpc_ctx, rpc_event, false); + rpc_system_app_confirm(app->rpc_ctx, false); } } diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c b/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c index 156dd97afa1..906218d74f0 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c @@ -24,17 +24,15 @@ bool lfrfid_scene_rpc_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { consumed = true; if(event.event == LfRfidEventExit) { - rpc_system_app_confirm(app->rpc_ctx, RpcAppEventAppExit, true); + rpc_system_app_confirm(app->rpc_ctx, true); scene_manager_stop(app->scene_manager); view_dispatcher_stop(app->view_dispatcher); } else if(event.event == LfRfidEventRpcSessionClose) { scene_manager_stop(app->scene_manager); view_dispatcher_stop(app->view_dispatcher); } else if(event.event == LfRfidEventRpcLoadFile) { - const char* arg = rpc_system_app_get_data(app->rpc_ctx); bool result = false; - if(arg && (app->rpc_state == LfRfidRpcStateIdle)) { - furi_string_set(app->file_path, arg); + if(app->rpc_state == LfRfidRpcStateIdle) { if(lfrfid_load_key_data(app, app->file_path, false)) { lfrfid_worker_start_thread(app->lfworker); lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id); @@ -48,7 +46,7 @@ bool lfrfid_scene_rpc_on_event(void* context, SceneManagerEvent event) { result = true; } } - rpc_system_app_confirm(app->rpc_ctx, RpcAppEventLoadFile, result); + rpc_system_app_confirm(app->rpc_ctx, result); } } return consumed; diff --git a/applications/main/nfc/helpers/nfc_custom_event.h b/applications/main/nfc/helpers/nfc_custom_event.h index 2a3b13e1a41..16fbc47492b 100644 --- a/applications/main/nfc/helpers/nfc_custom_event.h +++ b/applications/main/nfc/helpers/nfc_custom_event.h @@ -21,7 +21,7 @@ typedef enum { NfcCustomEventTextInputDone, NfcCustomEventDictAttackDone, - NfcCustomEventRpcLoad, + NfcCustomEventRpcLoadFile, NfcCustomEventRpcExit, NfcCustomEventRpcSessionClose, diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index bf6c0842fe5..87fbe9f0828 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -694,15 +694,17 @@ static bool nfc_protocol_support_scene_rpc_on_event(NfcApp* instance, SceneManag bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventRpcLoad && instance->rpc_state == NfcRpcStateIdle) { - furi_string_set(instance->file_path, rpc_system_app_get_data(instance->rpc_ctx)); - const bool load_success = nfc_load_file(instance, instance->file_path, false); - if(load_success) { - nfc_protocol_support_scene_rpc_setup_ui_and_emulate(instance); + if(event.event == NfcCustomEventRpcLoadFile) { + bool success = false; + if(instance->rpc_state == NfcRpcStateIdle) { + if(nfc_load_file(instance, instance->file_path, false)) { + nfc_protocol_support_scene_rpc_setup_ui_and_emulate(instance); + success = true; + } } - rpc_system_app_confirm(instance->rpc_ctx, RpcAppEventLoadFile, load_success); + rpc_system_app_confirm(instance->rpc_ctx, success); } else if(event.event == NfcCustomEventRpcExit) { - rpc_system_app_confirm(instance->rpc_ctx, RpcAppEventAppExit, true); + rpc_system_app_confirm(instance->rpc_ctx, true); scene_manager_stop(instance->scene_manager); view_dispatcher_stop(instance->view_dispatcher); } else if(event.event == NfcCustomEventRpcSessionClose) { diff --git a/applications/main/nfc/nfc_app.c b/applications/main/nfc/nfc_app.c index 9e0de8891bb..141a67e5cba 100644 --- a/applications/main/nfc/nfc_app.c +++ b/applications/main/nfc/nfc_app.c @@ -14,22 +14,24 @@ bool nfc_back_event_callback(void* context) { return scene_manager_handle_back_event(nfc->scene_manager); } -static void nfc_app_rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) { +static void nfc_app_rpc_command_callback(const RpcAppSystemEvent* event, void* context) { furi_assert(context); NfcApp* nfc = (NfcApp*)context; furi_assert(nfc->rpc_ctx); - if(rpc_event == RpcAppEventSessionClose) { + if(event->type == RpcAppEventTypeSessionClose) { view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcSessionClose); rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); nfc->rpc_ctx = NULL; - } else if(rpc_event == RpcAppEventAppExit) { + } else if(event->type == RpcAppEventTypeAppExit) { view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcExit); - } else if(rpc_event == RpcAppEventLoadFile) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcLoad); + } else if(event->type == RpcAppEventTypeLoadFile) { + furi_assert(event->data.type == RpcAppSystemEventDataTypeString); + furi_string_set(nfc->file_path, event->data.string); + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcLoadFile); } else { - rpc_system_app_confirm(nfc->rpc_ctx, rpc_event, false); + rpc_system_app_confirm(nfc->rpc_ctx, false); } } diff --git a/applications/main/subghz/scenes/subghz_scene_rpc.c b/applications/main/subghz/scenes/subghz_scene_rpc.c index d4bf3e808eb..f8bf066d549 100644 --- a/applications/main/subghz/scenes/subghz_scene_rpc.c +++ b/applications/main/subghz/scenes/subghz_scene_rpc.c @@ -33,13 +33,13 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { if(event.event == SubGhzCustomEventSceneExit) { scene_manager_stop(subghz->scene_manager); view_dispatcher_stop(subghz->view_dispatcher); - rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventAppExit, true); + rpc_system_app_confirm(subghz->rpc_ctx, true); } else if(event.event == SubGhzCustomEventSceneRpcSessionClose) { scene_manager_stop(subghz->scene_manager); view_dispatcher_stop(subghz->view_dispatcher); } else if(event.event == SubGhzCustomEventSceneRpcButtonPress) { bool result = false; - if((state == SubGhzRpcStateLoaded)) { + if(state == SubGhzRpcStateLoaded) { switch( subghz_txrx_tx_start(subghz->txrx, subghz_txrx_get_fff_data(subghz->txrx))) { case SubGhzTxRxStartTxStateErrorOnlyRx: @@ -61,7 +61,7 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { break; } } - rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result); + rpc_system_app_confirm(subghz->rpc_ctx, result); } else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) { bool result = false; if(state == SubGhzRpcStateTx) { @@ -70,15 +70,13 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { result = true; } state = SubGhzRpcStateIdle; - rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonRelease, result); + rpc_system_app_confirm(subghz->rpc_ctx, result); } else if(event.event == SubGhzCustomEventSceneRpcLoad) { bool result = false; - const char* arg = rpc_system_app_get_data(subghz->rpc_ctx); - if(arg && (state == SubGhzRpcStateIdle)) { - if(subghz_key_load(subghz, arg, false)) { + if(state == SubGhzRpcStateIdle) { + if(subghz_key_load(subghz, furi_string_get_cstr(subghz->file_path), false)) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateLoaded); - furi_string_set(subghz->file_path, arg); result = true; FuriString* file_name; file_name = furi_string_alloc(); @@ -97,7 +95,7 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { rpc_system_app_set_error_text(subghz->rpc_ctx, "Cannot parse file"); } } - rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventLoadFile, result); + rpc_system_app_confirm(subghz->rpc_ctx, result); } } return consumed; diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index e8148798ef4..69a72e95d63 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -20,29 +20,31 @@ void subghz_tick_event_callback(void* context) { scene_manager_handle_tick_event(subghz->scene_manager); } -static void subghz_rpc_command_callback(RpcAppSystemEvent event, void* context) { +static void subghz_rpc_command_callback(const RpcAppSystemEvent* event, void* context) { furi_assert(context); SubGhz* subghz = context; furi_assert(subghz->rpc_ctx); - if(event == RpcAppEventSessionClose) { + if(event->type == RpcAppEventTypeSessionClose) { view_dispatcher_send_custom_event( subghz->view_dispatcher, SubGhzCustomEventSceneRpcSessionClose); rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); subghz->rpc_ctx = NULL; - } else if(event == RpcAppEventAppExit) { + } else if(event->type == RpcAppEventTypeAppExit) { view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); - } else if(event == RpcAppEventLoadFile) { + } else if(event->type == RpcAppEventTypeLoadFile) { + furi_assert(event->data.type == RpcAppSystemEventDataTypeString); + furi_string_set(subghz->file_path, event->data.string); view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneRpcLoad); - } else if(event == RpcAppEventButtonPress) { + } else if(event->type == RpcAppEventTypeButtonPress) { view_dispatcher_send_custom_event( subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonPress); - } else if(event == RpcAppEventButtonRelease) { + } else if(event->type == RpcAppEventTypeButtonRelease) { view_dispatcher_send_custom_event( subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonRelease); } else { - rpc_system_app_confirm(subghz->rpc_ctx, event, false); + rpc_system_app_confirm(subghz->rpc_ctx, false); } } diff --git a/applications/services/rpc/rpc.c b/applications/services/rpc/rpc.c index 826f2225376..5880e7d9f96 100644 --- a/applications/services/rpc/rpc.c +++ b/applications/services/rpc/rpc.c @@ -477,12 +477,15 @@ void rpc_send_and_release(RpcSession* session, PB_Main* message) { } void rpc_send_and_release_empty(RpcSession* session, uint32_t command_id, PB_CommandStatus status) { + furi_assert(session); + PB_Main message = { .command_id = command_id, .command_status = status, .has_next = false, .which_content = PB_Main_empty_tag, }; + rpc_send_and_release(session, &message); pb_release(&PB_Main_msg, &message); } diff --git a/applications/services/rpc/rpc_app.c b/applications/services/rpc/rpc_app.c index e86eaa493d7..9af652dae17 100644 --- a/applications/services/rpc/rpc_app.c +++ b/applications/services/rpc/rpc_app.c @@ -10,49 +10,85 @@ struct RpcAppSystem { RpcSession* session; - RpcAppSystemCallback app_callback; - void* app_context; + RpcAppSystemCallback callback; + void* callback_context; - RpcAppSystemDataExchangeCallback data_exchange_callback; - void* data_exchange_context; + uint32_t error_code; + char* error_text; - PB_Main* state_msg; - PB_Main* error_msg; - - uint32_t last_id; - char* last_data; + uint32_t last_command_id; + RpcAppSystemEventType last_event_type; }; #define RPC_SYSTEM_APP_TEMP_ARGS_SIZE 16 +static void rpc_system_app_send_state_response( + RpcAppSystem* rpc_app, + PB_App_AppState state, + const char* name) { + PB_Main* response = malloc(sizeof(PB_Main)); + + response->which_content = PB_Main_app_state_response_tag; + response->content.app_state_response.state = state; + + FURI_LOG_D(TAG, "%s", name); + rpc_send(rpc_app->session, response); + + free(response); +} + +static void rpc_system_app_send_error_response( + RpcAppSystem* rpc_app, + uint32_t command_id, + PB_CommandStatus status, + const char* name) { + // Not describing all possible errors as only APP_NOT_RUNNING is used + const char* status_str = status == PB_CommandStatus_ERROR_APP_NOT_RUNNING ? "APP_NOT_RUNNING" : + "UNKNOWN"; + FURI_LOG_E(TAG, "%s: %s, id %lu, status: %d", name, status_str, command_id, status); + rpc_send_and_release_empty(rpc_app->session, command_id, status); +} + +static void rpc_system_app_set_last_command( + RpcAppSystem* rpc_app, + uint32_t command_id, + const RpcAppSystemEvent* event) { + furi_assert(rpc_app->last_command_id == 0); + furi_assert(rpc_app->last_event_type == RpcAppEventTypeInvalid); + + rpc_app->last_command_id = command_id; + rpc_app->last_event_type = event->type; +} + static void rpc_system_app_start_process(const PB_Main* request, void* context) { furi_assert(request); - furi_assert(context); - furi_assert(request->which_content == PB_Main_app_start_request_tag); - RpcAppSystem* rpc_app = context; - RpcSession* session = rpc_app->session; - rpc_system_app_error_reset(rpc_app); - furi_assert(session); - char args_temp[RPC_SYSTEM_APP_TEMP_ARGS_SIZE]; - furi_assert(!rpc_app->last_id); - furi_assert(!rpc_app->last_data); + RpcAppSystem* rpc_app = context; + furi_assert(rpc_app); + furi_assert(rpc_app->last_command_id == 0); + furi_assert(rpc_app->last_event_type == RpcAppEventTypeInvalid); FURI_LOG_D(TAG, "StartProcess: id %lu", request->command_id); - PB_CommandStatus result; - Loader* loader = furi_record_open(RECORD_LOADER); const char* app_name = request->content.app_start_request.name; + + PB_CommandStatus result; + if(app_name) { + rpc_system_app_error_reset(rpc_app); + + char app_args_temp[RPC_SYSTEM_APP_TEMP_ARGS_SIZE]; const char* app_args = request->content.app_start_request.args; + if(app_args && strcmp(app_args, "RPC") == 0) { // If app is being started in RPC mode - pass RPC context via args string - snprintf(args_temp, RPC_SYSTEM_APP_TEMP_ARGS_SIZE, "RPC %08lX", (uint32_t)rpc_app); - app_args = args_temp; + snprintf(app_args_temp, RPC_SYSTEM_APP_TEMP_ARGS_SIZE, "RPC %08lX", (uint32_t)rpc_app); + app_args = app_args_temp; } - LoaderStatus status = loader_start(loader, app_name, app_args, NULL); + + const LoaderStatus status = loader_start(loader, app_name, app_args, NULL); if(status == LoaderStatusErrorAppStarted) { result = PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED; } else if(status == LoaderStatusErrorInternal) { @@ -71,266 +107,271 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) furi_record_close(RECORD_LOADER); FURI_LOG_D(TAG, "StartProcess: response id %lu, result %d", request->command_id, result); - rpc_send_and_release_empty(session, request->command_id, result); + rpc_send_and_release_empty(rpc_app->session, request->command_id, result); } static void rpc_system_app_lock_status_process(const PB_Main* request, void* context) { furi_assert(request); - furi_assert(context); - furi_assert(request->which_content == PB_Main_app_lock_status_request_tag); + RpcAppSystem* rpc_app = context; + furi_assert(rpc_app); + rpc_system_app_error_reset(rpc_app); - RpcSession* session = rpc_app->session; - furi_assert(session); FURI_LOG_D(TAG, "LockStatus"); - Loader* loader = furi_record_open(RECORD_LOADER); - - PB_Main response = { - .has_next = false, - .command_status = PB_CommandStatus_OK, - .command_id = request->command_id, - .which_content = PB_Main_app_lock_status_response_tag, - }; + PB_Main* response = malloc(sizeof(PB_Main)); - response.content.app_lock_status_response.locked = loader_is_locked(loader); + response->command_id = request->command_id; + response->which_content = PB_Main_app_lock_status_response_tag; + Loader* loader = furi_record_open(RECORD_LOADER); + response->content.app_lock_status_response.locked = loader_is_locked(loader); furi_record_close(RECORD_LOADER); FURI_LOG_D(TAG, "LockStatus: response"); - rpc_send_and_release(session, &response); - pb_release(&PB_Main_msg, &response); + rpc_send_and_release(rpc_app->session, response); + + free(response); } static void rpc_system_app_exit_request(const PB_Main* request, void* context) { furi_assert(request); - furi_assert(context); - furi_assert(request->which_content == PB_Main_app_exit_request_tag); - RpcAppSystem* rpc_app = context; - rpc_system_app_error_reset(rpc_app); - RpcSession* session = rpc_app->session; - furi_assert(session); - PB_CommandStatus status; + RpcAppSystem* rpc_app = context; + furi_assert(rpc_app); - if(rpc_app->app_callback) { + if(rpc_app->callback) { FURI_LOG_D(TAG, "ExitRequest: id %lu", request->command_id); - furi_assert(!rpc_app->last_id); - furi_assert(!rpc_app->last_data); - rpc_app->last_id = request->command_id; - rpc_app->app_callback(RpcAppEventAppExit, rpc_app->app_context); + + const RpcAppSystemEvent event = { + .type = RpcAppEventTypeAppExit, + .data = + { + .type = RpcAppSystemEventDataTypeNone, + {0}, + }, + }; + + rpc_system_app_error_reset(rpc_app); + rpc_system_app_set_last_command(rpc_app, request->command_id, &event); + + rpc_app->callback(&event, rpc_app->callback_context); + } else { - status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; - FURI_LOG_E( - TAG, "ExitRequest: APP_NOT_RUNNING, id %lu, status: %d", request->command_id, status); - rpc_send_and_release_empty(session, request->command_id, status); + rpc_system_app_send_error_response( + rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, "ExitRequest"); } } static void rpc_system_app_load_file(const PB_Main* request, void* context) { furi_assert(request); - furi_assert(context); - furi_assert(request->which_content == PB_Main_app_load_file_request_tag); + RpcAppSystem* rpc_app = context; - rpc_system_app_error_reset(rpc_app); - RpcSession* session = rpc_app->session; - furi_assert(session); + furi_assert(rpc_app); - PB_CommandStatus status; - if(rpc_app->app_callback) { + if(rpc_app->callback) { FURI_LOG_D(TAG, "LoadFile: id %lu", request->command_id); - furi_assert(!rpc_app->last_id); - furi_assert(!rpc_app->last_data); - rpc_app->last_id = request->command_id; - rpc_app->last_data = strdup(request->content.app_load_file_request.path); - rpc_app->app_callback(RpcAppEventLoadFile, rpc_app->app_context); + + const RpcAppSystemEvent event = { + .type = RpcAppEventTypeLoadFile, + .data = + { + .type = RpcAppSystemEventDataTypeString, + .string = request->content.app_load_file_request.path, + }, + }; + + rpc_system_app_error_reset(rpc_app); + rpc_system_app_set_last_command(rpc_app, request->command_id, &event); + + rpc_app->callback(&event, rpc_app->callback_context); + } else { - status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; - FURI_LOG_E( - TAG, "LoadFile: APP_NOT_RUNNING, id %lu, status: %d", request->command_id, status); - rpc_send_and_release_empty(session, request->command_id, status); + rpc_system_app_send_error_response( + rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, "LoadFile"); } } static void rpc_system_app_button_press(const PB_Main* request, void* context) { furi_assert(request); - furi_assert(context); - furi_assert(request->which_content == PB_Main_app_button_press_request_tag); + RpcAppSystem* rpc_app = context; - rpc_system_app_error_reset(rpc_app); - RpcSession* session = rpc_app->session; - furi_assert(session); + furi_assert(rpc_app); - PB_CommandStatus status; - if(rpc_app->app_callback) { + if(rpc_app->callback) { FURI_LOG_D(TAG, "ButtonPress"); - furi_assert(!rpc_app->last_id); - furi_assert(!rpc_app->last_data); - rpc_app->last_id = request->command_id; - rpc_app->last_data = strdup(request->content.app_button_press_request.args); - rpc_app->app_callback(RpcAppEventButtonPress, rpc_app->app_context); + + RpcAppSystemEvent event; + event.type = RpcAppEventTypeButtonPress; + + if(strlen(request->content.app_button_press_request.args) != 0) { + event.data.type = RpcAppSystemEventDataTypeString; + event.data.string = request->content.app_button_press_request.args; + } else { + event.data.type = RpcAppSystemEventDataTypeInt32; + event.data.i32 = request->content.app_button_press_request.index; + } + + rpc_system_app_error_reset(rpc_app); + rpc_system_app_set_last_command(rpc_app, request->command_id, &event); + + rpc_app->callback(&event, rpc_app->callback_context); + } else { - status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; - FURI_LOG_E( - TAG, "ButtonPress: APP_NOT_RUNNING, id %lu, status: %d", request->command_id, status); - rpc_send_and_release_empty(session, request->command_id, status); + rpc_system_app_send_error_response( + rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, "ButtonPress"); } } static void rpc_system_app_button_release(const PB_Main* request, void* context) { furi_assert(request); furi_assert(request->which_content == PB_Main_app_button_release_request_tag); - furi_assert(context); RpcAppSystem* rpc_app = context; - rpc_system_app_error_reset(rpc_app); - RpcSession* session = rpc_app->session; - furi_assert(session); + furi_assert(rpc_app); - PB_CommandStatus status; - if(rpc_app->app_callback) { + if(rpc_app->callback) { FURI_LOG_D(TAG, "ButtonRelease"); - furi_assert(!rpc_app->last_id); - furi_assert(!rpc_app->last_data); - rpc_app->last_id = request->command_id; - rpc_app->app_callback(RpcAppEventButtonRelease, rpc_app->app_context); + + const RpcAppSystemEvent event = { + .type = RpcAppEventTypeButtonRelease, + .data = + { + .type = RpcAppSystemEventDataTypeNone, + {0}, + }, + }; + + rpc_system_app_error_reset(rpc_app); + rpc_system_app_set_last_command(rpc_app, request->command_id, &event); + + rpc_app->callback(&event, rpc_app->callback_context); + } else { - status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; - FURI_LOG_E( - TAG, "ButtonRelease: APP_NOT_RUNNING, id %lu, status: %d", request->command_id, status); - rpc_send_and_release_empty(session, request->command_id, status); + rpc_system_app_send_error_response( + rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, "ButtonRelease"); } } static void rpc_system_app_get_error_process(const PB_Main* request, void* context) { furi_assert(request); furi_assert(request->which_content == PB_Main_app_get_error_request_tag); - furi_assert(context); RpcAppSystem* rpc_app = context; - RpcSession* session = rpc_app->session; - furi_assert(session); + furi_assert(rpc_app); + + PB_Main* response = malloc(sizeof(PB_Main)); - rpc_app->error_msg->command_id = request->command_id; + response->command_id = request->command_id; + response->which_content = PB_Main_app_get_error_response_tag; + response->content.app_get_error_response.code = rpc_app->error_code; + response->content.app_get_error_response.text = rpc_app->error_text; FURI_LOG_D(TAG, "GetError"); - rpc_send(session, rpc_app->error_msg); + rpc_send(rpc_app->session, response); + + free(response); } static void rpc_system_app_data_exchange_process(const PB_Main* request, void* context) { furi_assert(request); furi_assert(request->which_content == PB_Main_app_data_exchange_request_tag); - furi_assert(context); RpcAppSystem* rpc_app = context; - rpc_system_app_error_reset(rpc_app); - RpcSession* session = rpc_app->session; - furi_assert(session); + furi_assert(rpc_app); - PB_CommandStatus command_status; - pb_bytes_array_t* data = request->content.app_data_exchange_request.data; + if(rpc_app->callback) { + FURI_LOG_D(TAG, "DataExchange"); - if(rpc_app->data_exchange_callback) { - uint8_t* data_bytes = NULL; - size_t data_size = 0; - if(data) { - data_bytes = data->bytes; - data_size = data->size; - } - rpc_app->data_exchange_callback(data_bytes, data_size, rpc_app->data_exchange_context); - command_status = PB_CommandStatus_OK; + const pb_bytes_array_t* data = request->content.app_data_exchange_request.data; + + const RpcAppSystemEvent event = { + .type = RpcAppEventTypeDataExchange, + .data = + { + .type = RpcAppSystemEventDataTypeBytes, + .bytes = + { + .ptr = data ? data->bytes : NULL, + .size = data ? data->size : 0, + }, + }, + }; + + rpc_system_app_error_reset(rpc_app); + rpc_system_app_set_last_command(rpc_app, request->command_id, &event); + + rpc_app->callback(&event, rpc_app->callback_context); } else { - command_status = PB_CommandStatus_ERROR_APP_CMD_ERROR; + rpc_system_app_send_error_response( + rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, "DataExchange"); } - - FURI_LOG_D(TAG, "DataExchange"); - rpc_send_and_release_empty(session, request->command_id, command_status); } void rpc_system_app_send_started(RpcAppSystem* rpc_app) { furi_assert(rpc_app); - RpcSession* session = rpc_app->session; - furi_assert(session); - - rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_STARTED; - - FURI_LOG_D(TAG, "SendStarted"); - rpc_send(session, rpc_app->state_msg); + rpc_system_app_send_state_response(rpc_app, PB_App_AppState_APP_STARTED, "SendStarted"); } void rpc_system_app_send_exited(RpcAppSystem* rpc_app) { furi_assert(rpc_app); - RpcSession* session = rpc_app->session; - furi_assert(session); - - rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_CLOSED; - - FURI_LOG_D(TAG, "SendExit"); - rpc_send(session, rpc_app->state_msg); -} - -const char* rpc_system_app_get_data(RpcAppSystem* rpc_app) { - furi_assert(rpc_app); - furi_assert(rpc_app->last_data); - return rpc_app->last_data; + rpc_system_app_send_state_response(rpc_app, PB_App_AppState_APP_CLOSED, "SendExit"); } -void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result) { +void rpc_system_app_confirm(RpcAppSystem* rpc_app, bool result) { furi_assert(rpc_app); - RpcSession* session = rpc_app->session; - furi_assert(session); - furi_assert(rpc_app->last_id); - - PB_CommandStatus status = result ? PB_CommandStatus_OK : PB_CommandStatus_ERROR_APP_CMD_ERROR; - - uint32_t last_id = 0; - switch(event) { - case RpcAppEventAppExit: - case RpcAppEventLoadFile: - case RpcAppEventButtonPress: - case RpcAppEventButtonRelease: - last_id = rpc_app->last_id; - rpc_app->last_id = 0; - if(rpc_app->last_data) { - free(rpc_app->last_data); - rpc_app->last_data = NULL; - } - FURI_LOG_D(TAG, "AppConfirm: event %d last_id %lu status %d", event, last_id, status); - rpc_send_and_release_empty(session, last_id, status); - break; - default: - furi_crash("RPC App state programming Error"); - break; - } + furi_assert(rpc_app->last_command_id != 0); + /* Ensure that only commands of these types can be confirmed */ + furi_assert( + rpc_app->last_event_type == RpcAppEventTypeAppExit || + rpc_app->last_event_type == RpcAppEventTypeLoadFile || + rpc_app->last_event_type == RpcAppEventTypeButtonPress || + rpc_app->last_event_type == RpcAppEventTypeButtonRelease || + rpc_app->last_event_type == RpcAppEventTypeDataExchange); + + const uint32_t last_command_id = rpc_app->last_command_id; + const RpcAppSystemEventType last_event_type = rpc_app->last_event_type; + + rpc_app->last_command_id = 0; + rpc_app->last_event_type = RpcAppEventTypeInvalid; + + const PB_CommandStatus status = result ? PB_CommandStatus_OK : + PB_CommandStatus_ERROR_APP_CMD_ERROR; + FURI_LOG_D( + TAG, + "AppConfirm: event %d last_id %lu status %d", + last_event_type, + last_command_id, + status); + + rpc_send_and_release_empty(rpc_app->session, last_command_id, status); } void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx) { furi_assert(rpc_app); - rpc_app->app_callback = callback; - rpc_app->app_context = ctx; + rpc_app->callback = callback; + rpc_app->callback_context = ctx; } void rpc_system_app_set_error_code(RpcAppSystem* rpc_app, uint32_t error_code) { furi_assert(rpc_app); - PB_App_GetErrorResponse* content = &rpc_app->error_msg->content.app_get_error_response; - content->code = error_code; + rpc_app->error_code = error_code; } void rpc_system_app_set_error_text(RpcAppSystem* rpc_app, const char* error_text) { furi_assert(rpc_app); - PB_App_GetErrorResponse* content = &rpc_app->error_msg->content.app_get_error_response; - if(content->text) { - free(content->text); + if(rpc_app->error_text) { + free(rpc_app->error_text); } - content->text = error_text ? strdup(error_text) : NULL; + rpc_app->error_text = error_text ? strdup(error_text) : NULL; } void rpc_system_app_error_reset(RpcAppSystem* rpc_app) { @@ -340,29 +381,13 @@ void rpc_system_app_error_reset(RpcAppSystem* rpc_app) { rpc_system_app_set_error_text(rpc_app, NULL); } -void rpc_system_app_set_data_exchange_callback( - RpcAppSystem* rpc_app, - RpcAppSystemDataExchangeCallback callback, - void* ctx) { - furi_assert(rpc_app); - - rpc_app->data_exchange_callback = callback; - rpc_app->data_exchange_context = ctx; -} - void rpc_system_app_exchange_data(RpcAppSystem* rpc_app, const uint8_t* data, size_t data_size) { furi_assert(rpc_app); - RpcSession* session = rpc_app->session; - furi_assert(session); - PB_Main message = { - .command_id = 0, - .command_status = PB_CommandStatus_OK, - .has_next = false, - .which_content = PB_Main_app_data_exchange_request_tag, - }; + PB_Main* request = malloc(sizeof(PB_Main)); - PB_App_DataExchangeRequest* content = &message.content.app_data_exchange_request; + request->which_content = PB_Main_app_data_exchange_request_tag; + PB_App_DataExchangeRequest* content = &request->content.app_data_exchange_request; if(data && data_size) { content->data = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(data_size)); @@ -372,7 +397,9 @@ void rpc_system_app_exchange_data(RpcAppSystem* rpc_app, const uint8_t* data, si content->data = NULL; } - rpc_send_and_release(session, &message); + rpc_send_and_release(rpc_app->session, request); + + free(request); } void* rpc_system_app_alloc(RpcSession* session) { @@ -381,18 +408,6 @@ void* rpc_system_app_alloc(RpcSession* session) { RpcAppSystem* rpc_app = malloc(sizeof(RpcAppSystem)); rpc_app->session = session; - // App exit message - rpc_app->state_msg = malloc(sizeof(PB_Main)); - rpc_app->state_msg->which_content = PB_Main_app_state_response_tag; - rpc_app->state_msg->command_status = PB_CommandStatus_OK; - - // App error message - rpc_app->error_msg = malloc(sizeof(PB_Main)); - rpc_app->error_msg->which_content = PB_Main_app_get_error_response_tag; - rpc_app->error_msg->command_status = PB_CommandStatus_OK; - rpc_app->error_msg->content.app_get_error_response.code = 0; - rpc_app->error_msg->content.app_get_error_response.text = NULL; - RpcHandler rpc_handler = { .message_handler = NULL, .decode_submessage = NULL, @@ -429,24 +444,24 @@ void* rpc_system_app_alloc(RpcSession* session) { void rpc_system_app_free(void* context) { RpcAppSystem* rpc_app = context; furi_assert(rpc_app); - RpcSession* session = rpc_app->session; - furi_assert(session); - - if(rpc_app->app_callback) { - rpc_app->app_callback(RpcAppEventSessionClose, rpc_app->app_context); + furi_assert(rpc_app->session); + + if(rpc_app->callback) { + const RpcAppSystemEvent event = { + .type = RpcAppEventTypeSessionClose, + .data = + { + .type = RpcAppSystemEventDataTypeNone, + {0}, + }, + }; + + rpc_app->callback(&event, rpc_app->callback_context); } - while(rpc_app->app_callback) { + while(rpc_app->callback) { furi_delay_tick(1); } - furi_assert(!rpc_app->data_exchange_callback); - - if(rpc_app->last_data) free(rpc_app->last_data); - - pb_release(&PB_Main_msg, rpc_app->error_msg); - - free(rpc_app->error_msg); - free(rpc_app->state_msg); free(rpc_app); } diff --git a/applications/services/rpc/rpc_app.h b/applications/services/rpc/rpc_app.h index d5c6fee9629..4ee5a24d379 100644 --- a/applications/services/rpc/rpc_app.h +++ b/applications/services/rpc/rpc_app.h @@ -1,45 +1,213 @@ +/** + * @file rpc_app.h + * @brief Application RPC subsystem interface. + * + * The application RPC subsystem provides facilities for interacting with applications, + * such as starting/stopping, passing parameters, sending commands and exchanging arbitrary data. + * + * All commands are handled asynchronously via a user-settable callback. + * + * For a complete description of message types handled in this subsystem, + * see https://github.com/flipperdevices/flipperzero-protobuf/blob/dev/application.proto + */ #pragma once + #include "rpc.h" #ifdef __cplusplus extern "C" { #endif +/** + * @brief Enumeration of possible event data types. + */ typedef enum { - RpcAppEventSessionClose, - RpcAppEventAppExit, - RpcAppEventLoadFile, - RpcAppEventButtonPress, - RpcAppEventButtonRelease, + RpcAppSystemEventDataTypeNone, /**< No data is provided by the event. */ + RpcAppSystemEventDataTypeString, /**< Event data contains a zero-terminated string. */ + RpcAppSystemEventDataTypeInt32, /**< Event data contains a signed 32-bit integer. */ + RpcAppSystemEventDataTypeBytes, /**< Event data contains zero or more bytes. */ +} RpcAppSystemEventDataType; + +/** + * @brief Event data structure, containing the type and associated data. + * + * All below fields except for type are valid only if the respective type is set. + */ +typedef struct { + RpcAppSystemEventDataType + type; /**< Type of the data. The meaning of other fields depends on this one. */ + union { + const char* string; /**< Pointer to a zero-terminated character string. */ + int32_t i32; /**< Signed 32-bit integer value. */ + struct { + const uint8_t* ptr; /**< Pointer to the byte array data. */ + size_t size; /**< Size of the byte array, in bytes. */ + } bytes; /**< Byte array of arbitrary length. */ + }; +} RpcAppSystemEventData; + +/** + * @brief Enumeration of possible event types. + */ +typedef enum { + /** + * @brief Denotes an invalid state. + * + * An event of this type shall never be passed into the callback. + */ + RpcAppEventTypeInvalid, + /** + * @brief The client side has closed the session. + * + * After receiving this event, the RPC context is no more valid. + */ + RpcAppEventTypeSessionClose, + /** + * @brief The client has requested the application to exit. + * + * The application must exit after receiving this command. + */ + RpcAppEventTypeAppExit, + /** + * @brief The client has requested the application to load a file. + * + * This command's meaning is application-specific, i.e. the application might or + * might not require additional commands after loading a file to do anything useful. + */ + RpcAppEventTypeLoadFile, + /** + * @brief The client has informed the application that a button has been pressed. + * + * This command's meaning is application-specific, e.g. to select a part of the + * previously loaded file or to invoke a particular function within the application. + */ + RpcAppEventTypeButtonPress, + /** + * @brief The client has informed the application that a button has been released. + * + * This command's meaning is application-specific, e.g. to cease + * all activities to be conducted while a button is being pressed. + */ + RpcAppEventTypeButtonRelease, + /** + * @brief The client has sent a byte array of arbitrary size. + * + * This command's purpose is bi-directional exchange of arbitrary raw data. + * Useful for implementing higher-level protocols while using the RPC as a transport layer. + */ + RpcAppEventTypeDataExchange, +} RpcAppSystemEventType; + +/** + * @brief RPC application subsystem event structure. + */ +typedef struct { + RpcAppSystemEventType type; /**< Type of the event. */ + RpcAppSystemEventData data; /**< Data associated with the event. */ } RpcAppSystemEvent; -typedef void (*RpcAppSystemCallback)(RpcAppSystemEvent event, void* context); -typedef void ( - *RpcAppSystemDataExchangeCallback)(const uint8_t* data, size_t data_size, void* context); +/** + * @brief Callback function type. + * + * A function of this type must be passed to rpc_system_app_set_callback() by the user code. + * + * @warning The event pointer is valid ONLY inside the callback function. + * + * @param[in] event pointer to the event object. Valid only inside the callback function. + * @param[in,out] context pointer to the user-defined context object. + */ +typedef void (*RpcAppSystemCallback)(const RpcAppSystemEvent* event, void* context); +/** + * @brief RPC application subsystem opaque type declaration. + */ typedef struct RpcAppSystem RpcAppSystem; -void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx); +/** + * @brief Set the callback function for use by an RpcAppSystem instance. + * + * @param[in,out] rpc_app pointer to the instance to be configured. + * @param[in] callback pointer to the function to be called upon message reception. + * @param[in,out] context pointer to the user-defined context object. Will be passed to the callback. + */ +void rpc_system_app_set_callback( + RpcAppSystem* rpc_app, + RpcAppSystemCallback callback, + void* context); +/** + * @brief Send a notification that an RpcAppSystem instance has been started and is ready. + * + * Call this function once right after acquiring an RPC context and setting the callback. + * + * @param[in,out] rpc_app pointer to the instance to be used. + */ void rpc_system_app_send_started(RpcAppSystem* rpc_app); +/** + * @brief Send a notification that the application using an RpcAppSystem instance is about to exit. + * + * Call this function when the application is about to exit (usually in the *_free() function). + * + * @param[in,out] rpc_app pointer to the instance to be used. + */ void rpc_system_app_send_exited(RpcAppSystem* rpc_app); -const char* rpc_system_app_get_data(RpcAppSystem* rpc_app); - -void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result); +/** + * @brief Send a confirmation that the application using an RpcAppSystem instance has handled the event. + * + * An explicit confirmation is required for the following event types: + * - RpcAppEventTypeAppExit + * - RpcAppEventTypeLoadFile + * - RpcAppEventTypeButtonPress + * - RpcAppEventTypeButtonRelease + * - RpcAppEventTypeDataExchange + * + * Not confirming these events will result in a client-side timeout. + * + * @param[in,out] rpc_app pointer to the instance to be used. + * @param[in] result whether the command was successfully handled or not (true for success). + */ +void rpc_system_app_confirm(RpcAppSystem* rpc_app, bool result); +/** + * @brief Set the error code stored in an RpcAppSystem instance. + * + * The error code can be retrieved by the client at any time by using the GetError request. + * The error code value has no meaning within the subsystem, i.e. it is only passed through to the client. + * + * @param[in,out] rpc_app pointer to the instance to be modified. + * @param[in] error_code arbitrary error code to be set. + */ void rpc_system_app_set_error_code(RpcAppSystem* rpc_app, uint32_t error_code); +/** + * @brief Set the error text stored in an RpcAppSystem instance. + * + * The error text can be retrieved by the client at any time by using the GetError request. + * The text has no meaning within the subsystem, i.e. it is only passed through to the client. + * + * @param[in,out] rpc_app pointer to the instance to be modified. + * @param[in] error_text Pointer to a zero-terminated string containing the error text. + */ void rpc_system_app_set_error_text(RpcAppSystem* rpc_app, const char* error_text); +/** + * @brief Reset the error code and text stored in an RpcAppSystem instance. + * + * Resets the error code to 0 and error text to "" (empty string). + * + * @param[in,out] rpc_app pointer to the instance to be reset. + */ void rpc_system_app_error_reset(RpcAppSystem* rpc_app); -void rpc_system_app_set_data_exchange_callback( - RpcAppSystem* rpc_app, - RpcAppSystemDataExchangeCallback callback, - void* ctx); - +/** + * @brief Send a byte array of arbitrary data to the client using an RpcAppSystem instance. + * + * @param[in,out] rpc_app pointer to the instance to be used. + * @param[in] data pointer to the data buffer to be sent. + * @param[in] data_size size of the data buffer, in bytes. + */ void rpc_system_app_exchange_data(RpcAppSystem* rpc_app, const uint8_t* data, size_t data_size); #ifdef __cplusplus diff --git a/assets/protobuf b/assets/protobuf index 327163d5867..23ad19a7566 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit 327163d5867c7aa3051334c93ced718d15bfe4da +Subproject commit 23ad19a756649ed9f6677b598e5361c5cce6847b diff --git a/scripts/fbt_tools/fbt_assets.py b/scripts/fbt_tools/fbt_assets.py index 5c32ae1a983..492a66b6654 100644 --- a/scripts/fbt_tools/fbt_assets.py +++ b/scripts/fbt_tools/fbt_assets.py @@ -80,22 +80,35 @@ def __invoke_git(args, source_dir): def _proto_ver_generator(target, source, env): target_file = target[0] src_dir = source[0].dir.abspath - try: - __invoke_git( - ["fetch", "--tags"], - source_dir=src_dir, - ) - except (subprocess.CalledProcessError, EnvironmentError): - # Not great, not terrible - print(fg.boldred("Git: fetch failed")) - - try: - git_describe = __invoke_git( - ["describe", "--tags", "--abbrev=0"], - source_dir=src_dir, - ) - except (subprocess.CalledProcessError, EnvironmentError): - raise StopError("Git: describe failed") + + def fetch(unshallow=False): + git_args = ["fetch", "--tags"] + if unshallow: + git_args.append("--unshallow") + + try: + __invoke_git(git_args, source_dir=src_dir) + except (subprocess.CalledProcessError, EnvironmentError): + # Not great, not terrible + print(fg.boldred("Git: fetch failed")) + + def describe(): + try: + return __invoke_git( + ["describe", "--tags", "--abbrev=0"], + source_dir=src_dir, + ) + except (subprocess.CalledProcessError, EnvironmentError): + return None + + fetch() + git_describe = describe() + if not git_describe: + fetch(unshallow=True) + git_describe = describe() + + if not git_describe: + raise StopError("Failed to process git tags for protobuf versioning") git_major, git_minor = git_describe.split(".") version_file_data = ( diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 5f8e836c007..0eadd799d47 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1950,14 +1950,12 @@ Function,+,rpc_session_set_close_callback,void,"RpcSession*, RpcSessionClosedCal Function,+,rpc_session_set_context,void,"RpcSession*, void*" Function,+,rpc_session_set_send_bytes_callback,void,"RpcSession*, RpcSendBytesCallback" Function,+,rpc_session_set_terminated_callback,void,"RpcSession*, RpcSessionTerminatedCallback" -Function,+,rpc_system_app_confirm,void,"RpcAppSystem*, RpcAppSystemEvent, _Bool" +Function,+,rpc_system_app_confirm,void,"RpcAppSystem*, _Bool" Function,+,rpc_system_app_error_reset,void,RpcAppSystem* Function,+,rpc_system_app_exchange_data,void,"RpcAppSystem*, const uint8_t*, size_t" -Function,+,rpc_system_app_get_data,const char*,RpcAppSystem* Function,+,rpc_system_app_send_exited,void,RpcAppSystem* Function,+,rpc_system_app_send_started,void,RpcAppSystem* Function,+,rpc_system_app_set_callback,void,"RpcAppSystem*, RpcAppSystemCallback, void*" -Function,+,rpc_system_app_set_data_exchange_callback,void,"RpcAppSystem*, RpcAppSystemDataExchangeCallback, void*" Function,+,rpc_system_app_set_error_code,void,"RpcAppSystem*, uint32_t" Function,+,rpc_system_app_set_error_text,void,"RpcAppSystem*, const char*" Function,-,rpmatch,int,const char* diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 57adf96aeca..98345319536 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -2493,14 +2493,12 @@ Function,+,rpc_session_set_close_callback,void,"RpcSession*, RpcSessionClosedCal Function,+,rpc_session_set_context,void,"RpcSession*, void*" Function,+,rpc_session_set_send_bytes_callback,void,"RpcSession*, RpcSendBytesCallback" Function,+,rpc_session_set_terminated_callback,void,"RpcSession*, RpcSessionTerminatedCallback" -Function,+,rpc_system_app_confirm,void,"RpcAppSystem*, RpcAppSystemEvent, _Bool" +Function,+,rpc_system_app_confirm,void,"RpcAppSystem*, _Bool" Function,+,rpc_system_app_error_reset,void,RpcAppSystem* Function,+,rpc_system_app_exchange_data,void,"RpcAppSystem*, const uint8_t*, size_t" -Function,+,rpc_system_app_get_data,const char*,RpcAppSystem* Function,+,rpc_system_app_send_exited,void,RpcAppSystem* Function,+,rpc_system_app_send_started,void,RpcAppSystem* Function,+,rpc_system_app_set_callback,void,"RpcAppSystem*, RpcAppSystemCallback, void*" -Function,+,rpc_system_app_set_data_exchange_callback,void,"RpcAppSystem*, RpcAppSystemDataExchangeCallback, void*" Function,+,rpc_system_app_set_error_code,void,"RpcAppSystem*, uint32_t" Function,+,rpc_system_app_set_error_text,void,"RpcAppSystem*, const char*" Function,-,rpmatch,int,const char*