mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2024-12-27 15:22:29 +03:00
49dcf81743
* 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 <hedger@nanode.su> Co-authored-by: hedger <hedger@users.noreply.github.com> Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
468 lines
15 KiB
C
468 lines
15 KiB
C
#include "flipper.pb.h"
|
|
#include <core/record.h>
|
|
#include "rpc_i.h"
|
|
#include <furi.h>
|
|
#include <loader/loader.h>
|
|
#include "rpc_app.h"
|
|
|
|
#define TAG "RpcSystemApp"
|
|
|
|
struct RpcAppSystem {
|
|
RpcSession* session;
|
|
|
|
RpcAppSystemCallback callback;
|
|
void* callback_context;
|
|
|
|
uint32_t error_code;
|
|
char* error_text;
|
|
|
|
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(request->which_content == PB_Main_app_start_request_tag);
|
|
|
|
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);
|
|
|
|
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(app_args_temp, RPC_SYSTEM_APP_TEMP_ARGS_SIZE, "RPC %08lX", (uint32_t)rpc_app);
|
|
app_args = app_args_temp;
|
|
}
|
|
|
|
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) {
|
|
result = PB_CommandStatus_ERROR_APP_CANT_START;
|
|
} else if(status == LoaderStatusErrorUnknownApp) {
|
|
result = PB_CommandStatus_ERROR_INVALID_PARAMETERS;
|
|
} else if(status == LoaderStatusOk) {
|
|
result = PB_CommandStatus_OK;
|
|
} else {
|
|
furi_crash();
|
|
}
|
|
} else {
|
|
result = PB_CommandStatus_ERROR_INVALID_PARAMETERS;
|
|
}
|
|
|
|
furi_record_close(RECORD_LOADER);
|
|
|
|
FURI_LOG_D(TAG, "StartProcess: response id %lu, result %d", 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(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);
|
|
|
|
FURI_LOG_D(TAG, "LockStatus");
|
|
|
|
PB_Main* response = malloc(sizeof(PB_Main));
|
|
|
|
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(rpc_app->session, response);
|
|
|
|
free(response);
|
|
}
|
|
|
|
static void rpc_system_app_exit_request(const PB_Main* request, void* context) {
|
|
furi_assert(request);
|
|
furi_assert(request->which_content == PB_Main_app_exit_request_tag);
|
|
|
|
RpcAppSystem* rpc_app = context;
|
|
furi_assert(rpc_app);
|
|
|
|
if(rpc_app->callback) {
|
|
FURI_LOG_D(TAG, "ExitRequest: id %lu", request->command_id);
|
|
|
|
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 {
|
|
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(request->which_content == PB_Main_app_load_file_request_tag);
|
|
|
|
RpcAppSystem* rpc_app = context;
|
|
furi_assert(rpc_app);
|
|
|
|
if(rpc_app->callback) {
|
|
FURI_LOG_D(TAG, "LoadFile: id %lu", request->command_id);
|
|
|
|
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 {
|
|
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(request->which_content == PB_Main_app_button_press_request_tag);
|
|
|
|
RpcAppSystem* rpc_app = context;
|
|
furi_assert(rpc_app);
|
|
|
|
if(rpc_app->callback) {
|
|
FURI_LOG_D(TAG, "ButtonPress");
|
|
|
|
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 {
|
|
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);
|
|
|
|
RpcAppSystem* rpc_app = context;
|
|
furi_assert(rpc_app);
|
|
|
|
if(rpc_app->callback) {
|
|
FURI_LOG_D(TAG, "ButtonRelease");
|
|
|
|
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 {
|
|
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);
|
|
|
|
RpcAppSystem* rpc_app = context;
|
|
furi_assert(rpc_app);
|
|
|
|
PB_Main* response = malloc(sizeof(PB_Main));
|
|
|
|
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(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);
|
|
|
|
RpcAppSystem* rpc_app = context;
|
|
furi_assert(rpc_app);
|
|
|
|
if(rpc_app->callback) {
|
|
FURI_LOG_D(TAG, "DataExchange");
|
|
|
|
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 {
|
|
rpc_system_app_send_error_response(
|
|
rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, "DataExchange");
|
|
}
|
|
}
|
|
|
|
void rpc_system_app_send_started(RpcAppSystem* rpc_app) {
|
|
furi_assert(rpc_app);
|
|
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);
|
|
rpc_system_app_send_state_response(rpc_app, PB_App_AppState_APP_CLOSED, "SendExit");
|
|
}
|
|
|
|
void rpc_system_app_confirm(RpcAppSystem* rpc_app, bool result) {
|
|
furi_assert(rpc_app);
|
|
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->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);
|
|
rpc_app->error_code = error_code;
|
|
}
|
|
|
|
void rpc_system_app_set_error_text(RpcAppSystem* rpc_app, const char* error_text) {
|
|
furi_assert(rpc_app);
|
|
|
|
if(rpc_app->error_text) {
|
|
free(rpc_app->error_text);
|
|
}
|
|
|
|
rpc_app->error_text = error_text ? strdup(error_text) : NULL;
|
|
}
|
|
|
|
void rpc_system_app_error_reset(RpcAppSystem* rpc_app) {
|
|
furi_assert(rpc_app);
|
|
|
|
rpc_system_app_set_error_code(rpc_app, 0);
|
|
rpc_system_app_set_error_text(rpc_app, NULL);
|
|
}
|
|
|
|
void rpc_system_app_exchange_data(RpcAppSystem* rpc_app, const uint8_t* data, size_t data_size) {
|
|
furi_assert(rpc_app);
|
|
|
|
PB_Main* request = malloc(sizeof(PB_Main));
|
|
|
|
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));
|
|
content->data->size = data_size;
|
|
memcpy(content->data->bytes, data, data_size);
|
|
} else {
|
|
content->data = NULL;
|
|
}
|
|
|
|
rpc_send_and_release(rpc_app->session, request);
|
|
|
|
free(request);
|
|
}
|
|
|
|
void* rpc_system_app_alloc(RpcSession* session) {
|
|
furi_assert(session);
|
|
|
|
RpcAppSystem* rpc_app = malloc(sizeof(RpcAppSystem));
|
|
rpc_app->session = session;
|
|
|
|
RpcHandler rpc_handler = {
|
|
.message_handler = NULL,
|
|
.decode_submessage = NULL,
|
|
.context = rpc_app,
|
|
};
|
|
|
|
rpc_handler.message_handler = rpc_system_app_start_process;
|
|
rpc_add_handler(session, PB_Main_app_start_request_tag, &rpc_handler);
|
|
|
|
rpc_handler.message_handler = rpc_system_app_lock_status_process;
|
|
rpc_add_handler(session, PB_Main_app_lock_status_request_tag, &rpc_handler);
|
|
|
|
rpc_handler.message_handler = rpc_system_app_exit_request;
|
|
rpc_add_handler(session, PB_Main_app_exit_request_tag, &rpc_handler);
|
|
|
|
rpc_handler.message_handler = rpc_system_app_load_file;
|
|
rpc_add_handler(session, PB_Main_app_load_file_request_tag, &rpc_handler);
|
|
|
|
rpc_handler.message_handler = rpc_system_app_button_press;
|
|
rpc_add_handler(session, PB_Main_app_button_press_request_tag, &rpc_handler);
|
|
|
|
rpc_handler.message_handler = rpc_system_app_button_release;
|
|
rpc_add_handler(session, PB_Main_app_button_release_request_tag, &rpc_handler);
|
|
|
|
rpc_handler.message_handler = rpc_system_app_get_error_process;
|
|
rpc_add_handler(session, PB_Main_app_get_error_request_tag, &rpc_handler);
|
|
|
|
rpc_handler.message_handler = rpc_system_app_data_exchange_process;
|
|
rpc_add_handler(session, PB_Main_app_data_exchange_request_tag, &rpc_handler);
|
|
|
|
return rpc_app;
|
|
}
|
|
|
|
void rpc_system_app_free(void* context) {
|
|
RpcAppSystem* rpc_app = context;
|
|
furi_assert(rpc_app);
|
|
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->callback) {
|
|
furi_delay_tick(1);
|
|
}
|
|
|
|
free(rpc_app);
|
|
}
|