mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-13 09:08:35 -07:00
Merge branch 'ofw_dev' into nfcrefactoring
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
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);
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
response.content.app_lock_status_response.locked = loader_is_locked(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);
|
||||
furi_assert(rpc_app);
|
||||
|
||||
PB_CommandStatus status;
|
||||
|
||||
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);
|
||||
|
||||
PB_CommandStatus status;
|
||||
if(rpc_app->app_callback) {
|
||||
RpcAppSystem* rpc_app = context;
|
||||
furi_assert(rpc_app);
|
||||
|
||||
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);
|
||||
|
||||
PB_CommandStatus status;
|
||||
if(rpc_app->app_callback) {
|
||||
RpcAppSystem* rpc_app = context;
|
||||
furi_assert(rpc_app);
|
||||
|
||||
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);
|
||||
|
||||
rpc_app->error_msg->command_id = request->command_id;
|
||||
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(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);
|
||||
rpc_system_app_send_state_response(rpc_app, PB_App_AppState_APP_CLOSED, "SendExit");
|
||||
}
|
||||
|
||||
const char* rpc_system_app_get_data(RpcAppSystem* rpc_app) {
|
||||
void rpc_system_app_confirm(RpcAppSystem* rpc_app, bool result) {
|
||||
furi_assert(rpc_app);
|
||||
furi_assert(rpc_app->last_data);
|
||||
return rpc_app->last_data;
|
||||
}
|
||||
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);
|
||||
|
||||
void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result) {
|
||||
furi_assert(rpc_app);
|
||||
RpcSession* session = rpc_app->session;
|
||||
furi_assert(session);
|
||||
furi_assert(rpc_app->last_id);
|
||||
const uint32_t last_command_id = rpc_app->last_command_id;
|
||||
const RpcAppSystemEventType last_event_type = rpc_app->last_event_type;
|
||||
|
||||
PB_CommandStatus status = result ? PB_CommandStatus_OK : PB_CommandStatus_ERROR_APP_CMD_ERROR;
|
||||
rpc_app->last_command_id = 0;
|
||||
rpc_app->last_event_type = RpcAppEventTypeInvalid;
|
||||
|
||||
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;
|
||||
}
|
||||
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);
|
||||
furi_assert(rpc_app->session);
|
||||
|
||||
if(rpc_app->app_callback) {
|
||||
rpc_app->app_callback(RpcAppEventSessionClose, rpc_app->app_context);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user