Update On Thu Jan 29 20:06:00 CET 2026

This commit is contained in:
github-action[bot]
2026-01-29 20:06:00 +01:00
parent fb225df993
commit 00e2b04dfc
141 changed files with 2787 additions and 593 deletions
+1
View File
@@ -1255,3 +1255,4 @@ Update On Sun Jan 25 19:40:50 CET 2026
Update On Mon Jan 26 19:49:29 CET 2026
Update On Tue Jan 27 19:51:58 CET 2026
Update On Wed Jan 28 19:49:27 CET 2026
Update On Thu Jan 29 20:05:52 CET 2026
+1 -1
View File
@@ -38,7 +38,7 @@ require (
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141
github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443
github.com/metacubex/tls v0.1.0
github.com/metacubex/tls v0.1.2
github.com/metacubex/utls v1.8.4
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f
github.com/mroth/weightedrand/v2 v2.1.0
+2 -2
View File
@@ -141,8 +141,8 @@ github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141 h1:DK2l6m2Fc85H2Bhi
github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141/go.mod h1:/yI4OiGOSn0SURhZdJF3CbtPg3nwK700bG8TZLMBvAg=
github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443 h1:H6TnfM12tOoTizYE/qBHH3nEuibIelmHI+BVSxVJr8o=
github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
github.com/metacubex/tls v0.1.0 h1:1kjR/1q2uU1cZIwiHYEnWzS4L+0Cu1/X3yfIQ76BzNY=
github.com/metacubex/tls v0.1.0/go.mod h1:0XeVdL0cBw+8i5Hqy3lVeP9IyD/LFTq02ExvHM6rzEM=
github.com/metacubex/tls v0.1.2 h1:qM0ATVnPFts5kZNTb+TukdaJ3hIf1ZVgKnXJCGfBmGI=
github.com/metacubex/tls v0.1.2/go.mod h1:0XeVdL0cBw+8i5Hqy3lVeP9IyD/LFTq02ExvHM6rzEM=
github.com/metacubex/utls v1.8.4 h1:HmL9nUApDdWSkgUyodfwF6hSjtiwCGGdyhaSpEejKpg=
github.com/metacubex/utls v1.8.4/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f h1:FGBPRb1zUabhPhDrlKEjQ9lgIwQ6cHL4x8M9lrERhbk=
+2 -2
View File
@@ -2,7 +2,7 @@
"manifest_version": 1,
"latest": {
"mihomo": "v1.19.19",
"mihomo_alpha": "alpha-d18a14a",
"mihomo_alpha": "alpha-e45c896",
"clash_rs": "v0.9.4",
"clash_premium": "2023-09-05-gdcc8d87",
"clash_rs_alpha": "0.9.4-alpha+sha.001aa41"
@@ -69,5 +69,5 @@
"linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf"
}
},
"updated_at": "2026-01-27T22:22:01.219Z"
"updated_at": "2026-01-28T22:22:39.173Z"
}
+8 -14
View File
@@ -152,6 +152,13 @@ define U-Boot/rock-4se-rk3399
radxa_rock-4se
endef
define U-Boot/rock-4c-plus-rk3399
$(U-Boot/rk3399/Default)
NAME:=ROCK 4C+
BUILD_DEVICES:= \
radxa_rock-4c-plus
endef
define U-Boot/rock-pi-4-rk3399
$(U-Boot/rk3399/Default)
NAME:=Rock Pi 4
@@ -381,15 +388,6 @@ define U-Boot/generic-rk3576
friendlyarm_nanopi-r76s
endef
define U-Boot/nanopi-m5-rk3576
$(U-Boot/rk3576/Default)
NAME:=NanoPi M5
BUILD_DEVICES:= \
friendlyarm_nanopi-m5 \
friendlyarm_nanopi-r76s
IDB_PRE_BUILD:=1
endef
define U-Boot/rock-4d-rk3576
$(U-Boot/rk3576/Default)
NAME:=ROCK 4D
@@ -512,7 +510,6 @@ UBOOT_TARGETS := \
rock-3b-rk3568 \
station-p2-rk3568 \
generic-rk3576 \
nanopi-m5-rk3576 \
rock-4d-rk3576 \
sige5-rk3576 \
generic-rk3588 \
@@ -533,6 +530,7 @@ UBOOT_TARGETS := \
nanopc-t4-rk3399 \
nanopi-r4s-rk3399 \
nanopi-r4se-rk3399 \
rock-4c-plus-rk3399 \
rock-pi-4-rk3399 \
rock-4se-rk3399 \
rockpro64-rk3399 \
@@ -562,12 +560,8 @@ define Build/InstallDev
ifneq ($(USE_RKBIN),)
$(STAGING_DIR_IMAGE)/loaderimage --pack --uboot $(PKG_BUILD_DIR)/u-boot-dtb.bin $(PKG_BUILD_DIR)/uboot.img 0x800000
$(CP) $(PKG_BUILD_DIR)/uboot.img $(STAGING_DIR_IMAGE)/$(BUILD_VARIANT)-uboot.img
else
ifneq ($(IDB_PRE_BUILD),)
$(CP) ./files/$(BUILD_VARIANT)-idbloader.img $(STAGING_DIR_IMAGE)/$(BUILD_VARIANT)-idbloader.img
else
$(CP) $(PKG_BUILD_DIR)/idbloader.img $(STAGING_DIR_IMAGE)/$(BUILD_VARIANT)-idbloader.img
endif
$(CP) $(PKG_BUILD_DIR)/u-boot.itb $(STAGING_DIR_IMAGE)/$(BUILD_VARIANT)-u-boot.itb
endif
endef
@@ -0,0 +1,121 @@
From 6b152999908b526c73b2ce91ecf444bae892796e Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Wed, 29 Jan 2025 22:36:27 +0000
Subject: [PATCH 1/7] rockchip: mkimage: Split size_and_off and size_and_nimage
Split 32-bit size_and_off and size_and_nimage fields of the v2 image
format header into their own 16-bit size, offset and num_images fields.
Set num_images based on number of images passed by the datafile
parameter and size based on the offset to the hash field to fix using a
single init data file and no boot data file for the v2 image format.
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
tools/rkcommon.c | 44 ++++++++++++++++++++++++--------------------
1 file changed, 24 insertions(+), 20 deletions(-)
--- a/tools/rkcommon.c
+++ b/tools/rkcommon.c
@@ -34,15 +34,16 @@ enum hash_type {
/**
* struct image_entry
*
- * @size_and_off: [31:16]image size;[15:0]image offset
- * @address: default as 0xFFFFFFFF
+ * @offset: image offset (unit as 512 byte blocks)
+ * @size: image size (unit as 512 byte blocks)
+ * @address: load address (default as 0xFFFFFFFF)
* @flag: no use
* @counter: no use
* @hash: hash of image
- *
*/
struct image_entry {
- uint32_t size_and_off;
+ uint16_t offset;
+ uint16_t size;
uint32_t address;
uint32_t flag;
uint32_t counter;
@@ -56,16 +57,17 @@ struct image_entry {
* This is stored at SD card block 64 (where each block is 512 bytes)
*
* @magic: Magic (must be RK_MAGIC_V2)
- * @size_and_nimage: [31:16]number of images;[15:0]
- * offset to hash field of header(unit as 4Byte)
- * @boot_flag: [3:0]hash type(0:none,1:sha256,2:sha512)
- * @signature: hash or signature for header info
- *
+ * @size: offset to hash field of header (unit as 4 bytes)
+ * @num_images: number of images
+ * @boot_flag: [3:0] hash type (0:none, 1:sha256, 2:sha512)
+ * @images: images
+ * @hash: hash or signature for header info
*/
struct header0_info_v2 {
uint32_t magic;
uint8_t reserved[4];
- uint32_t size_and_nimage;
+ uint16_t size;
+ uint16_t num_images;
uint32_t boot_flag;
uint8_t reserved1[104];
struct image_entry images[4];
@@ -350,17 +352,18 @@ static void rkcommon_set_header0_v2(void
printf("Image Type: Rockchip %s boot image\n",
rkcommon_get_spl_hdr(params));
memset(buf, '\0', RK_INIT_OFFSET * RK_BLK_SIZE);
- hdr->magic = cpu_to_le32(RK_MAGIC_V2);
- hdr->size_and_nimage = cpu_to_le32((2 << 16) + 384);
+ hdr->magic = cpu_to_le32(RK_MAGIC_V2);
hdr->boot_flag = cpu_to_le32(HASH_SHA256);
sector_offset = 4;
image_size_array[0] = spl_params.init_size;
image_size_array[1] = spl_params.boot_size;
for (i = 0; i < 2; i++) {
+ if (!image_size_array[i])
+ break;
image_sector_count = image_size_array[i] / RK_BLK_SIZE;
- hdr->images[i].size_and_off = cpu_to_le32((image_sector_count
- << 16) + sector_offset);
+ hdr->images[i].offset = cpu_to_le16(sector_offset);
+ hdr->images[i].size = cpu_to_le16(image_sector_count);
hdr->images[i].address = 0xFFFFFFFF;
hdr->images[i].counter = cpu_to_le32(i + 1);
image_ptr = buf + sector_offset * RK_BLK_SIZE;
@@ -369,6 +372,8 @@ static void rkcommon_set_header0_v2(void
sector_offset = sector_offset + image_sector_count;
}
+ hdr->num_images = cpu_to_le16(i);
+ hdr->size = cpu_to_le16(offsetof(typeof(*hdr), hash) / sizeof(uint32_t));
do_sha256_hash(buf, (void *)hdr->hash - buf, hdr->hash);
}
@@ -515,10 +520,8 @@ void rkcommon_print_header(const void *b
return;
}
- init_size = header0_v2.images[0].size_and_off >> 16;
- init_size = init_size * RK_BLK_SIZE;
- boot_size = header0_v2.images[1].size_and_off >> 16;
- boot_size = boot_size * RK_BLK_SIZE;
+ init_size = le16_to_cpu(header0_v2.images[0].size) * RK_BLK_SIZE;
+ boot_size = le16_to_cpu(header0_v2.images[1].size) * RK_BLK_SIZE;
} else {
ret = rkcommon_parse_header(buf, &header0, &spl_info);
@@ -532,8 +535,9 @@ void rkcommon_print_header(const void *b
}
image_type = ret;
- init_size = header0.init_size * RK_BLK_SIZE;
- boot_size = header0.init_boot_size * RK_BLK_SIZE - init_size;
+ init_size = le16_to_cpu(header0.init_size) * RK_BLK_SIZE;
+ boot_size = le16_to_cpu(header0.init_boot_size) * RK_BLK_SIZE -
+ init_size;
printf("Image Type: Rockchip %s (%s) boot image\n",
spl_info->spl_hdr,
@@ -0,0 +1,108 @@
From beea345ad405bd236d15d53da1256eaa95014c7b Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Wed, 29 Jan 2025 22:36:28 +0000
Subject: [PATCH 2/7] rockchip: mkimage: Print image information for all
embedded images
The v2 image format can embed up to 4 data files compared to the two
init and boot data files using the older image format.
Add support for displaying more of the image header information that
exists in the v2 image format, e.g. image load address and flag.
Example for v2 image format:
> tools/mkimage -l rk3576_idblock_v1.09.107.img
Rockchip Boot Image (v2)
Image 1: 4096 @ 0x1000
- Load address: 0x3ffc0000
Image 2: 77824 @ 0x2000
- Load address: 0x3ff81000
Image 3: 262144 @ 0x15000
Example for older image format:
> tools/mkimage -l u-boot-rockchip.bin
Rockchip RK32 (SD/MMC) Boot Image
Init Data: 20480 @ 0x800
Boot Data: 112640 @ 0x5800
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
tools/rkcommon.c | 41 +++++++++++++++++++++++++++++++----------
1 file changed, 31 insertions(+), 10 deletions(-)
--- a/tools/rkcommon.c
+++ b/tools/rkcommon.c
@@ -349,8 +349,6 @@ static void rkcommon_set_header0_v2(void
uint8_t *image_ptr = NULL;
int i;
- printf("Image Type: Rockchip %s boot image\n",
- rkcommon_get_spl_hdr(params));
memset(buf, '\0', RK_INIT_OFFSET * RK_BLK_SIZE);
hdr->magic = cpu_to_le32(RK_MAGIC_V2);
hdr->boot_flag = cpu_to_le32(HASH_SHA256);
@@ -504,6 +502,29 @@ int rkcommon_verify_header(unsigned char
return -ENOENT;
}
+static void rkcommon_print_header_v2(const struct header0_info_v2 *hdr)
+{
+ uint32_t val;
+ int i;
+
+ printf("Rockchip Boot Image (v2)\n");
+
+ for (i = 0; i < le16_to_cpu(hdr->num_images); i++) {
+ printf("Image %u: %u @ 0x%x\n",
+ le32_to_cpu(hdr->images[i].counter),
+ le16_to_cpu(hdr->images[i].size) * RK_BLK_SIZE,
+ le16_to_cpu(hdr->images[i].offset) * RK_BLK_SIZE);
+
+ val = le32_to_cpu(hdr->images[i].address);
+ if (val != 0xFFFFFFFF)
+ printf("- Load address: 0x%x\n", val);
+
+ val = le32_to_cpu(hdr->images[i].flag);
+ if (val)
+ printf("- Flag: 0x%x\n", val);
+ }
+}
+
void rkcommon_print_header(const void *buf, struct image_tool_params *params)
{
struct header0_info header0;
@@ -520,8 +541,7 @@ void rkcommon_print_header(const void *b
return;
}
- init_size = le16_to_cpu(header0_v2.images[0].size) * RK_BLK_SIZE;
- boot_size = le16_to_cpu(header0_v2.images[1].size) * RK_BLK_SIZE;
+ rkcommon_print_header_v2(&header0_v2);
} else {
ret = rkcommon_parse_header(buf, &header0, &spl_info);
@@ -539,15 +559,16 @@ void rkcommon_print_header(const void *b
boot_size = le16_to_cpu(header0.init_boot_size) * RK_BLK_SIZE -
init_size;
- printf("Image Type: Rockchip %s (%s) boot image\n",
- spl_info->spl_hdr,
+ printf("Rockchip %s (%s) Boot Image\n", spl_info->spl_hdr,
(image_type == IH_TYPE_RKSD) ? "SD/MMC" : "SPI");
- }
- printf("Init Data Size: %d bytes\n", init_size);
+ printf("Init Data: %d @ 0x%x\n", init_size,
+ le16_to_cpu(header0.init_offset) * RK_BLK_SIZE);
- if (boot_size != RK_MAX_BOOT_SIZE)
- printf("Boot Data Size: %d bytes\n", boot_size);
+ if (boot_size != RK_MAX_BOOT_SIZE)
+ printf("Boot Data: %d @ 0x%x\n", boot_size, init_size +
+ le16_to_cpu(header0.init_offset) * RK_BLK_SIZE);
+ }
}
void rkcommon_rc4_encode_spl(void *buf, unsigned int offset, unsigned int size)
@@ -0,0 +1,70 @@
From cad4d0d7d04f71e9b8cc7704254abe74e7b17b50 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Wed, 29 Jan 2025 22:36:29 +0000
Subject: [PATCH 3/7] rockchip: mkimage: Print boot0 and boot1 parameters
The v2 image format embeds boot0 and boot1 parameters, the vendor tool
boot_merger may write these parameters based on the rkboot miniall.ini
files.
E.g. a RK3576 boot image may contain a boot1 parameter that signals
BootROM or vendor blobs to use 1 GHz instead of the regular 24 MHz rate
for the high precision timer.
Add support for printing boot0 and boot1 parameters, e.g.:
> tools/mkimage -l rk3576_idblock_v1.09.107.img
Rockchip Boot Image (v2)
Boot1 2: 0x100
Image 1: 4096 @ 0x1000
- Load address: 0x3ffc0000
Image 2: 77824 @ 0x2000
- Load address: 0x3ff81000
Image 3: 262144 @ 0x15000
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
tools/rkcommon.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
--- a/tools/rkcommon.c
+++ b/tools/rkcommon.c
@@ -62,6 +62,8 @@ struct image_entry {
* @boot_flag: [3:0] hash type (0:none, 1:sha256, 2:sha512)
* @images: images
* @hash: hash or signature for header info
+ *
+ * Other fields are not used by U-Boot
*/
struct header0_info_v2 {
uint32_t magic;
@@ -69,7 +71,9 @@ struct header0_info_v2 {
uint16_t size;
uint16_t num_images;
uint32_t boot_flag;
- uint8_t reserved1[104];
+ uint8_t reserved1[32];
+ uint32_t boot0_param[10];
+ uint32_t boot1_param[8];
struct image_entry images[4];
uint8_t reserved2[1064];
uint8_t hash[512];
@@ -509,6 +513,18 @@ static void rkcommon_print_header_v2(con
printf("Rockchip Boot Image (v2)\n");
+ for (i = 0; i < ARRAY_SIZE(hdr->boot0_param); i++) {
+ val = le32_to_cpu(hdr->boot0_param[i]);
+ if (val)
+ printf("Boot0 %d: 0x%x\n", i, val);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(hdr->boot1_param); i++) {
+ val = le32_to_cpu(hdr->boot1_param[i]);
+ if (val)
+ printf("Boot1 %d: 0x%x\n", i, val);
+ }
+
for (i = 0; i < le16_to_cpu(hdr->num_images); i++) {
printf("Image %u: %u @ 0x%x\n",
le32_to_cpu(hdr->images[i].counter),
@@ -0,0 +1,208 @@
From 3df6b71b8226dfd34d587b01d0674a22ef4ca440 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Wed, 29 Jan 2025 22:36:30 +0000
Subject: [PATCH 4/7] rockchip: mkimage: Add option to change image offset
alignment
The vendor boot_merger tool support a ALIGN parameter that is used to
define offset alignment of the embedded images.
Vendor use this for RK3576 to change offset alignment from the common
2 KiB to 4 KiB, presumably it may have something to do with UFS.
Testing with eMMC has shown that using a 512-byte alignment also work.
Add support for overriding offset alignment in case this is needed for
e.g. RK3576 in the future.
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
tools/rkcommon.c | 75 +++++++++++++++++++++++++++++++-----------------
tools/rkcommon.h | 2 --
2 files changed, 49 insertions(+), 28 deletions(-)
--- a/tools/rkcommon.c
+++ b/tools/rkcommon.c
@@ -140,6 +140,7 @@ struct spl_info {
const uint32_t spl_size;
const bool spl_rc4;
const uint32_t header_ver;
+ const uint32_t align;
};
static struct spl_info spl_infos[] = {
@@ -199,14 +200,19 @@ static struct spl_info *rkcommon_get_spl
return NULL;
}
-static int rkcommon_get_aligned_size(struct image_tool_params *params,
- const char *fname)
+static bool rkcommon_is_header_v2(struct image_tool_params *params)
{
- int size;
+ struct spl_info *info = rkcommon_get_spl_info(params->imagename);
- size = imagetool_get_filesize(params, fname);
- if (size < 0)
- return -1;
+ return (info->header_ver == RK_HEADER_V2);
+}
+
+static int rkcommon_get_aligned_size(struct image_tool_params *params, int size)
+{
+ struct spl_info *info = rkcommon_get_spl_info(params->imagename);
+
+ if (info->align)
+ return ROUND(size, info->align * RK_BLK_SIZE);
/*
* Pad to a 2KB alignment, as required for init/boot size by the ROM
@@ -215,6 +221,27 @@ static int rkcommon_get_aligned_size(str
return ROUND(size, RK_SIZE_ALIGN);
}
+static int rkcommon_get_header_size(struct image_tool_params *params)
+{
+ int header_size = rkcommon_is_header_v2(params) ?
+ sizeof(struct header0_info_v2) :
+ sizeof(struct header0_info);
+
+ return rkcommon_get_aligned_size(params, header_size);
+}
+
+static int rkcommon_get_aligned_filesize(struct image_tool_params *params,
+ const char *fname)
+{
+ int size;
+
+ size = imagetool_get_filesize(params, fname);
+ if (size < 0)
+ return -1;
+
+ return rkcommon_get_aligned_size(params, size);
+}
+
int rkcommon_check_params(struct image_tool_params *params)
{
int i, size;
@@ -237,14 +264,14 @@ int rkcommon_check_params(struct image_t
spl_params.boot_file += 1;
}
- size = rkcommon_get_aligned_size(params, spl_params.init_file);
+ size = rkcommon_get_aligned_filesize(params, spl_params.init_file);
if (size < 0)
return EXIT_FAILURE;
spl_params.init_size = size;
/* Boot file is optional, and only for back-to-bootrom functionality. */
if (spl_params.boot_file) {
- size = rkcommon_get_aligned_size(params, spl_params.boot_file);
+ size = rkcommon_get_aligned_filesize(params, spl_params.boot_file);
if (size < 0)
return EXIT_FAILURE;
spl_params.boot_size = size;
@@ -301,13 +328,6 @@ bool rkcommon_need_rc4_spl(struct image_
return info->spl_rc4;
}
-static bool rkcommon_is_header_v2(struct image_tool_params *params)
-{
- struct spl_info *info = rkcommon_get_spl_info(params->imagename);
-
- return (info->header_ver == RK_HEADER_V2);
-}
-
static void do_sha256_hash(uint8_t *buf, uint32_t size, uint8_t *out)
{
sha256_context ctx;
@@ -320,12 +340,13 @@ static void do_sha256_hash(uint8_t *buf,
static void rkcommon_set_header0(void *buf, struct image_tool_params *params)
{
struct header0_info *hdr = buf;
- uint32_t init_boot_size;
+ uint32_t init_boot_size, init_offset;
- memset(buf, '\0', RK_INIT_OFFSET * RK_BLK_SIZE);
+ init_offset = rkcommon_get_header_size(params) / RK_BLK_SIZE;
+ memset(buf, '\0', init_offset * RK_BLK_SIZE);
hdr->magic = cpu_to_le32(RK_MAGIC);
hdr->disable_rc4 = cpu_to_le32(!rkcommon_need_rc4_spl(params));
- hdr->init_offset = cpu_to_le16(RK_INIT_OFFSET);
+ hdr->init_offset = cpu_to_le16(init_offset);
hdr->init_size = cpu_to_le16(spl_params.init_size / RK_BLK_SIZE);
/*
@@ -353,10 +374,10 @@ static void rkcommon_set_header0_v2(void
uint8_t *image_ptr = NULL;
int i;
- memset(buf, '\0', RK_INIT_OFFSET * RK_BLK_SIZE);
+ sector_offset = rkcommon_get_header_size(params) / RK_BLK_SIZE;
+ memset(buf, '\0', sector_offset * RK_BLK_SIZE);
hdr->magic = cpu_to_le32(RK_MAGIC_V2);
hdr->boot_flag = cpu_to_le32(HASH_SHA256);
- sector_offset = 4;
image_size_array[0] = spl_params.init_size;
image_size_array[1] = spl_params.boot_size;
@@ -382,11 +403,12 @@ static void rkcommon_set_header0_v2(void
void rkcommon_set_header(void *buf, struct stat *sbuf, int ifd,
struct image_tool_params *params)
{
- struct header1_info *hdr = buf + RK_SPL_HDR_START;
-
if (rkcommon_is_header_v2(params)) {
rkcommon_set_header0_v2(buf, params);
} else {
+ int header_size = rkcommon_get_header_size(params);
+ struct header1_info *hdr = buf + header_size;
+
rkcommon_set_header0(buf, params);
/* Set up the SPL name (i.e. copy spl_hdr over) */
@@ -394,12 +416,12 @@ void rkcommon_set_header(void *buf, str
memcpy(&hdr->magic, rkcommon_get_spl_hdr(params), RK_SPL_HDR_SIZE);
if (rkcommon_need_rc4_spl(params))
- rkcommon_rc4_encode_spl(buf, RK_SPL_HDR_START,
+ rkcommon_rc4_encode_spl(buf, header_size,
spl_params.init_size);
if (spl_params.boot_file) {
if (rkcommon_need_rc4_spl(params))
- rkcommon_rc4_encode_spl(buf + RK_SPL_HDR_START,
+ rkcommon_rc4_encode_spl(buf + header_size,
spl_params.init_size,
spl_params.boot_size);
}
@@ -624,7 +646,7 @@ int rkcommon_vrec_header(struct image_to
* 4 bytes of these images can safely be overwritten using the
* boot magic.
*/
- tparams->header_size = RK_SPL_HDR_START;
+ tparams->header_size = rkcommon_get_header_size(params);
/* Allocate, clear and install the header */
tparams->hdr = malloc(tparams->header_size);
@@ -642,7 +664,8 @@ int rkcommon_vrec_header(struct image_to
params->orig_file_size = tparams->header_size +
spl_params.init_size + spl_params.boot_size;
- params->file_size = ROUND(params->orig_file_size, RK_SIZE_ALIGN);
+ params->file_size = rkcommon_get_aligned_size(params,
+ params->orig_file_size);
/* Ignoring pad len, since we are using our own copy_image() */
return 0;
--- a/tools/rkcommon.h
+++ b/tools/rkcommon.h
@@ -10,9 +10,7 @@
enum {
RK_BLK_SIZE = 512,
RK_SIZE_ALIGN = 2048,
- RK_INIT_OFFSET = 4,
RK_MAX_BOOT_SIZE = 512 << 10,
- RK_SPL_HDR_START = RK_INIT_OFFSET * RK_BLK_SIZE,
RK_SPL_HDR_SIZE = 4,
};
@@ -0,0 +1,202 @@
From ed28c9e3d54cbf25dea55030c2f5a889ae3da915 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Wed, 29 Jan 2025 22:36:31 +0000
Subject: [PATCH 5/7] rockchip: mkimage: Add support for up to 4 input files
The v2 image format can support up to 4 embedded images that can be
loaded by the BootROM using the back-to-bootrom method.
Currently two input files can be passed in using the datafile parameter,
separated by a colon (":").
Extend the datafile parameter parsing to support up to 4 input files
separated by a colon (":") for use with the v2 image format.
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
tools/rkcommon.c | 93 +++++++++++++++++++++++-------------------------
1 file changed, 44 insertions(+), 49 deletions(-)
--- a/tools/rkcommon.c
+++ b/tools/rkcommon.c
@@ -166,17 +166,15 @@ static struct spl_info spl_infos[] = {
/**
* struct spl_params - spl params parsed in check_params()
*
- * @init_file: Init data file path
- * @init_size: Aligned size of init data in bytes
- * @boot_file: Boot data file path
- * @boot_size: Aligned size of boot data in bytes
+ * @file: image file path
+ * @size: aligned size of image in bytes
*/
struct spl_params {
- char *init_file;
- uint32_t init_size;
- char *boot_file;
- uint32_t boot_size;
+ struct {
+ char *file;
+ uint32_t size;
+ } images[4];
};
static struct spl_params spl_params = { 0 };
@@ -256,31 +254,32 @@ int rkcommon_check_params(struct image_t
if (!rkcommon_get_spl_info(params->imagename))
goto err_spl_info;
- spl_params.init_file = params->datafile;
+ spl_params.images[0].file = params->datafile;
+ for (i = 1; i < ARRAY_SIZE(spl_params.images); i++) {
+ spl_params.images[i].file =
+ strchr(spl_params.images[i - 1].file, ':');
+ if (!spl_params.images[i].file)
+ break;
- spl_params.boot_file = strchr(spl_params.init_file, ':');
- if (spl_params.boot_file) {
- *spl_params.boot_file = '\0';
- spl_params.boot_file += 1;
+ *spl_params.images[i].file = '\0';
+ spl_params.images[i].file += 1;
}
- size = rkcommon_get_aligned_filesize(params, spl_params.init_file);
- if (size < 0)
- return EXIT_FAILURE;
- spl_params.init_size = size;
+ for (i = 0; i < ARRAY_SIZE(spl_params.images); i++) {
+ if (!spl_params.images[i].file)
+ break;
- /* Boot file is optional, and only for back-to-bootrom functionality. */
- if (spl_params.boot_file) {
- size = rkcommon_get_aligned_filesize(params, spl_params.boot_file);
+ size = rkcommon_get_aligned_filesize(params,
+ spl_params.images[i].file);
if (size < 0)
return EXIT_FAILURE;
- spl_params.boot_size = size;
+ spl_params.images[i].size = size;
}
- if (spl_params.init_size > rkcommon_get_spl_size(params)) {
+ if (spl_params.images[0].size > rkcommon_get_spl_size(params)) {
fprintf(stderr,
"Error: SPL image is too large (size %#x than %#x)\n",
- spl_params.init_size, rkcommon_get_spl_size(params));
+ spl_params.images[0].size, rkcommon_get_spl_size(params));
return EXIT_FAILURE;
}
@@ -347,7 +346,7 @@ static void rkcommon_set_header0(void *b
hdr->magic = cpu_to_le32(RK_MAGIC);
hdr->disable_rc4 = cpu_to_le32(!rkcommon_need_rc4_spl(params));
hdr->init_offset = cpu_to_le16(init_offset);
- hdr->init_size = cpu_to_le16(spl_params.init_size / RK_BLK_SIZE);
+ hdr->init_size = cpu_to_le16(spl_params.images[0].size / RK_BLK_SIZE);
/*
* init_boot_size needs to be set, as it is read by the BootROM
@@ -357,10 +356,11 @@ static void rkcommon_set_header0(void *b
* see https://lists.denx.de/pipermail/u-boot/2017-May/293267.html
* for a more detailed explanation by Andy Yan
*/
- if (spl_params.boot_file)
- init_boot_size = spl_params.init_size + spl_params.boot_size;
+ if (spl_params.images[1].file)
+ init_boot_size = spl_params.images[0].size +
+ spl_params.images[1].size;
else
- init_boot_size = spl_params.init_size + RK_MAX_BOOT_SIZE;
+ init_boot_size = spl_params.images[0].size + RK_MAX_BOOT_SIZE;
hdr->init_boot_size = cpu_to_le16(init_boot_size / RK_BLK_SIZE);
rc4_encode(buf, RK_BLK_SIZE, rc4_key);
@@ -370,7 +370,6 @@ static void rkcommon_set_header0_v2(void
{
struct header0_info_v2 *hdr = buf;
uint32_t sector_offset, image_sector_count;
- uint32_t image_size_array[2];
uint8_t *image_ptr = NULL;
int i;
@@ -378,19 +377,17 @@ static void rkcommon_set_header0_v2(void
memset(buf, '\0', sector_offset * RK_BLK_SIZE);
hdr->magic = cpu_to_le32(RK_MAGIC_V2);
hdr->boot_flag = cpu_to_le32(HASH_SHA256);
- image_size_array[0] = spl_params.init_size;
- image_size_array[1] = spl_params.boot_size;
- for (i = 0; i < 2; i++) {
- if (!image_size_array[i])
+ for (i = 0; i < ARRAY_SIZE(spl_params.images); i++) {
+ if (!spl_params.images[i].size)
break;
- image_sector_count = image_size_array[i] / RK_BLK_SIZE;
+ image_sector_count = spl_params.images[i].size / RK_BLK_SIZE;
hdr->images[i].offset = cpu_to_le16(sector_offset);
hdr->images[i].size = cpu_to_le16(image_sector_count);
hdr->images[i].address = 0xFFFFFFFF;
hdr->images[i].counter = cpu_to_le32(i + 1);
image_ptr = buf + sector_offset * RK_BLK_SIZE;
- do_sha256_hash(image_ptr, image_size_array[i],
+ do_sha256_hash(image_ptr, spl_params.images[i].size,
hdr->images[i].hash);
sector_offset = sector_offset + image_sector_count;
}
@@ -417,13 +414,13 @@ void rkcommon_set_header(void *buf, str
if (rkcommon_need_rc4_spl(params))
rkcommon_rc4_encode_spl(buf, header_size,
- spl_params.init_size);
+ spl_params.images[0].size);
- if (spl_params.boot_file) {
+ if (spl_params.images[1].file) {
if (rkcommon_need_rc4_spl(params))
rkcommon_rc4_encode_spl(buf + header_size,
- spl_params.init_size,
- spl_params.boot_size);
+ spl_params.images[0].size,
+ spl_params.images[1].size);
}
}
}
@@ -661,8 +658,9 @@ int rkcommon_vrec_header(struct image_to
* We need to store the original file-size (i.e. before padding), as
* imagetool does not set this during its adjustment of file_size.
*/
- params->orig_file_size = tparams->header_size +
- spl_params.init_size + spl_params.boot_size;
+ params->orig_file_size = tparams->header_size;
+ for (int i = 0; i < ARRAY_SIZE(spl_params.images); i++)
+ params->orig_file_size += spl_params.images[i].size;
params->file_size = rkcommon_get_aligned_size(params,
params->orig_file_size);
@@ -749,16 +747,13 @@ err_close:
int rockchip_copy_image(int ifd, struct image_tool_params *params)
{
- int ret;
-
- ret = copy_file(params, ifd, spl_params.init_file,
- spl_params.init_size);
- if (ret)
- return ret;
+ int i, ret;
- if (spl_params.boot_file) {
- ret = copy_file(params, ifd, spl_params.boot_file,
- spl_params.boot_size);
+ for (i = 0; i < ARRAY_SIZE(spl_params.images); i++) {
+ if (!spl_params.images[i].size)
+ break;
+ ret = copy_file(params, ifd, spl_params.images[i].file,
+ spl_params.images[i].size);
if (ret)
return ret;
}
@@ -0,0 +1,49 @@
From 54d7a09689d795110d93e64ee630e121b20bc269 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Wed, 29 Jan 2025 22:36:32 +0000
Subject: [PATCH 6/7] rockchip: mkimage: Add option for image load address and
flag
The v2 image format supports defining a load address and flag for each
embedded image.
Add initial support for writing the image load address and flag to the
v2 image format header.
This may later be used for RK3576 to embed a minimal initial image that
if required to fix booting from SD-card due to a BootROM issue.
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
tools/rkcommon.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
--- a/tools/rkcommon.c
+++ b/tools/rkcommon.c
@@ -168,12 +168,16 @@ static struct spl_info spl_infos[] = {
*
* @file: image file path
* @size: aligned size of image in bytes
+ * @address: image load address
+ * @flag: no use
*/
struct spl_params {
struct {
char *file;
uint32_t size;
+ uint32_t address;
+ uint32_t flag;
} images[4];
};
@@ -384,7 +388,8 @@ static void rkcommon_set_header0_v2(void
image_sector_count = spl_params.images[i].size / RK_BLK_SIZE;
hdr->images[i].offset = cpu_to_le16(sector_offset);
hdr->images[i].size = cpu_to_le16(image_sector_count);
- hdr->images[i].address = 0xFFFFFFFF;
+ hdr->images[i].address = spl_params.images[i].address ?: 0xFFFFFFFF;
+ hdr->images[i].flag = spl_params.images[i].flag;
hdr->images[i].counter = cpu_to_le32(i + 1);
image_ptr = buf + sector_offset * RK_BLK_SIZE;
do_sha256_hash(image_ptr, spl_params.images[i].size,
@@ -0,0 +1,112 @@
From d43031c4045ef8aaf29c467b910207e721dabce0 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Tue, 28 Jan 2025 01:30:12 +0000
Subject: [PATCH 7/7] WIP: rockchip: mkimage: Add rk3576 align and sd-card
workaround
The BootROM on RK3576 has an issue loading boot images from an SD-card.
This issue can be worked around by injecting an initial boot image
before TPL that:
writel(0x3ffff800, 0x3ff803b0)
Prepend an image containing binary code that does this and return to
BootROM to load next image, TPL.
TODO: embed the binary code into rkcommon.c
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
rk3576-boost.bin | Bin 0 -> 24 bytes
rk3576-boost.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++
tools/rkcommon.c | 18 ++++++++++++++++-
3 files changed, 66 insertions(+), 1 deletion(-)
create mode 100644 rk3576-boost.bin
create mode 100644 rk3576-boost.c
--- /dev/null
+++ b/rk3576-boost.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright Contributors to the U-Boot project.
+
+/*
+ * Generate assembly code for the c code in this file:
+ * aarch64-linux-gnu-gcc -nostdlib -ffreestanding -Os -S -o rk3576-boost.S rk3576-boost.c
+ *
+ * Compile assembly code and extract the AArch64 binary code:
+ * aarch64-linux-gnu-as -o rk3576-boost.o rk3576-boost.S
+ * aarch64-linux-gnu-objcopy -O binary -j .text rk3576-boost.o rk3576-boost.bin
+ */
+
+#include <stdint.h>
+
+#define SYS_SRAM_BASE 0x3ff80000
+#define OFFSET 0x03b0
+
+int _start(void)
+{
+ uint32_t *sram = (void*)(SYS_SRAM_BASE + OFFSET);
+
+ /* set unknown value in sram to fix boot from sdmmc */
+ *(sram) = 0x3ffff800;
+
+ return 0;
+}
+
+/*
+ .arch armv8-a
+ .file "rk3576-boost.c"
+ .text
+ .align 2
+ .global _start
+ .type _start, %function
+_start:
+.LFB0:
+ .cfi_startproc
+ mov x0, 944
+ mov w1, 1073739776
+ movk x0, 0x3ff8, lsl 16
+ str w1, [x0]
+ mov w0, 0
+ ret
+ .cfi_endproc
+.LFE0:
+ .size _start, .-_start
+ .ident "GCC: (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0"
+ .section .note.GNU-stack,"",@progbits
+*/
--- a/tools/rkcommon.c
+++ b/tools/rkcommon.c
@@ -159,7 +159,7 @@ static struct spl_info spl_infos[] = {
{ "rv1126", "110B", 0x10000 - 0x1000, false, RK_HEADER_V1 },
{ "rk3528", "RK35", 0x10000 - 0x1000, false, RK_HEADER_V2 },
{ "rk3568", "RK35", 0x10000 - 0x1000, false, RK_HEADER_V2 },
- { "rk3576", "RK35", 0x80000 - 0x1000, false, RK_HEADER_V2 },
+ { "rk3576", "RK35", 0x80000 - 0x1000, false, RK_HEADER_V2, 8 },
{ "rk3588", "RK35", 0x100000 - 0x1000, false, RK_HEADER_V2 },
};
@@ -287,6 +287,22 @@ int rkcommon_check_params(struct image_t
return EXIT_FAILURE;
}
+ if (!strcmp(params->imagename, "rk3576")) {
+ size = rkcommon_get_aligned_filesize(params, "rk3576-boost.bin");
+ if (size < 0)
+ return EXIT_SUCCESS;
+
+ for (i = ARRAY_SIZE(spl_params.images) - 1; i > 0; i--) {
+ spl_params.images[i] = spl_params.images[i - 1];
+ }
+
+ spl_params.images[0].file = "rk3576-boost.bin";
+ spl_params.images[0].size = size;
+
+ spl_params.images[0].address = 0x3ffc0000;
+ spl_params.images[1].address = 0x3ff81000;
+ }
+
return EXIT_SUCCESS;
err_spl_info:
@@ -0,0 +1,755 @@
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
* Copyright (c) 2019 Radxa Limited
* Copyright (c) 2022 Amarula Solutions(India)
*/
/dts-v1/;
#include <dt-bindings/leds/common.h>
#include "rk3399-t.dtsi"
/ {
model = "Radxa ROCK 4C+";
compatible = "radxa,rock-4c-plus", "rockchip,rk3399";
aliases {
ethernet0 = &gmac;
mmc0 = &sdhci;
mmc1 = &sdmmc;
led-boot = &led_blue;
led-failsafe = &led_blue;
led-running = &led_blue;
led-upgrade = &led_blue;
};
chosen {
stdout-path = "serial2:1500000n8";
};
clkin_gmac: external-gmac-clock {
compatible = "fixed-clock";
clock-frequency = <125000000>;
clock-output-names = "clkin_gmac";
#clock-cells = <0>;
};
leds {
compatible = "gpio-leds";
pinctrl-names = "default";
pinctrl-0 = <&user_led1 &user_led2>;
/* USER_LED1 */
led-0 {
function = LED_FUNCTION_POWER;
color = <LED_COLOR_ID_GREEN>;
default-state = "on";
gpios = <&gpio3 RK_PD4 GPIO_ACTIVE_LOW>;
};
/* USER_LED2 */
led_blue: led-1 {
function = LED_FUNCTION_STATUS;
color = <LED_COLOR_ID_BLUE>;
gpios = <&gpio3 RK_PD5 GPIO_ACTIVE_HIGH>;
};
};
rk809-sound {
compatible = "simple-audio-card";
simple-audio-card,format = "i2s";
simple-audio-card,name = "Analog RK809";
simple-audio-card,mclk-fs = <256>;
simple-audio-card,cpu {
sound-dai = <&i2s0>;
};
simple-audio-card,codec {
sound-dai = <&rk809>;
};
};
sdio_pwrseq: sdio-pwrseq {
compatible = "mmc-pwrseq-simple";
clocks = <&rk809 1>;
clock-names = "ext_clock";
pinctrl-names = "default";
pinctrl-0 = <&wifi_enable_h>;
reset-gpios = <&gpio0 RK_PB2 GPIO_ACTIVE_LOW>;
};
vcc_3v3: regulator-vcc-3v3 {
compatible = "regulator-fixed";
regulator-name = "vcc_3v3";
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
vin-supply = <&vcc3v3_sys>;
};
vcc3v3_phy1: regulator-vcc3v3-phy1 {
compatible = "regulator-fixed";
regulator-name = "vcc3v3_phy1";
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
vin-supply = <&vcc_3v3>;
};
vcc5v0_host1: regulator-vcc5v0-host {
compatible = "regulator-fixed";
enable-active-high;
gpio = <&gpio3 RK_PD6 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&vcc5v0_host_en>;
regulator-name = "vcc5v0_host1";
regulator-always-on;
regulator-boot-on;
vin-supply = <&vcc5v0_host0_s0>;
};
vcc5v0_sys: regulator-vcc5v0-sys {
compatible = "regulator-fixed";
regulator-name = "vcc5v0_sys";
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
};
vcc5v0_typec: regulator-vcc5v0-typec {
compatible = "regulator-fixed";
enable-active-high;
gpio = <&gpio1 RK_PA3 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&vcc5v0_typec0_en>;
regulator-name = "vcc5v0_typec";
regulator-always-on;
regulator-boot-on;
vin-supply = <&vcc5v0_sys>;
};
vdd_log: regulator-vdd-log {
compatible = "regulator-fixed";
regulator-name = "vdd_log";
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <950000>;
regulator-max-microvolt = <950000>;
vin-supply = <&vcc5v0_sys>;
};
};
&cpu_l0 {
cpu-supply = <&vdd_cpu_l>;
};
&cpu_l1 {
cpu-supply = <&vdd_cpu_l>;
};
&cpu_l2 {
cpu-supply = <&vdd_cpu_l>;
};
&cpu_l3 {
cpu-supply = <&vdd_cpu_l>;
};
&cpu_b0 {
cpu-supply = <&vdd_cpu_b>;
};
&cpu_b1 {
cpu-supply = <&vdd_cpu_b>;
};
&emmc_phy {
rockchip,enable-strobe-pulldown;
status = "okay";
};
&gmac {
assigned-clocks = <&cru SCLK_RMII_SRC>;
assigned-clock-parents = <&clkin_gmac>;
clock_in_out = "input";
phy-supply = <&vcc3v3_phy1>;
phy-mode = "rgmii";
pinctrl-names = "default";
pinctrl-0 = <&rgmii_pins>;
snps,reset-gpio = <&gpio3 RK_PB7 GPIO_ACTIVE_LOW>;
snps,reset-active-low;
snps,reset-delays-us = <0 10000 50000>;
tx_delay = <0x2a>;
rx_delay = <0x21>;
status = "okay";
};
&gpu {
mali-supply = <&vdd_gpu>;
status = "okay";
};
&hdmi {
avdd-0v9-supply = <&vcc_0v9_s0>;
avdd-1v8-supply = <&vcc_1v8_s0>;
ddc-i2c-bus = <&i2c3>;
pinctrl-names = "default";
pinctrl-0 = <&hdmi_cec>;
status = "okay";
};
&hdmi_sound {
status = "okay";
};
&i2c0 {
status = "okay";
i2c-scl-falling-time-ns = <30>;
i2c-scl-rising-time-ns = <180>;
clock-frequency = <400000>;
rk809: pmic@20 {
compatible = "rockchip,rk809";
reg = <0x20>;
interrupt-parent = <&gpio1>;
interrupts = <RK_PC5 IRQ_TYPE_LEVEL_LOW>;
#clock-cells = <1>;
clock-names = "mclk";
clocks = <&cru SCLK_I2S_8CH_OUT>;
clock-output-names = "rk808-clkout1", "rk808-clkout2";
pinctrl-names = "default";
pinctrl-0 = <&pmic_int_l>, <&i2s_8ch_mclk>;
system-power-controller;
#sound-dai-cells = <0>;
wakeup-source;
vcc1-supply = <&vcc5v0_sys>;
vcc2-supply = <&vcc5v0_sys>;
vcc3-supply = <&vcc5v0_sys>;
vcc4-supply = <&vcc5v0_sys>;
vcc5-supply = <&vcc_buck5_s3>;
vcc6-supply = <&vcc_buck5_s3>;
vcc7-supply = <&vcc5v0_sys>;
vcc8-supply = <&vcc3v3_sys>;
vcc9-supply = <&vcc5v0_sys>;
regulators {
vdd_center: DCDC_REG1 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <1350000>;
regulator-initial-mode = <0x2>;
regulator-name = "vdd_center";
regulator-state-mem {
regulator-off-in-suspend;
regulator-suspend-microvolt = <900000>;
};
};
vdd_cpu_l: DCDC_REG2 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <1350000>;
regulator-ramp-delay = <6001>;
regulator-initial-mode = <0x2>;
regulator-name = "vdd_cpu_l";
regulator-state-mem {
regulator-off-in-suspend;
};
};
vcc_ddr: DCDC_REG3 {
regulator-always-on;
regulator-boot-on;
regulator-name = "vcc_ddr";
regulator-initial-mode = <0x2>;
regulator-state-mem {
regulator-on-in-suspend;
};
};
vcc3v3_sys: DCDC_REG4 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-initial-mode = <0x2>;
regulator-name = "vcc3v3_sys";
regulator-state-mem {
regulator-on-in-suspend;
regulator-suspend-microvolt = <3300000>;
};
};
vcc_buck5_s3: DCDC_REG5 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-name = "vcc_buck5_s3";
regulator-state-mem {
regulator-on-in-suspend;
regulator-suspend-microvolt = <3300000>;
};
};
vcc_0v9_s3: LDO_REG1 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <900000>;
regulator-max-microvolt = <900000>;
regulator-name = "vcc_0v9_s3";
regulator-state-mem {
regulator-off-in-suspend;
};
};
vcc_1v8_s3: LDO_REG2 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-name = "vcc_1v8_s3";
regulator-state-mem {
regulator-on-in-suspend;
regulator-suspend-microvolt = <1800000>;
};
};
vcc_0v9_s0: LDO_REG3 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <900000>;
regulator-max-microvolt = <900000>;
regulator-name = "vcc_0v9_s0";
regulator-state-mem {
regulator-on-in-suspend;
regulator-suspend-microvolt = <900000>;
};
};
vcc_1v8_s0: LDO_REG4 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-name = "vcc_1v8_s0";
regulator-state-mem {
regulator-off-in-suspend;
};
};
vcc_mipi: LDO_REG5 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <3000000>;
regulator-max-microvolt = <3000000>;
regulator-name = "vcc_mipi";
regulator-state-mem {
regulator-off-in-suspend;
};
};
vcc_1v5_s0: LDO_REG6 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <1500000>;
regulator-max-microvolt = <1500000>;
regulator-name = "vcc_1v5_s0";
regulator-state-mem {
regulator-off-in-suspend;
};
};
vcc_3v0_s0: LDO_REG7 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <3000000>;
regulator-max-microvolt = <3000000>;
regulator-name = "vcc_3v0_s0";
regulator-state-mem {
regulator-off-in-suspend;
};
};
vcc_sdio_s0: LDO_REG8 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-name = "vcc_sdio_s0";
regulator-state-mem {
regulator-off-in-suspend;
};
};
vcc_cam: LDO_REG9 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-name = "vcc_cam";
regulator-state-mem {
regulator-off-in-suspend;
};
};
vcc5v0_host0_s0: SWITCH_REG1 {
regulator-always-on;
regulator-boot-on;
regulator-name = "vcc5v0_host0_s0";
regulator-state-mem {
regulator-on-in-suspend;
};
};
lcd_3v3: SWITCH_REG2 {
regulator-always-on;
regulator-boot-on;
regulator-name = "lcd_3v3";
regulator-state-mem {
regulator-off-in-suspend;
};
};
};
};
vdd_cpu_b: regulator@40 {
compatible = "silergy,syr827";
reg = <0x40>;
fcs,suspend-voltage-selector = <1>;
pinctrl-0 = <&vsel1_gpio>;
vsel-gpios = <&gpio1 RK_PC1 GPIO_ACTIVE_HIGH>;
regulator-name = "vdd_cpu_b";
regulator-min-microvolt = <712500>;
regulator-max-microvolt = <1500000>;
regulator-ramp-delay = <1000>;
regulator-always-on;
regulator-boot-on;
vin-supply = <&vcc5v0_sys>;
regulator-state-mem {
regulator-off-in-suspend;
};
};
vdd_gpu: regulator@41 {
compatible = "silergy,syr828";
reg = <0x41>;
fcs,suspend-voltage-selector = <1>;
pinctrl-0 = <&vsel2_gpio>;
vsel-gpios = <&gpio1 RK_PB6 GPIO_ACTIVE_HIGH>;
regulator-name = "vdd_gpu";
regulator-min-microvolt = <712500>;
regulator-max-microvolt = <1500000>;
regulator-ramp-delay = <1000>;
regulator-always-on;
regulator-boot-on;
vin-supply = <&vcc5v0_sys>;
regulator-initial-mode = <1>; /* 1:force PWM 2:auto */
regulator-state-mem {
regulator-off-in-suspend;
};
};
};
&i2c3 {
i2c-scl-rising-time-ns = <450>;
i2c-scl-falling-time-ns = <15>;
status = "okay";
};
&i2s0 {
status = "okay";
};
&i2s0_8ch_bus {
rockchip,pins =
<3 RK_PD0 1 &pcfg_pull_none>,
<3 RK_PD2 1 &pcfg_pull_none>,
<3 RK_PD3 1 &pcfg_pull_none>,
<3 RK_PD7 1 &pcfg_pull_none>;
};
&i2s0_8ch_bus_bclk_off {
rockchip,pins =
<3 RK_PD0 RK_FUNC_GPIO &pcfg_pull_none>,
<3 RK_PD2 1 &pcfg_pull_none>,
<3 RK_PD3 1 &pcfg_pull_none>,
<3 RK_PD7 1 &pcfg_pull_none>;
};
&i2s2 {
status = "okay";
};
&io_domains {
audio-supply = <&vcc_1v8_s0>;
bt656-supply = <&vcc_3v0_s0>;
gpio1830-supply = <&vcc_3v0_s0>;
sdmmc-supply = <&vcc_sdio_s0>;
status = "okay";
};
&pinctrl {
bt {
bt_enable_h: bt-enable-h {
rockchip,pins = <0 RK_PB1 RK_FUNC_GPIO &pcfg_pull_none>;
};
bt_host_wake_l: bt-host-wake-l {
rockchip,pins = <0 RK_PA4 RK_FUNC_GPIO &pcfg_pull_none>;
};
bt_wake_l: bt-wake-l {
rockchip,pins = <2 RK_PD3 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
i2s0 {
i2s_8ch_mclk: i2s-8ch-mclk {
rockchip,pins = <4 RK_PA0 1 &pcfg_pull_none>;
};
};
leds {
user_led1: user-led1 {
rockchip,pins = <3 RK_PD4 RK_FUNC_GPIO &pcfg_pull_none>;
};
user_led2: user-led2 {
rockchip,pins = <3 RK_PD5 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
pmic {
pmic_int_l: pmic-int-l {
rockchip,pins = <1 RK_PC5 RK_FUNC_GPIO &pcfg_pull_up>;
};
vsel1_gpio: vsel1-gpio-pin {
rockchip,pins = <1 RK_PC1 RK_FUNC_GPIO &pcfg_pull_down>;
};
vsel2_gpio: vsel2-gpio-pin {
rockchip,pins = <1 RK_PB6 RK_FUNC_GPIO &pcfg_pull_down>;
};
};
sdmmc {
sdmmc_bus4: sdmmc-bus4 {
rockchip,pins = <4 8 1 &pcfg_pull_up_8ma>,
<4 9 1 &pcfg_pull_up_8ma>,
<4 10 1 &pcfg_pull_up_8ma>,
<4 11 1 &pcfg_pull_up_8ma>;
};
sdmmc_clk: sdmmc-clk {
rockchip,pins = <4 12 1 &pcfg_pull_none_18ma>;
};
sdmmc_cmd: sdmmc-cmd {
rockchip,pins = <4 13 1 &pcfg_pull_up_8ma>;
};
};
usb-typec {
vcc5v0_typec0_en: vcc5v0-typec-en {
rockchip,pins = <1 RK_PA3 RK_FUNC_GPIO &pcfg_pull_up>;
};
};
usb2 {
vcc5v0_host_en: vcc5v0-host-en {
rockchip,pins = <3 RK_PD6 RK_FUNC_GPIO &pcfg_pull_up>;
};
};
wifi {
wifi_enable_h: wifi-enable-h {
rockchip,pins = <0 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>;
};
wifi_host_wake_l: wifi-host-wake-l {
rockchip,pins = <0 RK_PA3 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
};
&pmu_io_domains {
pmu1830-supply = <&vcc_3v0_s0>;
status = "okay";
};
&saradc {
status = "okay";
vref-supply = <&vcc_1v8_s3>;
};
&sdhci {
max-frequency = <150000000>;
bus-width = <8>;
mmc-hs400-1_8v;
mmc-hs400-enhanced-strobe;
non-removable;
status = "okay";
};
&sdio0 {
#address-cells = <1>;
#size-cells = <0>;
bus-width = <4>;
clock-frequency = <50000000>;
cap-sdio-irq;
cap-sd-highspeed;
keep-power-in-suspend;
mmc-pwrseq = <&sdio_pwrseq>;
non-removable;
pinctrl-names = "default";
pinctrl-0 = <&sdio0_bus4 &sdio0_cmd &sdio0_clk>;
sd-uhs-sdr104;
status = "okay";
brcmf: wifi@1 {
compatible = "brcm,bcm4329-fmac";
reg = <1>;
interrupt-parent = <&gpio0>;
interrupts = <RK_PA3 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "host-wake";
pinctrl-names = "default";
pinctrl-0 = <&wifi_host_wake_l>;
};
};
&sdmmc {
bus-width = <4>;
cap-mmc-highspeed;
cap-sd-highspeed;
card-detect-delay = <800>;
disable-wp;
pinctrl-names = "default";
pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_bus4>;
cd-gpios = <&gpio0 RK_PA7 GPIO_ACTIVE_LOW>;
vqmmc-supply = <&vcc_sdio_s0>;
status = "okay";
};
&tcphy0 {
status = "okay";
};
&tcphy1 {
status = "okay";
};
&tsadc {
rockchip,hw-tshut-mode = <1>;
rockchip,hw-tshut-polarity = <1>;
status = "okay";
};
&u2phy0 {
status = "okay";
u2phy0_otg: otg-port {
status = "okay";
};
u2phy0_host: host-port {
phy-supply = <&vcc5v0_host1>;
status = "okay";
};
};
&u2phy1 {
status = "okay";
u2phy1_otg: otg-port {
status = "okay";
};
u2phy1_host: host-port {
phy-supply = <&vcc5v0_host1>;
status = "okay";
};
};
&uart0 {
pinctrl-names = "default";
pinctrl-0 = <&uart0_xfer &uart0_cts &uart0_rts>;
status = "okay";
bluetooth {
compatible = "brcm,bcm4345c5";
clocks = <&rk809 1>;
clock-names = "lpo";
device-wakeup-gpios = <&gpio2 RK_PD3 GPIO_ACTIVE_HIGH>;
host-wakeup-gpios = <&gpio0 RK_PA4 GPIO_ACTIVE_HIGH>;
shutdown-gpios = <&gpio0 RK_PB1 GPIO_ACTIVE_HIGH>;
max-speed = <1500000>;
pinctrl-names = "default";
pinctrl-0 = <&bt_host_wake_l &bt_wake_l &bt_enable_h>;
vbat-supply = <&vcc3v3_sys>;
vddio-supply = <&vcc_1v8_s3>;
};
};
&uart2 {
status = "okay";
};
&usb_host0_ehci {
status = "okay";
};
&usb_host0_ohci {
status = "okay";
};
&usb_host1_ehci {
status = "okay";
};
&usb_host1_ohci {
status = "okay";
};
&usbdrd3_0 {
extcon = <&u2phy0>;
status = "okay";
};
&usbdrd_dwc3_0 {
status = "okay";
dr_mode = "host";
};
&usbdrd3_1 {
status = "okay";
};
&usbdrd_dwc3_1 {
status = "okay";
dr_mode = "host";
};
&vopb {
status = "okay";
};
&vopb_mmu {
status = "okay";
};
&vopl {
status = "okay";
};
&vopl_mmu {
status = "okay";
};
@@ -0,0 +1,116 @@
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2016-2017 Fuzhou Rockchip Electronics Co., Ltd
* Copyright (c) 2022 Radxa Limited
*/
#include "rk3399-base.dtsi"
/ {
cluster0_opp: opp-table-0 {
compatible = "operating-points-v2";
opp-shared;
opp00 {
opp-hz = /bits/ 64 <408000000>;
opp-microvolt = <875000 875000 1250000>;
clock-latency-ns = <40000>;
};
opp01 {
opp-hz = /bits/ 64 <600000000>;
opp-microvolt = <875000 875000 1250000>;
};
opp02 {
opp-hz = /bits/ 64 <816000000>;
opp-microvolt = <900000 900000 1250000>;
};
opp03 {
opp-hz = /bits/ 64 <1008000000>;
opp-microvolt = <975000 975000 1250000>;
};
};
cluster1_opp: opp-table-1 {
compatible = "operating-points-v2";
opp-shared;
opp00 {
opp-hz = /bits/ 64 <408000000>;
opp-microvolt = <875000 875000 1250000>;
clock-latency-ns = <40000>;
};
opp01 {
opp-hz = /bits/ 64 <600000000>;
opp-microvolt = <875000 875000 1250000>;
};
opp02 {
opp-hz = /bits/ 64 <816000000>;
opp-microvolt = <875000 875000 1250000>;
};
opp03 {
opp-hz = /bits/ 64 <1008000000>;
opp-microvolt = <925000 925000 1250000>;
};
opp04 {
opp-hz = /bits/ 64 <1200000000>;
opp-microvolt = <1000000 1000000 1250000>;
};
opp05 {
opp-hz = /bits/ 64 <1416000000>;
opp-microvolt = <1075000 1075000 1250000>;
};
opp06 {
opp-hz = /bits/ 64 <1512000000>;
opp-microvolt = <1150000 1150000 1250000>;
};
};
gpu_opp_table: opp-table-2 {
compatible = "operating-points-v2";
opp00 {
opp-hz = /bits/ 64 <200000000>;
opp-microvolt = <875000 875000 1150000>;
};
opp01 {
opp-hz = /bits/ 64 <300000000>;
opp-microvolt = <875000 875000 1150000>;
};
opp02 {
opp-hz = /bits/ 64 <400000000>;
opp-microvolt = <875000 875000 1150000>;
};
opp03 {
opp-hz = /bits/ 64 <600000000>;
opp-microvolt = <975000 975000 1150000>;
};
};
};
&cpu_l0 {
operating-points-v2 = <&cluster0_opp>;
};
&cpu_l1 {
operating-points-v2 = <&cluster0_opp>;
};
&cpu_l2 {
operating-points-v2 = <&cluster0_opp>;
};
&cpu_l3 {
operating-points-v2 = <&cluster0_opp>;
};
&cpu_b0 {
operating-points-v2 = <&cluster1_opp>;
};
&cpu_b1 {
operating-points-v2 = <&cluster1_opp>;
};
&gpu {
operating-points-v2 = <&gpu_opp_table>;
};
+11 -2
View File
@@ -172,7 +172,7 @@ define Device/friendlyarm_nanopi-m5
DEVICE_MODEL := NanoPi M5
SOC := rk3576
DEVICE_DTS := rk3576-nanopi-m5
UBOOT_DEVICE_NAME := nanopi-m5-rk3576
UBOOT_DEVICE_NAME := generic-rk3576
DEVICE_PACKAGES := kmod-gpio-button-hotplug
IMAGE/sysupgrade.img.gz := boot-common | boot-script | pine64-img | gzip | append-metadata
endef
@@ -302,7 +302,7 @@ define Device/friendlyarm_nanopi-r76s
DEVICE_MODEL := NanoPi R76S
SOC := rk3576
DEVICE_DTS := rk3576-nanopi-r76s
UBOOT_DEVICE_NAME := nanopi-m5-rk3576
UBOOT_DEVICE_NAME := generic-rk3576
DEVICE_PACKAGES := kmod-gpio-button-hotplug kmod-r8125
IMAGE/sysupgrade.img.gz := boot-common | boot-script | pine64-img | gzip | append-metadata
endef
@@ -524,6 +524,15 @@ define Device/radxa_rock-3c
endef
TARGET_DEVICES += radxa_rock-3c
define Device/radxa_rock-4c-plus
DEVICE_VENDOR := Radxa
DEVICE_MODEL := ROCK 4C+
SOC := rk3399
IMAGE/sysupgrade.img.gz := boot-common | boot-script | pine64-img | gzip | append-metadata
UBOOT_DEVICE_NAME := rock-4c-plus-rk3399
endef
TARGET_DEVICES += radxa_rock-4c-plus
define Device/radxa_rock-pi-4a
DEVICE_VENDOR := Radxa
DEVICE_MODEL := ROCK Pi 4A
+1 -1
View File
@@ -38,7 +38,7 @@ require (
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141
github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443
github.com/metacubex/tls v0.1.0
github.com/metacubex/tls v0.1.2
github.com/metacubex/utls v1.8.4
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f
github.com/mroth/weightedrand/v2 v2.1.0
+2 -2
View File
@@ -141,8 +141,8 @@ github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141 h1:DK2l6m2Fc85H2Bhi
github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141/go.mod h1:/yI4OiGOSn0SURhZdJF3CbtPg3nwK700bG8TZLMBvAg=
github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443 h1:H6TnfM12tOoTizYE/qBHH3nEuibIelmHI+BVSxVJr8o=
github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
github.com/metacubex/tls v0.1.0 h1:1kjR/1q2uU1cZIwiHYEnWzS4L+0Cu1/X3yfIQ76BzNY=
github.com/metacubex/tls v0.1.0/go.mod h1:0XeVdL0cBw+8i5Hqy3lVeP9IyD/LFTq02ExvHM6rzEM=
github.com/metacubex/tls v0.1.2 h1:qM0ATVnPFts5kZNTb+TukdaJ3hIf1ZVgKnXJCGfBmGI=
github.com/metacubex/tls v0.1.2/go.mod h1:0XeVdL0cBw+8i5Hqy3lVeP9IyD/LFTq02ExvHM6rzEM=
github.com/metacubex/utls v1.8.4 h1:HmL9nUApDdWSkgUyodfwF6hSjtiwCGGdyhaSpEejKpg=
github.com/metacubex/utls v1.8.4/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f h1:FGBPRb1zUabhPhDrlKEjQ9lgIwQ6cHL4x8M9lrERhbk=
@@ -85,7 +85,7 @@ function get_containers()
end
for ii,iv in ipairs(images) do
if iv.Id == v.ImageID then
data[index]["_image"] = iv.RepoTags and iv.RepoTags[1] or (next(iv.RepoDigests) and (iv.RepoDigests[1]:gsub("(.-)@.+", "%1") .. ":&lt;none&gt;")) or ""
data[index]["_image"] = (iv.RepoTags and iv.RepoTags[1]) or (iv.RepoDigests and next(iv.RepoDigests) and (iv.RepoDigests[1]:gsub("(.-)@.+", "%1") .. ":&lt;none&gt;")) or "&lt;none&gt;:&lt;none&gt;"
end
end
data[index]["_id_name"] = '<a href='..luci.dispatcher.build_url("admin/docker/container/"..v.Id)..' class="dockerman_link" title="'..translate("Container detail")..'">'.. data[index]["_name"] .. "<br><font color='#9f9f9f'>ID: " .. data[index]["_id"]
@@ -87,6 +87,7 @@ function index()
entry({"admin", "services", appname, "save_node_order"}, call("save_node_order")).leaf = true
entry({"admin", "services", appname, "save_node_list_opt"}, call("save_node_list_opt")).leaf = true
entry({"admin", "services", appname, "update_rules"}, call("update_rules")).leaf = true
entry({"admin", "services", appname, "rollback_rules"}, call("rollback_rules")).leaf = true
entry({"admin", "services", appname, "subscribe_del_node"}, call("subscribe_del_node")).leaf = true
entry({"admin", "services", appname, "subscribe_del_all"}, call("subscribe_del_all")).leaf = true
entry({"admin", "services", appname, "subscribe_manual"}, call("subscribe_manual")).leaf = true
@@ -611,6 +612,19 @@ function update_rules()
http_write_json()
end
function rollback_rules()
local arg_type = http.formvalue("type")
if arg_type ~= "geoip" and arg_type ~= "geosite" then
http_write_json_error()
return
end
local bak_dir = "/tmp/bak_v2ray/"
local geo_dir = (uci:get(appname, "@global_rules[0]", "v2ray_location_asset") or "/usr/share/v2ray/")
fs.move(bak_dir .. arg_type .. ".dat", geo_dir .. arg_type .. ".dat")
fs.rmdir(bak_dir)
http_write_json_ok()
end
function server_user_status()
local e = {}
e.index = http.formvalue("index")
@@ -121,17 +121,6 @@ if (has_singbox or has_xray) and #nodes_table > 0 then
local vid = v.id
s:tab("Shunt", translate("Shunt Rule"))
s:tab("ShuntDNS", translate("Shunt Rule") .. " DNS")
-- shunt node type, Sing-Box or Xray
local type = s:taboption("Shunt", ListValue, vid .. "-type", translate("Type"))
if has_singbox then
type:value("sing-box", translate("Sing-Box"))
end
if has_xray then
type:value("Xray", translate("Xray"))
end
type.cfgvalue = get_cfgvalue(v.id, "type")
type.write = get_write(v.id, "type")
-- pre-proxy
o = s:taboption("Shunt", Flag, vid .. "-preproxy_enabled", translate("Preproxy"))
o:depends("node", v.id)
@@ -169,18 +158,15 @@ if (has_singbox or has_xray) and #nodes_table > 0 then
o.cfgvalue = get_cfgvalue(v.id, "main_node")
o.write = get_write(v.id, "main_node")
o = s:taboption("ShuntDNS", Flag, vid .. "-fakedns", "FakeDNS")
o = s:taboption("ShuntDNS", Flag, vid .. "-fakedns", '<a style="color:#FF8C00">FakeDNS</a>', translate("Use FakeDNS work in the domain that proxy.") .. "<br>" ..
translate("Suitable scenarios for let the node servers get the target domain names.") .. "<br>" ..
translate("Such as: DNS unlocking of streaming media, reducing DNS query latency, etc.")
)
o:depends("node", v.id)
o.cfgvalue = get_cfgvalue(v.id, "fakedns")
o.write = get_write(v.id, "fakedns")
o.remove = get_remove(v.id, "fakedns")
if (has_singbox and has_xray) or (v.type == "sing-box" and not has_singbox) or (v.type == "Xray" and not has_xray) then
type:depends("node", v.id)
else
type:depends({ __hide = true }) -- Always hidden.
end
m.uci:foreach(appname, "shunt_rules", function(e)
local id = e[".name"]
local node_option = vid .. "-" .. id .. "_node"
@@ -204,7 +190,7 @@ if (has_singbox or has_xray) and #nodes_table > 0 then
o:value("", translate("Close (Not use)"))
pt:value("main", translate("Use preproxy node"))
local fakedns_tag = s:taboption("ShuntDNS", Flag, vid .. "-".. id .. "_fakedns", string.format('* <a style="color:red">%s</a>', e.remarks .. " " .. "FakeDNS"))
local fakedns_tag = s:taboption("ShuntDNS", Flag, vid .. "-".. id .. "_fakedns", string.format('* <a style="color:#FF8C00">%s</a>', e.remarks))
fakedns_tag.cfgvalue = get_cfgvalue(v.id, id .. "_fakedns")
fakedns_tag.write = get_write(v.id, id .. "_fakedns")
fakedns_tag.remove = get_remove(v.id, id .. "_fakedns")
@@ -272,7 +272,10 @@ if load_shunt_options then -- [[ Shunt Start ]]
o.default = o.keylist[1]
end
o = s:option(Flag, _n("fakedns"), "FakeDNS")
o = s:option(Flag, _n("fakedns"), '<a style="color:#FF8C00">FakeDNS</a>', translate("Use FakeDNS work in the domain that proxy.") .. "<br>" ..
translate("Suitable scenarios for let the node servers get the target domain names.") .. "<br>" ..
translate("Such as: DNS unlocking of streaming media, reducing DNS query latency, etc.")
)
o:depends({ [_n("protocol")] = "_shunt" })
end
m.uci:foreach(appname, "shunt_rules", function(e)
@@ -292,7 +295,7 @@ if load_shunt_options then -- [[ Shunt Start ]]
pt:value("", translate("Close (Not use)"))
pt:value("main", translate("Use preproxy node"))
local fakedns_tag = s:option(Flag, _n(e[".name"] .. "_fakedns"), string.format('* <a style="color:red">%s</a>', e.remarks .. " " .. "FakeDNS"), translate("Use FakeDNS work in the domain that proxy."))
local fakedns_tag = s:option(Flag, _n(e[".name"] .. "_fakedns"), string.format('* <a style="color:#FF8C00">%s</a>', e.remarks .. " " .. "FakeDNS"))
for k, v in pairs(socks_list) do
o:value(v.id, v.remark)
@@ -249,7 +249,10 @@ if load_shunt_options then -- [[ Shunt Start ]]
o.default = o.keylist[1]
end
o = s:option(Flag, _n("fakedns"), "FakeDNS")
o = s:option(Flag, _n("fakedns"), '<a style="color:#FF8C00">FakeDNS</a>', translate("Use FakeDNS work in the domain that proxy.") .. "<br>" ..
translate("Suitable scenarios for let the node servers get the target domain names.") .. "<br>" ..
translate("Such as: DNS unlocking of streaming media, reducing DNS query latency, etc.")
)
o:depends({ [_n("protocol")] = "_shunt" })
end
m.uci:foreach(appname, "shunt_rules", function(e)
@@ -269,7 +272,7 @@ if load_shunt_options then -- [[ Shunt Start ]]
pt:value("", translate("Close (Not use)"))
pt:value("main", translate("Use preproxy node"))
local fakedns_tag = s:option(Flag, _n(e[".name"] .. "_fakedns"), string.format('* <a style="color:red">%s</a>', e.remarks .. " " .. "FakeDNS"), translate("Use FakeDNS work in the domain that proxy."))
local fakedns_tag = s:option(Flag, _n(e[".name"] .. "_fakedns"), string.format('* <a style="color:#FF8C00">%s</a>', e.remarks .. " " .. "FakeDNS"))
for k, v in pairs(socks_list) do
o:value(v.id, v.remark)
@@ -113,8 +113,6 @@ window.lv_dropdown_data["<%=cbid%>"] = <%=json.stringify(dropdown_data)%>;
</div>
</div>
<%+cbi/valuefooter%>
<script type="text/javascript">
//<![CDATA[
(function(){
@@ -143,3 +141,5 @@ window.lv_dropdown_data["<%=cbid%>"] = <%=json.stringify(dropdown_data)%>;
})();
//]]>
</script>
<%+cbi/valuefooter%>
@@ -108,7 +108,6 @@ end
<span id="count-<%=self.option%>" style="color:#666;"><%:Selected:%> <span style='color:red;'><%=selected_count%>/<%=total_count%></span></span>
</div>
</div>
<%+cbi/valuefooter%>
<script type="text/javascript">
//<![CDATA[
@@ -129,3 +128,5 @@ end
})();
//]]>
</script>
<%+cbi/valuefooter%>
@@ -126,8 +126,6 @@ window.v_dropdown_data["<%=cbid%>"] = <%=json.stringify(dropdown_data)%>;
</div>
</div>
<%+cbi/valuefooter%>
<script type="text/javascript">
//<![CDATA[
(function(){
@@ -157,3 +155,5 @@ window.v_dropdown_data["<%=cbid%>"] = <%=json.stringify(dropdown_data)%>;
})();
//]]>
</script>
<%+cbi/valuefooter%>
@@ -129,7 +129,7 @@ local api = self.api
hiddenSelect.addEventListener("change", function(el){
let new_val = el.target.value
const new_hasItem = shunt_list.find(element => element.id == new_val);
if (new_hasItem || (o_hasItem && !new_hasItem)) {
if (new_hasItem) {
XHR.get('<%=api.url("update_node")%>', {
id: "<%=self.global_cfgid%>",
data: JSON.stringify({
@@ -27,7 +27,7 @@ local api = self.api
if (el.value.startsWith("_") || (o_val.startsWith("_") && !el.value.startsWith("_"))) {
let name_split = el.name.split(".");
let name = name_split[name_split.length - 1];
let save = false;
let save = true;
if (save) {
update_node({
protocol: getOption(name).value
@@ -1,5 +1,8 @@
<%
local api = require "luci.passwall2.api"
local fs = api.fs
local has_old_geoip = fs.access("/tmp/bak_v2ray/geoip.dat")
local has_old_geosite = fs.access("/tmp/bak_v2ray/geosite.dat")
-%>
<script src="<%=resource%>/view/<%=api.appname%>/Sortable.min.js?v=26.1.9"></script>
@@ -48,7 +51,14 @@ local api = require "luci.passwall2.api"
<input class="cbi-input-checkbox" type="checkbox" name="geosite" value="1" />
geosite
</label>
<br><br><input class="btn cbi-button cbi-button-apply" type="button" id="update_rules_btn" onclick="update_rules(this)" value="<%:Manually update%>" />
<br><br>
<input class="btn cbi-button cbi-button-apply" type="button" id="update_rules_btn" onclick="update_rules(this)" value="<%:Manually update%>" />
<% if has_old_geoip then %>
<input class="btn cbi-button cbi-button-reset" type="button" onclick="rollback_rules(this, 'geoip')" value="<%:Rollback%> GeoIP" />
<% end %>
<% if has_old_geosite then %>
<input class="btn cbi-button cbi-button-reset" type="button" onclick="rollback_rules(this, 'geosite')" value="<%:Rollback%> Geosite" />
<% end %>
</div>
</div>
</div>
@@ -120,6 +130,27 @@ local api = require "luci.passwall2.api"
}
);
}
function rollback_rules(btn, type) {
let ori_val = btn.value;
btn.disabled = true;
btn.value = '<%:Rollbacking...%>';
XHR.get('<%=api.url("rollback_rules")%>', {
type: type
},
function(x, data) {
if(x && x.status == 200 && data.code == 1) {
alert("<%:Success%>");
btn.style.display = "none";
} else {
alert("<%:Error%>");
btn.disabled = false;
btn.value = ori_val;
}
}
);
}
//分流规则添加拖拽排序
document.addEventListener("DOMContentLoaded", function () {
function initSortableForTable() {
@@ -169,6 +169,12 @@ msgstr "远程查询策略"
msgid "Use FakeDNS work in the domain that proxy."
msgstr "需要代理的域名使用 FakeDNS。"
msgid "Suitable scenarios for let the node servers get the target domain names."
msgstr "适合让节点服务器获取目标域名的场景。"
msgid "Such as: DNS unlocking of streaming media, reducing DNS query latency, etc."
msgstr "例如:流媒体的 DNS 解锁、减少 DNS 查询延迟等。"
msgid "Domain Override"
msgstr "域名重写"
@@ -286,6 +292,9 @@ msgstr "清空所有节点"
msgid "Are you sure to clear all nodes?"
msgstr "你确定要清空所有节点吗?"
msgid "Success"
msgstr "成功"
msgid "Error"
msgstr "错误"
@@ -841,6 +850,12 @@ msgstr "规则版本"
msgid "Manually update"
msgstr "手动更新"
msgid "Rollback"
msgstr "回滚"
msgid "Rollbacking..."
msgstr "回滚中..."
msgid "Enable auto update rules"
msgstr "开启自动更新规则"
@@ -1957,8 +1972,8 @@ msgstr "Socks节点:[%s]%s,启动 %s:%s"
msgid "To enable experimental IPv6 transparent proxy (TProxy), please ensure your node and type support IPv6!"
msgstr "开启实验性IPv6透明代理(TProxy),请确认您的节点及类型支持IPv6!"
msgid "[%s] process %s error, skip!"
msgstr "【%s】 进程 %s 错误,跳过!"
msgid "[%s] process %s error, skip this transparent proxy!"
msgstr "【%s】 进程 %s 错误,跳过此透明代理"
msgid "Analyzing the node configuration of the Socks service..."
msgstr "分析 Socks 服务的节点配置..."
@@ -169,6 +169,12 @@ msgstr "遠程查詢策略"
msgid "Use FakeDNS work in the domain that proxy."
msgstr "需要代理的域名使用 FakeDNS。"
msgid "Suitable scenarios for let the node servers get the target domain names."
msgstr "適合讓節點伺服器取得目標網域的場景。"
msgid "Such as: DNS unlocking of streaming media, reducing DNS query latency, etc."
msgstr "例如:流媒體的 DNS 解鎖、減少 DNS 查詢延遲等。"
msgid "Domain Override"
msgstr "域名重寫"
@@ -286,6 +292,9 @@ msgstr "清空所有節點"
msgid "Are you sure to clear all nodes?"
msgstr "你確定要清空所有節點吗?"
msgid "Success"
msgstr "成功"
msgid "Error"
msgstr "錯誤"
@@ -841,6 +850,12 @@ msgstr "規則版本"
msgid "Manually update"
msgstr "手動更新"
msgid "Rollback"
msgstr "回滾"
msgid "Rollbacking..."
msgstr "回滾中..."
msgid "Enable auto update rules"
msgstr "開啟自動更新規則"
@@ -1957,8 +1972,8 @@ msgstr "Socks節點:[%s]%s,啟動 %s:%s"
msgid "To enable experimental IPv6 transparent proxy (TProxy), please ensure your node and type support IPv6!"
msgstr "開啟實驗性IPv6透明代理(TProxy),請確認您的節點及類型支持IPv6!"
msgid "[%s] process %s error, skip!"
msgstr "【%s】 進程 %s 錯誤,跳過!"
msgid "[%s] process %s error, skip this transparent proxy!"
msgstr "【%s】 進程 %s 錯誤,跳過此透明代理"
msgid "Analyzing the node configuration of the Socks service..."
msgstr "分析 Socks 服務的節點配置..."
@@ -223,10 +223,14 @@ run_xray() {
local _json_arg="$(json_dump)"
lua $UTIL_XRAY gen_config "${_json_arg}" > $config_file
$XRAY_BIN run -test -c "$config_file" > $log_file; local status=$?
test_log_file=$log_file
[ "$test_log_file" = "/dev/null" ] && test_log_file="${TMP_PATH}/test.log"
$XRAY_BIN run -test -c "$config_file" > $test_log_file; local status=$?
if [ "${status}" == 0 ]; then
ln_run "$XRAY_BIN" xray $log_file run -c "$config_file"
else
_error_log_file=$test_log_file
return ${status}
fi
}
@@ -357,10 +361,14 @@ run_singbox() {
local _json_arg="$(json_dump)"
lua $UTIL_SINGBOX gen_config "${_json_arg}" > $config_file
$SINGBOX_BIN check -c "$config_file" > $log_file 2>&1; local status=$?
test_log_file=$log_file
[ "$test_log_file" = "/dev/null" ] && test_log_file="${TMP_PATH}/test.log"
$SINGBOX_BIN check -c "$config_file" > $test_log_file 2>&1; local status=$?
if [ "${status}" == 0 ]; then
ln_run "$SINGBOX_BIN" "sing-box" "${log_file}" run -c "$config_file"
else
_error_log_file=$test_log_file
return ${status}
fi
}
@@ -669,7 +677,9 @@ run_global() {
if [ "$status" == 0 ]; then
log 0 ${dns_msg}
else
log_i18n 0 "[%s] process %s error, skip!" $(i18n "Global") "${V2RAY_CONFIG}"
log_i18n 0 "[%s] process %s error, skip this transparent proxy!" $(i18n "Global") "${V2RAY_CONFIG}"
cat ${_error_log_file} >> ${LOG_FILE}
unset _error_log_file
ENABLED_DEFAULT_ACL=0
return 1
fi
@@ -1092,7 +1102,9 @@ acl_app() {
write_ipset_direct=${write_ipset_direct} config_file=${config_file}
local status=$?
if [ "$status" != 0 ]; then
log_i18n 2 "[%s] process %s error, skip!" "${remarks}" "${config_file}"
log_i18n 2 "[%s] process %s error, skip this transparent proxy!" "${remarks}" "${config_file}"
cat ${_error_log_file} >> ${LOG_FILE}
unset _error_log_file
continue
fi
fi
@@ -20,6 +20,7 @@ local geoip_url = uci:get(name, "@global_rules[0]", "geoip_url") or "https://git
local geosite_url = uci:get(name, "@global_rules[0]", "geosite_url") or "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat"
local asset_location = uci:get(name, "@global_rules[0]", "v2ray_location_asset") or "/usr/share/v2ray/"
asset_location = asset_location:match("/$") and asset_location or (asset_location .. "/")
local backup_path = "/tmp/bak_v2ray/"
if arg3 == "cron" then
arg2 = nil
@@ -98,7 +99,7 @@ local function fetch_geofile(geo_name, geo_type, url)
local content = f:read("*l")
f:close()
if content then
content = content:gsub(down_filename, tmp_path)
content = content:gsub("(%x+)%s+.+", "%1 " .. tmp_path)
f = io.open(sha_path, "w")
if f then
f:write(content)
@@ -128,7 +129,8 @@ local function fetch_geofile(geo_name, geo_type, url)
if sret_tmp == 200 then
if sha_verify then
if verify_sha256(sha_path) then
sys.call(string.format("mkdir -p %s && cp -f %s %s", asset_location, tmp_path, asset_path))
sys.call(string.format("mkdir -p %s && mv -f %s %s", backup_path, asset_path, backup_path))
sys.call(string.format("mkdir -p %s && mv -f %s %s", asset_location, tmp_path, asset_path))
reboot = 1
log(1, api.i18n.translatef("%s update success.", geo_type))
else
@@ -140,7 +142,8 @@ local function fetch_geofile(geo_name, geo_type, url)
log(1, api.i18n.translatef("%s version is the same and does not need to be updated.", geo_type))
return 0
end
sys.call(string.format("mkdir -p %s && cp -f %s %s", asset_location, tmp_path, asset_path))
sys.call(string.format("mkdir -p %s && mv -f %s %s", backup_path, asset_path, backup_path))
sys.call(string.format("mkdir -p %s && mv -f %s %s", asset_location, tmp_path, asset_path))
reboot = 1
log(1, api.i18n.translatef("%s update success.", geo_type))
end
+9 -9
View File
@@ -17,17 +17,17 @@ import (
)
var (
debugEnabled bool
target string
platform string
//withTailscale bool
debugEnabled bool
target string
platform string
// withTailscale bool
)
func init() {
flag.BoolVar(&debugEnabled, "debug", false, "enable debug")
flag.StringVar(&target, "target", "android", "target platform")
flag.StringVar(&platform, "platform", "", "specify platform")
//flag.BoolVar(&withTailscale, "with-tailscale", false, "build tailscale for iOS and tvOS")
// flag.BoolVar(&withTailscale, "with-tailscale", false, "build tailscale for iOS and tvOS")
}
func main() {
@@ -48,7 +48,7 @@ var (
debugFlags []string
sharedTags []string
darwinTags []string
//memcTags []string
// memcTags []string
notMemcTags []string
debugTags []string
)
@@ -65,7 +65,7 @@ func init() {
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_naive_outbound", "with_clash_api", "with_conntrack", "badlinkname", "tfogo_checklinkname0")
darwinTags = append(darwinTags, "with_dhcp", "grpcnotrace")
//memcTags = append(memcTags, "with_tailscale")
// memcTags = append(memcTags, "with_tailscale")
sharedTags = append(sharedTags, "with_tailscale", "ts_omit_logtail", "ts_omit_ssh", "ts_omit_drive", "ts_omit_taildrop", "ts_omit_webclient", "ts_omit_doctor", "ts_omit_capture", "ts_omit_kube", "ts_omit_aws", "ts_omit_synology", "ts_omit_bird")
notMemcTags = append(notMemcTags, "with_low_memory")
debugTags = append(debugTags, "debug")
@@ -165,7 +165,7 @@ func buildAndroid() {
// Build main variant (SDK 23)
mainTags := append([]string{}, sharedTags...)
//mainTags = append(mainTags, memcTags...)
// mainTags = append(mainTags, memcTags...)
if debugEnabled {
mainTags = append(mainTags, debugTags...)
}
@@ -177,7 +177,7 @@ func buildAndroid() {
// Build legacy variant (SDK 21, no naive outbound)
legacyTags := filterTags(sharedTags, "with_naive_outbound")
//legacyTags = append(legacyTags, memcTags...)
// legacyTags = append(legacyTags, memcTags...)
if debugEnabled {
legacyTags = append(legacyTags, debugTags...)
}
+8
View File
@@ -8,8 +8,16 @@ icon: material/alert-decagram
#### 1.12.18
* Add fallback routing rule for `auto_redirect` **1**
* Fixes and improvements
**1**:
Adds a fallback iproute2 rule checked after system default rules (32766: main, 32767: default),
ensuring traffic is routed to the sing-box table when no route is found in system tables.
The rule index can be customized via `auto_redirect_iproute2_fallback_rule_index` (default: 32768).
#### 1.13.0-beta.6
* Update uTLS to v1.8.2 **1**
+14 -1
View File
@@ -6,7 +6,8 @@ icon: material/new-box
:material-plus: [auto_redirect_reset_mark](#auto_redirect_reset_mark)
:material-plus: [auto_redirect_nfqueue](#auto_redirect_nfqueue)
:material-plus: [exclude_mptcp](#exclude_mptcp)
:material-plus: [exclude_mptcp](#exclude_mptcp)
:material-plus: [auto_redirect_iproute2_fallback_rule_index](#auto_redirect_iproute2_fallback_rule_index)
!!! quote "Changes in sing-box 1.12.0"
@@ -71,6 +72,7 @@ icon: material/new-box
"auto_redirect_output_mark": "0x2024",
"auto_redirect_reset_mark": "0x2025",
"auto_redirect_nfqueue": 100,
"auto_redirect_iproute2_fallback_rule_index": 32768,
"exclude_mptcp": false,
"loopback_address": [
"10.7.0.1"
@@ -303,6 +305,17 @@ NFQueue number used by `auto_redirect` pre-matching.
`100` is used by default.
#### auto_redirect_iproute2_fallback_rule_index
!!! question "Since sing-box 1.12.18"
Linux iproute2 fallback rule index generated by `auto_redirect`.
This rule is checked after system default rules (32766: main, 32767: default),
routing traffic to the sing-box table only when no route is found in system tables.
`32768` is used by default.
#### exclude_mptcp
!!! question "Since sing-box 1.13.0"
+15 -2
View File
@@ -6,7 +6,8 @@ icon: material/new-box
:material-plus: [auto_redirect_reset_mark](#auto_redirect_reset_mark)
:material-plus: [auto_redirect_nfqueue](#auto_redirect_nfqueue)
:material-plus: [exclude_mptcp](#exclude_mptcp)
:material-plus: [exclude_mptcp](#exclude_mptcp)
:material-plus: [auto_redirect_iproute2_fallback_rule_index](#auto_redirect_iproute2_fallback_rule_index)
!!! quote "sing-box 1.12.0 中的更改"
@@ -71,6 +72,7 @@ icon: material/new-box
"auto_redirect_output_mark": "0x2024",
"auto_redirect_reset_mark": "0x2025",
"auto_redirect_nfqueue": 100,
"auto_redirect_iproute2_fallback_rule_index": 32768,
"exclude_mptcp": false,
"loopback_address": [
"10.7.0.1"
@@ -302,13 +304,24 @@ tun 接口的 IPv6 前缀。
默认使用 `100`
#### auto_redirect_iproute2_fallback_rule_index
!!! question "自 sing-box 1.12.18 起"
`auto_redirect` 生成的 iproute2 回退规则索引。
此规则在系统默认规则(32766: main32767: default)之后检查,
仅当系统路由表中未找到路由时才将流量路由到 sing-box 路由表。
默认使用 `32768`
#### exclude_mptcp
!!! question "自 sing-box 1.13.0 起"
!!! quote ""
仅支持 Linux,且需要 nftables`auto_route``auto_redirect` 已启用。
仅支持 Linux,且需要 nftables`auto_route``auto_redirect` 已启用。
由于协议限制,MPTCP 无法被透明代理。
+1 -1
View File
@@ -39,7 +39,7 @@ require (
github.com/sagernet/sing-shadowsocks v0.2.8
github.com/sagernet/sing-shadowsocks2 v0.2.1
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11
github.com/sagernet/sing-tun v0.8.0-beta.14
github.com/sagernet/sing-tun v0.8.0-beta.15
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1
github.com/sagernet/smux v1.5.50-sing-box-mod.1
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.6
+2 -2
View File
@@ -222,8 +222,8 @@ github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnq
github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w=
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
github.com/sagernet/sing-tun v0.8.0-beta.14 h1:LlIhiynP1aggwDYeHDa+JeJAuptYy+OtQU+hPiHTXbY=
github.com/sagernet/sing-tun v0.8.0-beta.14/go.mod h1:+HAK/y9GZljdT0KYKMYDR8MjjqnqDDQZYp5ZZQoRzS8=
github.com/sagernet/sing-tun v0.8.0-beta.15 h1:R5PMHCXuSUs5g6aor4UhxfaIx+t6yofAYB9YAea7UZM=
github.com/sagernet/sing-tun v0.8.0-beta.15/go.mod h1:+HAK/y9GZljdT0KYKMYDR8MjjqnqDDQZYp5ZZQoRzS8=
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1 h1:aSwUNYUkVyVvdmBSufR8/nRFonwJeKSIROxHcm5br9o=
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1/go.mod h1:P11scgTxMxVVQ8dlM27yNm3Cro40mD0+gHbnqrNGDuY=
github.com/sagernet/smux v1.5.50-sing-box-mod.1 h1:XkJcivBC9V4wBjiGXIXZ229aZCU1hzcbp6kSkkyQ478=
+31 -30
View File
@@ -11,36 +11,37 @@ import (
)
type TunInboundOptions struct {
InterfaceName string `json:"interface_name,omitempty"`
MTU uint32 `json:"mtu,omitempty"`
Address badoption.Listable[netip.Prefix] `json:"address,omitempty"`
AutoRoute bool `json:"auto_route,omitempty"`
IPRoute2TableIndex int `json:"iproute2_table_index,omitempty"`
IPRoute2RuleIndex int `json:"iproute2_rule_index,omitempty"`
AutoRedirect bool `json:"auto_redirect,omitempty"`
AutoRedirectInputMark FwMark `json:"auto_redirect_input_mark,omitempty"`
AutoRedirectOutputMark FwMark `json:"auto_redirect_output_mark,omitempty"`
AutoRedirectResetMark FwMark `json:"auto_redirect_reset_mark,omitempty"`
AutoRedirectNFQueue uint16 `json:"auto_redirect_nfqueue,omitempty"`
ExcludeMPTCP bool `json:"exclude_mptcp,omitempty"`
LoopbackAddress badoption.Listable[netip.Addr] `json:"loopback_address,omitempty"`
StrictRoute bool `json:"strict_route,omitempty"`
RouteAddress badoption.Listable[netip.Prefix] `json:"route_address,omitempty"`
RouteAddressSet badoption.Listable[string] `json:"route_address_set,omitempty"`
RouteExcludeAddress badoption.Listable[netip.Prefix] `json:"route_exclude_address,omitempty"`
RouteExcludeAddressSet badoption.Listable[string] `json:"route_exclude_address_set,omitempty"`
IncludeInterface badoption.Listable[string] `json:"include_interface,omitempty"`
ExcludeInterface badoption.Listable[string] `json:"exclude_interface,omitempty"`
IncludeUID badoption.Listable[uint32] `json:"include_uid,omitempty"`
IncludeUIDRange badoption.Listable[string] `json:"include_uid_range,omitempty"`
ExcludeUID badoption.Listable[uint32] `json:"exclude_uid,omitempty"`
ExcludeUIDRange badoption.Listable[string] `json:"exclude_uid_range,omitempty"`
IncludeAndroidUser badoption.Listable[int] `json:"include_android_user,omitempty"`
IncludePackage badoption.Listable[string] `json:"include_package,omitempty"`
ExcludePackage badoption.Listable[string] `json:"exclude_package,omitempty"`
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
Stack string `json:"stack,omitempty"`
Platform *TunPlatformOptions `json:"platform,omitempty"`
InterfaceName string `json:"interface_name,omitempty"`
MTU uint32 `json:"mtu,omitempty"`
Address badoption.Listable[netip.Prefix] `json:"address,omitempty"`
AutoRoute bool `json:"auto_route,omitempty"`
IPRoute2TableIndex int `json:"iproute2_table_index,omitempty"`
IPRoute2RuleIndex int `json:"iproute2_rule_index,omitempty"`
AutoRedirect bool `json:"auto_redirect,omitempty"`
AutoRedirectInputMark FwMark `json:"auto_redirect_input_mark,omitempty"`
AutoRedirectOutputMark FwMark `json:"auto_redirect_output_mark,omitempty"`
AutoRedirectResetMark FwMark `json:"auto_redirect_reset_mark,omitempty"`
AutoRedirectNFQueue uint16 `json:"auto_redirect_nfqueue,omitempty"`
AutoRedirectFallbackRuleIndex int `json:"auto_redirect_iproute2_fallback_rule_index,omitempty"`
ExcludeMPTCP bool `json:"exclude_mptcp,omitempty"`
LoopbackAddress badoption.Listable[netip.Addr] `json:"loopback_address,omitempty"`
StrictRoute bool `json:"strict_route,omitempty"`
RouteAddress badoption.Listable[netip.Prefix] `json:"route_address,omitempty"`
RouteAddressSet badoption.Listable[string] `json:"route_address_set,omitempty"`
RouteExcludeAddress badoption.Listable[netip.Prefix] `json:"route_exclude_address,omitempty"`
RouteExcludeAddressSet badoption.Listable[string] `json:"route_exclude_address_set,omitempty"`
IncludeInterface badoption.Listable[string] `json:"include_interface,omitempty"`
ExcludeInterface badoption.Listable[string] `json:"exclude_interface,omitempty"`
IncludeUID badoption.Listable[uint32] `json:"include_uid,omitempty"`
IncludeUIDRange badoption.Listable[string] `json:"include_uid_range,omitempty"`
ExcludeUID badoption.Listable[uint32] `json:"exclude_uid,omitempty"`
ExcludeUIDRange badoption.Listable[string] `json:"exclude_uid_range,omitempty"`
IncludeAndroidUser badoption.Listable[int] `json:"include_android_user,omitempty"`
IncludePackage badoption.Listable[string] `json:"include_package,omitempty"`
ExcludePackage badoption.Listable[string] `json:"exclude_package,omitempty"`
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
Stack string `json:"stack,omitempty"`
Platform *TunPlatformOptions `json:"platform,omitempty"`
InboundOptions
// Deprecated: removed
+31 -29
View File
@@ -174,6 +174,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
if ruleIndex == 0 {
ruleIndex = tun.DefaultIPRoute2RuleIndex
}
autoRedirectFallbackRuleIndex := options.AutoRedirectFallbackRuleIndex
inputMark := uint32(options.AutoRedirectInputMark)
if inputMark == 0 {
inputMark = tun.DefaultAutoRedirectInputMark
@@ -200,35 +201,36 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
logger: logger,
inboundOptions: options.InboundOptions,
tunOptions: tun.Options{
Name: options.InterfaceName,
MTU: tunMTU,
GSO: enableGSO,
Inet4Address: inet4Address,
Inet6Address: inet6Address,
AutoRoute: options.AutoRoute,
IPRoute2TableIndex: tableIndex,
IPRoute2RuleIndex: ruleIndex,
AutoRedirectInputMark: inputMark,
AutoRedirectOutputMark: outputMark,
AutoRedirectResetMark: resetMark,
AutoRedirectNFQueue: nfQueue,
ExcludeMPTCP: options.ExcludeMPTCP,
Inet4LoopbackAddress: common.Filter(options.LoopbackAddress, netip.Addr.Is4),
Inet6LoopbackAddress: common.Filter(options.LoopbackAddress, netip.Addr.Is6),
StrictRoute: options.StrictRoute,
IncludeInterface: options.IncludeInterface,
ExcludeInterface: options.ExcludeInterface,
Inet4RouteAddress: inet4RouteAddress,
Inet6RouteAddress: inet6RouteAddress,
Inet4RouteExcludeAddress: inet4RouteExcludeAddress,
Inet6RouteExcludeAddress: inet6RouteExcludeAddress,
IncludeUID: includeUID,
ExcludeUID: excludeUID,
IncludeAndroidUser: options.IncludeAndroidUser,
IncludePackage: options.IncludePackage,
ExcludePackage: options.ExcludePackage,
InterfaceMonitor: networkManager.InterfaceMonitor(),
EXP_MultiPendingPackets: multiPendingPackets,
Name: options.InterfaceName,
MTU: tunMTU,
GSO: enableGSO,
Inet4Address: inet4Address,
Inet6Address: inet6Address,
AutoRoute: options.AutoRoute,
IPRoute2TableIndex: tableIndex,
IPRoute2RuleIndex: ruleIndex,
IPRoute2AutoRedirectFallbackRuleIndex: autoRedirectFallbackRuleIndex,
AutoRedirectInputMark: inputMark,
AutoRedirectOutputMark: outputMark,
AutoRedirectResetMark: resetMark,
AutoRedirectNFQueue: nfQueue,
ExcludeMPTCP: options.ExcludeMPTCP,
Inet4LoopbackAddress: common.Filter(options.LoopbackAddress, netip.Addr.Is4),
Inet6LoopbackAddress: common.Filter(options.LoopbackAddress, netip.Addr.Is6),
StrictRoute: options.StrictRoute,
IncludeInterface: options.IncludeInterface,
ExcludeInterface: options.ExcludeInterface,
Inet4RouteAddress: inet4RouteAddress,
Inet6RouteAddress: inet6RouteAddress,
Inet4RouteExcludeAddress: inet4RouteExcludeAddress,
Inet6RouteExcludeAddress: inet6RouteExcludeAddress,
IncludeUID: includeUID,
ExcludeUID: excludeUID,
IncludeAndroidUser: options.IncludeAndroidUser,
IncludePackage: options.IncludePackage,
ExcludePackage: options.ExcludePackage,
InterfaceMonitor: networkManager.InterfaceMonitor(),
EXP_MultiPendingPackets: multiPendingPackets,
},
udpTimeout: udpTimeout,
stack: options.Stack,
+2 -2
View File
@@ -21,13 +21,13 @@ define Download/geoip
HASH:=ed2de9add79623e2e5dbc5930ee39cc7037a7c6e0ecd58ba528b6f73d61457b5
endef
GEOSITE_VER:=20260128050754
GEOSITE_VER:=20260129053508
GEOSITE_FILE:=dlc.dat.$(GEOSITE_VER)
define Download/geosite
URL:=https://github.com/v2fly/domain-list-community/releases/download/$(GEOSITE_VER)/
URL_FILE:=dlc.dat
FILE:=$(GEOSITE_FILE)
HASH:=90582c09fed4583bae5a3645182034abecc134ae13176fe8754a8ae53fea26d7
HASH:=792b3745f5a16d5e5cc7a24a762027e7f39bb612651a0ff362801bc09b74ff88
endef
GEOSITE_IRAN_VER:=202601260049
+2 -2
View File
@@ -12,8 +12,8 @@ android {
applicationId = "com.v2ray.ang"
minSdk = 24
targetSdk = 36
versionCode = 706
versionName = "2.0.6"
versionCode = 707
versionName = "2.0.7"
multiDexEnabled = true
val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';')
@@ -1,4 +1,4 @@
package com.v2ray.ang.ui
package com.v2ray.ang.contracts
/**
* A common Adapter -> host callback interface that includes common actions: edit, remove and refresh.
@@ -1,4 +1,4 @@
package com.v2ray.ang.ui
package com.v2ray.ang.contracts
import com.v2ray.ang.dto.ProfileItem
@@ -1,4 +1,4 @@
package com.v2ray.ang.service
package com.v2ray.ang.contracts
import android.app.Service
@@ -1,4 +1,4 @@
package com.v2ray.ang.service
package com.v2ray.ang.contracts
/**
* Interface that defines the control operations for tun2socks implementations.
@@ -0,0 +1,6 @@
package com.v2ray.ang.dto
data class AssetUrlCache(
val guid: String,
val assetUrl: AssetUrlItem
)
@@ -5,6 +5,7 @@ import com.v2ray.ang.AppConfig.PORT_SOCKS
import com.v2ray.ang.AppConfig.TAG_BLOCKED
import com.v2ray.ang.AppConfig.TAG_DIRECT
import com.v2ray.ang.AppConfig.TAG_PROXY
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.util.Utils
data class ProfileItem(
@@ -42,6 +43,7 @@ data class ProfileItem(
var insecure: Boolean? = null,
var echConfigList: String? = null,
var echForceQuery: String? = null,
var pinnedCA256: String? = null,
var publicKey: String? = null,
var shortId: String? = null,
@@ -0,0 +1,6 @@
package com.v2ray.ang.dto
data class SubscriptionCache(
val guid: String,
val subscription: SubscriptionItem
)
@@ -2,6 +2,7 @@ package com.v2ray.ang.dto
import com.google.gson.annotations.SerializedName
import com.v2ray.ang.AppConfig
import com.v2ray.ang.enums.EConfigType
data class V2rayConfig(
var remarks: String? = null,
@@ -271,6 +272,7 @@ data class V2rayConfig(
val enableSessionResumption: Boolean? = null,
var echConfigList: String? = null,
var echForceQuery: String? = null,
var pinnedPeerCertSha256: String? = null,
// REALITY settings
val show: Boolean = false,
var publicKey: String? = null,
@@ -1,8 +1,7 @@
package com.v2ray.ang.dto
package com.v2ray.ang.enums
import com.v2ray.ang.AppConfig
enum class EConfigType(val value: Int, val protocolScheme: String) {
VMESS(1, AppConfig.VMESS),
CUSTOM(2, AppConfig.CUSTOM),
@@ -21,4 +20,4 @@ enum class EConfigType(val value: Int, val protocolScheme: String) {
companion object {
fun fromInt(value: Int) = entries.firstOrNull { it.value == value }
}
}
}
@@ -1,4 +1,4 @@
package com.v2ray.ang.dto
package com.v2ray.ang.enums
enum class Language(val code: String) {
AUTO("auto"),
@@ -1,4 +1,4 @@
package com.v2ray.ang.dto
package com.v2ray.ang.enums
enum class NetworkType(val type: String) {
TCP("tcp"),
@@ -1,4 +1,4 @@
package com.v2ray.ang.dto
package com.v2ray.ang.enums
import android.Manifest
import android.os.Build
@@ -1,4 +1,4 @@
package com.v2ray.ang.dto
package com.v2ray.ang.enums
enum class RoutingType(val fileName: String) {
WHITE("custom_routing_white"),
@@ -1,4 +1,4 @@
package com.v2ray.ang.dto
package com.v2ray.ang.enums
/**
* VPN interface address configuration enum class
@@ -11,6 +11,7 @@ import com.v2ray.ang.AngApplication
import es.dmoral.toasty.Toasty
import java.io.Serializable
import java.net.URI
import java.util.Locale
val Context.v2RayApplication: AngApplication?
get() = applicationContext as? AngApplication
@@ -92,7 +93,7 @@ fun Long.toTrafficString(): String {
size /= DIVISOR
unitIndex++
}
return String.format("%.1f %s", size, units[unitIndex])
return String.format(Locale.getDefault(), "%.1f %s", size, units[unitIndex])
}
val URI.idnHost: String
@@ -105,6 +106,13 @@ val URI.idnHost: String
*/
fun String?.removeWhiteSpace(): String? = this?.replace(" ", "")
/**
* Returns null if the string is null or blank, otherwise returns the string itself.
*
* @return The string or null.
*/
fun String?.nullIfBlank(): String? = this?.takeIf { it.isNotBlank() }
/**
* Converts the string to a Long value, or returns 0 if the conversion fails.
*
@@ -1,6 +1,6 @@
package com.v2ray.ang.fmt
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.dto.V2rayConfig
import com.v2ray.ang.util.JsonUtil
@@ -1,9 +1,9 @@
package com.v2ray.ang.fmt
import com.v2ray.ang.AppConfig
import com.v2ray.ang.dto.NetworkType
import com.v2ray.ang.enums.NetworkType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.extension.isNotNullEmpty
import com.v2ray.ang.extension.nullIfBlank
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.util.HttpUtil
import com.v2ray.ang.util.Utils
@@ -83,6 +83,7 @@ open class FmtBase {
config.fingerPrint = queryParam["fp"]
config.alpn = queryParam["alpn"]
config.echConfigList = queryParam["ech"]
config.pinnedCA256 = queryParam["pcs"]
config.publicKey = queryParam["pbk"]
config.shortId = queryParam["sid"]
config.spiderX = queryParam["spx"]
@@ -99,15 +100,16 @@ open class FmtBase {
fun getQueryDic(config: ProfileItem): HashMap<String, String> {
val dicQuery = HashMap<String, String>()
dicQuery["security"] = config.security?.ifEmpty { "none" }.orEmpty()
config.sni.let { if (it.isNotNullEmpty()) dicQuery["sni"] = it.orEmpty() }
config.alpn.let { if (it.isNotNullEmpty()) dicQuery["alpn"] = it.orEmpty() }
config.echConfigList.let { if (it.isNotNullEmpty()) dicQuery["ech"] = it.orEmpty() }
config.fingerPrint.let { if (it.isNotNullEmpty()) dicQuery["fp"] = it.orEmpty() }
config.publicKey.let { if (it.isNotNullEmpty()) dicQuery["pbk"] = it.orEmpty() }
config.shortId.let { if (it.isNotNullEmpty()) dicQuery["sid"] = it.orEmpty() }
config.spiderX.let { if (it.isNotNullEmpty()) dicQuery["spx"] = it.orEmpty() }
config.mldsa65Verify.let { if (it.isNotNullEmpty()) dicQuery["pqv"] = it.orEmpty() }
config.flow.let { if (it.isNotNullEmpty()) dicQuery["flow"] = it.orEmpty() }
config.sni?.nullIfBlank()?.let { dicQuery["sni"] = it }
config.alpn?.nullIfBlank()?.let { dicQuery["alpn"] = it }
config.echConfigList?.nullIfBlank()?.let { dicQuery["ech"] = it }
config.pinnedCA256?.nullIfBlank()?.let { dicQuery["pcs"] = it }
config.fingerPrint?.nullIfBlank()?.let { dicQuery["fp"] = it }
config.publicKey?.nullIfBlank()?.let { dicQuery["pbk"] = it }
config.shortId?.nullIfBlank()?.let { dicQuery["sid"] = it }
config.spiderX?.nullIfBlank()?.let { dicQuery["spx"] = it }
config.mldsa65Verify?.nullIfBlank()?.let { dicQuery["pqv"] = it }
config.flow?.nullIfBlank()?.let { dicQuery["flow"] = it }
// Add two keys for compatibility: "insecure" and "allowInsecure"
if (config.security == AppConfig.TLS) {
val insecureFlag = if (config.insecure == true) "1" else "0"
@@ -121,42 +123,42 @@ open class FmtBase {
when (networkType) {
NetworkType.TCP -> {
dicQuery["headerType"] = config.headerType?.ifEmpty { "none" }.orEmpty()
config.host.let { if (it.isNotNullEmpty()) dicQuery["host"] = it.orEmpty() }
config.host?.nullIfBlank()?.let { dicQuery["host"] = it }
}
NetworkType.KCP -> {
dicQuery["headerType"] = config.headerType?.ifEmpty { "none" }.orEmpty()
config.seed.let { if (it.isNotNullEmpty()) dicQuery["seed"] = it.orEmpty() }
config.seed?.nullIfBlank()?.let { dicQuery["seed"] = it }
}
NetworkType.WS, NetworkType.HTTP_UPGRADE -> {
config.host.let { if (it.isNotNullEmpty()) dicQuery["host"] = it.orEmpty() }
config.path.let { if (it.isNotNullEmpty()) dicQuery["path"] = it.orEmpty() }
config.host?.nullIfBlank()?.let { dicQuery["host"] = it }
config.path?.nullIfBlank()?.let { dicQuery["path"] = it }
}
NetworkType.XHTTP -> {
config.host.let { if (it.isNotNullEmpty()) dicQuery["host"] = it.orEmpty() }
config.path.let { if (it.isNotNullEmpty()) dicQuery["path"] = it.orEmpty() }
config.xhttpMode.let { if (it.isNotNullEmpty()) dicQuery["mode"] = it.orEmpty() }
config.xhttpExtra.let { if (it.isNotNullEmpty()) dicQuery["extra"] = it.orEmpty() }
config.host?.nullIfBlank()?.let { dicQuery["host"] = it }
config.path?.nullIfBlank()?.let { dicQuery["path"] = it }
config.xhttpMode?.nullIfBlank()?.let { dicQuery["mode"] = it }
config.xhttpExtra?.nullIfBlank()?.let { dicQuery["extra"] = it }
}
NetworkType.HTTP, NetworkType.H2 -> {
dicQuery["type"] = "http"
config.host.let { if (it.isNotNullEmpty()) dicQuery["host"] = it.orEmpty() }
config.path.let { if (it.isNotNullEmpty()) dicQuery["path"] = it.orEmpty() }
config.host?.nullIfBlank()?.let { dicQuery["host"] = it }
config.path?.nullIfBlank()?.let { dicQuery["path"] = it }
}
// NetworkType.QUIC -> {
// dicQuery["headerType"] = config.headerType?.ifEmpty { "none" }.orEmpty()
// config.quicSecurity.let { if (it.isNotNullEmpty()) dicQuery["quicSecurity"] = it.orEmpty() }
// config.quicKey.let { if (it.isNotNullEmpty()) dicQuery["key"] = it.orEmpty() }
// config.quicSecurity?.nullIfBlank()?.let { dicQuery["quicSecurity"] = it }
// config.quicKey?.nullIfBlank()?.let { dicQuery["key"] = it }
// }
NetworkType.GRPC -> {
config.mode.let { if (it.isNotNullEmpty()) dicQuery["mode"] = it.orEmpty() }
config.authority.let { if (it.isNotNullEmpty()) dicQuery["authority"] = it.orEmpty() }
config.serviceName.let { if (it.isNotNullEmpty()) dicQuery["serviceName"] = it.orEmpty() }
config.mode?.nullIfBlank()?.let { dicQuery["mode"] = it }
config.authority?.nullIfBlank()?.let { dicQuery["authority"] = it }
config.serviceName?.nullIfBlank()?.let { dicQuery["serviceName"] = it }
}
else -> {}
@@ -1,6 +1,6 @@
package com.v2ray.ang.fmt
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.dto.V2rayConfig.OutboundBean
import com.v2ray.ang.extension.isNotNullEmpty
@@ -1,14 +1,15 @@
package com.v2ray.ang.fmt
import com.v2ray.ang.AppConfig
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.NetworkType
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.enums.NetworkType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.dto.V2rayConfig.OutboundBean
import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean
import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean.UdpMasksBean.UdpMasksSettingsBean
import com.v2ray.ang.extension.idnHost
import com.v2ray.ang.extension.isNotNullEmpty
import com.v2ray.ang.extension.nullIfBlank
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.V2rayConfigManager
import com.v2ray.ang.util.Utils
@@ -61,8 +62,8 @@ object Hysteria2Fmt : FmtBase() {
val dicQuery = HashMap<String, String>()
config.security.let { if (it != null) dicQuery["security"] = it }
config.sni.let { if (it.isNotNullEmpty()) dicQuery["sni"] = it.orEmpty() }
config.alpn.let { if (it.isNotNullEmpty()) dicQuery["alpn"] = it.orEmpty() }
config.sni?.nullIfBlank()?.let { dicQuery["sni"] = it }
config.alpn?.nullIfBlank()?.let { dicQuery["alpn"] = it }
config.insecure.let { dicQuery["insecure"] = if (it == true) "1" else "0" }
if (config.obfsPassword.isNotNullEmpty()) {
@@ -2,8 +2,8 @@ package com.v2ray.ang.fmt
import android.util.Log
import com.v2ray.ang.AppConfig
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.NetworkType
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.enums.NetworkType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.dto.V2rayConfig.OutboundBean
import com.v2ray.ang.extension.idnHost
@@ -1,6 +1,6 @@
package com.v2ray.ang.fmt
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.dto.V2rayConfig.OutboundBean
import com.v2ray.ang.extension.idnHost
@@ -1,8 +1,8 @@
package com.v2ray.ang.fmt
import com.v2ray.ang.AppConfig
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.NetworkType
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.enums.NetworkType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.dto.V2rayConfig.OutboundBean
import com.v2ray.ang.extension.idnHost
@@ -1,7 +1,7 @@
package com.v2ray.ang.fmt
import com.v2ray.ang.AppConfig
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.dto.V2rayConfig.OutboundBean
import com.v2ray.ang.extension.idnHost
@@ -3,13 +3,13 @@ package com.v2ray.ang.fmt
import android.text.TextUtils
import android.util.Log
import com.v2ray.ang.AppConfig
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.NetworkType
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.enums.NetworkType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.dto.V2rayConfig.OutboundBean
import com.v2ray.ang.dto.VmessQRCode
import com.v2ray.ang.extension.idnHost
import com.v2ray.ang.extension.isNotNullEmpty
import com.v2ray.ang.extension.nullIfBlank
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.V2rayConfigManager
import com.v2ray.ang.util.JsonUtil
@@ -132,8 +132,8 @@ object VmessFmt : FmtBase() {
else -> {}
}
config.host.let { if (it.isNotNullEmpty()) vmessQRCode.host = it.orEmpty() }
config.path.let { if (it.isNotNullEmpty()) vmessQRCode.path = it.orEmpty() }
config.host?.nullIfBlank()?.let { vmessQRCode.host = it }
config.path?.nullIfBlank()?.let { vmessQRCode.path = it }
vmessQRCode.tls = config.security.orEmpty()
vmessQRCode.sni = config.sni.orEmpty()
@@ -2,10 +2,11 @@ package com.v2ray.ang.fmt
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V4
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.dto.V2rayConfig.OutboundBean
import com.v2ray.ang.extension.idnHost
import com.v2ray.ang.extension.nullIfBlank
import com.v2ray.ang.extension.removeWhiteSpace
import com.v2ray.ang.handler.V2rayConfigManager
import com.v2ray.ang.util.Utils
@@ -32,7 +33,7 @@ object WireguardFmt : FmtBase() {
config.secretKey = uri.userInfo.orEmpty()
config.localAddress = queryParam["address"] ?: WIREGUARD_LOCAL_ADDRESS_V4
config.publicKey = queryParam["publickey"].orEmpty()
config.preSharedKey = queryParam["presharedkey"]?.takeIf { it.isNotEmpty() }
config.preSharedKey = queryParam["presharedkey"]?.nullIfBlank()
config.mtu = Utils.parseInt(queryParam["mtu"] ?: AppConfig.WIREGUARD_LOCAL_MTU)
config.reserved = queryParam["reserved"] ?: "0,0,0"
@@ -84,7 +85,7 @@ object WireguardFmt : FmtBase() {
config.localAddress = interfaceParams["address"] ?: WIREGUARD_LOCAL_ADDRESS_V4
config.mtu = Utils.parseInt(interfaceParams["mtu"] ?: AppConfig.WIREGUARD_LOCAL_MTU)
config.publicKey = peerParams["publickey"].orEmpty()
config.preSharedKey = peerParams["presharedkey"]?.takeIf { it.isNotEmpty() }
config.preSharedKey = peerParams["presharedkey"]?.nullIfBlank()
val endpoint = peerParams["endpoint"].orEmpty()
val endpointParts = endpoint.split(":", limit = 2)
if (endpointParts.size == 2) {
@@ -113,7 +114,7 @@ object WireguardFmt : FmtBase() {
wireguard.address = (profileItem.localAddress ?: WIREGUARD_LOCAL_ADDRESS_V4).split(",")
wireguard.peers?.firstOrNull()?.let { peer ->
peer.publicKey = profileItem.publicKey.orEmpty()
peer.preSharedKey = profileItem.preSharedKey?.takeIf { it.isNotEmpty() }
peer.preSharedKey = profileItem.preSharedKey?.nullIfBlank()
peer.endpoint = Utils.getIpv6Address(profileItem.server) + ":${profileItem.serverPort}"
}
wireguard.mtu = profileItem.mtu
@@ -7,8 +7,9 @@ import android.util.Log
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.HY2
import com.v2ray.ang.R
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.dto.SubscriptionCache
import com.v2ray.ang.dto.SubscriptionItem
import com.v2ray.ang.fmt.CustomFmt
import com.v2ray.ang.fmt.Hysteria2Fmt
@@ -399,28 +400,28 @@ object AngConfigManager {
* @param it The subscription item.
* @return The number of configurations updated.
*/
fun updateConfigViaSub(it: Pair<String, SubscriptionItem>): Int {
fun updateConfigViaSub(it: SubscriptionCache): Int {
try {
if (TextUtils.isEmpty(it.first)
|| TextUtils.isEmpty(it.second.remarks)
|| TextUtils.isEmpty(it.second.url)
if (TextUtils.isEmpty(it.guid)
|| TextUtils.isEmpty(it.subscription.remarks)
|| TextUtils.isEmpty(it.subscription.url)
) {
return 0
}
if (!it.second.enabled) {
if (!it.subscription.enabled) {
return 0
}
val url = HttpUtil.toIdnUrl(it.second.url)
val url = HttpUtil.toIdnUrl(it.subscription.url)
if (!Utils.isValidUrl(url)) {
return 0
}
if (!it.second.allowInsecureUrl) {
if (!it.subscription.allowInsecureUrl) {
if (!Utils.isValidSubUrl(url)) {
return 0
}
}
Log.i(AppConfig.TAG, url)
val userAgent = it.second.userAgent
val userAgent = it.subscription.userAgent
var configText = try {
val httpPort = SettingsManager.getHttpPort()
@@ -440,11 +441,11 @@ object AngConfigManager {
if (configText.isEmpty()) {
return 0
}
val count = parseConfigViaSub(configText, it.first, false)
val count = parseConfigViaSub(configText, it.guid, false)
if (count > 0) {
it.second.lastUpdated = System.currentTimeMillis()
MmkvManager.encodeSubscription(it.first, it.second)
Log.i(AppConfig.TAG, "Subscription updated: ${it.second.remarks}, $count configs")
it.subscription.lastUpdated = System.currentTimeMillis()
MmkvManager.encodeSubscription(it.guid, it.subscription)
Log.i(AppConfig.TAG, "Subscription updated: ${it.subscription.remarks}, $count configs")
}
return count
} catch (e: Exception) {
@@ -481,7 +482,7 @@ object AngConfigManager {
private fun importUrlAsSubscription(url: String): Int {
val subscriptions = MmkvManager.decodeSubscriptions()
subscriptions.forEach {
if (it.second.url == url) {
if (it.subscription.url == url) {
return 0
}
}
@@ -3,10 +3,12 @@ package com.v2ray.ang.handler
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig.PREF_IS_BOOTED
import com.v2ray.ang.AppConfig.PREF_ROUTING_RULESET
import com.v2ray.ang.dto.AssetUrlCache
import com.v2ray.ang.dto.AssetUrlItem
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.dto.RulesetItem
import com.v2ray.ang.dto.ServerAffiliationInfo
import com.v2ray.ang.dto.SubscriptionCache
import com.v2ray.ang.dto.SubscriptionItem
import com.v2ray.ang.dto.WebDavConfig
import com.v2ray.ang.util.JsonUtil
@@ -309,15 +311,15 @@ object MmkvManager {
*
* @return The list of subscriptions.
*/
fun decodeSubscriptions(): List<Pair<String, SubscriptionItem>> {
fun decodeSubscriptions(): List<SubscriptionCache> {
initSubsList()
val subscriptions = mutableListOf<Pair<String, SubscriptionItem>>()
val subscriptions = mutableListOf<SubscriptionCache>()
decodeSubsList().forEach { key ->
val json = subStorage.decodeString(key)
if (!json.isNullOrBlank()) {
val item = JsonUtil.fromJson(json, SubscriptionItem::class.java)?: SubscriptionItem()
subscriptions.add(Pair(key, item))
subscriptions.add(SubscriptionCache(key, item))
}
}
return subscriptions
@@ -397,16 +399,16 @@ object MmkvManager {
*
* @return The list of asset URLs.
*/
fun decodeAssetUrls(): List<Pair<String, AssetUrlItem>> {
val assetUrlItems = mutableListOf<Pair<String, AssetUrlItem>>()
fun decodeAssetUrls(): List<AssetUrlCache> {
val assetUrlItems = mutableListOf<AssetUrlCache>()
assetStorage.allKeys()?.forEach { key ->
val json = assetStorage.decodeString(key)
if (!json.isNullOrBlank()) {
val item = JsonUtil.fromJson(json, AssetUrlItem::class.java)?: AssetUrlItem()
assetUrlItems.add(Pair(key, item))
assetUrlItems.add(AssetUrlCache(key, item))
}
}
return assetUrlItems.sortedBy { (_, value) -> value.addedTime }
return assetUrlItems.sortedBy { it.assetUrl.addedTime }
}
/**
@@ -11,13 +11,13 @@ import com.v2ray.ang.AppConfig.GEOIP_PRIVATE
import com.v2ray.ang.AppConfig.GEOSITE_PRIVATE
import com.v2ray.ang.AppConfig.TAG_DIRECT
import com.v2ray.ang.AppConfig.VPN
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.Language
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.enums.Language
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.dto.RoutingType
import com.v2ray.ang.enums.RoutingType
import com.v2ray.ang.dto.RulesetItem
import com.v2ray.ang.dto.V2rayConfig
import com.v2ray.ang.dto.VpnInterfaceAddressConfig
import com.v2ray.ang.enums.VpnInterfaceAddressConfig
import com.v2ray.ang.handler.MmkvManager.decodeServerConfig
import com.v2ray.ang.handler.MmkvManager.decodeServerList
import com.v2ray.ang.util.JsonUtil
@@ -36,10 +36,10 @@ object SubscriptionUpdater {
override suspend fun doWork(): Result {
Log.i(AppConfig.TAG, "subscription automatic update starting")
val subs = MmkvManager.decodeSubscriptions().filter { it.second.autoUpdate }
val subs = MmkvManager.decodeSubscriptions().filter { it.subscription.autoUpdate }
for (sub in subs) {
val subItem = sub.second
val subItem = sub.subscription
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notification.setChannelId(AppConfig.SUBSCRIPTION_UPDATE_CHANNEL)
@@ -53,7 +53,7 @@ object SubscriptionUpdater {
}
notificationManager.notify(3, notification.build())
Log.i(AppConfig.TAG, "subscription automatic update: ---${subItem.remarks}")
AngConfigManager.updateConfigViaSub(Pair(sub.first, subItem))
AngConfigManager.updateConfigViaSub(sub)
notification.setContentText("Updating ${subItem.remarks}")
}
notificationManager.cancel(3)
@@ -10,10 +10,10 @@ import android.util.Log
import androidx.core.content.ContextCompat
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.extension.toast
import com.v2ray.ang.service.ServiceControl
import com.v2ray.ang.contracts.ServiceControl
import com.v2ray.ang.service.V2RayProxyOnlyService
import com.v2ray.ang.service.V2RayVpnService
import com.v2ray.ang.util.MessageUtil
@@ -151,6 +151,7 @@ object V2RayServiceManager {
}
try {
NotificationManager.showNotification(currentConfig)
coreController.startLoop(result.content, tunFd)
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to start Core loop", e)
@@ -165,7 +166,7 @@ object V2RayServiceManager {
try {
MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_SUCCESS, "")
NotificationManager.showNotification(currentConfig)
//NotificationManager.showNotification(currentConfig)
NotificationManager.startSpeedNotification(currentConfig)
} catch (e: Exception) {
@@ -6,8 +6,8 @@ import android.util.Log
import com.google.gson.JsonArray
import com.v2ray.ang.AppConfig
import com.v2ray.ang.dto.ConfigResult
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.NetworkType
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.enums.NetworkType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.dto.RulesetItem
import com.v2ray.ang.dto.V2rayConfig
@@ -16,6 +16,7 @@ import com.v2ray.ang.dto.V2rayConfig.OutboundBean.OutSettingsBean
import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean
import com.v2ray.ang.dto.V2rayConfig.RoutingBean.RulesBean
import com.v2ray.ang.extension.isNotNullEmpty
import com.v2ray.ang.extension.nullIfBlank
import com.v2ray.ang.fmt.HttpFmt
import com.v2ray.ang.fmt.Hysteria2Fmt
import com.v2ray.ang.fmt.ShadowsocksFmt
@@ -1296,28 +1297,21 @@ object V2rayConfigManager {
} else {
profileItem.sni
}
val fingerprint = profileItem.fingerPrint
val alpns = profileItem.alpn
val echConfigList = profileItem.echConfigList
val echForceQuery = profileItem.echForceQuery
val publicKey = profileItem.publicKey
val shortId = profileItem.shortId
val spiderX = profileItem.spiderX
val mldsa65Verify = profileItem.mldsa65Verify
streamSettings.security = if (streamSecurity.isEmpty()) null else streamSecurity
streamSettings.security = streamSecurity.nullIfBlank()
if (streamSettings.security == null) return
val tlsSetting = StreamSettingsBean.TlsSettingsBean(
allowInsecure = allowInsecure,
serverName = if (sni.isNullOrEmpty()) null else sni,
fingerprint = if (fingerprint.isNullOrEmpty()) null else fingerprint,
alpn = if (alpns.isNullOrEmpty()) null else alpns.split(",").map { it.trim() }.filter { it.isNotEmpty() },
echConfigList = if (echConfigList.isNullOrEmpty()) null else echConfigList,
echForceQuery = if (echForceQuery.isNullOrEmpty()) null else echForceQuery,
publicKey = if (publicKey.isNullOrEmpty()) null else publicKey,
shortId = if (shortId.isNullOrEmpty()) null else shortId,
spiderX = if (spiderX.isNullOrEmpty()) null else spiderX,
mldsa65Verify = if (mldsa65Verify.isNullOrEmpty()) null else mldsa65Verify,
serverName = sni.nullIfBlank(),
fingerprint = profileItem.fingerPrint.nullIfBlank(),
alpn = profileItem.alpn?.split(",")?.map { it.trim() }?.filter { it.isNotEmpty() }.takeIf { !it.isNullOrEmpty() },
echConfigList = profileItem.echConfigList.nullIfBlank(),
echForceQuery = profileItem.echForceQuery.nullIfBlank(),
pinnedPeerCertSha256 = profileItem.pinnedCA256.nullIfBlank(),
publicKey = profileItem.publicKey.nullIfBlank(),
shortId = profileItem.shortId.nullIfBlank(),
spiderX = profileItem.spiderX.nullIfBlank(),
mldsa65Verify = profileItem.mldsa65Verify.nullIfBlank(),
)
if (streamSettings.security == AppConfig.TLS) {
streamSettings.tlsSettings = tlsSetting
@@ -1,7 +1,10 @@
package com.v2ray.ang.handler
package com.v2ray.ang.helper
import androidx.preference.PreferenceDataStore
import com.v2ray.ang.AppConfig
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.SettingsChangeManager
import com.v2ray.ang.handler.SettingsManager
/**
* PreferenceDataStore implementation that bridges AndroidX Preference framework to MMKV storage.
@@ -6,7 +6,7 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.v2ray.ang.R
import com.v2ray.ang.dto.PermissionType
import com.v2ray.ang.enums.PermissionType
import com.v2ray.ang.extension.toast
/**
@@ -4,6 +4,7 @@ import android.content.Context
import android.os.ParcelFileDescriptor
import android.util.Log
import com.v2ray.ang.AppConfig
import com.v2ray.ang.contracts.Tun2SocksControl
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.SettingsManager
import java.io.File
@@ -4,6 +4,7 @@ import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.IBinder
import com.v2ray.ang.contracts.ServiceControl
import com.v2ray.ang.handler.SettingsManager
import com.v2ray.ang.handler.V2RayServiceManager
import com.v2ray.ang.util.MyContextWrapper
@@ -18,6 +18,8 @@ import androidx.annotation.RequiresApi
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.LOOPBACK
import com.v2ray.ang.BuildConfig
import com.v2ray.ang.contracts.ServiceControl
import com.v2ray.ang.contracts.Tun2SocksControl
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.NotificationManager
import com.v2ray.ang.handler.SettingsManager
@@ -76,7 +78,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
}
override fun onRevoke() {
stopV2Ray()
stopAllService()
}
// override fun onLowMemory() {
@@ -105,11 +107,15 @@ class V2RayVpnService : VpnService(), ServiceControl {
Log.e(AppConfig.TAG, "Failed to create VPN interface")
return
}
V2RayServiceManager.startCoreLoop(mInterface)
if (!V2RayServiceManager.startCoreLoop(mInterface)) {
Log.e(AppConfig.TAG, "Failed to start V2Ray core loop")
stopAllService()
return
}
}
override fun stopService() {
stopV2Ray(true)
stopAllService(true)
}
override fun vpnProtect(socket: Int): Boolean {
@@ -130,10 +136,14 @@ class V2RayVpnService : VpnService(), ServiceControl {
private fun setupVpnService() {
val prepare = prepare(this)
if (prepare != null) {
Log.e(AppConfig.TAG, "VPN preparation failed")
stopSelf()
return
}
if (configureVpnService() != true) {
Log.e(AppConfig.TAG, "VPN configuration failed")
stopSelf()
return
}
@@ -170,7 +180,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
return true
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to establish VPN interface", e)
stopV2Ray()
stopAllService()
}
return false
}
@@ -311,11 +321,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
tun2SocksService?.startTun2Socks()
}
/**
* Stops the V2Ray service.
* @param isForced Whether to force stop the service.
*/
private fun stopV2Ray(isForced: Boolean = true) {
private fun stopAllService(isForced: Boolean = true) {
// val configName = defaultDPreference.getPrefString(PREF_CURR_CONFIG_GUID, "")
// val emptyInfo = VpnNetworkInfo()
// val info = loadVpnNetworkInfo(configName, emptyInfo)!! + (lastNetworkInfo ?: emptyInfo)
@@ -27,7 +27,7 @@ import java.io.File
import java.text.SimpleDateFormat
import java.util.Locale
class BackupActivity : BaseActivity() {
class BackupActivity : HelperBaseActivity() {
private val binding by lazy { ActivityBackupBinding.inflate(layoutInflater) }
private val config_backup_options: Array<out String> by lazy {
@@ -1,7 +1,6 @@
package com.v2ray.ang.ui
import android.content.Context
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MenuItem
@@ -20,10 +19,6 @@ import com.v2ray.ang.R
import com.v2ray.ang.handler.SettingsManager
import com.v2ray.ang.helper.CustomDividerItemDecoration
import com.v2ray.ang.util.MyContextWrapper
import com.v2ray.ang.dto.PermissionType
import com.v2ray.ang.helper.FileChooserHelper
import com.v2ray.ang.helper.PermissionHelper
import com.v2ray.ang.helper.QRCodeScannerHelper
import com.v2ray.ang.util.Utils
@@ -41,15 +36,9 @@ import com.v2ray.ang.util.Utils
abstract class BaseActivity : AppCompatActivity() {
// Progress indicator that sits at the bottom of the toolbar
private var progressBar: LinearProgressIndicator? = null
private lateinit var fileChooser : FileChooserHelper
private lateinit var permissionRequester : PermissionHelper
private lateinit var qrCodeScanner : QRCodeScannerHelper
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
fileChooser = FileChooserHelper(this)
permissionRequester = PermissionHelper(this)
qrCodeScanner = QRCodeScannerHelper(this)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
if (!Utils.getDarkModeStatus(this)) {
@@ -224,61 +213,4 @@ abstract class BaseActivity : AppCompatActivity() {
protected fun isLoadingVisible(): Boolean {
return progressBar?.visibility == View.VISIBLE
}
/**
* Check if permission is granted and request it if not.
* Convenience method that delegates to permissionRequester.
*
* @param permissionType The type of permission to check and request
* @param onGranted Callback to execute when permission is granted
*/
protected fun checkAndRequestPermission(
permissionType: PermissionType,
onGranted: () -> Unit
) {
permissionRequester.request(permissionType, onGranted)
}
/**
* Launch file chooser with ACTION_GET_CONTENT intent.
* Convenience method that delegates to fileChooser helper.
*
* @param mimeType MIME type filter for files
* @param onResult Callback invoked with the selected file URI (null if cancelled)
*/
protected fun launchFileChooser(
mimeType: String = "*/*",
onResult: (Uri?) -> Unit
) {
checkAndRequestPermission(PermissionType.READ_STORAGE) {
fileChooser.launch(mimeType, onResult)
}
}
/**
* Launch document creator to create a new file at user-selected location.
* Convenience method that delegates to fileChooser helper.
* Note: No permission check needed as CreateDocument uses Storage Access Framework.
*
* @param fileName Default file name for the new document
* @param onResult Callback invoked with the created file URI (null if cancelled)
*/
protected fun launchCreateDocument(
fileName: String,
onResult: (Uri?) -> Unit
) {
fileChooser.createDocument(fileName, onResult)
}
/**
* Launch QR code scanner with camera permission check.
* Convenience method that delegates to qrCodeScanner helper.
*
* @param onResult Callback invoked with the scan result string (null if cancelled or failed)
*/
protected fun launchQRCodeScanner(onResult: (String?) -> Unit) {
checkAndRequestPermission(PermissionType.CAMERA) {
qrCodeScanner.launch(onResult)
}
}
}
@@ -13,9 +13,10 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.contracts.MainAdapterListener
import com.v2ray.ang.databinding.FragmentGroupServerBinding
import com.v2ray.ang.databinding.ItemQrcodeBinding
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.extension.toast
import com.v2ray.ang.extension.toastError
@@ -0,0 +1,90 @@
package com.v2ray.ang.ui
import android.net.Uri
import android.os.Bundle
import com.v2ray.ang.enums.PermissionType
import com.v2ray.ang.helper.FileChooserHelper
import com.v2ray.ang.helper.PermissionHelper
import com.v2ray.ang.helper.QRCodeScannerHelper
/**
* HelperBaseActivity extends BaseActivity and provides additional helpers for
* activities that need file chooser, permission requesting, or QR code scanning functionality.
*
* Activities that don't need these features should extend BaseActivity directly.
* Activities that need file selection, permissions, or QR code scanning should extend this class.
*
* Additional Responsibilities:
* - Provide file chooser helpers for selecting and creating files.
* - Provide permission request helpers with callbacks.
* - Provide QR code scanning helpers with camera permission handling.
*/
abstract class HelperBaseActivity : BaseActivity() {
private lateinit var fileChooser : FileChooserHelper
private lateinit var permissionRequester : PermissionHelper
private lateinit var qrCodeScanner : QRCodeScannerHelper
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
fileChooser = FileChooserHelper(this)
permissionRequester = PermissionHelper(this)
qrCodeScanner = QRCodeScannerHelper(this)
}
/**
* Check if permission is granted and request it if not.
* Convenience method that delegates to permissionRequester.
*
* @param permissionType The type of permission to check and request
* @param onGranted Callback to execute when permission is granted
*/
protected fun checkAndRequestPermission(
permissionType: PermissionType,
onGranted: () -> Unit
) {
permissionRequester.request(permissionType, onGranted)
}
/**
* Launch file chooser with ACTION_GET_CONTENT intent.
* Convenience method that delegates to fileChooser helper.
*
* @param mimeType MIME type filter for files
* @param onResult Callback invoked with the selected file URI (null if cancelled)
*/
protected fun launchFileChooser(
mimeType: String = "*/*",
onResult: (Uri?) -> Unit
) {
checkAndRequestPermission(PermissionType.READ_STORAGE) {
fileChooser.launch(mimeType, onResult)
}
}
/**
* Launch document creator to create a new file at user-selected location.
* Convenience method that delegates to fileChooser helper.
* Note: No permission check needed as CreateDocument uses Storage Access Framework.
*
* @param fileName Default file name for the new document
* @param onResult Callback invoked with the created file URI (null if cancelled)
*/
protected fun launchCreateDocument(
fileName: String,
onResult: (Uri?) -> Unit
) {
fileChooser.createDocument(fileName, onResult)
}
/**
* Launch QR code scanner with camera permission check.
* Convenience method that delegates to qrCodeScanner helper.
*
* @param onResult Callback invoked with the scan result string (null if cancelled or failed)
*/
protected fun launchQRCodeScanner(onResult: (String?) -> Unit) {
checkAndRequestPermission(PermissionType.CAMERA) {
qrCodeScanner.launch(onResult)
}
}
}
@@ -24,7 +24,8 @@ import com.google.android.material.tabs.TabLayoutMediator
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityMainBinding
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.enums.PermissionType
import com.v2ray.ang.extension.toast
import com.v2ray.ang.extension.toastError
import com.v2ray.ang.handler.AngConfigManager
@@ -32,7 +33,6 @@ import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.SettingsChangeManager
import com.v2ray.ang.handler.SettingsManager
import com.v2ray.ang.handler.V2RayServiceManager
import com.v2ray.ang.dto.PermissionType
import com.v2ray.ang.util.Utils
import com.v2ray.ang.viewmodel.MainViewModel
import kotlinx.coroutines.Dispatchers
@@ -40,7 +40,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener {
class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelectedListener {
private val binding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
@@ -9,6 +9,7 @@ import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.contracts.MainAdapterListener
import com.v2ray.ang.databinding.ItemRecyclerFooterBinding
import com.v2ray.ang.databinding.ItemRecyclerMainBinding
import com.v2ray.ang.dto.ProfileItem
@@ -8,6 +8,7 @@ import androidx.lifecycle.lifecycleScope
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityRoutingEditBinding
import com.v2ray.ang.dto.RulesetItem
import com.v2ray.ang.extension.nullIfBlank
import com.v2ray.ang.extension.toast
import com.v2ray.ang.extension.toastSuccess
import com.v2ray.ang.handler.SettingsManager
@@ -62,14 +63,11 @@ class RoutingEditActivity : BaseActivity() {
rulesetItem.apply {
remarks = binding.etRemarks.text.toString()
locked = binding.chkLocked.isChecked
domain = binding.etDomain.text.toString().takeIf { it.isNotEmpty() }
?.split(",")?.map { it.trim() }?.filter { it.isNotEmpty() }
ip = binding.etIp.text.toString().takeIf { it.isNotEmpty() }
?.split(",")?.map { it.trim() }?.filter { it.isNotEmpty() }
protocol = binding.etProtocol.text.toString().takeIf { it.isNotEmpty() }
?.split(",")?.map { it.trim() }?.filter { it.isNotEmpty() }
port = binding.etPort.text.toString().takeIf { it.isNotEmpty() }
network = binding.etNetwork.text.toString().takeIf { it.isNotEmpty() }
domain = binding.etDomain.text.toString().nullIfBlank()?.split(",")?.map { it.trim() }?.filter { it.isNotEmpty() }
ip = binding.etIp.text.toString().nullIfBlank()?.split(",")?.map { it.trim() }?.filter { it.isNotEmpty() }
protocol = binding.etProtocol.text.toString().nullIfBlank()?.split(",")?.map { it.trim() }?.filter { it.isNotEmpty() }
port = binding.etPort.text.toString().nullIfBlank()
network = binding.etNetwork.text.toString().nullIfBlank()
outboundTag = outbound_tag[binding.spOutboundTag.selectedItemPosition]
}
@@ -13,6 +13,7 @@ import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.contracts.BaseAdapterListener
import com.v2ray.ang.databinding.ActivityRoutingSettingBinding
import com.v2ray.ang.extension.toastError
import com.v2ray.ang.extension.toastSuccess
@@ -20,14 +21,13 @@ import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.SettingsManager
import com.v2ray.ang.helper.SimpleItemTouchHelperCallback
import com.v2ray.ang.util.JsonUtil
import com.v2ray.ang.dto.PermissionType
import com.v2ray.ang.util.Utils
import com.v2ray.ang.viewmodel.RoutingSettingsViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class RoutingSettingActivity : BaseActivity() {
class RoutingSettingActivity : HelperBaseActivity() {
private val binding by lazy { ActivityRoutingSettingBinding.inflate(layoutInflater) }
private val ownerActivity: RoutingSettingActivity
get() = this
@@ -6,6 +6,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import com.v2ray.ang.contracts.BaseAdapterListener
import com.v2ray.ang.databinding.ItemRecyclerRoutingSettingBinding
import com.v2ray.ang.helper.ItemTouchHelperAdapter
import com.v2ray.ang.helper.ItemTouchHelperViewHolder
@@ -7,7 +7,7 @@ import com.v2ray.ang.extension.toastError
import com.v2ray.ang.extension.toastSuccess
import com.v2ray.ang.handler.AngConfigManager
class ScScannerActivity : BaseActivity() {
class ScScannerActivity : HelperBaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -11,15 +11,13 @@ import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityNoneBinding
import com.v2ray.ang.extension.toast
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.dto.PermissionType
import com.v2ray.ang.extension.toastError
import com.v2ray.ang.util.QRCodeDecoder
import io.github.g00fy2.quickie.QRResult
import io.github.g00fy2.quickie.ScanCustomCode
import io.github.g00fy2.quickie.config.BarcodeFormat
import io.github.g00fy2.quickie.config.ScannerConfig
class ScannerActivity : BaseActivity() {
class ScannerActivity : HelperBaseActivity() {
private val binding by lazy { ActivityNoneBinding.inflate(layoutInflater) }
private val scanQrCode = registerForActivityResult(ScanCustomCode(), ::handleResult)
@@ -20,8 +20,8 @@ import com.v2ray.ang.AppConfig.TLS
import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_ADDRESS_V4
import com.v2ray.ang.AppConfig.WIREGUARD_LOCAL_MTU
import com.v2ray.ang.R
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.NetworkType
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.enums.NetworkType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.extension.isNotNullEmpty
import com.v2ray.ang.extension.toast
@@ -137,6 +137,8 @@ class ServerActivity : BaseActivity() {
private val container_ech_config_list: LinearLayout? by lazy { findViewById(R.id.lay_ech_config_list) }
private val sp_ech_force_query: Spinner? by lazy { findViewById(R.id.sp_ech_force_query) }
private val container_ech_force_query: LinearLayout? by lazy { findViewById(R.id.lay_ech_force_query) }
private val et_pinned_ca256: EditText? by lazy { findViewById(R.id.et_pinned_ca256) }
private val container_pinned_ca256: LinearLayout? by lazy { findViewById(R.id.lay_pinned_ca256) }
override fun onCreate(savedInstanceState: Bundle?) {
@@ -274,7 +276,8 @@ class ServerActivity : BaseActivity() {
container_spider_x,
container_mldsa65_verify,
container_ech_config_list,
container_ech_force_query
container_ech_force_query,
container_pinned_ca256
).forEach { it?.visibility = View.GONE }
}
@@ -286,7 +289,8 @@ class ServerActivity : BaseActivity() {
container_alpn,
container_allow_insecure,
container_ech_config_list,
container_ech_force_query
container_ech_force_query,
container_pinned_ca256
).forEach { it?.visibility = View.VISIBLE }
listOf(
container_public_key,
@@ -306,7 +310,8 @@ class ServerActivity : BaseActivity() {
container_alpn,
container_allow_insecure,
container_ech_config_list,
container_ech_force_query
container_ech_force_query,
container_pinned_ca256
).forEach { it?.visibility = View.GONE }
listOf(
container_public_key,
@@ -394,6 +399,7 @@ class ServerActivity : BaseActivity() {
val index = Utils.arrayFind(echForceQuerys, it)
index.let { sp_ech_force_query?.setSelection(if (it >= 0) it else 0) }
}
et_pinned_ca256?.text = Utils.getEditable(config.pinnedCA256)
} else if (config.security == REALITY) {
et_public_key?.text = Utils.getEditable(config.publicKey.orEmpty())
et_short_id?.text = Utils.getEditable(config.shortId.orEmpty())
@@ -566,6 +572,7 @@ class ServerActivity : BaseActivity() {
val mldsa65Verify = et_mldsa65_verify?.text?.toString()
val echConfigList = et_ech_config_list?.text?.toString()
val echForceQueryIndex = sp_ech_force_query?.selectedItemPosition ?: 0
val pinnedCA256 = et_pinned_ca256?.text?.toString()
val allowInsecure =
if (allowInsecureField == null || allowinsecures[allowInsecureField].isBlank()) {
@@ -585,6 +592,7 @@ class ServerActivity : BaseActivity() {
config.mldsa65Verify = mldsa65Verify
config.echConfigList = echConfigList
config.echForceQuery = echForceQuerys[echForceQueryIndex]
config.pinnedCA256 = pinnedCA256
}
private fun transportTypes(network: String?): Array<out String> {
@@ -11,7 +11,7 @@ import com.blacksquircle.ui.language.json.JsonLanguage
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityServerCustomConfigBinding
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.extension.toast
import com.v2ray.ang.extension.toastSuccess
@@ -8,7 +8,7 @@ import android.widget.ArrayAdapter
import androidx.appcompat.app.AlertDialog
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityServerGroupBinding
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.enums.EConfigType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.extension.toast
import com.v2ray.ang.extension.toastSuccess
@@ -119,13 +119,13 @@ class ServerGroupActivity : BaseActivity() {
val displayList = mutableListOf(getString(R.string.filter_config_all)) //none
subIds.clear()
subIds.add("") // index 0 => All
subs.forEach { (id, item) ->
subs.forEach { sub ->
val name = when {
item.remarks.isNotBlank() -> item.remarks
else -> id
sub.subscription.remarks.isNotBlank() -> sub.subscription.remarks
else -> sub.guid
}
displayList.add(name)
subIds.add(id)
subIds.add(sub.guid)
}
val subAdapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, displayList)
subAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
@@ -15,7 +15,7 @@ import com.v2ray.ang.AppConfig.VPN
import com.v2ray.ang.R
import com.v2ray.ang.extension.toLongEx
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.MmkvPreferenceDataStore
import com.v2ray.ang.helper.MmkvPreferenceDataStore
import com.v2ray.ang.handler.SubscriptionUpdater
import com.v2ray.ang.util.Utils
import java.util.concurrent.TimeUnit
@@ -14,6 +14,7 @@ import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.contracts.BaseAdapterListener
import com.v2ray.ang.databinding.ActivitySubSettingBinding
import com.v2ray.ang.databinding.ItemQrcodeBinding
import com.v2ray.ang.extension.toast
@@ -6,6 +6,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.v2ray.ang.contracts.BaseAdapterListener
import com.v2ray.ang.databinding.ItemRecyclerSubSettingBinding
import com.v2ray.ang.helper.ItemTouchHelperAdapter
import com.v2ray.ang.helper.ItemTouchHelperViewHolder
@@ -21,8 +22,8 @@ class SubSettingRecyclerAdapter(
override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
val subscriptions = viewModel.getAll()
val subId = subscriptions[position].first
val subItem = subscriptions[position].second
val subId = subscriptions[position].guid
val subItem = subscriptions[position].subscription
holder.itemSubSettingBinding.tvName.text = subItem.remarks
holder.itemSubSettingBinding.tvUrl.text = subItem.url
holder.itemSubSettingBinding.chkEnable.isChecked = subItem.enabled
@@ -23,7 +23,8 @@ class TaskerActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
//setContentView(binding.root)
setContentViewWithToolbar(binding.root, showHomeAsUp = true, title = "")
//add def value
lstData.add("Default")
@@ -14,6 +14,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.contracts.BaseAdapterListener
import com.v2ray.ang.databinding.ActivityUserAssetBinding
import com.v2ray.ang.dto.AssetUrlItem
import com.v2ray.ang.extension.toast
@@ -21,7 +22,6 @@ import com.v2ray.ang.extension.toastError
import com.v2ray.ang.extension.toastSuccess
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.SettingsManager
import com.v2ray.ang.dto.PermissionType
import com.v2ray.ang.util.Utils
import com.v2ray.ang.viewmodel.UserAssetViewModel
import kotlinx.coroutines.Dispatchers
@@ -29,7 +29,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
class UserAssetActivity : BaseActivity() {
class UserAssetActivity : HelperBaseActivity() {
private val binding by lazy { ActivityUserAssetBinding.inflate(layoutInflater) }
private val ownerActivity: UserAssetActivity
get() = this
@@ -103,7 +103,7 @@ class UserAssetActivity : BaseActivity() {
)
val assetList = MmkvManager.decodeAssetUrls()
if (assetList.any { it.second.remarks == assetItem.remarks && it.first != assetId }) {
if (assetList.any { it.assetUrl.remarks == assetItem.remarks && it.guid != assetId }) {
toast(R.string.msg_remark_is_duplicate)
} else {
MmkvManager.encodeAsset(assetId, assetItem)
@@ -212,10 +212,10 @@ class UserAssetActivity : BaseActivity() {
}
override fun onRemove(guid: String, position: Int) {
val asset = viewModel.getAsset(position)?.takeIf { it.first == guid }
?: viewModel.getAssets().find { it.first == guid }
val asset = viewModel.getAsset(position)?.takeIf { it.guid == guid }
?: viewModel.getAssets().find { it.guid == guid }
?: return
val file = extDir.listFiles()?.find { it.name == asset.second.remarks }
val file = extDir.listFiles()?.find { it.name == asset.assetUrl.remarks }
AlertDialog.Builder(ownerActivity).setMessage(R.string.del_config_comfirm)
.setPositiveButton(android.R.string.ok) { _, _ ->
@@ -6,6 +6,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.v2ray.ang.R
import com.v2ray.ang.contracts.BaseAdapterListener
import com.v2ray.ang.databinding.ItemRecyclerUserAssetBinding
import com.v2ray.ang.extension.toTrafficString
import com.v2ray.ang.viewmodel.UserAssetViewModel
@@ -34,9 +35,9 @@ class UserAssetAdapter(
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: UserAssetViewHolder, position: Int) {
val item = viewModel.getAsset(position) ?: return
val file = extDir.listFiles()?.find { it.name == item.second.remarks }
val file = extDir.listFiles()?.find { it.name == item.assetUrl.remarks }
holder.itemUserAssetBinding.assetName.text = item.second.remarks
holder.itemUserAssetBinding.assetName.text = item.assetUrl.remarks
if (file != null) {
val dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM)
@@ -47,10 +48,10 @@ class UserAssetAdapter(
holder.itemUserAssetBinding.root.context.getString(R.string.msg_file_not_found)
}
if (item.second.locked == true) {
if (item.assetUrl.locked == true) {
holder.itemUserAssetBinding.layoutEdit.visibility = View.GONE
} else {
holder.itemUserAssetBinding.layoutEdit.visibility = if (item.second.url == "file") {
holder.itemUserAssetBinding.layoutEdit.visibility = if (item.assetUrl.url == "file") {
View.GONE
} else {
View.VISIBLE
@@ -58,10 +59,10 @@ class UserAssetAdapter(
}
holder.itemUserAssetBinding.layoutEdit.setOnClickListener {
adapterListener?.onEdit(item.first, position)
adapterListener?.onEdit(item.guid, position)
}
holder.itemUserAssetBinding.layoutRemove.setOnClickListener {
adapterListener?.onRemove(item.first, position)
adapterListener?.onRemove(item.guid, position)
}
}

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