rpc_app.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. #include "flipper.pb.h"
  2. #include <core/record.h>
  3. #include "rpc_i.h"
  4. #include <furi.h>
  5. #include <loader/loader.h>
  6. #include "rpc_app.h"
  7. #define TAG "RpcSystemApp"
  8. struct RpcAppSystem {
  9. RpcSession* session;
  10. RpcAppSystemCallback app_callback;
  11. void* app_context;
  12. RpcAppSystemDataExchangeCallback data_exchange_callback;
  13. void* data_exchange_context;
  14. PB_Main* state_msg;
  15. PB_Main* error_msg;
  16. uint32_t last_id;
  17. char* last_data;
  18. };
  19. #define RPC_SYSTEM_APP_TEMP_ARGS_SIZE 16
  20. static void rpc_system_app_start_process(const PB_Main* request, void* context) {
  21. furi_assert(request);
  22. furi_assert(context);
  23. furi_assert(request->which_content == PB_Main_app_start_request_tag);
  24. RpcAppSystem* rpc_app = context;
  25. RpcSession* session = rpc_app->session;
  26. rpc_system_app_error_reset(rpc_app);
  27. furi_assert(session);
  28. char args_temp[RPC_SYSTEM_APP_TEMP_ARGS_SIZE];
  29. furi_assert(!rpc_app->last_id);
  30. furi_assert(!rpc_app->last_data);
  31. FURI_LOG_D(TAG, "StartProcess: id %lu", request->command_id);
  32. PB_CommandStatus result;
  33. Loader* loader = furi_record_open(RECORD_LOADER);
  34. const char* app_name = request->content.app_start_request.name;
  35. if(app_name) {
  36. const char* app_args = request->content.app_start_request.args;
  37. if(app_args && strcmp(app_args, "RPC") == 0) {
  38. // If app is being started in RPC mode - pass RPC context via args string
  39. snprintf(args_temp, RPC_SYSTEM_APP_TEMP_ARGS_SIZE, "RPC %08lX", (uint32_t)rpc_app);
  40. app_args = args_temp;
  41. }
  42. LoaderStatus status = loader_start(loader, app_name, app_args);
  43. if(status == LoaderStatusErrorAppStarted) {
  44. result = PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED;
  45. } else if(status == LoaderStatusErrorInternal) {
  46. result = PB_CommandStatus_ERROR_APP_CANT_START;
  47. } else if(status == LoaderStatusErrorUnknownApp) {
  48. result = PB_CommandStatus_ERROR_INVALID_PARAMETERS;
  49. } else if(status == LoaderStatusOk) {
  50. result = PB_CommandStatus_OK;
  51. } else {
  52. furi_crash(NULL);
  53. }
  54. } else {
  55. result = PB_CommandStatus_ERROR_INVALID_PARAMETERS;
  56. }
  57. furi_record_close(RECORD_LOADER);
  58. FURI_LOG_D(TAG, "StartProcess: response id %lu, result %d", request->command_id, result);
  59. rpc_send_and_release_empty(session, request->command_id, result);
  60. }
  61. static void rpc_system_app_lock_status_process(const PB_Main* request, void* context) {
  62. furi_assert(request);
  63. furi_assert(context);
  64. furi_assert(request->which_content == PB_Main_app_lock_status_request_tag);
  65. RpcAppSystem* rpc_app = context;
  66. rpc_system_app_error_reset(rpc_app);
  67. RpcSession* session = rpc_app->session;
  68. furi_assert(session);
  69. FURI_LOG_D(TAG, "LockStatus");
  70. Loader* loader = furi_record_open(RECORD_LOADER);
  71. PB_Main response = {
  72. .has_next = false,
  73. .command_status = PB_CommandStatus_OK,
  74. .command_id = request->command_id,
  75. .which_content = PB_Main_app_lock_status_response_tag,
  76. };
  77. response.content.app_lock_status_response.locked = loader_is_locked(loader);
  78. furi_record_close(RECORD_LOADER);
  79. FURI_LOG_D(TAG, "LockStatus: response");
  80. rpc_send_and_release(session, &response);
  81. pb_release(&PB_Main_msg, &response);
  82. }
  83. static void rpc_system_app_exit_request(const PB_Main* request, void* context) {
  84. furi_assert(request);
  85. furi_assert(context);
  86. furi_assert(request->which_content == PB_Main_app_exit_request_tag);
  87. RpcAppSystem* rpc_app = context;
  88. rpc_system_app_error_reset(rpc_app);
  89. RpcSession* session = rpc_app->session;
  90. furi_assert(session);
  91. PB_CommandStatus status;
  92. if(rpc_app->app_callback) {
  93. FURI_LOG_D(TAG, "ExitRequest: id %lu", request->command_id);
  94. furi_assert(!rpc_app->last_id);
  95. furi_assert(!rpc_app->last_data);
  96. rpc_app->last_id = request->command_id;
  97. rpc_app->app_callback(RpcAppEventAppExit, rpc_app->app_context);
  98. } else {
  99. status = PB_CommandStatus_ERROR_APP_NOT_RUNNING;
  100. FURI_LOG_E(
  101. TAG, "ExitRequest: APP_NOT_RUNNING, id %lu, status: %d", request->command_id, status);
  102. rpc_send_and_release_empty(session, request->command_id, status);
  103. }
  104. }
  105. static void rpc_system_app_load_file(const PB_Main* request, void* context) {
  106. furi_assert(request);
  107. furi_assert(context);
  108. furi_assert(request->which_content == PB_Main_app_load_file_request_tag);
  109. RpcAppSystem* rpc_app = context;
  110. rpc_system_app_error_reset(rpc_app);
  111. RpcSession* session = rpc_app->session;
  112. furi_assert(session);
  113. PB_CommandStatus status;
  114. if(rpc_app->app_callback) {
  115. FURI_LOG_D(TAG, "LoadFile: id %lu", request->command_id);
  116. furi_assert(!rpc_app->last_id);
  117. furi_assert(!rpc_app->last_data);
  118. rpc_app->last_id = request->command_id;
  119. rpc_app->last_data = strdup(request->content.app_load_file_request.path);
  120. rpc_app->app_callback(RpcAppEventLoadFile, rpc_app->app_context);
  121. } else {
  122. status = PB_CommandStatus_ERROR_APP_NOT_RUNNING;
  123. FURI_LOG_E(
  124. TAG, "LoadFile: APP_NOT_RUNNING, id %lu, status: %d", request->command_id, status);
  125. rpc_send_and_release_empty(session, request->command_id, status);
  126. }
  127. }
  128. static void rpc_system_app_button_press(const PB_Main* request, void* context) {
  129. furi_assert(request);
  130. furi_assert(context);
  131. furi_assert(request->which_content == PB_Main_app_button_press_request_tag);
  132. RpcAppSystem* rpc_app = context;
  133. rpc_system_app_error_reset(rpc_app);
  134. RpcSession* session = rpc_app->session;
  135. furi_assert(session);
  136. PB_CommandStatus status;
  137. if(rpc_app->app_callback) {
  138. FURI_LOG_D(TAG, "ButtonPress");
  139. furi_assert(!rpc_app->last_id);
  140. furi_assert(!rpc_app->last_data);
  141. rpc_app->last_id = request->command_id;
  142. rpc_app->last_data = strdup(request->content.app_button_press_request.args);
  143. rpc_app->app_callback(RpcAppEventButtonPress, rpc_app->app_context);
  144. } else {
  145. status = PB_CommandStatus_ERROR_APP_NOT_RUNNING;
  146. FURI_LOG_E(
  147. TAG, "ButtonPress: APP_NOT_RUNNING, id %lu, status: %d", request->command_id, status);
  148. rpc_send_and_release_empty(session, request->command_id, status);
  149. }
  150. }
  151. static void rpc_system_app_button_release(const PB_Main* request, void* context) {
  152. furi_assert(request);
  153. furi_assert(request->which_content == PB_Main_app_button_release_request_tag);
  154. furi_assert(context);
  155. RpcAppSystem* rpc_app = context;
  156. rpc_system_app_error_reset(rpc_app);
  157. RpcSession* session = rpc_app->session;
  158. furi_assert(session);
  159. PB_CommandStatus status;
  160. if(rpc_app->app_callback) {
  161. FURI_LOG_D(TAG, "ButtonRelease");
  162. furi_assert(!rpc_app->last_id);
  163. furi_assert(!rpc_app->last_data);
  164. rpc_app->last_id = request->command_id;
  165. rpc_app->app_callback(RpcAppEventButtonRelease, rpc_app->app_context);
  166. } else {
  167. status = PB_CommandStatus_ERROR_APP_NOT_RUNNING;
  168. FURI_LOG_E(
  169. TAG, "ButtonRelease: APP_NOT_RUNNING, id %lu, status: %d", request->command_id, status);
  170. rpc_send_and_release_empty(session, request->command_id, status);
  171. }
  172. }
  173. static void rpc_system_app_get_error_process(const PB_Main* request, void* context) {
  174. furi_assert(request);
  175. furi_assert(request->which_content == PB_Main_app_get_error_request_tag);
  176. furi_assert(context);
  177. RpcAppSystem* rpc_app = context;
  178. RpcSession* session = rpc_app->session;
  179. furi_assert(session);
  180. rpc_app->error_msg->command_id = request->command_id;
  181. FURI_LOG_D(TAG, "GetError");
  182. rpc_send(session, rpc_app->error_msg);
  183. }
  184. static void rpc_system_app_data_exchange_process(const PB_Main* request, void* context) {
  185. furi_assert(request);
  186. furi_assert(request->which_content == PB_Main_app_data_exchange_request_tag);
  187. furi_assert(context);
  188. RpcAppSystem* rpc_app = context;
  189. rpc_system_app_error_reset(rpc_app);
  190. RpcSession* session = rpc_app->session;
  191. furi_assert(session);
  192. PB_CommandStatus command_status;
  193. pb_bytes_array_t* data = request->content.app_data_exchange_request.data;
  194. if(rpc_app->data_exchange_callback) {
  195. uint8_t* data_bytes = NULL;
  196. size_t data_size = 0;
  197. if(data) {
  198. data_bytes = data->bytes;
  199. data_size = data->size;
  200. }
  201. rpc_app->data_exchange_callback(data_bytes, data_size, rpc_app->data_exchange_context);
  202. command_status = PB_CommandStatus_OK;
  203. } else {
  204. command_status = PB_CommandStatus_ERROR_APP_CMD_ERROR;
  205. }
  206. FURI_LOG_D(TAG, "DataExchange");
  207. rpc_send_and_release_empty(session, request->command_id, command_status);
  208. }
  209. void rpc_system_app_send_started(RpcAppSystem* rpc_app) {
  210. furi_assert(rpc_app);
  211. RpcSession* session = rpc_app->session;
  212. furi_assert(session);
  213. rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_STARTED;
  214. FURI_LOG_D(TAG, "SendStarted");
  215. rpc_send(session, rpc_app->state_msg);
  216. }
  217. void rpc_system_app_send_exited(RpcAppSystem* rpc_app) {
  218. furi_assert(rpc_app);
  219. RpcSession* session = rpc_app->session;
  220. furi_assert(session);
  221. rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_CLOSED;
  222. FURI_LOG_D(TAG, "SendExit");
  223. rpc_send(session, rpc_app->state_msg);
  224. }
  225. const char* rpc_system_app_get_data(RpcAppSystem* rpc_app) {
  226. furi_assert(rpc_app);
  227. furi_assert(rpc_app->last_data);
  228. return rpc_app->last_data;
  229. }
  230. void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result) {
  231. furi_assert(rpc_app);
  232. RpcSession* session = rpc_app->session;
  233. furi_assert(session);
  234. furi_assert(rpc_app->last_id);
  235. PB_CommandStatus status = result ? PB_CommandStatus_OK : PB_CommandStatus_ERROR_APP_CMD_ERROR;
  236. uint32_t last_id = 0;
  237. switch(event) {
  238. case RpcAppEventAppExit:
  239. case RpcAppEventLoadFile:
  240. case RpcAppEventButtonPress:
  241. case RpcAppEventButtonRelease:
  242. last_id = rpc_app->last_id;
  243. rpc_app->last_id = 0;
  244. if(rpc_app->last_data) {
  245. free(rpc_app->last_data);
  246. rpc_app->last_data = NULL;
  247. }
  248. FURI_LOG_D(TAG, "AppConfirm: event %d last_id %lu status %d", event, last_id, status);
  249. rpc_send_and_release_empty(session, last_id, status);
  250. break;
  251. default:
  252. furi_crash("RPC App state programming Error");
  253. break;
  254. }
  255. }
  256. void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx) {
  257. furi_assert(rpc_app);
  258. rpc_app->app_callback = callback;
  259. rpc_app->app_context = ctx;
  260. }
  261. void rpc_system_app_set_error_code(RpcAppSystem* rpc_app, uint32_t error_code) {
  262. furi_assert(rpc_app);
  263. PB_App_GetErrorResponse* content = &rpc_app->error_msg->content.app_get_error_response;
  264. content->code = error_code;
  265. }
  266. void rpc_system_app_set_error_text(RpcAppSystem* rpc_app, const char* error_text) {
  267. furi_assert(rpc_app);
  268. PB_App_GetErrorResponse* content = &rpc_app->error_msg->content.app_get_error_response;
  269. if(content->text) {
  270. free(content->text);
  271. }
  272. content->text = error_text ? strdup(error_text) : NULL;
  273. }
  274. void rpc_system_app_error_reset(RpcAppSystem* rpc_app) {
  275. furi_assert(rpc_app);
  276. rpc_system_app_set_error_code(rpc_app, 0);
  277. rpc_system_app_set_error_text(rpc_app, NULL);
  278. }
  279. void rpc_system_app_set_data_exchange_callback(
  280. RpcAppSystem* rpc_app,
  281. RpcAppSystemDataExchangeCallback callback,
  282. void* ctx) {
  283. furi_assert(rpc_app);
  284. rpc_app->data_exchange_callback = callback;
  285. rpc_app->data_exchange_context = ctx;
  286. }
  287. void rpc_system_app_exchange_data(RpcAppSystem* rpc_app, const uint8_t* data, size_t data_size) {
  288. furi_assert(rpc_app);
  289. RpcSession* session = rpc_app->session;
  290. furi_assert(session);
  291. PB_Main message = {
  292. .command_id = 0,
  293. .command_status = PB_CommandStatus_OK,
  294. .has_next = false,
  295. .which_content = PB_Main_app_data_exchange_request_tag,
  296. };
  297. PB_App_DataExchangeRequest* content = &message.content.app_data_exchange_request;
  298. if(data && data_size) {
  299. content->data = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(data_size));
  300. content->data->size = data_size;
  301. memcpy(content->data->bytes, data, data_size);
  302. } else {
  303. content->data = NULL;
  304. }
  305. rpc_send_and_release(session, &message);
  306. }
  307. void* rpc_system_app_alloc(RpcSession* session) {
  308. furi_assert(session);
  309. RpcAppSystem* rpc_app = malloc(sizeof(RpcAppSystem));
  310. rpc_app->session = session;
  311. // App exit message
  312. rpc_app->state_msg = malloc(sizeof(PB_Main));
  313. rpc_app->state_msg->which_content = PB_Main_app_state_response_tag;
  314. rpc_app->state_msg->command_status = PB_CommandStatus_OK;
  315. // App error message
  316. rpc_app->error_msg = malloc(sizeof(PB_Main));
  317. rpc_app->error_msg->which_content = PB_Main_app_get_error_response_tag;
  318. rpc_app->error_msg->command_status = PB_CommandStatus_OK;
  319. rpc_app->error_msg->content.app_get_error_response.code = 0;
  320. rpc_app->error_msg->content.app_get_error_response.text = NULL;
  321. RpcHandler rpc_handler = {
  322. .message_handler = NULL,
  323. .decode_submessage = NULL,
  324. .context = rpc_app,
  325. };
  326. rpc_handler.message_handler = rpc_system_app_start_process;
  327. rpc_add_handler(session, PB_Main_app_start_request_tag, &rpc_handler);
  328. rpc_handler.message_handler = rpc_system_app_lock_status_process;
  329. rpc_add_handler(session, PB_Main_app_lock_status_request_tag, &rpc_handler);
  330. rpc_handler.message_handler = rpc_system_app_exit_request;
  331. rpc_add_handler(session, PB_Main_app_exit_request_tag, &rpc_handler);
  332. rpc_handler.message_handler = rpc_system_app_load_file;
  333. rpc_add_handler(session, PB_Main_app_load_file_request_tag, &rpc_handler);
  334. rpc_handler.message_handler = rpc_system_app_button_press;
  335. rpc_add_handler(session, PB_Main_app_button_press_request_tag, &rpc_handler);
  336. rpc_handler.message_handler = rpc_system_app_button_release;
  337. rpc_add_handler(session, PB_Main_app_button_release_request_tag, &rpc_handler);
  338. rpc_handler.message_handler = rpc_system_app_get_error_process;
  339. rpc_add_handler(session, PB_Main_app_get_error_request_tag, &rpc_handler);
  340. rpc_handler.message_handler = rpc_system_app_data_exchange_process;
  341. rpc_add_handler(session, PB_Main_app_data_exchange_request_tag, &rpc_handler);
  342. return rpc_app;
  343. }
  344. void rpc_system_app_free(void* context) {
  345. RpcAppSystem* rpc_app = context;
  346. furi_assert(rpc_app);
  347. RpcSession* session = rpc_app->session;
  348. furi_assert(session);
  349. if(rpc_app->app_callback) {
  350. rpc_app->app_callback(RpcAppEventSessionClose, rpc_app->app_context);
  351. }
  352. while(rpc_app->app_callback) {
  353. furi_delay_tick(1);
  354. }
  355. furi_assert(!rpc_app->data_exchange_callback);
  356. if(rpc_app->last_data) free(rpc_app->last_data);
  357. pb_release(&PB_Main_msg, rpc_app->error_msg);
  358. free(rpc_app->error_msg);
  359. free(rpc_app->state_msg);
  360. free(rpc_app);
  361. }