You've already forked KernelSU-Next
mirror of
https://github.com/KernelSU-Next/KernelSU-Next.git
synced 2025-08-27 23:46:34 +00:00
Compare commits
405 Commits
v1.0.4
...
5c61a70e5a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c61a70e5a | ||
|
|
fdf1d61735 | ||
|
|
93dc61e113 | ||
|
|
d80a3ebcda | ||
|
|
4270fd8b1e | ||
|
|
764dbc3782 | ||
|
|
080ab9a952 | ||
|
|
a9cab5ccfd | ||
|
|
3d44602537 | ||
|
|
88eb2a2723 | ||
|
|
e272e557b0 | ||
|
|
2a4794e422 | ||
|
|
818bdbead6 | ||
|
|
76249fa67d | ||
|
|
ed50b57b57 | ||
|
|
f05f776a08 | ||
|
|
c9b79c3016 | ||
|
|
3ff10d6622 | ||
|
|
b95d2b69b6 | ||
|
|
2cd8453877 | ||
|
|
b7300b0525 | ||
|
|
3a278d560f | ||
|
|
03fa2eddb2 | ||
|
|
b74e953ad2 | ||
|
|
39717b0a3f | ||
|
|
7f0eccd3d5 | ||
|
|
78eb3b0b22 | ||
|
|
39f20bf573 | ||
|
|
092eb1b23d | ||
|
|
30e2ed5db5 | ||
|
|
dc7ae2db5f | ||
|
|
b3b7ef1cb3 | ||
|
|
4de4d1e091 | ||
|
|
0beea57ab7 | ||
|
|
49aee1ff4c | ||
|
|
f7a3699fe3 | ||
|
|
66d42de599 | ||
|
|
d562594ae1 | ||
|
|
02afc6710c | ||
|
|
9d9f9ed5d5 | ||
|
|
b3b6320946 | ||
|
|
d43b8320ad | ||
|
|
a2260ae330 | ||
|
|
aa221acad1 | ||
|
|
c41d35db62 | ||
|
|
e5f0c733b8 | ||
|
|
70b52f85e9 | ||
|
|
303b5192ec | ||
|
|
443d3a6abe | ||
|
|
a2a7383343 | ||
|
|
ed64f933f5 | ||
|
|
4c7ad8eac4 | ||
|
|
6a922febcd | ||
|
|
71eba5df8b | ||
|
|
f1ef0afc20 | ||
|
|
844c9f94e9 | ||
|
|
07a4d3457c | ||
|
|
8c9728df95 | ||
|
|
14ec1194e4 | ||
|
|
a37f398cc7 | ||
|
|
80bd797737 | ||
|
|
600d9ce5d2 | ||
|
|
f15d9b18e9 | ||
|
|
aa19e8c609 | ||
|
|
2b2320000c | ||
|
|
06135cc827 | ||
|
|
fc58bdf0e2 | ||
|
|
9b5e60912d | ||
|
|
c108a8ed32 | ||
|
|
adce657583 | ||
|
|
d6601e1e54 | ||
|
|
0d4efa649f | ||
|
|
85f4e6ac27 | ||
|
|
c91f9c18ec | ||
|
|
bf35f73430 | ||
|
|
ad290a51a0 | ||
|
|
d218346613 | ||
|
|
502e5599fe | ||
|
|
11fb52b929 | ||
|
|
057388ccef | ||
|
|
67759ad723 | ||
|
|
f231cbdba7 | ||
|
|
7abc9bc821 | ||
|
|
e7697d86fe | ||
|
|
68394fddd5 | ||
|
|
90d34bf511 | ||
|
|
236fbc7615 | ||
|
|
6871cbdba7 | ||
|
|
38a9949211 | ||
|
|
9fba0faa43 | ||
|
|
b1250b002e | ||
|
|
10f7d5cf50 | ||
|
|
0c883ddfd6 | ||
|
|
3921175e4c | ||
|
|
84fdcf8bf5 | ||
|
|
886cfd5a33 | ||
|
|
aea384bdd4 | ||
|
|
84695cea71 | ||
|
|
f91afe6c46 | ||
|
|
3f7e731df6 | ||
|
|
582662dce9 | ||
|
|
f3b49723e8 | ||
|
|
c4deee1e49 | ||
|
|
6a6fc07cd4 | ||
|
|
d8cb7ef772 | ||
|
|
dddf2f06a0 | ||
|
|
e7e935aeb2 | ||
|
|
cb146200aa | ||
|
|
609926bff1 | ||
|
|
8803058521 | ||
|
|
c74805b12b | ||
|
|
43870237fc | ||
|
|
ca24085b5b | ||
|
|
93191c2b8b | ||
|
|
ebc3ded2b2 | ||
|
|
d9d1c874ab | ||
|
|
08477fc361 | ||
|
|
11836b876f | ||
|
|
bf20965c46 | ||
|
|
22a48e52eb | ||
|
|
15b703b5f2 | ||
|
|
18f0eb8a36 | ||
|
|
32cdcc6fbe | ||
|
|
437c4b9bc5 | ||
|
|
0a42dbf5ba | ||
|
|
ea4f319898 | ||
|
|
7b6b944106 | ||
|
|
59c966771e | ||
|
|
a83c20b667 | ||
|
|
d5c4f85d73 | ||
|
|
36f683a299 | ||
|
|
652e9719ed | ||
|
|
011b658422 | ||
|
|
5fa1050e1b | ||
|
|
39617497ca | ||
|
|
44ad960da7 | ||
|
|
db223a1e92 | ||
|
|
ea4b8f51c2 | ||
|
|
407826396b | ||
|
|
124547c9c5 | ||
|
|
db7a9df880 | ||
|
|
29725214f7 | ||
|
|
9189de4db1 | ||
|
|
fec0032883 | ||
|
|
1ee737b1aa | ||
|
|
2a32e1d746 | ||
|
|
bdaf50ab0f | ||
|
|
37a129080a | ||
|
|
024b6ce292 | ||
|
|
b13a12e3d5 | ||
|
|
9d9738eed0 | ||
|
|
135fe1e5d4 | ||
|
|
2d033b6b87 | ||
|
|
9048ffbdab | ||
|
|
d43fdd3cb4 | ||
|
|
14d16eff46 | ||
|
|
1a603c2b48 | ||
|
|
2092161fe0 | ||
|
|
a8b92f50cc | ||
|
|
4556c9a5d1 | ||
|
|
e6fcae4f02 | ||
|
|
74ab5488e1 | ||
|
|
5cdec242eb | ||
|
|
e938a499f6 | ||
|
|
84b5915eea | ||
|
|
540ce1d2c3 | ||
|
|
b40f2af0f0 | ||
|
|
324d09a61e | ||
|
|
d5dfecefea | ||
|
|
ce37e17c87 | ||
|
|
3b1d5f15f4 | ||
|
|
714ec4695b | ||
|
|
3795d92d7a | ||
|
|
eed685507a | ||
|
|
26d3ec14a6 | ||
|
|
96d475407a | ||
|
|
27d8bc458f | ||
|
|
519f86c47e | ||
|
|
980f71c1bd | ||
|
|
7692665428 | ||
|
|
aaca0b5283 | ||
|
|
e5a495489d | ||
|
|
e07a6fb3ff | ||
|
|
b8c2660996 | ||
|
|
ec2ecdcacb | ||
|
|
3c3ab77f65 | ||
|
|
ffb2c89c36 | ||
|
|
bda62cc8a1 | ||
|
|
a052af4180 | ||
|
|
8835c37536 | ||
|
|
69f3c9f6ab | ||
|
|
af3e0bd6a5 | ||
|
|
5b14512323 | ||
|
|
6f176ad1c4 | ||
|
|
bc9d720a3c | ||
|
|
93c5013251 | ||
|
|
bc5c993093 | ||
|
|
759b3c5baf | ||
|
|
e1303d13a3 | ||
|
|
8824115697 | ||
|
|
a78a1e7d2e | ||
|
|
9e150b2c44 | ||
|
|
217d230b61 | ||
|
|
ba1b3c4fc7 | ||
|
|
5f871cd713 | ||
|
|
4a37422af5 | ||
|
|
a081fc87c9 | ||
|
|
0e8286e195 | ||
|
|
e9d53c4084 | ||
|
|
697a0ac9fc | ||
|
|
7f12e1c19a | ||
|
|
bf99cb50fd | ||
|
|
1333f0113f | ||
|
|
1b544bd22d | ||
|
|
24f514c949 | ||
|
|
8eed26e0a1 | ||
|
|
3c5b3f0a49 | ||
|
|
1d23b4bb67 | ||
|
|
48e533f660 | ||
|
|
86fbed60eb | ||
|
|
164341b543 | ||
|
|
94942fe488 | ||
|
|
ca64f6c8ac | ||
|
|
e8ef483098 | ||
|
|
85faf43fdd | ||
|
|
09620c269e | ||
|
|
0c05a4c375 | ||
|
|
298aa7960e | ||
|
|
b112513df0 | ||
|
|
f19b5a453a | ||
|
|
72e54653a2 | ||
|
|
5696d72a3f | ||
|
|
757d20166a | ||
|
|
1336996129 | ||
|
|
4f35240203 | ||
|
|
5565a6b3f8 | ||
|
|
9dbe135e66 | ||
|
|
fe5c031701 | ||
|
|
0511a90640 | ||
|
|
2c87dfbb35 | ||
|
|
9b4d07008d | ||
|
|
c40218a919 | ||
|
|
0c410b87ec | ||
|
|
114a07a440 | ||
|
|
babae3b2ad | ||
|
|
a7a9f86a71 | ||
|
|
c65fcd5133 | ||
|
|
b298b4203a | ||
|
|
250310fc9f | ||
|
|
89cf05d3a4 | ||
|
|
1d258196b1 | ||
|
|
63ebf730f9 | ||
|
|
810889a964 | ||
|
|
0a97eee7ad | ||
|
|
cc8f0e456a | ||
|
|
0f7e8d3214 | ||
|
|
2c193e0dd4 | ||
|
|
2512239ea7 | ||
|
|
6f6b797e22 | ||
|
|
d5f25590a5 | ||
|
|
bf81c9a17b | ||
|
|
5cec5aa656 | ||
|
|
02dbb7d0f7 | ||
|
|
c5aa6ffffc | ||
|
|
335ea3190f | ||
|
|
35c98aee76 | ||
|
|
dc992818de | ||
|
|
dfea38e0f2 | ||
|
|
6452427b6c | ||
|
|
feb3c47bdc | ||
|
|
de44373599 | ||
|
|
4bd7e7cc9f | ||
|
|
39014fb89c | ||
|
|
eeecbf255a | ||
|
|
11e4729519 | ||
|
|
f3093eace6 | ||
|
|
3a7edf48bf | ||
|
|
de189fe426 | ||
|
|
30835787c9 | ||
|
|
9fc1bd1876 | ||
|
|
a223781fae | ||
|
|
2a154c1985 | ||
|
|
110b8e92dc | ||
|
|
3b87014bf9 | ||
|
|
bd0bcc4337 | ||
|
|
916f8151ea | ||
|
|
59ca8fa2c2 | ||
|
|
238f2ee008 | ||
|
|
7eaf37bc4a | ||
|
|
022ee4bb1c | ||
|
|
d209dc087e | ||
|
|
d79ea50c6e | ||
|
|
d8944d641c | ||
|
|
1c9705fdd0 | ||
|
|
f369297be9 | ||
|
|
a68b4fe7e0 | ||
|
|
52399f7fd1 | ||
|
|
7218a504c9 | ||
|
|
9d5999c8c3 | ||
|
|
204805852c | ||
|
|
36b42e611e | ||
|
|
c50bbd32aa | ||
|
|
d2f6d00327 | ||
|
|
c9e4c8e186 | ||
|
|
3a6c30fba1 | ||
|
|
d5d4304120 | ||
|
|
4eac2f783e | ||
|
|
2cc765ee0a | ||
|
|
453524d382 | ||
|
|
fccc3db5c5 | ||
|
|
7a08683c74 | ||
|
|
2f3f444905 | ||
|
|
7746569fe9 | ||
|
|
3a601f86a5 | ||
|
|
e7dab63837 | ||
|
|
487e7d0012 | ||
|
|
ee71a992a3 | ||
|
|
195e1ba494 | ||
|
|
a0e4d01269 | ||
|
|
b8b0dc724a | ||
|
|
39777f301d | ||
|
|
f20662c6b6 | ||
|
|
3532f20b2f | ||
|
|
14218e81be | ||
|
|
d104250770 | ||
|
|
6100df6f8c | ||
|
|
e597bd1c66 | ||
|
|
0038ba6566 | ||
|
|
7bebb2a461 | ||
|
|
f3fee49f8c | ||
|
|
e8c080ba09 | ||
|
|
785d8143fb | ||
|
|
cd0031e8a5 | ||
|
|
c1273d35f4 | ||
|
|
15371bb1d6 | ||
|
|
097451d578 | ||
|
|
85866848ea | ||
|
|
d05d16c15d | ||
|
|
bb6c20339c | ||
|
|
af012ce349 | ||
|
|
43d30c8f2b | ||
|
|
a89985f33e | ||
|
|
42896dfab1 | ||
|
|
36111f4b89 | ||
|
|
74171a2930 | ||
|
|
c40bfd694c | ||
|
|
aa22fd880d | ||
|
|
fe9578433c | ||
|
|
a0278b1e45 | ||
|
|
0c7ba4dc6e | ||
|
|
b98c531ff8 | ||
|
|
373025248d | ||
|
|
60d2ad39d6 | ||
|
|
b807b38892 | ||
|
|
5ca5e2b027 | ||
|
|
608e949e23 | ||
|
|
c9dd12d50b | ||
|
|
7319dd25a3 | ||
|
|
fdd307fe8b | ||
|
|
b6c8203a97 | ||
|
|
b2a8fb66d3 | ||
|
|
74f55dd807 | ||
|
|
8560f35c8b | ||
|
|
b0f01cf7ff | ||
|
|
8ae0192b47 | ||
|
|
f247a6d5d8 | ||
|
|
d85bff2943 | ||
|
|
f5ac0f3589 | ||
|
|
a2fcd157a2 | ||
|
|
70ce97660e | ||
|
|
67967a8251 | ||
|
|
b25a21770d | ||
|
|
bbb05b292a | ||
|
|
8696ed8f36 | ||
|
|
682f93667b | ||
|
|
00cab2209e | ||
|
|
b6fcea9277 | ||
|
|
623f5e3f64 | ||
|
|
b18f89b434 | ||
|
|
2a152fdb22 | ||
|
|
dbadad1027 | ||
|
|
fcc4f0d206 | ||
|
|
c20fe6c886 | ||
|
|
19a15a71eb | ||
|
|
c446ee76f5 | ||
|
|
5435992911 | ||
|
|
c2ae844f65 | ||
|
|
8832532f09 | ||
|
|
cba275cde2 | ||
|
|
4401e28f57 | ||
|
|
ece6e3f694 | ||
|
|
d4db74f0fb | ||
|
|
e931792a4f | ||
|
|
3876b6b474 | ||
|
|
ae36e2085c | ||
|
|
3a8f4a2596 | ||
|
|
87fc6b2784 | ||
|
|
25b57204ef | ||
|
|
4ed362bea4 | ||
|
|
a2976fd926 | ||
|
|
b2bcc93f9a | ||
|
|
57b8dd54c5 | ||
|
|
ef32f3f9d2 | ||
|
|
4ac264135d | ||
|
|
a05b790da6 |
4
.github/ISSUE_TEMPLATE/config.yml
vendored
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Feature Request
|
||||
url: https://t.me/ksunext_discussions
|
||||
about: "We accept external Feature Requests, see this link for more details."
|
||||
url: https://t.me/ksunext/578
|
||||
about: "We do not accept external Feature Requests anymore, check the link for more details."
|
||||
|
||||
2
.github/workflows/avd-kernel.yml
vendored
2
.github/workflows/avd-kernel.yml
vendored
@@ -37,7 +37,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: Build ${{ inputs.version_name }}
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Maximize build space
|
||||
uses: easimon/maximize-build-space@master
|
||||
|
||||
26
.github/workflows/build-debug-kernel.yml
vendored
26
.github/workflows/build-debug-kernel.yml
vendored
@@ -7,9 +7,9 @@ jobs:
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android12-5.10
|
||||
version_name: android12-5.10.226
|
||||
tag: android12-5.10-2024-11
|
||||
os_patch_level: 2024-11
|
||||
version_name: android12-5.10.236
|
||||
tag: android12-5.10-2025-05
|
||||
os_patch_level: 2025-05
|
||||
patch_path: "5.10"
|
||||
debug: true
|
||||
build-debug-kernel-a13:
|
||||
@@ -17,11 +17,11 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.10"
|
||||
sub_level: 223
|
||||
os_patch_level: 2024-11
|
||||
sub_level: 236
|
||||
os_patch_level: 2025-05
|
||||
- version: "5.15"
|
||||
sub_level: 167
|
||||
os_patch_level: 2024-11
|
||||
sub_level: 180
|
||||
os_patch_level: 2025-05
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android13-${{ matrix.version }}
|
||||
@@ -34,11 +34,11 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.15"
|
||||
sub_level: 167
|
||||
os_patch_level: 2024-11
|
||||
sub_level: 180
|
||||
os_patch_level: 2025-05
|
||||
- version: "6.1"
|
||||
sub_level: 115
|
||||
os_patch_level: 2024-12
|
||||
sub_level: 138
|
||||
os_patch_level: 2025-06
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android14-${{ matrix.version }}
|
||||
@@ -51,8 +51,8 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "6.6"
|
||||
sub_level: 57
|
||||
os_patch_level: 2024-12
|
||||
sub_level: 89
|
||||
os_patch_level: 2025-06
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android15-${{ matrix.version }}
|
||||
|
||||
12
.github/workflows/build-kernel-a12.yml
vendored
12
.github/workflows/build-kernel-a12.yml
vendored
@@ -21,12 +21,14 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- sub_level: 209
|
||||
os_patch_level: 2024-05
|
||||
- sub_level: 218
|
||||
os_patch_level: 2024-08
|
||||
- sub_level: 226
|
||||
os_patch_level: 2024-11
|
||||
- sub_level: 233
|
||||
os_patch_level: 2025-02
|
||||
- sub_level: 236
|
||||
os_patch_level: 2025-05
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
@@ -112,7 +114,7 @@ jobs:
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android12-5.10
|
||||
version_name: android12-5.10.223
|
||||
tag: android12-5.10-2024-11
|
||||
os_patch_level: 2024-11
|
||||
version_name: android12-5.10.236
|
||||
tag: android12-5.10-2025-05
|
||||
os_patch_level: 2025-05
|
||||
patch_path: "5.10"
|
||||
32
.github/workflows/build-kernel-a13.yml
vendored
32
.github/workflows/build-kernel-a13.yml
vendored
@@ -21,9 +21,6 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.10"
|
||||
sub_level: 209
|
||||
os_patch_level: 2024-05
|
||||
- version: "5.10"
|
||||
sub_level: 210
|
||||
os_patch_level: 2024-06
|
||||
@@ -36,9 +33,15 @@ jobs:
|
||||
- version: "5.10"
|
||||
sub_level: 223
|
||||
os_patch_level: 2024-11
|
||||
- version: "5.15"
|
||||
sub_level: 148
|
||||
os_patch_level: 2024-05
|
||||
- version: "5.10"
|
||||
sub_level: 228
|
||||
os_patch_level: 2025-01
|
||||
- version: "5.10"
|
||||
sub_level: 234
|
||||
os_patch_level: 2025-03
|
||||
- version: "5.10"
|
||||
sub_level: 236
|
||||
os_patch_level: 2025-05
|
||||
- version: "5.15"
|
||||
sub_level: 149
|
||||
os_patch_level: 2024-07
|
||||
@@ -51,6 +54,15 @@ jobs:
|
||||
- version: "5.15"
|
||||
sub_level: 167
|
||||
os_patch_level: 2024-11
|
||||
- version: "5.15"
|
||||
sub_level: 170
|
||||
os_patch_level: 2025-01
|
||||
- version: "5.15"
|
||||
sub_level: 178
|
||||
os_patch_level: 2025-03
|
||||
- version: "5.15"
|
||||
sub_level: 180
|
||||
os_patch_level: 2025-05
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
@@ -137,11 +149,11 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.10"
|
||||
sub_level: 223
|
||||
os_patch_level: 2024-11
|
||||
sub_level: 236
|
||||
os_patch_level: 2025-05
|
||||
- version: "5.15"
|
||||
sub_level: 167
|
||||
os_patch_level: 2024-11
|
||||
sub_level: 180
|
||||
os_patch_level: 2025-05
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android13-${{ matrix.version }}
|
||||
|
||||
41
.github/workflows/build-kernel-a14.yml
vendored
41
.github/workflows/build-kernel-a14.yml
vendored
@@ -21,9 +21,6 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.15"
|
||||
sub_level: 148
|
||||
os_patch_level: 2024-05
|
||||
- version: "5.15"
|
||||
sub_level: 149
|
||||
os_patch_level: 2024-06
|
||||
@@ -39,9 +36,15 @@ jobs:
|
||||
- version: "5.15"
|
||||
sub_level: 167
|
||||
os_patch_level: 2024-11
|
||||
- version: "6.1"
|
||||
sub_level: 75
|
||||
os_patch_level: 2024-05
|
||||
- version: "5.15"
|
||||
sub_level: 170
|
||||
os_patch_level: 2025-01
|
||||
- version: "5.15"
|
||||
sub_level: 178
|
||||
os_patch_level: 2025-03
|
||||
- version: "5.15"
|
||||
sub_level: 180
|
||||
os_patch_level: 2025-05
|
||||
- version: "6.1"
|
||||
sub_level: 78
|
||||
os_patch_level: 2024-06
|
||||
@@ -63,6 +66,24 @@ jobs:
|
||||
- version: "6.1"
|
||||
sub_level: 115
|
||||
os_patch_level: 2024-12
|
||||
- version: "6.1"
|
||||
sub_level: 118
|
||||
os_patch_level: 2025-01
|
||||
- version: "6.1"
|
||||
sub_level: 124
|
||||
os_patch_level: 2025-02
|
||||
- version: "6.1"
|
||||
sub_level: 128
|
||||
os_patch_level: 2025-03
|
||||
- version: "6.1"
|
||||
sub_level: 129
|
||||
os_patch_level: 2025-04
|
||||
- version: "6.1"
|
||||
sub_level: 134
|
||||
os_patch_level: 2025-05
|
||||
- version: "6.1"
|
||||
sub_level: 138
|
||||
os_patch_level: 2025-06
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
@@ -149,11 +170,11 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.15"
|
||||
sub_level: 167
|
||||
os_patch_level: 2024-11
|
||||
sub_level: 180
|
||||
os_patch_level: 2025-05
|
||||
- version: "6.1"
|
||||
sub_level: 115
|
||||
os_patch_level: 2024-12
|
||||
sub_level: 138
|
||||
os_patch_level: 2025-06
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android14-${{ matrix.version }}
|
||||
|
||||
22
.github/workflows/build-kernel-a15.yml
vendored
22
.github/workflows/build-kernel-a15.yml
vendored
@@ -36,6 +36,24 @@ jobs:
|
||||
- version: "6.6"
|
||||
sub_level: 57
|
||||
os_patch_level: 2024-12
|
||||
- version: "6.6"
|
||||
sub_level: 58
|
||||
os_patch_level: 2025-01
|
||||
- version: "6.6"
|
||||
sub_level: 66
|
||||
os_patch_level: 2025-02
|
||||
- version: "6.6"
|
||||
sub_level: 77
|
||||
os_patch_level: 2025-03
|
||||
- version: "6.6"
|
||||
sub_level: 82
|
||||
os_patch_level: 2025-04
|
||||
- version: "6.6"
|
||||
sub_level: 87
|
||||
os_patch_level: 2025-05
|
||||
- version: "6.6"
|
||||
sub_level: 89
|
||||
os_patch_level: 2025-06
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
@@ -122,8 +140,8 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "6.6"
|
||||
sub_level: 57
|
||||
os_patch_level: 2024-12
|
||||
sub_level: 89
|
||||
os_patch_level: 2025-06
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android15-${{ matrix.version }}
|
||||
|
||||
137
.github/workflows/build-kernel-arcvm.yml
vendored
Normal file
137
.github/workflows/build-kernel-arcvm.yml
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
name: Build Kernel - ChromeOS ARCVM
|
||||
on:
|
||||
push:
|
||||
branches: ["next"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-arcvm.yml"
|
||||
- "kernel/**"
|
||||
pull_request:
|
||||
branches: ["next"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-arcvm.yml"
|
||||
- "kernel/**"
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
git_tag: chromeos-5.10-arcvm
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.event_name != 'pull_request' || (github.event_name == 'pull_request' && !github.event.pull_request.draft)
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- arch: x86_64
|
||||
kernel_image_name: bzImage
|
||||
build_config: build.config.gki.x86_64
|
||||
defconfig: x86_64_arcvm_defconfig
|
||||
- arch: arm64
|
||||
kernel_image_name: Image
|
||||
build_config: build.config.gki.aarch64
|
||||
defconfig: arm64_arcvm_defconfig
|
||||
|
||||
name: Build ChromeOS ARCVM kernel
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
LTO: thin
|
||||
ROOT_DIR: /
|
||||
KERNEL_DIR: ${{ github.workspace }}/kernel
|
||||
|
||||
steps:
|
||||
- name: Install Build Tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends bc \
|
||||
bison build-essential ca-certificates flex git gnupg \
|
||||
libelf-dev libssl-dev lsb-release software-properties-common wget \
|
||||
libncurses-dev binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu nuget gzip \
|
||||
rsync python3 device-tree-compiler
|
||||
|
||||
sudo ln -s --force python3 /usr/bin/python
|
||||
|
||||
export LLVM_VERSION=14
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh $LLVM_VERSION
|
||||
rm ./llvm.sh
|
||||
sudo ln -s --force /usr/bin/clang-$LLVM_VERSION /usr/bin/clang
|
||||
sudo ln -s --force /usr/bin/ld.lld-$LLVM_VERSION /usr/bin/ld.lld
|
||||
sudo ln -s --force /usr/bin/llvm-objdump-$LLVM_VERSION /usr/bin/llvm-objdump
|
||||
sudo ln -s --force /usr/bin/llvm-ar-$LLVM_VERSION /usr/bin/llvm-ar
|
||||
sudo ln -s --force /usr/bin/llvm-nm-$LLVM_VERSION /usr/bin/llvm-nm
|
||||
sudo ln -s --force /usr/bin/llvm-strip-$LLVM_VERSION /usr/bin/llvm-strip
|
||||
sudo ln -s --force /usr/bin/llvm-objcopy-$LLVM_VERSION /usr/bin/llvm-objcopy
|
||||
sudo ln -s --force /usr/bin/llvm-readelf-$LLVM_VERSION /usr/bin/llvm-readelf
|
||||
sudo ln -s --force /usr/bin/clang++-$LLVM_VERSION /usr/bin/clang++
|
||||
|
||||
- name: Checkout KernelSU-Next
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: KernelSU-Next
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup kernel source
|
||||
run: git clone https://chromium.googlesource.com/chromiumos/third_party/kernel.git -b ${{ env.git_tag }} --depth=1
|
||||
|
||||
- name: Extract version from Makefile
|
||||
working-directory: kernel
|
||||
run: |
|
||||
VERSION=$(grep -E '^VERSION = ' Makefile | awk '{print $3}')
|
||||
PATCHLEVEL=$(grep -E '^PATCHLEVEL = ' Makefile | awk '{print $3}')
|
||||
SUBLEVEL=$(grep -E '^SUBLEVEL = ' Makefile | awk '{print $3}')
|
||||
echo "ChromeOS ARCVM Linux kernel version: $VERSION.$PATCHLEVEL.$SUBLEVEL"
|
||||
echo "version=$VERSION.$PATCHLEVEL.$SUBLEVEL" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup KernelSU-Next
|
||||
working-directory: kernel
|
||||
run: |
|
||||
echo "[+] KernelSU-Next setup"
|
||||
KERNEL_ROOT=$GITHUB_WORKSPACE/kernel
|
||||
echo "[+] KERNEL_ROOT: $KERNEL_ROOT"
|
||||
echo "[+] Copy KernelSU-Next driver to $KERNEL_ROOT/drivers"
|
||||
ln -sf $GITHUB_WORKSPACE/KernelSU-Next/kernel $KERNEL_ROOT/drivers/kernelsu-next
|
||||
|
||||
echo "[+] Add KernelSU-Next driver to Makefile"
|
||||
DRIVER_MAKEFILE=$KERNEL_ROOT/drivers/Makefile
|
||||
DRIVER_KCONFIG=$KERNEL_ROOT/drivers/Kconfig
|
||||
grep -q "kernelsu-next" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu-next/\n" >> "$DRIVER_MAKEFILE"
|
||||
grep -q "kernelsu-next" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu-next/Kconfig\"" "$DRIVER_KCONFIG"
|
||||
|
||||
echo "[+] Apply KernelSU patches"
|
||||
cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU-Next/.github/patches/5.10/*.patch || echo "[-] No patch found"
|
||||
|
||||
echo "[+] Patch script/setlocalversion"
|
||||
sed -i 's/-dirty//g' $KERNEL_ROOT/scripts/setlocalversion
|
||||
|
||||
echo "[+] KernelSU-Next setup done."
|
||||
cd $GITHUB_WORKSPACE/KernelSU-Next
|
||||
KSU_VERSION=$(($(git rev-list --count HEAD) + 10200))
|
||||
echo "KernelSU-Next version: $KSU_VERSION"
|
||||
echo "kernelsu-next_version=$KSU_VERSION" >> $GITHUB_ENV
|
||||
|
||||
- name: Build Kernel
|
||||
working-directory: kernel
|
||||
env:
|
||||
KERNEL_IMAGE_NAME: ${{ matrix.kernel_image_name }}
|
||||
ARCH: ${{ matrix.arch }}
|
||||
run: |
|
||||
set -a && . ${{ matrix.build_config }}; set +a
|
||||
export DEFCONFIG=${{ matrix.defconfig }}
|
||||
if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then
|
||||
export KSU_EXPECTED_SIZE=${{ vars.EXPECTED_SIZE }}
|
||||
export KSU_EXPECTED_HASH=${{ vars.EXPECTED_HASH }}
|
||||
fi
|
||||
|
||||
make LLVM=1 LLVM_IAS=1 DEPMOD=depmod DTC=dtc O=${PWD} mrproper
|
||||
make LLVM=1 LLVM_IAS=1 DEPMOD=depmod DTC=dtc O=${PWD} ${DEFCONFIG} < /dev/null
|
||||
scripts/config --file .config -e LTO_CLANG -d LTO_NONE -e LTO_CLANG_THIN -d LTO_CLANG_FULL -e THINLTO
|
||||
make LLVM=1 LLVM_IAS=1 DEPMOD=depmod DTC=dtc O=${PWD} -j$(nproc) ${KERNEL_IMAGE_NAME} modules prepare-objtool
|
||||
ls -l -h ${PWD}/arch/${ARCH}/boot
|
||||
echo "file_path=${PWD}/arch/${ARCH}/boot/${KERNEL_IMAGE_NAME}" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload kernel-ARCVM-${{ matrix.arch }}-${{ env.version }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kernel-ARCVM-${{ matrix.arch }}-${{ env.version }}
|
||||
path: "${{ env.file_path }}"
|
||||
39
.github/workflows/build-kernel-avd.yml
vendored
Normal file
39
.github/workflows/build-kernel-avd.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Build Kernel - AVD
|
||||
on:
|
||||
push:
|
||||
branches: ["next"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-avd.yml"
|
||||
- ".github/workflows/avd-kernel.yml"
|
||||
- "kernel/**"
|
||||
pull_request:
|
||||
branches: ["next"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-avd.yml"
|
||||
- ".github/workflows/avd-kernel.yml"
|
||||
- "kernel/**"
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-kernel:
|
||||
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
|
||||
uses: ./.github/workflows/avd-kernel.yml
|
||||
secrets: inherit
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- version: "android-14-avd_x86_64"
|
||||
manifest: "android-14-avd_x86_64.xml"
|
||||
arch: "x86_64"
|
||||
- version: "android-15-avd_aarch64"
|
||||
manifest: "android-15-avd_aarch64.xml"
|
||||
arch: "aarch64"
|
||||
- version: "android-15-avd_x86_64"
|
||||
manifest: "android-15-avd_x86_64.xml"
|
||||
arch: "x86_64"
|
||||
with:
|
||||
version_name: ${{ matrix.version }}
|
||||
manifest_name: ${{ matrix.manifest }}
|
||||
arch: ${{ matrix.arch }}
|
||||
debug: true
|
||||
38
.github/workflows/build-kernel-wsa.yml
vendored
Normal file
38
.github/workflows/build-kernel-wsa.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: Build Kernel - WSA
|
||||
on:
|
||||
push:
|
||||
branches: ["next"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-wsa.yml"
|
||||
- ".github/workflows/wsa-kernel.yml"
|
||||
- "kernel/**"
|
||||
pull_request:
|
||||
branches: ["next"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-wsa.yml"
|
||||
- ".github/workflows/wsa-kernel.yml"
|
||||
- "kernel/**"
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86_64, arm64]
|
||||
version: ["5.15.94.2", "5.15.104.1", "5.15.104.2", "5.15.104.3", "5.15.104.4"]
|
||||
uses: ./.github/workflows/wsa-kernel.yml
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
version: ${{ matrix.version }}
|
||||
|
||||
check_build:
|
||||
if: (github.event_name == 'pull_request' && !github.event.pull_request.draft) || github.ref == 'refs/heads/checkci'
|
||||
uses: ./.github/workflows/wsa-kernel.yml
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86_64, arm64]
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
version: "5.15.104.4"
|
||||
24
.github/workflows/build-lkm.yml
vendored
24
.github/workflows/build-lkm.yml
vendored
@@ -15,23 +15,23 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "android12-5.10"
|
||||
sub_level: 226
|
||||
os_patch_level: 2024-11
|
||||
sub_level: 236
|
||||
os_patch_level: 2025-05
|
||||
- version: "android13-5.10"
|
||||
sub_level: 223
|
||||
os_patch_level: 2024-11
|
||||
sub_level: 236
|
||||
os_patch_level: 2025-05
|
||||
- version: "android13-5.15"
|
||||
sub_level: 167
|
||||
os_patch_level: 2024-11
|
||||
sub_level: 180
|
||||
os_patch_level: 2025-05
|
||||
- version: "android14-5.15"
|
||||
sub_level: 167
|
||||
os_patch_level: 2024-11
|
||||
sub_level: 180
|
||||
os_patch_level: 2025-05
|
||||
- version: "android14-6.1"
|
||||
sub_level: 115
|
||||
os_patch_level: 2024-12
|
||||
sub_level: 138
|
||||
os_patch_level: 2025-06
|
||||
- version: "android15-6.6"
|
||||
sub_level: 57
|
||||
os_patch_level: 2024-12
|
||||
sub_level: 89
|
||||
os_patch_level: 2025-06
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: ${{ matrix.version }}
|
||||
|
||||
107
.github/workflows/build-manager-ci.yml
vendored
107
.github/workflows/build-manager-ci.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Build Manager
|
||||
name: Build Manager CI
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -13,6 +13,7 @@ on:
|
||||
pull_request:
|
||||
branches: [ "next" ]
|
||||
paths:
|
||||
- '.github/workflows/build-manager-ci.yml'
|
||||
- 'manager/**'
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
@@ -27,46 +28,36 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- target: aarch64-linux-android
|
||||
os: ubuntu-latest
|
||||
- os: ubuntu-latest
|
||||
uses: ./.github/workflows/susfsd.yml
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
os: ${{ matrix.os }}
|
||||
|
||||
build-ksud_overlayfs:
|
||||
build-ksud:
|
||||
needs: build-susfsd
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- target: aarch64-linux-android
|
||||
os: ubuntu-latest
|
||||
uses: ./.github/workflows/ksud_overlayfs.yml
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
os: ${{ matrix.os }}
|
||||
|
||||
build-ksud_magic:
|
||||
needs: build-ksud_overlayfs
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- target: aarch64-linux-android
|
||||
- target: armv7-linux-androideabi
|
||||
os: ubuntu-latest
|
||||
uses: ./.github/workflows/ksud_magic.yml
|
||||
- target: x86_64-linux-android
|
||||
os: ubuntu-latest
|
||||
uses: ./.github/workflows/ksud.yml
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
os: ${{ matrix.os }}
|
||||
|
||||
build-manager:
|
||||
needs: build-ksud_magic
|
||||
needs: build-ksud
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./manager
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
@@ -80,7 +71,7 @@ jobs:
|
||||
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Write key
|
||||
- name: Write Key
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.KEYSTORE }}" ]; then
|
||||
{
|
||||
@@ -101,40 +92,87 @@ jobs:
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Setup Android SDK
|
||||
uses: android-actions/setup-android@v3
|
||||
|
||||
- name: Download arm64 susfsd
|
||||
- name: Download susfsd
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: susfsd-aarch64-linux-android
|
||||
name: susfsd-linux-android
|
||||
path: .
|
||||
|
||||
- name: Copy susfsd to app jniLibs
|
||||
run: |
|
||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||
cp -f ../arm64-v8a/susfsd ../manager/app/src/main/jniLibs/arm64-v8a/libsusfsd.so
|
||||
|
||||
mkdir -p app/src/main/jniLibs/armeabi-v7a
|
||||
cp -f ../armeabi-v7a/susfsd ../manager/app/src/main/jniLibs/armeabi-v7a/libsusfsd.so
|
||||
|
||||
mkdir -p app/src/main/jniLibs/x86_64
|
||||
cp -f ../x86_64/susfsd ../manager/app/src/main/jniLibs/x86_64/libsusfsd.so
|
||||
|
||||
|
||||
- name: Download arm64 ksud_overlayfs
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud_overlayfs-aarch64-linux-android
|
||||
path: ksud_overlayfs
|
||||
|
||||
|
||||
- name: Download arm ksud_overlayfs
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud_overlayfs-armv7-linux-androideabi
|
||||
path: ksud_overlayfs
|
||||
|
||||
- name: Download x86_64 ksud_overlayfs
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud_overlayfs-x86_64-linux-android
|
||||
path: ksud_overlayfs
|
||||
|
||||
- name: Copy ksud_overlayfs to app jniLibs
|
||||
run: |
|
||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||
|
||||
mkdir -p app/src/main/jniLibs/armeabi-v7a
|
||||
|
||||
mkdir -p app/src/main/jniLibs/x86_64
|
||||
|
||||
cp -f ../ksud_overlayfs/aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud_overlayfs.so
|
||||
|
||||
|
||||
cp -f ../ksud_overlayfs/armv7-linux-androideabi/release/ksud ../manager/app/src/main/jniLibs/armeabi-v7a/libksud_overlayfs.so
|
||||
|
||||
cp -f ../ksud_overlayfs/x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud_overlayfs.so
|
||||
|
||||
- name: Download arm64 ksud_magic
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud_magic-aarch64-linux-android
|
||||
path: ksud_magic
|
||||
|
||||
- name: Download arm ksud_magic
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud_magic-armv7-linux-androideabi
|
||||
path: ksud_magic
|
||||
|
||||
- name: Download x86_64 ksud_magic
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud_magic-x86_64-linux-android
|
||||
path: ksud_magic
|
||||
|
||||
- name: Copy ksud_magic to app jniLibs
|
||||
run: |
|
||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||
|
||||
mkdir -p app/src/main/jniLibs/armeabi-v7a
|
||||
|
||||
mkdir -p app/src/main/jniLibs/x86_64
|
||||
|
||||
cp -f ../ksud_magic/aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud_magic.so
|
||||
|
||||
cp -f ../ksud_magic/armv7-linux-androideabi/release/ksud ../manager/app/src/main/jniLibs/armeabi-v7a/libksud_magic.so
|
||||
|
||||
cp -f ../ksud_magic/x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud_magic.so
|
||||
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
@@ -148,19 +186,19 @@ jobs:
|
||||
chmod +x gradlew
|
||||
./gradlew clean assembleRelease
|
||||
|
||||
- name: Upload build artifact
|
||||
- name: Upload Build Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: manager
|
||||
name: Manager
|
||||
path: manager/app/build/outputs/apk/release/*.apk
|
||||
|
||||
- name: Upload mappings
|
||||
- name: Upload Mappings
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "mappings"
|
||||
path: "manager/app/build/outputs/mapping/release/"
|
||||
name: Mappings
|
||||
path: manager/app/build/outputs/mapping/release/
|
||||
|
||||
- name: Bot session cache
|
||||
- name: Bot Session Cache
|
||||
if: steps.need_upload.outputs.UPLOAD == 'true'
|
||||
id: bot_session_cache
|
||||
uses: actions/cache@v4
|
||||
@@ -168,7 +206,7 @@ jobs:
|
||||
path: scripts/ksunextbot.session
|
||||
key: ${{ runner.os }}-bot-session
|
||||
|
||||
- name: Upload to telegram
|
||||
- name: Upload to Telegram
|
||||
if: steps.need_upload.outputs.UPLOAD == 'true'
|
||||
env:
|
||||
API_ID: ${{ secrets.API_ID }}
|
||||
@@ -187,4 +225,3 @@ jobs:
|
||||
pip3 install telethon
|
||||
python3 $GITHUB_WORKSPACE/scripts/ksunextbot.py $APK
|
||||
fi
|
||||
|
||||
|
||||
222
.github/workflows/build-manager-spoofed.yml
vendored
Normal file
222
.github/workflows/build-manager-spoofed.yml
vendored
Normal file
@@ -0,0 +1,222 @@
|
||||
name: Build Manager Spoofed
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "next" ]
|
||||
paths:
|
||||
- '.github/workflows/build-manager-ci.yml'
|
||||
- 'manager/**'
|
||||
- 'kernel/**'
|
||||
- 'userspace/ksud_overlayfs**'
|
||||
- 'userspace/ksud_magic/**'
|
||||
- 'userspace/susfsd/**'
|
||||
pull_request:
|
||||
branches: [ "next" ]
|
||||
paths:
|
||||
- '.github/workflows/build-manager-ci.yml'
|
||||
- 'manager/**'
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 12 * * *" # 6 PM UTC+6 | 12 PM UTC
|
||||
|
||||
jobs:
|
||||
build-lkm:
|
||||
uses: ./.github/workflows/build-lkm.yml
|
||||
secrets: inherit
|
||||
|
||||
build-susfsd:
|
||||
needs: build-lkm
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
uses: ./.github/workflows/susfsd.yml
|
||||
with:
|
||||
os: ${{ matrix.os }}
|
||||
|
||||
build-ksud:
|
||||
needs: build-susfsd
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- target: aarch64-linux-android
|
||||
os: ubuntu-latest
|
||||
- target: armv7-linux-androideabi
|
||||
os: ubuntu-latest
|
||||
- target: x86_64-linux-android
|
||||
os: ubuntu-latest
|
||||
uses: ./.github/workflows/ksud.yml
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
os: ${{ matrix.os }}
|
||||
|
||||
build-manager:
|
||||
needs: build-ksud
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./manager
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Spoof Package ID
|
||||
run: |
|
||||
chmod +x spoof
|
||||
./spoof
|
||||
|
||||
- name: Setup need_upload
|
||||
id: need_upload
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
||||
echo "UPLOAD=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Write Key
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.KEYSTORE }}" ]; then
|
||||
{
|
||||
echo KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}'
|
||||
echo KEY_ALIAS='${{ secrets.KEY_ALIAS }}'
|
||||
echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}'
|
||||
echo KEYSTORE_FILE='key.jks'
|
||||
} >> gradle.properties
|
||||
echo ${{ secrets.KEYSTORE }} | base64 -d > key.jks
|
||||
fi
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Download susfsd
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: susfsd-linux-android
|
||||
path: .
|
||||
|
||||
- name: Copy susfsd to app jniLibs
|
||||
run: |
|
||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||
cp -f ../arm64-v8a/susfsd ../manager/app/src/main/jniLibs/arm64-v8a/libsusfsd.so
|
||||
|
||||
mkdir -p app/src/main/jniLibs/armeabi-v7a
|
||||
cp -f ../armeabi-v7a/susfsd ../manager/app/src/main/jniLibs/armeabi-v7a/libsusfsd.so
|
||||
|
||||
mkdir -p app/src/main/jniLibs/x86_64
|
||||
cp -f ../x86_64/susfsd ../manager/app/src/main/jniLibs/x86_64/libsusfsd.so
|
||||
|
||||
|
||||
- name: Download arm64 ksud_overlayfs
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud_overlayfs-aarch64-linux-android
|
||||
path: ksud_overlayfs
|
||||
|
||||
- name: Download arm ksud_overlayfs
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud_overlayfs-armv7-linux-androideabi
|
||||
path: ksud_overlayfs
|
||||
|
||||
- name: Download x86_64 ksud_overlayfs
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud_overlayfs-x86_64-linux-android
|
||||
path: ksud_overlayfs
|
||||
|
||||
- name: Copy ksud_overlayfs to app jniLibs
|
||||
run: |
|
||||
cp -f ../ksud_overlayfs/aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud_overlayfs.so
|
||||
|
||||
cp -f ../ksud_overlayfs/armv7-linux-androideabi/release/ksud ../manager/app/src/main/jniLibs/armeabi-v7a/libksud_overlayfs.so
|
||||
|
||||
cp -f ../ksud_overlayfs/x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud_overlayfs.so
|
||||
|
||||
- name: Download arm64 ksud_magic
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud_magic-aarch64-linux-android
|
||||
path: ksud_magic
|
||||
|
||||
- name: Download arm ksud_magic
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud_magic-armv7-linux-androideabi
|
||||
path: ksud_magic
|
||||
|
||||
- name: Download x86_64 ksud_magic
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud_magic-x86_64-linux-android
|
||||
path: ksud_magic
|
||||
|
||||
- name: Copy ksud_magic to app jniLibs
|
||||
run: |
|
||||
cp -f ../ksud_magic/aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud_magic.so
|
||||
|
||||
cp -f ../ksud_magic/armv7-linux-androideabi/release/ksud ../manager/app/src/main/jniLibs/armeabi-v7a/libksud_magic.so
|
||||
|
||||
cp -f ../ksud_magic/x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud_magic.so
|
||||
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
{
|
||||
echo 'org.gradle.parallel=true'
|
||||
echo 'org.gradle.vfs.watch=true'
|
||||
echo 'org.gradle.jvmargs=-Xmx2048m'
|
||||
echo 'android.native.buildOutput=verbose'
|
||||
} >> gradle.properties
|
||||
sed -i 's/org.gradle.configuration-cache=true//g' gradle.properties
|
||||
chmod +x gradlew
|
||||
./gradlew clean assembleRelease
|
||||
|
||||
- name: Upload Build Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Manager
|
||||
path: manager/app/build/outputs/apk/release/*.apk
|
||||
|
||||
- name: Upload Mappings
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Mappings
|
||||
path: manager/app/build/outputs/mapping/release/
|
||||
|
||||
- name: Bot Session Cache
|
||||
if: steps.need_upload.outputs.UPLOAD == 'true'
|
||||
id: bot_session_cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: scripts/ksunextbot.session
|
||||
key: ${{ runner.os }}-bot-session
|
||||
|
||||
- name: Upload to Telegram
|
||||
if: steps.need_upload.outputs.UPLOAD == 'true'
|
||||
env:
|
||||
API_ID: ${{ secrets.API_ID }}
|
||||
API_HASH: ${{ secrets.API_HASH }}
|
||||
CHAT_ID: ${{ secrets.CHAT_ID }}
|
||||
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
||||
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
|
||||
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||
COMMIT_URL: ${{ github.event.head_commit.url }}
|
||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
TITLE: CI Manager (SPOOFED BUILD)
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
||||
export VERSION=$(git rev-list --count HEAD)
|
||||
APK=$(find ./app/build/outputs/apk/release -name "*.apk")
|
||||
pip3 install telethon
|
||||
python3 $GITHUB_WORKSPACE/scripts/ksunextbot.py $APK
|
||||
fi
|
||||
93
.github/workflows/build-manager.yml
vendored
93
.github/workflows/build-manager.yml
vendored
@@ -13,6 +13,7 @@ on:
|
||||
# pull_request:
|
||||
# branches: [ "next" ]
|
||||
# paths:
|
||||
# - '.github/workflows/build-manager-ci.yml'
|
||||
# - 'manager/**'
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
@@ -27,46 +28,36 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- target: aarch64-linux-android
|
||||
os: ubuntu-latest
|
||||
- os: ubuntu-latest
|
||||
uses: ./.github/workflows/susfsd.yml
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
os: ${{ matrix.os }}
|
||||
|
||||
build-ksud_overlayfs:
|
||||
build-ksud:
|
||||
needs: build-susfsd
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- target: aarch64-linux-android
|
||||
os: ubuntu-latest
|
||||
uses: ./.github/workflows/ksud_overlayfs.yml
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
os: ${{ matrix.os }}
|
||||
|
||||
build-ksud_magic:
|
||||
needs: build-ksud_overlayfs
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- target: aarch64-linux-android
|
||||
- target: armv7-linux-androideabi
|
||||
os: ubuntu-latest
|
||||
uses: ./.github/workflows/ksud_magic.yml
|
||||
- target: x86_64-linux-android
|
||||
os: ubuntu-latest
|
||||
uses: ./.github/workflows/ksud.yml
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
os: ${{ matrix.os }}
|
||||
|
||||
build-manager:
|
||||
needs: build-ksud_magic
|
||||
needs: build-ksud
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./manager
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
@@ -80,7 +71,7 @@ jobs:
|
||||
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Write key
|
||||
- name: Write Key
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.KEYSTORE }}" ]; then
|
||||
{
|
||||
@@ -101,40 +92,75 @@ jobs:
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Setup Android SDK
|
||||
uses: android-actions/setup-android@v3
|
||||
|
||||
- name: Download arm64 susfsd
|
||||
- name: Download susfsd
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: susfsd-aarch64-linux-android
|
||||
name: susfsd-linux-android
|
||||
path: .
|
||||
|
||||
- name: Copy susfsd to app jniLibs
|
||||
run: |
|
||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||
cp -f ../arm64-v8a/susfsd ../manager/app/src/main/jniLibs/arm64-v8a/libsusfsd.so
|
||||
|
||||
mkdir -p app/src/main/jniLibs/armeabi-v7a
|
||||
cp -f ../armeabi-v7a/susfsd ../manager/app/src/main/jniLibs/armeabi-v7a/libsusfsd.so
|
||||
|
||||
mkdir -p app/src/main/jniLibs/x86_64
|
||||
cp -f ../x86_64/susfsd ../manager/app/src/main/jniLibs/x86_64/libsusfsd.so
|
||||
|
||||
|
||||
- name: Download arm64 ksud_overlayfs
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud_overlayfs-aarch64-linux-android
|
||||
path: ksud_overlayfs
|
||||
|
||||
|
||||
- name: Download arm ksud_overlayfs
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud_overlayfs-armv7-linux-androideabi
|
||||
path: ksud_overlayfs
|
||||
|
||||
- name: Download x86_64 ksud_overlayfs
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud_overlayfs-x86_64-linux-android
|
||||
path: ksud_overlayfs
|
||||
|
||||
- name: Copy ksud_overlayfs to app jniLibs
|
||||
run: |
|
||||
cp -f ../ksud_overlayfs/aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud_overlayfs.so
|
||||
|
||||
|
||||
cp -f ../ksud_overlayfs/armv7-linux-androideabi/release/ksud ../manager/app/src/main/jniLibs/armeabi-v7a/libksud_overlayfs.so
|
||||
|
||||
cp -f ../ksud_overlayfs/x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud_overlayfs.so
|
||||
|
||||
- name: Download arm64 ksud_magic
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud_magic-aarch64-linux-android
|
||||
path: ksud_magic
|
||||
|
||||
- name: Download arm ksud_magic
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud_magic-armv7-linux-androideabi
|
||||
path: ksud_magic
|
||||
|
||||
- name: Download x86_64 ksud_magic
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud_magic-x86_64-linux-android
|
||||
path: ksud_magic
|
||||
|
||||
- name: Copy ksud_magic to app jniLibs
|
||||
run: |
|
||||
cp -f ../ksud_magic/aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud_magic.so
|
||||
|
||||
cp -f ../ksud_magic/armv7-linux-androideabi/release/ksud ../manager/app/src/main/jniLibs/armeabi-v7a/libksud_magic.so
|
||||
|
||||
cp -f ../ksud_magic/x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud_magic.so
|
||||
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
@@ -148,19 +174,19 @@ jobs:
|
||||
chmod +x gradlew
|
||||
./gradlew clean assembleRelease
|
||||
|
||||
- name: Upload build artifact
|
||||
- name: Upload Build Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: manager
|
||||
name: Manager
|
||||
path: manager/app/build/outputs/apk/release/*.apk
|
||||
|
||||
- name: Upload mappings
|
||||
- name: Upload Mappings
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "mappings"
|
||||
path: "manager/app/build/outputs/mapping/release/"
|
||||
name: Mappings
|
||||
path: manager/app/build/outputs/mapping/release/
|
||||
|
||||
- name: Bot session cache
|
||||
- name: Bot Session Cache
|
||||
if: steps.need_upload.outputs.UPLOAD == 'true'
|
||||
id: bot_session_cache
|
||||
uses: actions/cache@v4
|
||||
@@ -168,7 +194,7 @@ jobs:
|
||||
path: scripts/ksunextbot.session
|
||||
key: ${{ runner.os }}-bot-session
|
||||
|
||||
- name: Upload to telegram
|
||||
- name: Upload to Telegram
|
||||
if: steps.need_upload.outputs.UPLOAD == 'true'
|
||||
env:
|
||||
API_ID: ${{ secrets.API_ID }}
|
||||
@@ -187,4 +213,3 @@ jobs:
|
||||
pip3 install telethon
|
||||
python3 $GITHUB_WORKSPACE/scripts/ksunextbot.py $APK
|
||||
fi
|
||||
|
||||
|
||||
43
.github/workflows/clippy.yml
vendored
43
.github/workflows/clippy.yml
vendored
@@ -6,13 +6,15 @@ on:
|
||||
- next
|
||||
paths:
|
||||
- '.github/workflows/clippy.yml'
|
||||
- 'userspace/ksud/**'
|
||||
- 'userspace/ksud_magic/**'
|
||||
- 'userspace/ksud_overlayfs/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- next
|
||||
paths:
|
||||
- '.github/workflows/clippy.yml'
|
||||
- 'userspace/ksud/**'
|
||||
- 'userspace/ksud_magic/**'
|
||||
- 'userspace/ksud_overlayfs/**'
|
||||
|
||||
env:
|
||||
RUSTFLAGS: '-Dwarnings'
|
||||
@@ -21,17 +23,32 @@ jobs:
|
||||
clippy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: rustup update --force-non-host stable-x86_64-unknown-linux-gnu
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Rust
|
||||
run: rustup update stable
|
||||
|
||||
- name: Setup Cross
|
||||
run: RUSTFLAGS="" cargo install cross
|
||||
|
||||
- name: Cache ksud_overlayfs
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: userspace/ksud
|
||||
workspaces: userspace/ksud_overlayfs
|
||||
|
||||
- name: Install cross
|
||||
run: |
|
||||
cargo install cross --git https://github.com/cross-rs/cross --rev 66845c1
|
||||
- name: Cache ksud_magic
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: userspace/ksud_magic
|
||||
|
||||
- name: Run clippy
|
||||
- name: Run Clippy
|
||||
run: |
|
||||
cross clippy --manifest-path userspace/ksud/Cargo.toml --target aarch64-linux-android --release
|
||||
cross clippy --manifest-path userspace/ksud/Cargo.toml --target x86_64-linux-android --release
|
||||
cross clippy --manifest-path userspace/ksud_magic/Cargo.toml --target aarch64-linux-android --release
|
||||
cross clippy --manifest-path userspace/ksud_overlayfs/Cargo.toml --target aarch64-linux-android --release
|
||||
|
||||
cross clippy --manifest-path userspace/ksud_magic/Cargo.toml --target armv7-linux-androideabi --release
|
||||
cross clippy --manifest-path userspace/ksud_overlayfs/Cargo.toml --target armv7-linux-androideabi --release
|
||||
|
||||
cross clippy --manifest-path userspace/ksud_magic/Cargo.toml --target x86_64-linux-android --release
|
||||
cross clippy --manifest-path userspace/ksud_overlayfs/Cargo.toml --target x86_64-linux-android --release
|
||||
5
.github/workflows/gki-kernel.yml
vendored
5
.github/workflows/gki-kernel.yml
vendored
@@ -198,6 +198,9 @@ jobs:
|
||||
- name: Make working directory clean to avoid dirty
|
||||
working-directory: android-kernel
|
||||
run: |
|
||||
if [ -f ./common/BUILD.bazel ]; then
|
||||
sed -i '/^[[:space:]]*"protected_exports_list"[[:space:]]*:[[:space:]]*"android\/abi_gki_protected_exports_aarch64",$/d' ./common/BUILD.bazel
|
||||
fi
|
||||
rm common/android/abi_gki_protected_exports_* || echo "No protected exports!"
|
||||
git config --global user.email "bot@kernelsu.org"
|
||||
git config --global user.name "KernelSU-NextBot"
|
||||
@@ -255,4 +258,4 @@ jobs:
|
||||
if: ${{ inputs.build_lkm == true }}
|
||||
with:
|
||||
name: ${{ inputs.version }}-lkm
|
||||
path: ./output/*_kernelsu.ko
|
||||
path: ./output/*_kernelsu.ko
|
||||
|
||||
84
.github/workflows/ksud.yml
vendored
Normal file
84
.github/workflows/ksud.yml
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
name: Build ksud
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
target:
|
||||
required: true
|
||||
type: string
|
||||
os:
|
||||
required: false
|
||||
type: string
|
||||
default: ubuntu-latest
|
||||
pack_lkm:
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
use_cache:
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ inputs.os }}
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Prepare LKM Files
|
||||
if: ${{ inputs.pack_lkm }}
|
||||
run: |
|
||||
cp android*-lkm/*_kernelsu.ko ./userspace/ksud_overlayfs/bin/aarch64/
|
||||
cp android*-lkm/*_kernelsu.ko ./userspace/ksud_magic/bin/aarch64/
|
||||
|
||||
- name: Import susfsd Libraries
|
||||
run: |
|
||||
cp susfsd-linux-android/arm64-v8a/susfsd ./userspace/ksud_overlayfs/bin/aarch64/
|
||||
cp susfsd-linux-android/arm64-v8a/susfsd ./userspace/ksud_magic/bin/aarch64/
|
||||
cp susfsd-linux-android/armeabi-v7a/susfsd ./userspace/ksud_overlayfs/bin/arm/
|
||||
cp susfsd-linux-android/armeabi-v7a/susfsd ./userspace/ksud_magic/bin/arm/
|
||||
cp susfsd-linux-android/x86_64/susfsd ./userspace/ksud_overlayfs/bin/x86_64/
|
||||
cp susfsd-linux-android/x86_64/susfsd ./userspace/ksud_magic/bin/x86_64/
|
||||
|
||||
- name: Setup Rust
|
||||
run: |
|
||||
rustup update stable
|
||||
rustup target add x86_64-apple-darwin
|
||||
rustup target add aarch64-apple-darwin
|
||||
|
||||
- name: Cache ksud_overlayfs
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: userspace/ksud_overlayfs
|
||||
cache-targets: false
|
||||
|
||||
- name: Cache ksud_magic
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: userspace/ksud_magic
|
||||
cache-targets: false
|
||||
|
||||
- name: Setup Cross
|
||||
run: |
|
||||
RUSTFLAGS="" cargo install cross --git https://github.com/cross-rs/cross --rev 66845c1 --force
|
||||
|
||||
- name: Build ksud
|
||||
run: |
|
||||
CROSS_NO_WARNINGS=0 cross build --target ${{ inputs.target }} --release --manifest-path ./userspace/ksud_overlayfs/Cargo.toml
|
||||
CROSS_NO_WARNINGS=0 cross build --target ${{ inputs.target }} --release --manifest-path ./userspace/ksud_magic/Cargo.toml
|
||||
|
||||
- name: Upload ksud_overlayfs artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ksud_overlayfs-${{ inputs.target }}
|
||||
path: userspace/ksud_overlayfs/target/**/release/ksud*
|
||||
|
||||
- name: Upload ksud_magic artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ksud_magic-${{ inputs.target }}
|
||||
path: userspace/ksud_magic/target/**/release/ksud*
|
||||
61
.github/workflows/ksud_magic.yml
vendored
61
.github/workflows/ksud_magic.yml
vendored
@@ -1,61 +0,0 @@
|
||||
name: Build ksud_magic
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
target:
|
||||
required: true
|
||||
type: string
|
||||
os:
|
||||
required: false
|
||||
type: string
|
||||
default: ubuntu-latest
|
||||
pack_lkm:
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
use_cache:
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ inputs.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Prepare LKM fies
|
||||
if: ${{ inputs.pack_lkm }}
|
||||
run: |
|
||||
cp android*-lkm/*_kernelsu.ko ./userspace/ksud_magic/bin/aarch64/
|
||||
|
||||
- name: Import susfsd lib
|
||||
run: |
|
||||
cp susfsd-aarch64-linux-android/arm64-v8a/susfsd ./userspace/ksud_magic/bin/aarch64/
|
||||
|
||||
- name: Setup rustup
|
||||
run: |
|
||||
rustup update stable
|
||||
rustup target add x86_64-apple-darwin
|
||||
rustup target add aarch64-apple-darwin
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: userspace/ksud_magic
|
||||
cache-targets: false
|
||||
|
||||
- name: Install cross
|
||||
run: |
|
||||
cargo install cross --git https://github.com/cross-rs/cross --rev 66845c1 --force
|
||||
|
||||
- name: Build ksud_magic
|
||||
run: CROSS_NO_WARNINGS=0 cross build --target ${{ inputs.target }} --release --manifest-path ./userspace/ksud_magic/Cargo.toml
|
||||
|
||||
- name: Upload ksud_magic artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ksud_magic-${{ inputs.target }}
|
||||
path: userspace/ksud_magic/target/**/release/ksud*
|
||||
61
.github/workflows/ksud_overlayfs.yml
vendored
61
.github/workflows/ksud_overlayfs.yml
vendored
@@ -1,61 +0,0 @@
|
||||
name: Build ksud_overlayfs
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
target:
|
||||
required: true
|
||||
type: string
|
||||
os:
|
||||
required: false
|
||||
type: string
|
||||
default: ubuntu-latest
|
||||
pack_lkm:
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
use_cache:
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ inputs.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Prepare LKM fies
|
||||
if: ${{ inputs.pack_lkm }}
|
||||
run: |
|
||||
cp android*-lkm/*_kernelsu.ko ./userspace/ksud_overlayfs/bin/aarch64/
|
||||
|
||||
- name: Import susfsd lib
|
||||
run: |
|
||||
cp susfsd-aarch64-linux-android/arm64-v8a/susfsd ./userspace/ksud_overlayfs/bin/aarch64/
|
||||
|
||||
- name: Setup rustup
|
||||
run: |
|
||||
rustup update stable
|
||||
rustup target add x86_64-apple-darwin
|
||||
rustup target add aarch64-apple-darwin
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: userspace/ksud_overlayfs
|
||||
cache-targets: false
|
||||
|
||||
- name: Install cross
|
||||
run: |
|
||||
cargo install cross --git https://github.com/cross-rs/cross --rev 66845c1 --force
|
||||
|
||||
- name: Build ksud_overlayfs
|
||||
run: CROSS_NO_WARNINGS=0 cross build --target ${{ inputs.target }} --release --manifest-path ./userspace/ksud_overlayfs/Cargo.toml
|
||||
|
||||
- name: Upload ksud_overlayfs artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ksud_overlayfs-${{ inputs.target }}
|
||||
path: userspace/ksud_overlayfs/target/**/release/ksud*
|
||||
37
.github/workflows/release.yml
vendored
37
.github/workflows/release.yml
vendored
@@ -21,12 +21,21 @@ jobs:
|
||||
build-a15-kernel:
|
||||
uses: ./.github/workflows/build-kernel-a15.yml
|
||||
secrets: inherit
|
||||
build-wsa-kernel:
|
||||
uses: ./.github/workflows/build-kernel-wsa.yml
|
||||
secrets: inherit
|
||||
build-arcvm-kernel:
|
||||
uses: ./.github/workflows/build-kernel-arcvm.yml
|
||||
secrets: inherit
|
||||
release:
|
||||
needs:
|
||||
- build-manager
|
||||
- build-a12-kernel
|
||||
- build-a13-kernel
|
||||
- build-a14-kernel
|
||||
- build-a15-kernel
|
||||
- build-wsa-kernel
|
||||
- build-arcvm-kernel
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
@@ -40,6 +49,24 @@ jobs:
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Zip WSA kernel
|
||||
run: |
|
||||
for dir in kernel-WSA-*; do
|
||||
if [ -d "$dir" ]; then
|
||||
echo "------ Zip $dir ----------"
|
||||
(cd $dir && zip -r9 "$dir".zip ./* -x .git .gitignore ./*.zip && mv *.zip ..)
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Zip ChromeOS ARCVM kernel
|
||||
run: |
|
||||
for dir in kernel-ARCVM-*; do
|
||||
if [ -d "$dir" ]; then
|
||||
echo "------ Zip $dir ----------"
|
||||
(cd $dir && zip -r9 "$dir".zip ./* -x .git .gitignore ./*.zip && mv *.zip ..)
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
|
||||
@@ -47,10 +74,12 @@ jobs:
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
manager/*.apk
|
||||
Manager/*.apk
|
||||
android*-lkm/*_kernelsu.ko
|
||||
AnyKernel3-*.zip
|
||||
boot-images-*/Image-*/*.img.gz
|
||||
ksud_magic-*
|
||||
ksud_overlayfs-*
|
||||
susfsd-*
|
||||
kernel-WSA*.zip
|
||||
kernel-ARCVM*.zip
|
||||
ksud_magic*.zip
|
||||
ksud_overlayfs*.zip
|
||||
susfsd*.zip
|
||||
13
.github/workflows/rustfmt.yml
vendored
13
.github/workflows/rustfmt.yml
vendored
@@ -6,13 +6,15 @@ on:
|
||||
- 'next'
|
||||
paths:
|
||||
- '.github/workflows/rustfmt.yml'
|
||||
- 'userspace/ksud/**'
|
||||
- 'userspace/ksud_magic/**'
|
||||
- 'userspace/ksud_overlayfs/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'next'
|
||||
paths:
|
||||
- '.github/workflows/rustfmt.yml'
|
||||
- 'userspace/ksud/**'
|
||||
- 'userspace/ksud_magic/**'
|
||||
- 'userspace/ksud_overlayfs/**'
|
||||
|
||||
permissions:
|
||||
checks: write
|
||||
@@ -30,4 +32,9 @@ jobs:
|
||||
- uses: LoliGothick/rustfmt-check@master
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
working-directory: userspace/ksud
|
||||
working-directory: userspace/ksud_magic
|
||||
|
||||
- uses: LoliGothick/rustfmt-check@master
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
working-directory: userspace/ksud_overlayfs
|
||||
2
.github/workflows/shellcheck.yml
vendored
2
.github/workflows/shellcheck.yml
vendored
@@ -24,4 +24,4 @@ jobs:
|
||||
uses: ludeeus/action-shellcheck@2.0.0
|
||||
with:
|
||||
ignore_names: gradlew
|
||||
ignore_paths: ./userspace/ksud/src/installer.sh
|
||||
ignore_paths: ./userspace/ksud_magic/src/installer.sh ./userspace/ksud_overlayfs/src/installer.sh
|
||||
9
.github/workflows/susfsd.yml
vendored
9
.github/workflows/susfsd.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Build susfsd
|
||||
name: Build susfsd aarch64
|
||||
on:
|
||||
push:
|
||||
branches: [ "next" ]
|
||||
@@ -8,9 +8,6 @@ on:
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
inputs:
|
||||
target:
|
||||
required: true
|
||||
type: string
|
||||
os:
|
||||
required: false
|
||||
type: string
|
||||
@@ -26,9 +23,9 @@ jobs:
|
||||
- name: Build susfsd
|
||||
working-directory: ./userspace/susfsd
|
||||
run: $ANDROID_NDK/ndk-build
|
||||
- name: Upload a Build Artifact
|
||||
- name: Upload Build Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: susfsd-aarch64-linux-android
|
||||
name: susfsd-linux-android
|
||||
path: ./userspace/susfsd/libs
|
||||
|
||||
|
||||
106
.github/workflows/wsa-kernel.yml
vendored
Normal file
106
.github/workflows/wsa-kernel.yml
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
name: Build Kernel - WSA
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
arch:
|
||||
required: true
|
||||
type: string
|
||||
description: >
|
||||
Build arch: x86_64 / arm64
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
description: >
|
||||
Build version
|
||||
jobs:
|
||||
build:
|
||||
name: Build WSA-Kernel-${{ inputs.version }}-${{ inputs.arch }}
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
|
||||
CCACHE_NOHASHDIR: "true"
|
||||
CCACHE_HARDLINK: "true"
|
||||
|
||||
steps:
|
||||
- name: Install Build Tools
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1
|
||||
with:
|
||||
packages: bc bison build-essential flex libelf-dev binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu gzip ccache
|
||||
version: 1.0
|
||||
|
||||
- name: Cache LLVM
|
||||
id: cache-llvm
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ./llvm
|
||||
key: llvm-12.0.1
|
||||
|
||||
- name: Setup LLVM
|
||||
uses: KyleMayes/install-llvm-action@v1
|
||||
with:
|
||||
version: "12.0.1"
|
||||
force-version: true
|
||||
ubuntu-version: "16.04"
|
||||
cached: ${{ steps.cache-llvm.outputs.cache-hit }}
|
||||
|
||||
- name: Checkout KernelSU-Next
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: KernelSU-Next
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup kernel source
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: microsoft/WSA-Linux-Kernel
|
||||
ref: android-lts/latte-2/${{ inputs.version }}
|
||||
path: WSA-Linux-Kernel
|
||||
|
||||
- name: Setup Ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: WSA-Kernel-${{ inputs.version }}-${{ inputs.arch }}
|
||||
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
||||
max-size: 2G
|
||||
|
||||
- name: Setup KernelSU-Next
|
||||
working-directory: WSA-Linux-Kernel
|
||||
run: |
|
||||
echo "[+] KernelSU-Next setup"
|
||||
KERNEL_ROOT=$GITHUB_WORKSPACE/WSA-Linux-Kernel
|
||||
echo "[+] KERNEL_ROOT: $KERNEL_ROOT"
|
||||
echo "[+] Copy KernelSU-Next driver to $KERNEL_ROOT/drivers"
|
||||
ln -sf $GITHUB_WORKSPACE/KernelSU-Next/kernel $KERNEL_ROOT/drivers/kernelsu
|
||||
echo "[+] Add KernelSU-Next driver to Makefile"
|
||||
DRIVER_MAKEFILE=$KERNEL_ROOT/drivers/Makefile
|
||||
DRIVER_KCONFIG=$KERNEL_ROOT/drivers/Kconfig
|
||||
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE"
|
||||
grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG"
|
||||
echo "[+] Apply KernelSU-Next patches"
|
||||
cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU-Next/.github/patches/5.15/*.patch || echo "[-] No patch found"
|
||||
echo "[+] KernelSU-Next setup done."
|
||||
cd $GITHUB_WORKSPACE/KernelSU-Next
|
||||
VERSION=$(($(git rev-list --count HEAD) + 10200))
|
||||
echo "VERSION: $VERSION"
|
||||
echo "kernelsu_version=$VERSION" >> $GITHUB_ENV
|
||||
|
||||
- name: Build Kernel
|
||||
working-directory: WSA-Linux-Kernel
|
||||
run: |
|
||||
if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then
|
||||
export KSU_EXPECTED_SIZE=${{ vars.EXPECTED_SIZE }}
|
||||
export KSU_EXPECTED_HASH=${{ vars.EXPECTED_HASH }}
|
||||
fi
|
||||
declare -A ARCH_MAP=(["x86_64"]="x64" ["arm64"]="arm64")
|
||||
cp configs/wsa/config-wsa-${ARCH_MAP[${{ inputs.arch }}]} .config
|
||||
make olddefconfig
|
||||
declare -A FILE_NAME=(["x86_64"]="bzImage" ["arm64"]="Image")
|
||||
make -j`nproc` LLVM=1 ARCH=${{ inputs.arch }} $(if [ "${{ inputs.arch }}" == "arm64" ]; then echo CROSS_COMPILE=aarch64-linux-gnu; fi) ${FILE_NAME[${{ inputs.arch }}]} CCACHE="/usr/bin/ccache"
|
||||
declare -A ARCH_MAP_FILE=(["x86_64"]="x86" ["arm64"]="arm64")
|
||||
echo "file_path=WSA-Linux-Kernel/arch/${ARCH_MAP_FILE[${{ inputs.arch }}]}/boot/${FILE_NAME[${{ inputs.arch }}]}" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload kernel-${{ inputs.arch }}-${{ inputs.version }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kernel-WSA-${{ inputs.arch }}-${{ inputs.version }}
|
||||
path: "${{ env.file_path }}"
|
||||
104
docs/README.md
104
docs/README.md
@@ -1,49 +1,89 @@
|
||||
**English** | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [ภาษาไทย](README_TH.md)
|
||||
**English** | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Українська](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md)
|
||||
|
||||
# KernelSU Next
|
||||
---
|
||||
|
||||
<img src="/assets/kernelsu_next.png" style="width: 96px;" alt="logo">
|
||||
<div align="center">
|
||||
<img src="/assets/kernelsu_next.png" width="96" alt="KernelSU Next Logo">
|
||||
|
||||
A Kernel-based root solution for Android devices.
|
||||
<h2>KernelSU Next</h2>
|
||||
<p><strong>A kernel-based root solution for Android devices.</strong></p>
|
||||
|
||||
[](https://github.com/rifsxd/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/rifsxd/KernelSU-Next/workflows/build-manager/next/manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
<p>
|
||||
<a href="https://github.com/KernelSU-Next/KernelSU-Next/releases/latest">
|
||||
<img src="https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github" alt="Latest Release">
|
||||
</a>
|
||||
<a href="https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager">
|
||||
<img src="https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff" alt="Nightly Build">
|
||||
</a>
|
||||
<a href="https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html">
|
||||
<img src="https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu" alt="License: GPL v2">
|
||||
</a>
|
||||
<a href="/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu" alt="GitHub License">
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
## Features
|
||||
---
|
||||
|
||||
1. Kernel-based `su` and root access management.
|
||||
2. Module system based on dynamic mount system [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
|
||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html): Lock up the root power in a cage.
|
||||
## 🚀 Features
|
||||
|
||||
## Compatibility State
|
||||
- Kernel-based `su` and root access management.
|
||||
- Module system based on [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) and [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
|
||||
- [App Profile](https://kernelsu.org/guide/app-profile.html): Limit root privileges per app.
|
||||
|
||||
KernelSU Next officially supports most Android kernels starting from 4.4 up to 6.6.
|
||||
- GKI 2.0 (5.10+) kernels can run pre-built images and LKM/KMI.
|
||||
- GKI 1.0 (4.19 - 5.4) kernels need to rebuilt with KernelSU driver.
|
||||
- EOL (<4.14) kernels also need to be rebuilt with KernelSU driver (3.18+ is experimental and may need some function backports).
|
||||
---
|
||||
|
||||
Currently, only `arm64-v8a` is supported.
|
||||
## ✅ Compatibility
|
||||
|
||||
## Usage
|
||||
KernelSU Next supports Android kernels from **4.4 up to 6.6**:
|
||||
|
||||
- [Installation instruction](https://rifsxd.github.io/KernelSU-Next/)
|
||||
| Kernel version | Support notes |
|
||||
|----------------------|-------------------------------------------------------------------------|
|
||||
| 5.10+ (GKI 2.0) | Supports pre-built images and LKM/KMI |
|
||||
| 4.19 – 5.4 (GKI 1.0) | Requires KernelSU driver built-in |
|
||||
| < 4.14 (EOL) | Requires KernelSU driver (3.18+ is experimental and may need backports) |
|
||||
|
||||
## Security
|
||||
**Supported architectures:** `arm64-v8a`, `armeabi-v7a` and `x86_64`
|
||||
|
||||
For information on reporting security vulnerabilities in KernelSU, see [SECURITY.md](/SECURITY.md).
|
||||
---
|
||||
|
||||
## License
|
||||
## 📦 Installation
|
||||
|
||||
- Files under the `kernel` directory are [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
- All other parts except the `kernel` directory are [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||
Please refer to the [Installation](https://kernelsu-next.github.io/webpage/pages/installation.html) guide for setup instructions.
|
||||
|
||||
## Credits
|
||||
---
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): the KernelSU idea.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): the powerful root tool.
|
||||
- [genuine](https://github.com/brevent/genuine/): apk v2 signature validation.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): some rootkit skills.
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): thanks to tiann, or else KernelSU Next wouldn't even exist.
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff for saving KernelSU!
|
||||
## 🔐 Security
|
||||
|
||||
To report security issues, please see [SECURITY.md](/SECURITY.md).
|
||||
|
||||
---
|
||||
|
||||
## 📜 License
|
||||
|
||||
- **`/kernel` directory:** [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
- **All other files:** [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||
|
||||
---
|
||||
|
||||
## 💸 Donations
|
||||
|
||||
If you’d like to support the project:
|
||||
|
||||
- **USDT (BEP20, ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
|
||||
- **USDT (TRC20)**: `TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh`
|
||||
- **ETH (ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
|
||||
- **LTC**: `Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL`
|
||||
- **BTC**: `19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6`
|
||||
|
||||
---
|
||||
|
||||
## 🙏 Credits
|
||||
|
||||
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/) – Concept inspiration
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk) – Core root implementation
|
||||
- [Genuine](https://github.com/brevent/genuine/) – APK v2 signature validation
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine) – Rootkit techniques
|
||||
- [KernelSU](https://github.com/tiann/KernelSU) – The original base that made KernelSU Next possible
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs) – 💜 to 5ec1cff for keeping KernelSU alive
|
||||
|
||||
58
docs/README_BG.md
Normal file
58
docs/README_BG.md
Normal file
@@ -0,0 +1,58 @@
|
||||
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | **Български** | [日本語](README_JA.md)
|
||||
|
||||
# KernelSU Next
|
||||
|
||||
<img src="/assets/kernelsu_next.png" style="width: 96px;" alt="лого">
|
||||
|
||||
Ядрено решение за root достъп за Android устройства.
|
||||
|
||||
[](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## Възможности
|
||||
|
||||
1. Управление на `su` и root достъп на ядрено ниво
|
||||
2. Система за модули базирана на [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS)
|
||||
3. [Профили за приложения](https://kernelsu.org/guide/app-profile.html): Ограничаване на root права за конкретни приложения
|
||||
|
||||
## Съвместимост
|
||||
|
||||
KernelSU Next официално поддържа повечето Android ядра от версия 4.4 до 6.6:
|
||||
- Ядра GKI 2.0 (5.10+) могат да използват предварително компилирани изображения и LKM/KMI
|
||||
- Ядра GKI 1.0 (4.19 - 5.4) изискват прекомпилиране с драйвера на KernelSU
|
||||
- Остарели ядра (<4.14) също изискват прекомпилиране (3.18+ е експериментална поддръжка)
|
||||
|
||||
В момента се поддържа само архитектурата `arm64-v8a`, `armeabi-v7a` & `x86_64`.
|
||||
|
||||
## Инсталация
|
||||
|
||||
- [Инструкции за инсталиране](https://ksunext.org/pages/installation.html)
|
||||
|
||||
## Сигурност
|
||||
|
||||
За докладване на уязвимости вижте [SECURITY.md](/SECURITY.md).
|
||||
|
||||
## Лиценз
|
||||
|
||||
- Файловете в директорията `kernel` са [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
- Всички останали файлове са [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
## Дарения
|
||||
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT BEP20 ]
|
||||
- TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh [ USDT TRC20 ]
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT ERC20 ]
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ ETH ERC20 ]
|
||||
- Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL [ LTC ]
|
||||
- 19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6 [ BTC ]
|
||||
|
||||
## Благодарности
|
||||
|
||||
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): Идеята за KernelSU
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): Мощният root инструмент
|
||||
- [genuine](https://github.com/brevent/genuine/): Валидация на APK подписи v2
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): Rootkit техники
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): Благодарности към tiann за създаването на KernelSU
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff за спасяването на KernelSU
|
||||
@@ -1,4 +1,4 @@
|
||||
[English](README.md) | **简体中文** | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [ภาษาไทย](README_TH.md)
|
||||
[English](README.md) | **简体中文** | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md)
|
||||
|
||||
# KernelSU Next
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
|
||||
安卓基于内核的 Root 方案
|
||||
|
||||
[](https://github.com/rifsxd/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/rifsxd/KernelSU-Next/workflows/build-manager/next/manager)
|
||||
[](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
[](/LICENSE)
|
||||
|
||||
## 特性
|
||||
|
||||
1. 基于内核的 `SU` 和权限管理
|
||||
1. 基于内核的 `su` 和超级用户权限管理
|
||||
2. 基于动态挂载系统 [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) 的模块系统。
|
||||
3. [App Profile](https://kernelsu.org/zh_CN/guide/app-profile.html):把 Root 权限关进笼子里
|
||||
|
||||
@@ -24,11 +24,11 @@ KernelSU Next 支持从 4.4 到 6.6 的大多数安卓内核
|
||||
- GKI 1.0(4.19 - 5.4)内核需要使用 KernelSU 内核驱动重新编译
|
||||
- EOL (<4.14) 内核也需要使用 KernelSU 内核驱动重新编译 (3.18+ 的版本处于试验阶段,可能需要移植一些功能)
|
||||
|
||||
目前只支持 `arm64-v8a` 架构
|
||||
目前只支持 `arm64-v8a`, `armeabi-v7a` & `x86_64` 架构
|
||||
|
||||
## 用法
|
||||
|
||||
- [安装说明](https://rifsxd.github.io/KernelSU-Next/)
|
||||
- [安装说明](https://ksunext.org/pages/installation.html)
|
||||
|
||||
## 安全性
|
||||
|
||||
@@ -41,9 +41,9 @@ KernelSU Next 支持从 4.4 到 6.6 的大多数安卓内核
|
||||
|
||||
## 鸣谢
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU 的灵感.
|
||||
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU 的灵感.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): 强大的 Root 工具.
|
||||
- [genuine](https://github.com/brevent/genuine/): apk v2 签名验证。
|
||||
- [genuine](https://github.com/brevent/genuine/): APK v2 签名验证。
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): 一些 Rootkit 技巧。
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): 感谢 tiann,否则 KernelSU Next 根本不会存在。
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff 為了拯救 KernelSU!
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff 为了拯救 KernelSU!
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | **Français** | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [ภาษาไทย](README_TH.md)
|
||||
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | **Français** | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md)
|
||||
|
||||
# KernelSU Next
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
|
||||
Une solution root basée sur le noyau pour les appareils Android.
|
||||
|
||||
[](https://github.com/rifsxd/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/rifsxd/KernelSU-Next/workflows/build-manager/next/manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
[](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## Fonctionnalités
|
||||
|
||||
@@ -24,11 +24,11 @@ KernelSU Next prend officiellement en charge la plupart des noyaux Android de la
|
||||
- Les noyaux GKI 1.0 (4.19 - 5.4) doivent être reconstruits avec le pilote KernelSU.
|
||||
- Les noyaux EOL (<4.14) doivent également être reconstruits avec le pilote KernelSU (3.18+ est expérimental et peut nécessiter des rétroportages fonctionnels).
|
||||
|
||||
Actuellement, seul `arm64-v8a` est pris en charge.
|
||||
Actuellement, seul `arm64-v8a`, `armeabi-v7a` & `x86_64` est pris en charge.
|
||||
|
||||
## Utilisation
|
||||
|
||||
- [Instructions d'installation](https://rifsxd.github.io/KernelSU-Next/)
|
||||
- [Instructions d'installation](https://ksunext.org/pages/installation.html)
|
||||
|
||||
## Sécurité
|
||||
|
||||
@@ -41,9 +41,9 @@ Pour signaler des vulnérabilités de sécurité dans KernelSU, consultez [SECUR
|
||||
|
||||
## Crédits
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/) : l'idée de KernelSU.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk) : l'outil root puissant.
|
||||
- [genuine](https://github.com/brevent/genuine/) : validation de signature apk v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine) : quelques techniques de rootkit.
|
||||
- [KernelSU](https://github.com/tiann/KernelSU) : merci à tiann, sans qui KernelSU Next n'existerait même pas.
|
||||
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/) : L'idée de KernelSU.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk) : L'outil root puissant.
|
||||
- [genuine](https://github.com/brevent/genuine/) : Validation de signature APK v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine) : Quelques techniques de rootkit.
|
||||
- [KernelSU](https://github.com/tiann/KernelSU) : Merci à tiann, sans qui KernelSU Next n'existerait même pas.
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs) : 💜 5ec1cff pour avoir sauvé KernelSU !
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | **Bahasa Indonesia** | [Русский](README_RU.md) | [ภาษาไทย](README_TH.md)
|
||||
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | **Bahasa Indonesia** | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md)
|
||||
|
||||
# KernelSU Next
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
|
||||
Sebuah solusi root berbasis Kernel untuk perangkat Android.
|
||||
|
||||
[](https://github.com/rifsxd/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/rifsxd/KernelSU-Next/workflows/build-manager/next/manager)
|
||||
[](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
[](/LICENSE)
|
||||
|
||||
## Fitur
|
||||
|
||||
@@ -24,11 +24,11 @@ KernelSU Next secara resmi mendukung sebagian besar kernel Android mulai dari 4.
|
||||
- Kernel GKI 1.0 (4.19 - 5.4) perlu dibangun ulang dengan driver KernelSU.
|
||||
- Kernel EOL (<4.14) juga perlu dibangun ulang dengan driver KernelSU (3.18+ bersifat eksperimental dan mungkin memerlukan beberapa backport fungsi).
|
||||
|
||||
Saat ini, hanya `arm64-v8a` yang didukung.
|
||||
Saat ini, hanya `arm64-v8a`, `armeabi-v7a` & `x86_64` yang didukung.
|
||||
|
||||
## Penggunaan
|
||||
|
||||
- [Petunjuk instalasi](https://rifsxd.github.io/KernelSU-Next/)
|
||||
- [Petunjuk instalasi](https://ksunext.org/pages/installation.html)
|
||||
|
||||
## Keamanan
|
||||
|
||||
@@ -41,9 +41,9 @@ Untuk informasi tentang melaporkan kerentanannya di KernelSU, lihat [SECURITY.md
|
||||
|
||||
## Kredit
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): ide KernelSU.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): alat root yang kuat.
|
||||
- [genuine](https://github.com/brevent/genuine/): validasi tanda tangan apk v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): beberapa keterampilan rootkit.
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): terima kasih kepada tiann, jika tidak, KernelSU Next bahkan tidak akan ada.
|
||||
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): Ide KernelSU.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): Alat root yang kuat.
|
||||
- [genuine](https://github.com/brevent/genuine/): Validasi tanda tangan APK v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): Beberapa keterampilan rootkit.
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): Terima kasih kepada tiann, jika tidak, KernelSU Next bahkan tidak akan ada.
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff karena menyelamatkan KernelSU!
|
||||
|
||||
63
docs/README_IT.md
Normal file
63
docs/README_IT.md
Normal file
@@ -0,0 +1,63 @@
|
||||
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | **Italiano** | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md)
|
||||
|
||||
# KernelSU Next
|
||||
|
||||
<img src="/assets/kernelsu_next.png" style="width: 96px;" alt="logo">
|
||||
|
||||
Una soluzione root basata sul kernel per dispositivi Android.
|
||||
|
||||
[](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## Caratteristiche
|
||||
|
||||
1. Gestione degli accessi `su` e root basata sul kernel.
|
||||
2. Sistema modulare basato sul sistema di montaggio dinamico [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
|
||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html): Rinchiudi il potere della radice in una gabbia.
|
||||
|
||||
## Stato compatibilità
|
||||
|
||||
KernelSU Next supporta ufficialmente la maggior parte dei kernel Android dalla versione 4.4 alla 6.6.
|
||||
- I kernel GKI 2.0 (5.10+) possono eseguire immagini precostruite e LKM/KMI.
|
||||
- I kernel GKI 1.0 (4.19 - 5.4) devono essere ricostruiti con il driver KernelSU.
|
||||
- Anche i kernel EOL (<4.14) devono essere ricostruiti con il driver KernelSU (la versione 3.18+ è sperimentale e potrebbe richiedere alcuni backport di funzioni).
|
||||
|
||||
Attualmente è supportata solo l'architettura `arm64-v8a`, `armeabi-v7a` & `x86_64`.
|
||||
|
||||
## Utilizzo
|
||||
|
||||
- [Istruzioni per l'installazione](https://ksunext.org/pages/installation.html)
|
||||
|
||||
## Security
|
||||
|
||||
Per informazioni sulla segnalazione delle vulnerabilità di sicurezza in KernelSU, vedere [SECURITY.md](/SECURITY.md).
|
||||
|
||||
## Licenza
|
||||
|
||||
- I file nella directory `kernel` sono [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
- Tutte le altre parti eccetto la directory `kernel` sono [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||
|
||||
## Donazioni
|
||||
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT BEP20 ]
|
||||
|
||||
- TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh [ USDT TRC20 ]
|
||||
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT ERC20 ]
|
||||
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ ETH ERC20 ]
|
||||
|
||||
- Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL [ LTC ]
|
||||
|
||||
- 19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6 [ BTC ]
|
||||
|
||||
## Crediti
|
||||
|
||||
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): L'idea di KernelSU.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): Il potente strumento di root.
|
||||
- [genuine](https://github.com/brevent/genuine/): Convalida della firma APK v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): Alcune competenze sui rootkit.
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): Grazie a tiann, altrimenti KernelSU Next non esisterebbe nemmeno.
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff per aver salvato KernelSU!
|
||||
63
docs/README_JA.md
Normal file
63
docs/README_JA.md
Normal file
@@ -0,0 +1,63 @@
|
||||
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | **日本語**
|
||||
|
||||
# KernelSU Next
|
||||
|
||||
<img src="/assets/kernelsu_next.png" style="width: 96px;" alt="logo">
|
||||
|
||||
Android デバイス用のカーネルベースな root ソリューション。
|
||||
|
||||
[](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## 機能
|
||||
|
||||
1. カーネルベースの `su` および root アクセスの管理。
|
||||
2. 動的マウントシステム [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) をベースとしたモジュールシステム。
|
||||
3. [アプリプロファイル](https://kernelsu.org/guide/app-profile.html): root 権限をケージに閉じ込めます。
|
||||
|
||||
## 互換性の状態
|
||||
|
||||
KernelSU Next は 4.4 から 6.6 までのほとんどの Android カーネルを公式でサポートしています。
|
||||
- GKI 2.0 (5.10 以降) のカーネルはビルド済みイメージで LKM/KMI を実行できます。
|
||||
- GKI 1.0 (4.19 - 5.4) のカーネルは、KernelSU ドライバを使用してビルドする必要があります。
|
||||
- EOL (4.14 未満) のカーネルも KernelSU ドライバを使用して再ビルドする必要があります (3.18 以降は実験中の段階であり、一部の関数のバックポートが必要になる場合があります)。
|
||||
|
||||
現在 `arm64-v8a`, `armeabi-v7a` & `x86_64` アーキテクチャのみをサポートしています。
|
||||
|
||||
## 使い方
|
||||
|
||||
- [インストール手順](https://ksunext.org/pages/installation.html)
|
||||
|
||||
## セキュリティ
|
||||
|
||||
KernelSU のセキュリティ脆弱性の報告については [SECURITY.md](/SECURITY.md) を参照してください。
|
||||
|
||||
## ライセンス
|
||||
|
||||
- `kernel` ディレクトリ内のファイルは [GPL-2.0](https://www.gnu.org/licenses/old-licenses/gpl-2.0.ja.html) のみライセンス下にあります。
|
||||
- `kernel` ディレクトリを除くその他すべての部分は [GPL-3.0 またはそれ以降](https://www.gnu.org/licenses/gpl-3.0.html) のライセンス下にあります。
|
||||
|
||||
## 寄付
|
||||
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT BEP20 ]
|
||||
|
||||
- TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh [ USDT TRC20 ]
|
||||
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT ERC20 ]
|
||||
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ ETH ERC20 ]
|
||||
|
||||
- Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL [ LTC ]
|
||||
|
||||
- 19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6 [ BTC ]
|
||||
|
||||
## クレジット
|
||||
|
||||
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU のアイデアを考案。
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): パワフルな root ツール。
|
||||
- [genuine](https://github.com/brevent/genuine/): APK v2 署名認証。
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): いくつかの rootkit スキル。
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): tiann に感謝を申し上げます。これが存在しなければ KernelSU Next は存在しませんでした。
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff へ KernelSU を救ってくれてありがとう!
|
||||
@@ -1,4 +1,4 @@
|
||||
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | **한국어** | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [ภาษาไทย](README_TH.md)
|
||||
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | **한국어** | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md)
|
||||
|
||||
# KernelSU Next
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
|
||||
안드로이드 기기들을 위한 커널 기반 루팅 솔루션입니다.
|
||||
|
||||
[](https://github.com/rifsxd/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/rifsxd/KernelSU-Next/workflows/build-manager/next/manager)
|
||||
[](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
[](/LICENSE)
|
||||
|
||||
## 기능
|
||||
|
||||
@@ -24,11 +24,11 @@ KernelSU Next는 공식적으로 대부분의 4.4부터 6.6의 안드로이드
|
||||
- GKI 1.0 (4.19 - 5.4) 커널은 KernelSU 드라이버로 다시 빌드해야 합니다.
|
||||
- EOL (<4.14) 커널도 역시 KernelSU 드라이버로 다시 빌드해야 합니다.(3.18+는 실험적이며 일부 함수의 이식이 필요할 수 있습니다.).
|
||||
|
||||
현재는, `arm64-v8a`만 지원됩니다.
|
||||
현재는, `arm64-v8a`, `armeabi-v7a` & `x86_64` 만 지원됩니다.
|
||||
|
||||
## 사용 방법
|
||||
|
||||
- [설치 방법](https://rifsxd.github.io/KernelSU-Next/)
|
||||
- [설치 방법](https://ksunext.org/pages/installation.html)
|
||||
|
||||
## 보안
|
||||
|
||||
@@ -41,9 +41,9 @@ KernelSU의 보안 취약점 보고에 대한 자세한 내용은 [SECURITY.md](
|
||||
|
||||
## 크레딧
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU의 아이디어
|
||||
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU의 아이디어
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): 강력한 루팅 도구
|
||||
- [genuine](https://github.com/brevent/genuine/): apk v2 서명 검사
|
||||
- [genuine](https://github.com/brevent/genuine/): APK v2 서명 검사
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): 일부 rootkit 기술
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): KernelSU Next가 존재할 수 있게 해 준 tiann에게 감사드립니다.
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): KernelSU를 구해준 5ec1cff에게 감사드립니다!
|
||||
|
||||
63
docs/README_PL.md
Normal file
63
docs/README_PL.md
Normal file
@@ -0,0 +1,63 @@
|
||||
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | **Polski** | [Български](README_BG.md) | [日本語](README_JA.md)
|
||||
|
||||
# KernelSU Next
|
||||
|
||||
<img src="/assets/kernelsu_next.png" style="width: 96px;" alt="logo">
|
||||
|
||||
Bazujące na jądrze rozwiązanie root dla urządzeń z Androidem.
|
||||
|
||||
[](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## Funkcjonalności
|
||||
|
||||
1. Oparte na jądrze `su` i zarządzanie dostępem do roota.
|
||||
2. System modułów oparty na dynamicznym systemie montowania [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
|
||||
3. [Profil aplikacji](https://kernelsu.org/guide/app-profile.html): Ujarzmij moc roota poprzez możliwość nakładania ograniczeń na uprawnienia roota dla poszczególnych aplikacji.
|
||||
|
||||
## Stan zgodności
|
||||
|
||||
KernelSU Next oficjalnie obsługuje większość jąder Androida od wersji 4.4 do 6.6.
|
||||
- Jądra GKI 2.0 (5.10+) mogą uruchamiać wstępnie przygotowane obrazy i LKM/KMI.
|
||||
- Jądra GKI 1.0 (4.19 - 5.4) muszą zostać zrekompilowane z dodatkiem sterownika KernelSU.
|
||||
- Jądra EOL (<4.14) również muszą zostać zrekompilowane z dodatkiem sterownika KernelSU (obsługa 3.18+ jest eksperymentalna i może wymagać backportu pewnych funkcji).
|
||||
|
||||
Obecnie obsługiwana jest tylko architektura `arm64-v8a`, `armeabi-v7a` & `x86_64`
|
||||
|
||||
## Użycie
|
||||
|
||||
- [Instrukcja instalacji](https://ksunext.org/pages/installation.html)
|
||||
|
||||
## Bezpieczeństwo
|
||||
|
||||
Informacje na temat zgłaszania luk bezpieczeństwa w KernelSU znajdziesz w [SECURITY.md](/SECURITY.md).
|
||||
|
||||
## Licencje
|
||||
|
||||
- Pliki w katalogu `kernel` są dostępne na licencji [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
- Wszystkie inne elementy, z wyjątkiem katalogu `kernel`, są dostępne na licencji [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||
|
||||
## Darowizny
|
||||
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT BEP20 ]
|
||||
|
||||
- TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh [ USDT TRC20 ]
|
||||
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT ERC20 ]
|
||||
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ ETH ERC20 ]
|
||||
|
||||
- Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL [ LTC ]
|
||||
|
||||
- 19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6 [ BTC ]
|
||||
|
||||
## Podziękowania
|
||||
|
||||
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): Idea, na której opiera się KernelSU.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): Potężne narzędzie do rootowania.
|
||||
- [genuine](https://github.com/brevent/genuine/): Walidacja podpisu APK v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): Część zdolności rootkitowych.
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): Dzięki tiann, bez ciebie KernelSU Next w ogóle by nie istniał.
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff za uratowanie KernelSU!
|
||||
@@ -1,49 +1,89 @@
|
||||
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | **Português (Brasil)** | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [ภาษาไทย](README_TH.md)
|
||||
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | **Português (Brasil)** | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md)
|
||||
|
||||
# KernelSU Next
|
||||
---
|
||||
|
||||
<img src="/assets/kernelsu_next.png" style="width: 96px;" alt="logo">
|
||||
<div align="center">
|
||||
<img src="/assets/kernelsu_next.png" width="96" alt="KernelSU Next Logo">
|
||||
|
||||
Uma solução root baseada em kernel para dispositivos Android.
|
||||
<h2>KernelSU Next</h2>
|
||||
<p><strong>Uma solução root baseada em kernel para dispositivos Android.</strong></p>
|
||||
|
||||
[](https://github.com/rifsxd/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/rifsxd/KernelSU-Next/workflows/build-manager/next/manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
<p>
|
||||
<a href="https://github.com/KernelSU-Next/KernelSU-Next/releases/latest">
|
||||
<img src="https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github" alt="Latest Release">
|
||||
</a>
|
||||
<a href="https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager">
|
||||
<img src="https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff" alt="Nightly Build">
|
||||
</a>
|
||||
<a href="https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html">
|
||||
<img src="https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu" alt="License: GPL v2">
|
||||
</a>
|
||||
<a href="/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu" alt="GitHub License">
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
## Características
|
||||
---
|
||||
|
||||
1. `su` e gerenciamento de acesso root baseado em kernel.
|
||||
2. Sistema de módulos baseado em sistema de montagem dinâmica [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
|
||||
3. [Perfil do Aplicativo](https://kernelsu.org/pt_BR/guide/app-profile.html): Tranque o poder root em uma gaiola.
|
||||
## 🚀 Características
|
||||
|
||||
## Estado de compatibilidade
|
||||
- `su` e gerenciamento de acesso root baseado em kernel.
|
||||
- Sistema de módulos baseado em [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) e [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
|
||||
- [Perfil do app](https://kernelsu.org/pt_BR/guide/app-profile.html): Limitar privilégios root por app.
|
||||
|
||||
KernelSU Next suporta oficialmente a maioria dos kernels Android a partir de 4.4 até 6.6.
|
||||
- Os kernels GKI 2.0 (5.10+) podem executar imagens pré-construídas e LKM/KMI.
|
||||
- Os kernels GKI 1.0 (4.19 - 5.4) precisam ser reconstruídos com o driver KernelSU.
|
||||
- Os kernels EOL (<4.14) também precisam ser reconstruídos com o driver KernelSU (3.18+ é experimental e pode precisar portar algumas funções).
|
||||
---
|
||||
|
||||
Atualmente, apenas `arm64-v8a` é suportado.
|
||||
## ✅ Compatibilidade
|
||||
|
||||
## Uso
|
||||
O KernelSU Next oferece suporte a kernels Android **4.4 até 6.6**:
|
||||
|
||||
- [Instruções de instalação](https://rifsxd.github.io/KernelSU-Next/)
|
||||
| Versão do kernel | Notas de suporte |
|
||||
|----------------------|-------------------------------------------------------------------------------|
|
||||
| 5.10+ (GKI 2.0) | Suporta imagens pré-compiladas e LKM/KMI |
|
||||
| 4.19 – 5.4 (GKI 1.0) | Requer driver do KernelSU integrado |
|
||||
| < 4.14 (EOL) | Requer driver do KernelSU (3.18+ é experimental e pode precisar de backports) |
|
||||
|
||||
## Segurança
|
||||
**Arquiteturas suportadas:** `arm64-v8a`, `armeabi-v7a` e `x86_64`
|
||||
|
||||
Para obter informações sobre como relatar vulnerabilidades de segurança do KernelSU, consulte [SECURITY.md](/SECURITY.md).
|
||||
---
|
||||
|
||||
## Licença
|
||||
## 📦 Instalação
|
||||
|
||||
- Os arquivos no diretório `kernel` são [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
- Todas as outras partes, exceto o diretório `kernel` são [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||
Consulte o guia de [Instalação](https://kernelsu-next.github.io/webpage/pt_BR/pages/installation.html) para obter instruções de configuração.
|
||||
|
||||
## Créditos
|
||||
---
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): a ideia do KernelSU.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): a poderosa ferramenta root.
|
||||
- [genuine](https://github.com/brevent/genuine/): validação de assinatura apk v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): algumas habilidades de rootkit.
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): obrigado a tiann, ou então o KernelSU Next nem existiria.
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff por salvar o KernelSU!
|
||||
## 🔐 Segurança
|
||||
|
||||
Para relatar problemas de segurança, consulte [SECURITY.md](/SECURITY.md).
|
||||
|
||||
---
|
||||
|
||||
## 📜 Licença
|
||||
|
||||
- **Diretório `/kernel`:** [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
- **Todos os outros arquivos:** [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||
|
||||
---
|
||||
|
||||
## 💸 Doações
|
||||
|
||||
Se você quiser apoiar o projeto:
|
||||
|
||||
- **USDT (BEP20, ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
|
||||
- **USDT (TRC20)**: `TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh`
|
||||
- **ETH (ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
|
||||
- **LTC**: `Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL`
|
||||
- **BTC**: `19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6`
|
||||
|
||||
---
|
||||
|
||||
## 🙏 Créditos
|
||||
|
||||
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/) – Inspiração do conceito
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk) – Implementação root principal
|
||||
- [Genuine](https://github.com/brevent/genuine/) – Validação de assinatura APK v2
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine) – Técnicas de rootkit
|
||||
- [KernelSU](https://github.com/tiann/KernelSU) – A base original que tornou o KernelSU Next possível
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs) – 💜 para 5ec1cff por manter o KernelSU vivo
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | **Русский** | [ภาษาไทย](README_TH.md)
|
||||
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | **Русский** | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md)
|
||||
|
||||
# KernelSU Next
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
|
||||
Root-решение для Android на базе ядра.
|
||||
|
||||
[](https://github.com/rifsxd/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/rifsxd/KernelSU-Next/workflows/build-manager/next/manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
[](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## Функции
|
||||
|
||||
@@ -24,11 +24,11 @@ KernelSU Next работает с большинством ядер Android (4.4
|
||||
- GKI 1.0 (4.19 - 5.4) требуют пересборки с драйвером KernelSU.
|
||||
- EOL (<4.14) также требуют пересборки с драйвером KernelSU (версии 3.18+ экспериментальные и могут потребовать некоторые функции бэкпортов).
|
||||
|
||||
Сейчас поддерживается только `arm64-v8a`.
|
||||
Сейчас поддерживается только `arm64-v8a`, `armeabi-v7a` & `x86_64`.
|
||||
|
||||
## Использование
|
||||
|
||||
- [Инструкция по установке](https://rifsxd.github.io/KernelSU-Next/)
|
||||
- [Инструкция по установке](https://ksunext.org/pages/installation.html)
|
||||
|
||||
## Безопасность
|
||||
|
||||
@@ -39,11 +39,25 @@ KernelSU Next работает с большинством ядер Android (4.4
|
||||
- Всё, что в директории `kernel`, — [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
- Остальной код, кроме директории `kernel`, под [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||
|
||||
## Донаты
|
||||
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT BEP20 ]
|
||||
|
||||
- TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh [ USDT TRC20 ]
|
||||
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT ERC20 ]
|
||||
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ ETH ERC20 ]
|
||||
|
||||
- Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL [ LTC ]
|
||||
|
||||
- 19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6 [ BTC ]
|
||||
|
||||
## Благодарность
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): идея KernelSU.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): топовый инструмент для root.
|
||||
- [genuine](https://github.com/brevent/genuine/): валидация подписи APK v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): некоторые навыки rootkit.
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): спасибо tiann, без него KernelSU Next не релизнулся бы.
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff за сохранение KernelSU!
|
||||
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): Идея KernelSU.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): Топовый инструмент для root.
|
||||
- [genuine](https://github.com/brevent/genuine/): Валидация подписи APK v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): Некоторые навыки rootkit.
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): Спасибо tiann, без него KernelSU Next даже не существовал бы.
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff за сохранение KernelSU!
|
||||
|
||||
@@ -1,49 +1,63 @@
|
||||
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | **ภาษาไทย**
|
||||
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | **ภาษาไทย** | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md)
|
||||
|
||||
# KernelSU Next
|
||||
|
||||
<img src="/assets/kernelsu_next.png" style="width: 96px;" alt="logo">
|
||||
|
||||
โซลูชันรูทบนพื้นฐานเคอร์เนลสำหรับอุปกรณ์ Android
|
||||
โซลูชั่นรูทบนพื้นฐานเคอร์เนลสำหรับอุปกรณ์ Android
|
||||
|
||||
[](https://github.com/rifsxd/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/rifsxd/KernelSU-Next/workflows/build-manager/next/manager)
|
||||
[](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
[](/LICENSE)
|
||||
|
||||
## คุณสมบัติ
|
||||
|
||||
1. การจัดการการเข้าถึงรูทและ `su` บนเคอร์เนล
|
||||
2. ระบบโมดูลที่ใช้ระบบการติดตั้งแบบไดนามิก [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS)
|
||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html): จำกัดพลังรูทไว้สำหรับแอปต่างๆ
|
||||
1. จัดการการเข้าถึงรูท และ `su` บนพื้นฐานเคอร์เนล
|
||||
2. ระบบโมดูลแบบไดนามิกเมานต์ [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS)
|
||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html): จำกัดสิทธิ์เข้าถึงรูทสำหรับแอปต่างๆ
|
||||
|
||||
## การเข้ากันในอุปกรณ์ต่างๆ
|
||||
|
||||
KernelSU Next รองรับแบบเป็นทางการตั้งแต่เคอร์เนลแอนดรอยด์ 4.4 ถึง 6.6
|
||||
- GKI 2.0 (5.10+) เคอร์เนลสามารถรันแบบไฟล์สำเร็จรูปและ LKM/KMI ได้
|
||||
- GKI 1.0 (4.19 - 5.4) เคอร์เนลต้องการ build ร่วมกับไดร์เวอร์ของทาง KernelSU
|
||||
- EOL (<4.14) เคอร์เนลก็ต้องการ build ร่วมกับไดร์เวอร์ของทาง KernelSU เช่นกัน (3.18+ ยังเป็นการทดลองอยู่และยังต้องการเขียนในหลังบ้านเพิ่มเติม)
|
||||
- GKI 2.0 (5.10+) เคอร์เนลสามารถรันไฟล์อิมเมจสำเร็จรูป และ LKM/KMI ได้
|
||||
- GKI 1.0 (4.19 - 5.4) เคอร์เนลจะต้องรีบิ้วร่วมกับไดร์เวอร์ของ KernelSU
|
||||
- EOL (<4.14) เคอร์เนลก็ต้องรีบิ้วร่วมกับไดร์เวอร์ของ KernelSU เช่นกัน (3.18+ ยังเป็นเวอร์ชั่นทดลอง และยังต้องเขียนฟังก์ชั่นหลังบ้านเพิ่มเติม)
|
||||
|
||||
ในขณะนี้, มีแค่สถาปัตยกรรม `arm64-v8a` ที่รองรับเท่านั้น
|
||||
ในขณะนี้, มีแค่สถาปัตยกรรม `arm64-v8a`, `armeabi-v7a` & `x86_64` ที่รองรับเท่านั้น
|
||||
|
||||
## การใช้งาน
|
||||
|
||||
- [คำแนะนำในการติดตั้ง](https://rifsxd.github.io/KernelSU-Next/)
|
||||
- [คำแนะนำในการติดตั้ง](https://ksunext.org/pages/installation.html)
|
||||
|
||||
## ความปลอดภัย
|
||||
|
||||
สำหรับข้อมูลเกี่ยวกับการรายงานช่องโหว่ด้านความปลอดภัยใน KernelSU โปรดดูที่ [SECURITY.md](/SECURITY.md).
|
||||
สำหรับข้อมูลการรายงานช่องโหว่ด้านความปลอดภัยของ KernelSU โปรดดูที่ [SECURITY.md](/SECURITY.md).
|
||||
|
||||
## ใบอนุญาต
|
||||
## สัญญาอนุญาต
|
||||
|
||||
- ไฟล์ที่ภายใต้โฟลเดอร์ `kernel` ถือว่าเป็น [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
- ไฟล์ที่นอกเหนือจากโฟลเดอร์ `kernel` ถือว่าเป็น [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||
- ไฟล์ภายใต้โฟลเดอร์ `kernel` ถือว่าเป็นสัญญาอนุญาต [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
- ไฟล์ที่นอกเหนือจากโฟลเดอร์ `kernel` ถือว่าเป็นสัญญาอนุญาต [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||
|
||||
## การบริจาก
|
||||
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT BEP20 ]
|
||||
|
||||
- TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh [ USDT TRC20 ]
|
||||
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT ERC20 ]
|
||||
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ ETH ERC20 ]
|
||||
|
||||
- Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL [ LTC ]
|
||||
|
||||
- 19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6 [ BTC ]
|
||||
|
||||
## เครดิต
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): ที่เป็นคนริเริ่มไอเดียเกี่ยวกับ KernelSU
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): อุปกรณ์มือเกี่ยวกับรูทที่ทรงพลัง
|
||||
- [genuine](https://github.com/brevent/genuine/): การออกลายเซ็นให้กับไฟล์ apk v2
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): ความรู้ความสามารถเกี่ยวกับ rootkit
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): ต้องขอบคุณ tiann ถ้าไม่มีคนนั้นก็ไม่มีสิ่งที่เรียกว่า KernelSU เกิดขึ้น
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff ที่ช่วย KernelSU ไว้
|
||||
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): ที่เป็นคนริเริ่มไอเดีย KernelSU
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): เครื่องมือรูทที่ทรงพลัง
|
||||
- [genuine](https://github.com/brevent/genuine/): การตรวจสอบลายเซ็น APK v2
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): ความรู้เกี่ยวกับ rootkit
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): ต้องขอบคุณ tiann ไม่งั้นจะไม่มี KernelSU ขึ้นมา
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff ที่ช่วย KernelSU เอาไว้!
|
||||
|
||||
@@ -1,49 +1,63 @@
|
||||
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | **Türkçe** | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [ภาษาไทย](README_TH.md)
|
||||
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | **Türkçe** | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md)
|
||||
|
||||
# KernelSU Next
|
||||
|
||||
<img src="/assets/kernelsu_next.png" style="width: 96px;" alt="logo">
|
||||
|
||||
Android cihazlar için Kernel tabanlı bir root çözümü.
|
||||
Android cihazlar için çekirdek tabanlı root çözümü.
|
||||
|
||||
[](https://github.com/rifsxd/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/rifsxd/KernelSU-Next/workflows/build-manager/next/manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
[](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## Özellikler
|
||||
|
||||
1. Çekirdek tabanlı `su` ve kök erişim yönetimi.
|
||||
2. Dinamik montaj sistemine dayalı modül sistemi [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
|
||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html): Kök gücünü bir kafese kilitleyin.
|
||||
1. Çekirdek tabanlı `su` ve root erişimi yönetimi.
|
||||
2. Dinamik bağlama sistemi [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) tabanlı modül sistemi.
|
||||
3. [Uygulama Profili](https://kernelsu.org/guide/app-profile.html): Root yetkisini bir kafese kilitleyin.
|
||||
|
||||
## Uyumluluk Durumu
|
||||
|
||||
KernelSU Next, 4.4'dan başlayarak 6.6'ya kadar çoğu Android çekirdeğini resmi olarak desteklemektedir.
|
||||
- GKI 2.0 (5.10+) çekirdekleri önceden oluşturulmuş görüntüleri ve LKM/KMI'yi çalıştırabilir.
|
||||
- GKI 1.0 (4.19 - 5.4) çekirdeklerinin KernelSU sürücüsü ile yeniden oluşturulması gerekir.
|
||||
- EOL (<4.14) çekirdeklerinin de KernelSU sürücüsü ile yeniden oluşturulması gerekir. (3.18+ deneyseldir ve bazı fonksiyon geri yüklemelerine ihtiyaç duyulabilir.)
|
||||
KernelSU Next, resmi olarak Android çekirdeklerinin çoğunu 4.4 sürümünden 6.6 sürümüne kadar destekler.
|
||||
- GKI 2.0 (5.10+) çekirdekleri, hazır imajları ve LKM/KMI desteğini çalıştırabilir.
|
||||
- GKI 1.0 (4.19 - 5.4) çekirdeklerinin KernelSU sürücüsü ile yeniden derlenmesi gerekir.
|
||||
- EOL (<4.14) çekirdekler de KernelSU sürücüsüyle yeniden derlenmelidir (3.18+ deneysel olup bazı fonksiyonların geri aktarımı gerekebilir).
|
||||
|
||||
Şu anda sadece `arm64-v8a` desteklenmektedir.
|
||||
Şu anda yalnızca `arm64-v8a`, `armeabi-v7a` & `x86_64` mimarisi desteklenmektedir.
|
||||
|
||||
## Kullanım
|
||||
|
||||
- [Kurulum Talimatı](https://rifsxd.github.io/KernelSU-Next/)
|
||||
- [Kurulum Talimatları](https://ksunext.org/pages/installation.html)
|
||||
|
||||
## Güvenlik
|
||||
|
||||
KernelSU'daki güvenlik açıklarını bildirme hakkında bilgi için [SECURITY.md](/SECURITY.md) bölümüne bakın.
|
||||
KernelSU'daki güvenlik açıklarını bildirme hakkında bilgi için bkz: [SECURITY.md](/SECURITY.md)
|
||||
|
||||
## Lisans
|
||||
|
||||
- `kernel` dizini altındaki dosyalar sadece [GPL-2.0](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) lisansına tabiidir.
|
||||
- `kernel` dizini dışındaki diğer tüm kısımlar [GPL-3.0](https://www.gnu.org/licenses/gpl-3.0.html) ya da daha sonraki bir sürüm lisansa tabiidir.
|
||||
- `kernel` dizinindeki dosyalar [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) lisanslıdır.
|
||||
- `kernel` dizini dışındaki tüm diğer bölümler [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html) lisansı altındadır.
|
||||
|
||||
## Krediler
|
||||
## Bağışlar
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU fikri.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): güçlü kök aracı.
|
||||
- [genuine](https://github.com/brevent/genuine/): apk v2 imza doğrulama.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): bazı rootkit becerileri.
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): tiann'a teşekkürler, yoksa KernelSU Next var olamazdı bile.
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff KernelSU'yu kurtardığınız için!
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT BEP20 ]
|
||||
|
||||
- TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh [ USDT TRC20 ]
|
||||
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT ERC20 ]
|
||||
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ ETH ERC20 ]
|
||||
|
||||
- Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL [ LTC ]
|
||||
|
||||
- 19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6 [ BTC ]
|
||||
|
||||
## Katkıda Bulunanlar
|
||||
|
||||
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU fikrinin temeli.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): Güçlü root aracı.
|
||||
- [genuine](https://github.com/brevent/genuine/): APK v2 imza doğrulama.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): Bazı rootkit teknikleri.
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): tiann'a teşekkürler, KernelSU Next onun sayesinde var.
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 KernelSU'yu kurtardığı için 5ec1cff'e teşekkürler!
|
||||
@@ -1,19 +1,19 @@
|
||||
[English](README.md) | [简体中文](README_CN.md) | **繁體中文** | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [ภาษาไทย](README_TH.md)
|
||||
[English](README.md) | [简体中文](README_CN.md) | **繁體中文** | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md)
|
||||
|
||||
# KernelSU Next
|
||||
|
||||
<img src="/assets/kernelsu_next.png" style="width: 96px;" alt="logo">
|
||||
|
||||
基於內核的 Android 設備 root 解決方案
|
||||
基於內核的 Android 設備 Root 解決方案
|
||||
|
||||
[](https://github.com/rifsxd/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/rifsxd/KernelSU-Next/workflows/build-manager/next/manager)
|
||||
[](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
[](/LICENSE)
|
||||
|
||||
## 特性
|
||||
|
||||
1. 基於內核的 `su` 和 root 權限管理
|
||||
1. 基於內核的 `su` 和 Root 權限管理
|
||||
2. 基於動態掛載系統 [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) 的模塊系統。
|
||||
3. [App Profile](https://kernelsu.org/zh_CN/guide/app-profile.html):把 Root 權限關進籠子裡
|
||||
|
||||
@@ -24,11 +24,11 @@ KernelSU Next 正式支持大多數從 4.4 到 6.6 的 Android 內核
|
||||
- GKI 1.0 (4.19 - 5.4) 內核需要重新編譯 KernelSU 驅動程序
|
||||
- EOL (<4.14) 內核也需要重新編譯 KernelSU 驅動程序(3.18+ 是實驗性的,可能需要移植一些功能)
|
||||
|
||||
目前僅支持 `arm64-v8a`
|
||||
目前僅支持 `arm64-v8a`, `armeabi-v7a` & `x86_64`
|
||||
|
||||
## 用法
|
||||
|
||||
- [安裝說明](https://rifsxd.github.io/KernelSU-Next/)
|
||||
- [安裝說明](https://ksunext.org/pages/installation.html)
|
||||
|
||||
## 安全性
|
||||
|
||||
@@ -41,10 +41,9 @@ KernelSU Next 正式支持大多數從 4.4 到 6.6 的 Android 內核
|
||||
|
||||
## 鳴謝
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU 的靈感.
|
||||
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU 的靈感.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): 強大的 Root 工具.
|
||||
- [genuine](https://github.com/brevent/genuine/): apk v2 簽名驗證。
|
||||
- [genuine](https://github.com/brevent/genuine/): APK v2 簽名驗證。
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): 一些 Rootkit 技巧。
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): 感謝 tiann,否則 KernelSU Next 根本不會存在。
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff 為了拯救 KernelSU!
|
||||
|
||||
|
||||
91
docs/README_UA.md
Normal file
91
docs/README_UA.md
Normal file
@@ -0,0 +1,91 @@
|
||||
**Languages**:
|
||||
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md) | **Українська**
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<img src="/assets/kernelsu_next.png" width="96" alt="KernelSU Next Logo">
|
||||
|
||||
<h2>KernelSU Next</h2>
|
||||
<p><strong>Рішення для root-прав на основі ядра для пристроїв Android.</strong></p>
|
||||
|
||||
<p>
|
||||
<a href="https://github.com/KernelSU-Next/KernelSU-Next/releases/latest">
|
||||
<img src="https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github" alt="Latest Release">
|
||||
</a>
|
||||
<a href="https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager">
|
||||
<img src="https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff" alt="Nightly Build">
|
||||
</a>
|
||||
<a href="https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html">
|
||||
<img src="https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu" alt="License: GPL v2">
|
||||
</a>
|
||||
<a href="/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu" alt="GitHub License">
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Особливості
|
||||
|
||||
- Керування `su` та root-доступом на основі ядра
|
||||
- Модульна система на основі **[Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount)** та **[OverlayFS](https://en.wikipedia.org/wiki/OverlayFS)**
|
||||
- [Профілі програм](https://kernelsu.org/guide/app-profile.html): Обмеження root-прав для кожної програми
|
||||
|
||||
---
|
||||
|
||||
## ✅ Сумісність
|
||||
|
||||
KernelSU Next підтримує ядра Android від **4.4 до 6.6**:
|
||||
|
||||
| Версія ядра | Примітки підтримки |
|
||||
|----------------|---------------|
|
||||
| 5.10+ (GKI 2.0) | Підтримує попередньо створені образи та LKM/KMI |
|
||||
| 4.19 – 5.4 (GKI 1.0) | Потрібен вбудований драйвер KernelSU |
|
||||
| <4.14 (EOL) | Потрібен драйвер KernelSU (версія 3.18+ є експериментальною, може знадобитися портування) |
|
||||
|
||||
**Підтримувані архітектури:**
|
||||
`arm64-v8a`, `armeabi-v7a`, `x86_64`
|
||||
|
||||
---
|
||||
|
||||
## 📦 Встановлення
|
||||
|
||||
Будь ласка, зверніться до [Посібника з встановлення](https://kernelsu-next.github.io/webpage/pages/installation.html) для отримання інструкцій з налаштування.
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Безпека
|
||||
|
||||
Щоб повідомити про проблеми безпеки, див. [SECURITY.md](/SECURITY.md).
|
||||
|
||||
---
|
||||
|
||||
## 📜 Ліцензія
|
||||
|
||||
- **Каталог `/kernel`:** [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
- **Усі інші файли:** [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
---
|
||||
|
||||
## 💸 Пожертви
|
||||
|
||||
Якщо ви хочете підтримати проєкт:
|
||||
|
||||
- **USDT (BEP20, ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
|
||||
- **USDT (TRC20)**: `TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh`
|
||||
- **ETH (ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
|
||||
- **LTC**: `Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL`
|
||||
- **BTC**: `19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6`
|
||||
|
||||
---
|
||||
|
||||
## 🙏 Подяки
|
||||
|
||||
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/) – Натхнення для концепції
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk) – Топовий інструмент для root
|
||||
- [Genuine](https://github.com/brevent/genuine/) – Перевірка підпису APK версії 2
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine) – Деякі навики RootKit
|
||||
- [KernelSU](https://github.com/tiann/KernelSU) – Основа для KernelSU Next
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs) – 💜 до 5ec1cff за збереження KernelSU
|
||||
63
docs/README_VI.md
Normal file
63
docs/README_VI.md
Normal file
@@ -0,0 +1,63 @@
|
||||
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | **Tiếng Việt** | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md)
|
||||
|
||||
# KernelSU Next
|
||||
|
||||
<img src="/assets/kernelsu_next.png" style="width: 96px;" alt="logo">
|
||||
|
||||
Một giải pháp root từ nhân linux dành cho các thiết bị chạy Android
|
||||
|
||||
[](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
|
||||
[](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## Tính năng
|
||||
|
||||
1. Quản lý quyền truy cập SU dựa trên kernel android.
|
||||
2. Hệ thống mount module dựa trên 1 trong 2 cơ chế mount [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
|
||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html): Quản lý quyền truy cập root 1 cách chặt chẽ
|
||||
|
||||
## Danh sách tương thích
|
||||
|
||||
KernelSU Next hỗ trợ chính thức các kernel Android từ phiên bản 4.4 đến 6.6
|
||||
- GKI 2.0 (5.10+) kernels có thể cài đặt qua những .img/.zip đã được build sẵn và LKM/KMI hoặc tự vá qua manager (nếu được)
|
||||
- GKI 1.0 (4.19 - 5.4) kernels cần dược build lại với các nhân KernelSU Next
|
||||
- EOL (<4.14) kernels cần dược build lại với các nhân KernelSU Next (các kernels 3.18+ đang dược thử nghiệm và có thể cần backports 1 vài thứ ).
|
||||
|
||||
Hiện tại kernelSU Next chỉ hỗ trợ những cpu có `arm64-v8a`, `armeabi-v7a` & `x86_64`
|
||||
|
||||
## Sử dụng
|
||||
|
||||
- [Hướng dẫn vá KernelSU Next vào Kernel của bạn (yêu cầu kernel source)](https://ksunext.org/pages/installation.html)
|
||||
|
||||
## Bảo mật
|
||||
|
||||
Để biết thêm thông tin về việc báo cáo lỗ hổng bảo mật trong KernelSU Next vui lòng đọc (Thông tin sẽ dược gửi về KernelSU)[SECURITY.md](/SECURITY.md).
|
||||
|
||||
## Gíây phép
|
||||
|
||||
- Những thư mục/tập tin trong `kernel` là giấy phép [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
- Những thư mục/tập tin ngoài `kernel` là giấy phép [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||
|
||||
## Quyên góp/Hỗ trợ
|
||||
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT BEP20 ]
|
||||
|
||||
- TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh [ USDT TRC20 ]
|
||||
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT ERC20 ]
|
||||
|
||||
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ ETH ERC20 ]
|
||||
|
||||
- Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL [ LTC ]
|
||||
|
||||
- 19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6 [ BTC ]
|
||||
|
||||
## Lời cảm ơn tới...
|
||||
|
||||
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): Ý tưởng cho sự ra đời của KernelSU.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): Công cụ root mạnh mẽ, quen thuộc và tương thích cao cho các thiết bị chạy Android.
|
||||
- [genuine](https://github.com/brevent/genuine/): Chữ kí apk v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): Một vài kỹ năng rootkit.
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): Nguồn gốc của KernelSU Next, thanks to tiann.
|
||||
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 5ec1cff - người đã cứu lấy KernelSU💜 !
|
||||
@@ -9,6 +9,14 @@ config KSU
|
||||
To compile as a module, choose M here: the
|
||||
module will be called kernelsu.
|
||||
|
||||
config KSU_KPROBES_HOOK
|
||||
bool "Use kprobes for kernelsu"
|
||||
depends on KSU
|
||||
depends on KPROBES
|
||||
default y
|
||||
help
|
||||
Disable if you use manual hooks.
|
||||
|
||||
config KSU_DEBUG
|
||||
bool "KernelSU debug mode"
|
||||
depends on KSU
|
||||
@@ -24,4 +32,12 @@ config KSU_ALLOWLIST_WORKAROUND
|
||||
Enable session keyring init workaround for problematic devices.
|
||||
Useful for situations where the SU allowlist is not kept after a reboot.
|
||||
|
||||
config KSU_LSM_SECURITY_HOOKS
|
||||
bool "use lsm security hooks"
|
||||
depends on KSU
|
||||
default y
|
||||
help
|
||||
Disabling this is mostly only useful for kernel 4.1 and older.
|
||||
Make sure to implement manual hooks on security/security.c.
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -16,18 +16,32 @@ ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-
|
||||
|
||||
obj-$(CONFIG_KSU) += kernelsu.o
|
||||
|
||||
# .git is a text file while the module is imported by 'git submodule add'.
|
||||
ifeq ($(shell test -e $(srctree)/$(src)/../.git; echo $$?),0)
|
||||
$(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin [ -f ../.git/shallow ] && git fetch --unshallow)
|
||||
KSU_GIT_VERSION := $(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git rev-list --count HEAD)
|
||||
# ksu_version: major * 10000 + git version + 200 for historical reasons
|
||||
$(eval KSU_VERSION=$(shell expr 10000 + $(KSU_GIT_VERSION) + 200))
|
||||
REPO_OWNER := KernelSU-Next
|
||||
REPO_NAME := KernelSU-Next
|
||||
REPO_BRANCH := next
|
||||
|
||||
GIT_BIN := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git
|
||||
CURL_BIN := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin curl
|
||||
|
||||
KSU_GITHUB_VERSION := $(shell $(CURL_BIN) -sI "https://api.github.com/repos/$(REPO_OWNER)/$(REPO_NAME)/commits?sha=$(REPO_BRANCH)&per_page=1" | grep -i "link:" | sed -n 's/.*page=\([0-9]*\)>; rel="last".*/\1/p')
|
||||
|
||||
ifeq ($(KSU_GITHUB_VERSION),)
|
||||
ifeq ($(shell test -e $(srctree)/$(src)/../.git; echo $$?),0)
|
||||
$(shell cd $(srctree)/$(src); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
|
||||
KSU_LOCAL_VERSION := $(shell cd $(srctree)/$(src); $(GIT_BIN) rev-list --count HEAD)
|
||||
$(eval KSU_VERSION := $(shell expr 10000 + $(KSU_LOCAL_VERSION) + 200))
|
||||
$(info -- KernelSU-Next version (local .git): $(KSU_VERSION))
|
||||
else
|
||||
$(eval KSU_VERSION := 11998)
|
||||
$(warning -- Could not fetch version online or via local .git! Using fallback version: $(KSU_VERSION))
|
||||
endif
|
||||
else
|
||||
$(eval KSU_VERSION := $(shell expr 10000 + $(KSU_GITHUB_VERSION) + 200))
|
||||
$(info -- KernelSU-Next version (GitHub): $(KSU_VERSION))
|
||||
endif
|
||||
|
||||
$(info -- KernelSU-Next version: $(KSU_VERSION))
|
||||
ccflags-y += -DKSU_VERSION=$(KSU_VERSION)
|
||||
else # If there is no .git file, the default version will be passed.
|
||||
$(warning "KSU_GIT_VERSION not defined! It is better to make KernelSU-Next a git submodule!")
|
||||
ccflags-y += -DKSU_VERSION=16
|
||||
endif
|
||||
|
||||
ifeq ($(shell grep -q " current_sid(void)" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
|
||||
ccflags-y += -DKSU_COMPAT_HAS_CURRENT_SID
|
||||
@@ -37,6 +51,18 @@ ifeq ($(shell grep -q "struct selinux_state " $(srctree)/security/selinux/includ
|
||||
ccflags-y += -DKSU_COMPAT_HAS_SELINUX_STATE
|
||||
endif
|
||||
|
||||
ifeq ($(shell grep -q "strncpy_from_user_nofault" $(srctree)/include/linux/uaccess.h; echo $$?),0)
|
||||
ccflags-y += -DKSU_STRNCPY_FROM_USER_NOFAULT
|
||||
endif
|
||||
|
||||
ifeq ($(shell grep -q "ssize_t kernel_read" $(srctree)/fs/read_write.c; echo $$?),0)
|
||||
ccflags-y += -DKSU_KERNEL_READ
|
||||
endif
|
||||
|
||||
ifeq ($(shell grep "ssize_t kernel_write" $(srctree)/fs/read_write.c | grep -q "const void" ; echo $$?),0)
|
||||
ccflags-y += -DKSU_KERNEL_WRITE
|
||||
endif
|
||||
|
||||
ifndef KSU_NEXT_EXPECTED_SIZE
|
||||
KSU_NEXT_EXPECTED_SIZE := 0x3e6
|
||||
endif
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <linux/capability.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/gfp.h>
|
||||
@@ -64,12 +65,14 @@ static void remove_uid_from_arr(uid_t uid)
|
||||
|
||||
static void init_default_profiles()
|
||||
{
|
||||
kernel_cap_t full_cap = CAP_FULL_SET;
|
||||
|
||||
default_root_profile.uid = 0;
|
||||
default_root_profile.gid = 0;
|
||||
default_root_profile.groups_count = 1;
|
||||
default_root_profile.groups[0] = 0;
|
||||
memset(&default_root_profile.capabilities, 0xff,
|
||||
sizeof(default_root_profile.capabilities));
|
||||
memcpy(&default_root_profile.capabilities.effective, &full_cap,
|
||||
sizeof(default_root_profile.capabilities.effective));
|
||||
default_root_profile.namespaces = 0;
|
||||
strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
|
||||
|
||||
@@ -110,6 +113,7 @@ void ksu_show_allow_list(void)
|
||||
static void ksu_grant_root_to_shell()
|
||||
{
|
||||
struct app_profile profile = {
|
||||
.version = KSU_APP_PROFILE_VER,
|
||||
.allow_su = true,
|
||||
.current_uid = 2000,
|
||||
};
|
||||
@@ -152,11 +156,6 @@ static bool profile_valid(struct app_profile *profile)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (forbid_system_uid(profile->current_uid)) {
|
||||
pr_err("uid lower than 2000 is unsupported: %d\n", profile->current_uid);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (profile->version < KSU_APP_PROFILE_VER) {
|
||||
pr_info("Unsupported profile version: %d\n", profile->version);
|
||||
return false;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "apk_sign.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "kernel_compat.h"
|
||||
#include "throne_tracker.h"
|
||||
|
||||
|
||||
struct sdesc {
|
||||
@@ -316,5 +317,21 @@ module_param_cb(ksu_debug_manager_uid, &expected_size_ops,
|
||||
|
||||
bool is_manager_apk(char *path)
|
||||
{
|
||||
int tries = 0;
|
||||
|
||||
while (tries++ < 10) {
|
||||
if (!is_lock_held(path))
|
||||
break;
|
||||
|
||||
pr_info("%s: waiting for %s\n", __func__, path);
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
// let it go, if retry fails, check_v2_signature will fail to open it anyway
|
||||
if (tries == 10) {
|
||||
pr_info("%s: timeout for %s\n", __func__, path);
|
||||
return false;
|
||||
}
|
||||
|
||||
return check_v2_signature(path, EXPECTED_NEXT_SIZE, EXPECTED_NEXT_HASH);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kprobes.h>
|
||||
#ifdef CONFIG_KSU_LSM_SECURITY_HOOKS
|
||||
#include <linux/lsm_hooks.h>
|
||||
#endif
|
||||
#include <linux/mm.h>
|
||||
#include <linux/nsproxy.h>
|
||||
#include <linux/path.h>
|
||||
@@ -53,6 +55,10 @@ static bool ksu_module_mounted = false;
|
||||
|
||||
extern int handle_sepolicy(unsigned long arg3, void __user *arg4);
|
||||
|
||||
static bool ksu_su_compat_enabled = true;
|
||||
extern void ksu_sucompat_init();
|
||||
extern void ksu_sucompat_exit();
|
||||
|
||||
static inline bool is_allow_su()
|
||||
{
|
||||
if (is_manager()) {
|
||||
@@ -112,6 +118,7 @@ static void setup_groups(struct root_profile *profile, struct cred *cred)
|
||||
|
||||
groups_sort(group_info);
|
||||
set_groups(cred, group_info);
|
||||
put_group_info(group_info);
|
||||
}
|
||||
|
||||
static void disable_seccomp(void)
|
||||
@@ -136,27 +143,17 @@ void escape_to_root(void)
|
||||
{
|
||||
struct cred *cred;
|
||||
|
||||
#ifdef KSU_GET_CRED_RCU
|
||||
rcu_read_lock();
|
||||
|
||||
do {
|
||||
cred = (struct cred *)__task_cred((current));
|
||||
BUG_ON(!cred);
|
||||
} while (!get_cred_rcu(cred));
|
||||
cred = prepare_creds();
|
||||
if (!cred) {
|
||||
pr_warn("prepare_creds failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cred->euid.val == 0) {
|
||||
pr_warn("Already root, don't escape!\n");
|
||||
rcu_read_unlock();
|
||||
abort_creds(cred);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
cred = (struct cred *)__task_cred(current);
|
||||
|
||||
if (cred->euid.val == 0) {
|
||||
pr_warn("Already root, don't escape!\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct root_profile *profile = ksu_get_root_profile(cred->uid.val);
|
||||
|
||||
@@ -169,6 +166,7 @@ void escape_to_root(void)
|
||||
cred->fsgid.val = profile->gid;
|
||||
cred->sgid.val = profile->gid;
|
||||
cred->egid.val = profile->gid;
|
||||
cred->securebits = 0;
|
||||
|
||||
BUILD_BUG_ON(sizeof(profile->capabilities.effective) !=
|
||||
sizeof(kernel_cap_t));
|
||||
@@ -180,24 +178,18 @@ void escape_to_root(void)
|
||||
profile->capabilities.effective | CAP_DAC_READ_SEARCH;
|
||||
memcpy(&cred->cap_effective, &cap_for_ksud,
|
||||
sizeof(cred->cap_effective));
|
||||
memcpy(&cred->cap_inheritable, &profile->capabilities.effective,
|
||||
sizeof(cred->cap_inheritable));
|
||||
memcpy(&cred->cap_permitted, &profile->capabilities.effective,
|
||||
sizeof(cred->cap_permitted));
|
||||
memcpy(&cred->cap_bset, &profile->capabilities.effective,
|
||||
sizeof(cred->cap_bset));
|
||||
memcpy(&cred->cap_ambient, &profile->capabilities.effective,
|
||||
sizeof(cred->cap_ambient));
|
||||
// set ambient caps to all-zero
|
||||
// fixes "operation not permitted" on dbus cap dropping
|
||||
memset(&cred->cap_ambient, 0,
|
||||
sizeof(cred->cap_ambient));
|
||||
|
||||
setup_groups(profile, cred);
|
||||
|
||||
#ifdef KSU_GET_CRED_RCU
|
||||
rcu_read_unlock();
|
||||
#endif
|
||||
|
||||
commit_creds(cred);
|
||||
|
||||
// Refer to kernel/seccomp.c: seccomp_set_mode_strict
|
||||
// When disabling Seccomp, ensure that current->sighand->siglock is held during the operation.
|
||||
@@ -247,6 +239,26 @@ int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nuke_ext4_sysfs() {
|
||||
struct path path;
|
||||
int err = kern_path("/data/adb/modules", 0, &path);
|
||||
if (err) {
|
||||
pr_err("nuke path err: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
struct super_block* sb = path.dentry->d_inode->i_sb;
|
||||
const char* name = sb->s_type->name;
|
||||
if (strcmp(name, "ext4") != 0) {
|
||||
pr_info("nuke but module aren't mounted\n");
|
||||
path_put(&path);
|
||||
return;
|
||||
}
|
||||
|
||||
ext4_unregister_sysfs(sb);
|
||||
path_put(&path);
|
||||
}
|
||||
|
||||
int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5)
|
||||
{
|
||||
@@ -305,17 +317,29 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
if (copy_to_user(arg3, &version, sizeof(version))) {
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
u32 version_flags = 0;
|
||||
#ifdef MODULE
|
||||
u32 is_lkm = 0x1;
|
||||
#else
|
||||
u32 is_lkm = 0x0;
|
||||
version_flags |= 0x1;
|
||||
#endif
|
||||
if (arg4 && copy_to_user(arg4, &is_lkm, sizeof(is_lkm))) {
|
||||
if (arg4 &&
|
||||
copy_to_user(arg4, &version_flags, sizeof(version_flags))) {
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_HOOK_MODE) {
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
const char *mode = "Kprobes";
|
||||
#else
|
||||
const char *mode = "Manual";
|
||||
#endif
|
||||
if (copy_to_user((void __user *)arg3, mode, strlen(mode) + 1)) {
|
||||
pr_info("hook: copy_to_user() failed\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_REPORT_EVENT) {
|
||||
if (!from_root) {
|
||||
return 0;
|
||||
@@ -341,6 +365,7 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
case EVENT_MODULE_MOUNTED: {
|
||||
ksu_module_mounted = true;
|
||||
pr_info("module mounted!\n");
|
||||
nuke_ext4_sysfs();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -456,6 +481,42 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_IS_SU_ENABLED) {
|
||||
if (copy_to_user(arg3, &ksu_su_compat_enabled,
|
||||
sizeof(ksu_su_compat_enabled))) {
|
||||
pr_err("copy su compat failed\n");
|
||||
return 0;
|
||||
}
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_ENABLE_SU) {
|
||||
bool enabled = (arg3 != 0);
|
||||
if (enabled == ksu_su_compat_enabled) {
|
||||
pr_info("cmd enable su but no need to change.\n");
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {// return the reply_ok directly
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
ksu_sucompat_init();
|
||||
} else {
|
||||
ksu_sucompat_exit();
|
||||
}
|
||||
ksu_su_compat_enabled = enabled;
|
||||
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -508,11 +569,13 @@ static void try_umount(const char *mnt, bool check_mnt, int flags)
|
||||
|
||||
if (path.dentry != path.mnt->mnt_root) {
|
||||
// it is not root mountpoint, maybe umounted by others already.
|
||||
path_put(&path);
|
||||
return;
|
||||
}
|
||||
|
||||
// we are only interest in some specific mounts
|
||||
if (check_mnt && !should_umount(&path)) {
|
||||
path_put(&path);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -576,6 +639,7 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
||||
|
||||
// fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and
|
||||
// filter the mountpoint whose target is `/data/adb`
|
||||
try_umount("/odm", true, 0);
|
||||
try_umount("/system", true, 0);
|
||||
try_umount("/system_ext", true, 0);
|
||||
try_umount("/vendor", true, 0);
|
||||
@@ -585,10 +649,14 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
||||
// try umount ksu temp path
|
||||
try_umount("/debug_ramdisk", false, MNT_DETACH);
|
||||
try_umount("/sbin", false, MNT_DETACH);
|
||||
|
||||
|
||||
// try umount hosts file
|
||||
try_umount("/system/etc/hosts", false, MNT_DETACH);
|
||||
|
||||
// try umount lsposed dex2oat bins
|
||||
try_umount("/apex/com.android.art/bin/dex2oat64", false, MNT_DETACH);
|
||||
try_umount("/apex/com.android.art/bin/dex2oat32", false, MNT_DETACH);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -661,15 +729,22 @@ __maybe_unused int ksu_kprobe_exit(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5)
|
||||
extern int ksu_handle_devpts(struct inode *inode); // sucompat.c
|
||||
|
||||
static int ksu_inode_permission(struct inode *inode, int mask)
|
||||
{
|
||||
ksu_handle_prctl(option, arg2, arg3, arg4, arg5);
|
||||
return -ENOSYS;
|
||||
if (unlikely(inode->i_sb && inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)) {
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_info("%s: devpts inode accessed with mask: %x\n", __func__, mask);
|
||||
#endif
|
||||
ksu_handle_devpts(inode);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
// kernel 4.4 and 4.9
|
||||
|
||||
// kernel 4.9 and older
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||
static int ksu_key_permission(key_ref_t key_ref, const struct cred *cred,
|
||||
int ksu_key_permission(key_ref_t key_ref, const struct cred *cred,
|
||||
unsigned perm)
|
||||
{
|
||||
if (init_session_keyring != NULL) {
|
||||
@@ -684,6 +759,15 @@ static int ksu_key_permission(key_ref_t key_ref, const struct cred *cred,
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KSU_LSM_SECURITY_HOOKS
|
||||
static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5)
|
||||
{
|
||||
ksu_handle_prctl(option, arg2, arg3, arg4, arg5);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
|
||||
struct inode *new_inode, struct dentry *new_dentry)
|
||||
{
|
||||
@@ -701,6 +785,7 @@ static struct security_hook_list ksu_hooks[] = {
|
||||
LSM_HOOK_INIT(task_prctl, ksu_task_prctl),
|
||||
LSM_HOOK_INIT(inode_rename, ksu_inode_rename),
|
||||
LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid),
|
||||
LSM_HOOK_INIT(inode_permission, ksu_inode_permission),
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||
LSM_HOOK_INIT(key_permission, ksu_key_permission)
|
||||
#endif
|
||||
@@ -882,16 +967,21 @@ void __init ksu_lsm_hook_init(void)
|
||||
}
|
||||
smp_mb();
|
||||
}
|
||||
#endif
|
||||
#endif // MODULE
|
||||
#endif // CONFIG_KSU_LSM_SECURITY_HOOKS
|
||||
|
||||
void __init ksu_core_init(void)
|
||||
{
|
||||
#ifdef CONFIG_KSU_LSM_SECURITY_HOOKS
|
||||
ksu_lsm_hook_init();
|
||||
#else
|
||||
pr_info("ksu_core_init: LSM hooks not in use.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ksu_core_exit(void)
|
||||
{
|
||||
#ifdef CONFIG_KPROBES
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
pr_info("ksu_core_kprobe_exit\n");
|
||||
// we dont use this now
|
||||
// ksu_kprobe_exit();
|
||||
|
||||
@@ -107,7 +107,7 @@ struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
|
||||
ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
||||
loff_t *pos)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || defined(KSU_KERNEL_READ)
|
||||
return kernel_read(p, buf, count, pos);
|
||||
#else
|
||||
loff_t offset = pos ? *pos : 0;
|
||||
@@ -122,7 +122,7 @@ ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
||||
ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count,
|
||||
loff_t *pos)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || defined(KSU_KERNEL_WRITE)
|
||||
return kernel_write(p, buf, count, pos);
|
||||
#else
|
||||
loff_t offset = pos ? *pos : 0;
|
||||
@@ -134,7 +134,7 @@ ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count,
|
||||
#endif
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) || defined(KSU_STRNCPY_FROM_USER_NOFAULT)
|
||||
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
||||
long count)
|
||||
{
|
||||
|
||||
@@ -57,7 +57,7 @@ int __init kernelsu_init(void)
|
||||
|
||||
ksu_throne_tracker_init();
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
ksu_sucompat_init();
|
||||
ksu_ksud_init();
|
||||
#else
|
||||
@@ -80,7 +80,7 @@ void kernelsu_exit(void)
|
||||
|
||||
destroy_workqueue(ksu_workqueue);
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
ksu_ksud_exit();
|
||||
ksu_sucompat_exit();
|
||||
#endif
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
#define CMD_SET_APP_PROFILE 11
|
||||
#define CMD_UID_GRANTED_ROOT 12
|
||||
#define CMD_UID_SHOULD_UMOUNT 13
|
||||
#define CMD_IS_SU_ENABLED 14
|
||||
#define CMD_ENABLE_SU 15
|
||||
#define CMD_HOOK_MODE 16
|
||||
|
||||
#define EVENT_POST_FS_DATA 1
|
||||
#define EVENT_BOOT_COMPLETED 2
|
||||
|
||||
@@ -54,18 +54,23 @@ static void stop_vfs_read_hook();
|
||||
static void stop_execve_hook();
|
||||
static void stop_input_hook();
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
static struct work_struct stop_vfs_read_work;
|
||||
static struct work_struct stop_execve_hook_work;
|
||||
static struct work_struct stop_input_hook_work;
|
||||
#else
|
||||
#endif
|
||||
|
||||
bool ksu_vfs_read_hook __read_mostly = true;
|
||||
bool ksu_execveat_hook __read_mostly = true;
|
||||
bool ksu_input_hook __read_mostly = true;
|
||||
#endif
|
||||
|
||||
|
||||
u32 ksu_devpts_sid;
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
bool ksu_is_compat __read_mostly = false;
|
||||
#endif
|
||||
|
||||
void on_post_fs_data(void)
|
||||
{
|
||||
static bool done = false;
|
||||
@@ -107,6 +112,7 @@ static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr)
|
||||
if (get_user(compat, argv.ptr.compat + nr))
|
||||
return ERR_PTR(-EFAULT);
|
||||
|
||||
ksu_is_compat = true;
|
||||
return compat_ptr(compat);
|
||||
}
|
||||
#endif
|
||||
@@ -157,7 +163,7 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||
struct user_arg_ptr *argv,
|
||||
struct user_arg_ptr *envp, int *flags)
|
||||
{
|
||||
#ifndef CONFIG_KPROBES
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_execveat_hook) {
|
||||
return 0;
|
||||
}
|
||||
@@ -313,7 +319,7 @@ static ssize_t read_iter_proxy(struct kiocb *iocb, struct iov_iter *to)
|
||||
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
||||
size_t *count_ptr, loff_t **pos)
|
||||
{
|
||||
#ifndef CONFIG_KPROBES
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_vfs_read_hook) {
|
||||
return 0;
|
||||
}
|
||||
@@ -332,7 +338,7 @@ int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!d_is_reg(file->f_path.dentry)) {
|
||||
if (!S_ISREG(file->f_path.dentry->d_inode->i_mode)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -390,10 +396,12 @@ int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
||||
if (orig_read) {
|
||||
fops_proxy.read = read_proxy;
|
||||
}
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
|
||||
orig_read_iter = file->f_op->read_iter;
|
||||
if (orig_read_iter) {
|
||||
fops_proxy.read_iter = read_iter_proxy;
|
||||
}
|
||||
#endif
|
||||
// replace the file_operations
|
||||
file->f_op = &fops_proxy;
|
||||
read_count_append = rc_count;
|
||||
@@ -426,7 +434,7 @@ static bool is_volumedown_enough(unsigned int count)
|
||||
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
||||
int *value)
|
||||
{
|
||||
#ifndef CONFIG_KPROBES
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_input_hook) {
|
||||
return 0;
|
||||
}
|
||||
@@ -468,7 +476,37 @@ bool ksu_is_safe_mode()
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
/*
|
||||
* ksu_handle_execve_ksud, execve_ksud handler for non kprobe
|
||||
* adapted from sys_execve_handler_pre
|
||||
* https://github.com/tiann/KernelSU/commit/2027ac3
|
||||
*/
|
||||
__maybe_unused int ksu_handle_execve_ksud(const char __user *filename_user,
|
||||
const char __user *const __user *__argv)
|
||||
{
|
||||
struct user_arg_ptr argv = { .ptr.native = __argv };
|
||||
struct filename filename_in, *filename_p;
|
||||
char path[32];
|
||||
|
||||
// return early if disabled.
|
||||
if (!ksu_execveat_hook) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!filename_user)
|
||||
return 0;
|
||||
|
||||
memset(path, 0, sizeof(path));
|
||||
ksu_strncpy_from_user_nofault(path, filename_user, 32);
|
||||
|
||||
// this is because ksu_handle_execveat_ksud calls it filename->name
|
||||
filename_in.name = path;
|
||||
filename_p = &filename_in;
|
||||
|
||||
return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, &argv, NULL, NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
|
||||
// https://elixir.bootlin.com/linux/v5.10.158/source/fs/exec.c#L1864
|
||||
static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
@@ -596,9 +634,32 @@ static void do_stop_input_hook(struct work_struct *work)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
|
||||
#include "objsec.h" // task_security_struct
|
||||
bool is_ksu_transition(const struct task_security_struct *old_tsec,
|
||||
const struct task_security_struct *new_tsec)
|
||||
{
|
||||
static u32 ksu_sid;
|
||||
char *secdata;
|
||||
u32 seclen;
|
||||
bool allowed = false;
|
||||
|
||||
if (!ksu_sid)
|
||||
security_secctx_to_secid("u:r:su:s0", strlen("u:r:su:s0"), &ksu_sid);
|
||||
|
||||
if (security_secid_to_secctx(old_tsec->sid, &secdata, &seclen))
|
||||
return false;
|
||||
|
||||
allowed = (!strcmp("u:r:init:s0", secdata) && new_tsec->sid == ksu_sid);
|
||||
security_release_secctx(secdata, seclen);
|
||||
|
||||
return allowed;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void stop_vfs_read_hook()
|
||||
{
|
||||
#ifdef CONFIG_KPROBES
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
bool ret = schedule_work(&stop_vfs_read_work);
|
||||
pr_info("unregister vfs_read kprobe: %d!\n", ret);
|
||||
#else
|
||||
@@ -609,7 +670,7 @@ static void stop_vfs_read_hook()
|
||||
|
||||
static void stop_execve_hook()
|
||||
{
|
||||
#ifdef CONFIG_KPROBES
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
bool ret = schedule_work(&stop_execve_hook_work);
|
||||
pr_info("unregister execve kprobe: %d!\n", ret);
|
||||
#else
|
||||
@@ -620,15 +681,16 @@ static void stop_execve_hook()
|
||||
|
||||
static void stop_input_hook()
|
||||
{
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
static bool input_hook_stopped = false;
|
||||
if (input_hook_stopped) {
|
||||
return;
|
||||
}
|
||||
input_hook_stopped = true;
|
||||
#ifdef CONFIG_KPROBES
|
||||
bool ret = schedule_work(&stop_input_hook_work);
|
||||
pr_info("unregister input kprobe: %d!\n", ret);
|
||||
#else
|
||||
if (!ksu_input_hook) { return; }
|
||||
ksu_input_hook = false;
|
||||
pr_info("stop input_hook\n");
|
||||
#endif
|
||||
@@ -637,7 +699,7 @@ static void stop_input_hook()
|
||||
// ksud: module support
|
||||
void ksu_ksud_init()
|
||||
{
|
||||
#ifdef CONFIG_KPROBES
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
int ret;
|
||||
|
||||
ret = register_kprobe(&execve_kp);
|
||||
@@ -657,10 +719,10 @@ void ksu_ksud_init()
|
||||
|
||||
void ksu_ksud_exit()
|
||||
{
|
||||
#ifdef CONFIG_KPROBES
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
unregister_kprobe(&execve_kp);
|
||||
// this should be done before unregister vfs_read_kp
|
||||
// unregister_kprobe(&vfs_read_kp);
|
||||
unregister_kprobe(&input_event_kp);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,17 +149,45 @@ void apply_kernelsu_rules()
|
||||
#define CMD_TYPE_CHANGE 8
|
||||
#define CMD_GENFSCON 9
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
struct sepol_data {
|
||||
u32 cmd;
|
||||
u32 subcmd;
|
||||
char __user *sepol1;
|
||||
char __user *sepol2;
|
||||
char __user *sepol3;
|
||||
char __user *sepol4;
|
||||
char __user *sepol5;
|
||||
char __user *sepol6;
|
||||
char __user *sepol7;
|
||||
u64 field_sepol1;
|
||||
u64 field_sepol2;
|
||||
u64 field_sepol3;
|
||||
u64 field_sepol4;
|
||||
u64 field_sepol5;
|
||||
u64 field_sepol6;
|
||||
u64 field_sepol7;
|
||||
};
|
||||
#ifdef CONFIG_COMPAT
|
||||
extern bool ksu_is_compat __read_mostly;
|
||||
struct sepol_compat_data {
|
||||
u32 cmd;
|
||||
u32 subcmd;
|
||||
u32 field_sepol1;
|
||||
u32 field_sepol2;
|
||||
u32 field_sepol3;
|
||||
u32 field_sepol4;
|
||||
u32 field_sepol5;
|
||||
u32 field_sepol6;
|
||||
u32 field_sepol7;
|
||||
};
|
||||
#endif // CONFIG_COMPAT
|
||||
#else
|
||||
struct sepol_data {
|
||||
u32 cmd;
|
||||
u32 subcmd;
|
||||
u32 field_sepol1;
|
||||
u32 field_sepol2;
|
||||
u32 field_sepol3;
|
||||
u32 field_sepol4;
|
||||
u32 field_sepol5;
|
||||
u32 field_sepol6;
|
||||
u32 field_sepol7;
|
||||
};
|
||||
#endif // CONFIG_64BIT
|
||||
|
||||
static int get_object(char *buf, char __user *user_object, size_t buf_sz,
|
||||
char **object)
|
||||
@@ -204,15 +232,59 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||
if (!getenforce()) {
|
||||
pr_info("SELinux permissive or disabled when handle policy!\n");
|
||||
}
|
||||
|
||||
u32 cmd, subcmd;
|
||||
char __user *sepol1, *sepol2, *sepol3, *sepol4, *sepol5, *sepol6, *sepol7;
|
||||
|
||||
#if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT)
|
||||
if (unlikely(ksu_is_compat)) {
|
||||
struct sepol_compat_data compat_data;
|
||||
if (copy_from_user(&compat_data, arg4, sizeof(struct sepol_compat_data))) {
|
||||
pr_err("sepol: copy sepol_data failed.\n");
|
||||
return -1;
|
||||
}
|
||||
sepol1 = compat_ptr(compat_data.field_sepol1);
|
||||
sepol2 = compat_ptr(compat_data.field_sepol2);
|
||||
sepol3 = compat_ptr(compat_data.field_sepol3);
|
||||
sepol4 = compat_ptr(compat_data.field_sepol4);
|
||||
sepol5 = compat_ptr(compat_data.field_sepol5);
|
||||
sepol6 = compat_ptr(compat_data.field_sepol6);
|
||||
sepol7 = compat_ptr(compat_data.field_sepol7);
|
||||
cmd = compat_data.cmd;
|
||||
subcmd = compat_data.subcmd;
|
||||
} else {
|
||||
struct sepol_data data;
|
||||
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
|
||||
pr_err("sepol: copy sepol_data failed.\n");
|
||||
return -1;
|
||||
}
|
||||
sepol1 = data.field_sepol1;
|
||||
sepol2 = data.field_sepol2;
|
||||
sepol3 = data.field_sepol3;
|
||||
sepol4 = data.field_sepol4;
|
||||
sepol5 = data.field_sepol5;
|
||||
sepol6 = data.field_sepol6;
|
||||
sepol7 = data.field_sepol7;
|
||||
cmd = data.cmd;
|
||||
subcmd = data.subcmd;
|
||||
}
|
||||
#else
|
||||
// basically for full native, say (64BIT=y COMPAT=n) || (64BIT=n)
|
||||
struct sepol_data data;
|
||||
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
|
||||
pr_err("sepol: copy sepol_data failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u32 cmd = data.cmd;
|
||||
u32 subcmd = data.subcmd;
|
||||
sepol1 = data.field_sepol1;
|
||||
sepol2 = data.field_sepol2;
|
||||
sepol3 = data.field_sepol3;
|
||||
sepol4 = data.field_sepol4;
|
||||
sepol5 = data.field_sepol5;
|
||||
sepol6 = data.field_sepol6;
|
||||
sepol7 = data.field_sepol7;
|
||||
cmd = data.cmd;
|
||||
subcmd = data.subcmd;
|
||||
#endif
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
@@ -226,22 +298,22 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||
char perm_buf[MAX_SEPOL_LEN];
|
||||
|
||||
char *s, *t, *c, *p;
|
||||
if (get_object(src_buf, data.sepol1, sizeof(src_buf), &s) < 0) {
|
||||
if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) {
|
||||
pr_err("sepol: copy src failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (get_object(tgt_buf, data.sepol2, sizeof(tgt_buf), &t) < 0) {
|
||||
if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) {
|
||||
pr_err("sepol: copy tgt failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (get_object(cls_buf, data.sepol3, sizeof(cls_buf), &c) < 0) {
|
||||
if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) {
|
||||
pr_err("sepol: copy cls failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (get_object(perm_buf, data.sepol4, sizeof(perm_buf), &p) <
|
||||
if (get_object(perm_buf, sepol4, sizeof(perm_buf), &p) <
|
||||
0) {
|
||||
pr_err("sepol: copy perm failed.\n");
|
||||
goto exit;
|
||||
@@ -271,24 +343,24 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||
char perm_set[MAX_SEPOL_LEN];
|
||||
|
||||
char *s, *t, *c;
|
||||
if (get_object(src_buf, data.sepol1, sizeof(src_buf), &s) < 0) {
|
||||
if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) {
|
||||
pr_err("sepol: copy src failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (get_object(tgt_buf, data.sepol2, sizeof(tgt_buf), &t) < 0) {
|
||||
if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) {
|
||||
pr_err("sepol: copy tgt failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (get_object(cls_buf, data.sepol3, sizeof(cls_buf), &c) < 0) {
|
||||
if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) {
|
||||
pr_err("sepol: copy cls failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(operation, data.sepol4,
|
||||
if (strncpy_from_user(operation, sepol4,
|
||||
sizeof(operation)) < 0) {
|
||||
pr_err("sepol: copy operation failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(perm_set, data.sepol5, sizeof(perm_set)) <
|
||||
if (strncpy_from_user(perm_set, sepol5, sizeof(perm_set)) <
|
||||
0) {
|
||||
pr_err("sepol: copy perm_set failed.\n");
|
||||
goto exit;
|
||||
@@ -308,7 +380,7 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||
} else if (cmd == CMD_TYPE_STATE) {
|
||||
char src[MAX_SEPOL_LEN];
|
||||
|
||||
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
|
||||
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
|
||||
pr_err("sepol: copy src failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
@@ -328,11 +400,11 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||
char type[MAX_SEPOL_LEN];
|
||||
char attr[MAX_SEPOL_LEN];
|
||||
|
||||
if (strncpy_from_user(type, data.sepol1, sizeof(type)) < 0) {
|
||||
if (strncpy_from_user(type, sepol1, sizeof(type)) < 0) {
|
||||
pr_err("sepol: copy type failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(attr, data.sepol2, sizeof(attr)) < 0) {
|
||||
if (strncpy_from_user(attr, sepol2, sizeof(attr)) < 0) {
|
||||
pr_err("sepol: copy attr failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
@@ -352,7 +424,7 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||
} else if (cmd == CMD_ATTR) {
|
||||
char attr[MAX_SEPOL_LEN];
|
||||
|
||||
if (strncpy_from_user(attr, data.sepol1, sizeof(attr)) < 0) {
|
||||
if (strncpy_from_user(attr, sepol1, sizeof(attr)) < 0) {
|
||||
pr_err("sepol: copy attr failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
@@ -369,28 +441,28 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||
char default_type[MAX_SEPOL_LEN];
|
||||
char object[MAX_SEPOL_LEN];
|
||||
|
||||
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
|
||||
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
|
||||
pr_err("sepol: copy src failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(tgt, data.sepol2, sizeof(tgt)) < 0) {
|
||||
if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) {
|
||||
pr_err("sepol: copy tgt failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(cls, data.sepol3, sizeof(cls)) < 0) {
|
||||
if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) {
|
||||
pr_err("sepol: copy cls failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(default_type, data.sepol4,
|
||||
if (strncpy_from_user(default_type, sepol4,
|
||||
sizeof(default_type)) < 0) {
|
||||
pr_err("sepol: copy default_type failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
char *real_object;
|
||||
if (data.sepol5 == NULL) {
|
||||
if (sepol5 == NULL) {
|
||||
real_object = NULL;
|
||||
} else {
|
||||
if (strncpy_from_user(object, data.sepol5,
|
||||
if (strncpy_from_user(object, sepol5,
|
||||
sizeof(object)) < 0) {
|
||||
pr_err("sepol: copy object failed.\n");
|
||||
goto exit;
|
||||
@@ -409,19 +481,19 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||
char cls[MAX_SEPOL_LEN];
|
||||
char default_type[MAX_SEPOL_LEN];
|
||||
|
||||
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
|
||||
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
|
||||
pr_err("sepol: copy src failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(tgt, data.sepol2, sizeof(tgt)) < 0) {
|
||||
if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) {
|
||||
pr_err("sepol: copy tgt failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(cls, data.sepol3, sizeof(cls)) < 0) {
|
||||
if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) {
|
||||
pr_err("sepol: copy cls failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(default_type, data.sepol4,
|
||||
if (strncpy_from_user(default_type, sepol4,
|
||||
sizeof(default_type)) < 0) {
|
||||
pr_err("sepol: copy default_type failed.\n");
|
||||
goto exit;
|
||||
@@ -442,15 +514,15 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||
char name[MAX_SEPOL_LEN];
|
||||
char path[MAX_SEPOL_LEN];
|
||||
char context[MAX_SEPOL_LEN];
|
||||
if (strncpy_from_user(name, data.sepol1, sizeof(name)) < 0) {
|
||||
if (strncpy_from_user(name, sepol1, sizeof(name)) < 0) {
|
||||
pr_err("sepol: copy name failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(path, data.sepol2, sizeof(path)) < 0) {
|
||||
if (strncpy_from_user(path, sepol2, sizeof(path)) < 0) {
|
||||
pr_err("sepol: copy path failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (strncpy_from_user(context, data.sepol3, sizeof(context)) <
|
||||
if (strncpy_from_user(context, sepol3, sizeof(context)) <
|
||||
0) {
|
||||
pr_err("sepol: copy context failed.\n");
|
||||
goto exit;
|
||||
|
||||
@@ -39,7 +39,7 @@ perform_cleanup() {
|
||||
# Sets up or update KernelSU-Next environment
|
||||
setup_kernelsu() {
|
||||
echo "[+] Setting up KernelSU-Next..."
|
||||
test -d "$GKI_ROOT/KernelSU-Next" || git clone https://github.com/rifsxd/KernelSU-Next && echo "[+] Repository cloned."
|
||||
test -d "$GKI_ROOT/KernelSU-Next" || git clone https://github.com/KernelSU-Next/KernelSU-Next && echo "[+] Repository cloned."
|
||||
cd "$GKI_ROOT/KernelSU-Next"
|
||||
git stash && echo "[-] Stashed current changes."
|
||||
if [ "$(git status | grep -Po 'v\d+(\.\d+)*' | head -n1)" ]; then
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
#define SU_PATH "/system/bin/su"
|
||||
#define SH_PATH "/system/bin/sh"
|
||||
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
static bool ksu_sucompat_non_kp __read_mostly = true;
|
||||
#endif
|
||||
|
||||
extern void escape_to_root();
|
||||
|
||||
static void __user *userspace_stack_buffer(const void *d, size_t len)
|
||||
@@ -54,6 +58,12 @@ int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
||||
{
|
||||
const char su[] = SU_PATH;
|
||||
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_sucompat_non_kp) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!ksu_is_allow_uid(current_uid().val)) {
|
||||
return 0;
|
||||
}
|
||||
@@ -75,6 +85,12 @@ int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
|
||||
// const char sh[] = SH_PATH;
|
||||
const char su[] = SU_PATH;
|
||||
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_sucompat_non_kp){
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!ksu_is_allow_uid(current_uid().val)) {
|
||||
return 0;
|
||||
}
|
||||
@@ -119,6 +135,12 @@ int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
||||
const char sh[] = KSUD_PATH;
|
||||
const char su[] = SU_PATH;
|
||||
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_sucompat_non_kp) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (unlikely(!filename_ptr))
|
||||
return 0;
|
||||
|
||||
@@ -148,11 +170,32 @@ int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
|
||||
const char su[] = SU_PATH;
|
||||
char path[sizeof(su) + 1];
|
||||
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_sucompat_non_kp) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (unlikely(!filename_user))
|
||||
return 0;
|
||||
|
||||
memset(path, 0, sizeof(path));
|
||||
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
||||
// nofault variant fails probably due to pagefault_disable
|
||||
// some cpus dont really have that good speculative execution
|
||||
// substitute set_fs, check if pointer is valid
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0)
|
||||
if (!access_ok(VERIFY_READ, *filename_user, sizeof(path)))
|
||||
return 0;
|
||||
#else
|
||||
if (!access_ok(*filename_user, sizeof(path)))
|
||||
return 0;
|
||||
#endif
|
||||
// success = returns number of bytes and should be less than path
|
||||
long len = strncpy_from_user(path, *filename_user, sizeof(path));
|
||||
if (len <= 0 || len > sizeof(path))
|
||||
return 0;
|
||||
|
||||
// strncpy_from_user_nofault does this too
|
||||
path[sizeof(path) - 1] = '\0';
|
||||
|
||||
if (likely(memcmp(path, su, sizeof(su))))
|
||||
return 0;
|
||||
@@ -170,6 +213,12 @@ int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
|
||||
|
||||
int ksu_handle_devpts(struct inode *inode)
|
||||
{
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_sucompat_non_kp) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!current->mm) {
|
||||
return 0;
|
||||
}
|
||||
@@ -198,21 +247,9 @@ int ksu_handle_devpts(struct inode *inode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
|
||||
__maybe_unused static int faccessat_handler_pre(struct kprobe *p,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
int *dfd = (int *)&PT_REGS_PARM1(regs);
|
||||
const char __user **filename_user = (const char **)&PT_REGS_PARM2(regs);
|
||||
int *mode = (int *)&PT_REGS_PARM3(regs);
|
||||
// Both sys_ and do_ is C function
|
||||
int *flags = (int *)&PT_REGS_CCALL_PARM4(regs);
|
||||
|
||||
return ksu_handle_faccessat(dfd, filename_user, mode, flags);
|
||||
}
|
||||
|
||||
static int sys_faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
|
||||
@@ -223,23 +260,7 @@ static int sys_faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
return ksu_handle_faccessat(dfd, filename_user, mode, NULL);
|
||||
}
|
||||
|
||||
__maybe_unused static int newfstatat_handler_pre(struct kprobe *p,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
int *dfd = (int *)&PT_REGS_PARM1(regs);
|
||||
const char __user **filename_user = (const char **)&PT_REGS_PARM2(regs);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||
// static int vfs_statx(int dfd, const char __user *filename, int flags, struct kstat *stat, u32 request_mask)
|
||||
int *flags = (int *)&PT_REGS_PARM3(regs);
|
||||
#else
|
||||
// int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,int flag)
|
||||
int *flags = (int *)&PT_REGS_CCALL_PARM4(regs);
|
||||
#endif
|
||||
|
||||
return ksu_handle_stat(dfd, filename_user, flags);
|
||||
}
|
||||
|
||||
static int sys_newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
|
||||
@@ -250,17 +271,7 @@ static int sys_newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
return ksu_handle_stat(dfd, filename_user, flags);
|
||||
}
|
||||
|
||||
// https://elixir.bootlin.com/linux/v5.10.158/source/fs/exec.c#L1864
|
||||
static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
int *fd = (int *)&PT_REGS_PARM1(regs);
|
||||
struct filename **filename_ptr =
|
||||
(struct filename **)&PT_REGS_PARM2(regs);
|
||||
|
||||
return ksu_handle_execveat_sucompat(fd, filename_ptr, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||
const char __user **filename_user =
|
||||
@@ -270,56 +281,6 @@ static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
NULL);
|
||||
}
|
||||
|
||||
#if 1
|
||||
static struct kprobe faccessat_kp = {
|
||||
.symbol_name = SYS_FACCESSAT_SYMBOL,
|
||||
.pre_handler = sys_faccessat_handler_pre,
|
||||
};
|
||||
#else
|
||||
static struct kprobe faccessat_kp = {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
|
||||
.symbol_name = "do_faccessat",
|
||||
#else
|
||||
.symbol_name = "sys_faccessat",
|
||||
#endif
|
||||
.pre_handler = faccessat_handler_pre,
|
||||
};
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
static struct kprobe newfstatat_kp = {
|
||||
.symbol_name = SYS_NEWFSTATAT_SYMBOL,
|
||||
.pre_handler = sys_newfstatat_handler_pre,
|
||||
};
|
||||
#else
|
||||
static struct kprobe newfstatat_kp = {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||
.symbol_name = "vfs_statx",
|
||||
#else
|
||||
.symbol_name = "vfs_fstatat",
|
||||
#endif
|
||||
.pre_handler = newfstatat_handler_pre,
|
||||
};
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
static struct kprobe execve_kp = {
|
||||
.symbol_name = SYS_EXECVE_SYMBOL,
|
||||
.pre_handler = sys_execve_handler_pre,
|
||||
};
|
||||
#else
|
||||
static struct kprobe execve_kp = {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
||||
.symbol_name = "do_execveat_common",
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
|
||||
.symbol_name = "__do_execve_file",
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
|
||||
.symbol_name = "do_execveat_common",
|
||||
#endif
|
||||
.pre_handler = execve_handler_pre,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int pts_unix98_lookup_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct inode *inode;
|
||||
@@ -333,35 +294,61 @@ static int pts_unix98_lookup_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
return ksu_handle_devpts(inode);
|
||||
}
|
||||
|
||||
static struct kprobe pts_unix98_lookup_kp = { .symbol_name =
|
||||
"pts_unix98_lookup",
|
||||
.pre_handler =
|
||||
pts_unix98_lookup_pre };
|
||||
static struct kprobe *init_kprobe(const char *name,
|
||||
kprobe_pre_handler_t handler)
|
||||
{
|
||||
struct kprobe *kp = kzalloc(sizeof(struct kprobe), GFP_KERNEL);
|
||||
if (!kp)
|
||||
return NULL;
|
||||
kp->symbol_name = name;
|
||||
kp->pre_handler = handler;
|
||||
|
||||
int ret = register_kprobe(kp);
|
||||
pr_info("sucompat: register_%s kprobe: %d\n", name, ret);
|
||||
if (ret) {
|
||||
kfree(kp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return kp;
|
||||
}
|
||||
|
||||
static void destroy_kprobe(struct kprobe **kp_ptr)
|
||||
{
|
||||
struct kprobe *kp = *kp_ptr;
|
||||
if (!kp)
|
||||
return;
|
||||
unregister_kprobe(kp);
|
||||
synchronize_rcu();
|
||||
kfree(kp);
|
||||
*kp_ptr = NULL;
|
||||
}
|
||||
|
||||
static struct kprobe *su_kps[4];
|
||||
#endif
|
||||
|
||||
// sucompat: permited process can execute 'su' to gain root access.
|
||||
void ksu_sucompat_init()
|
||||
{
|
||||
#ifdef CONFIG_KPROBES
|
||||
int ret;
|
||||
ret = register_kprobe(&execve_kp);
|
||||
pr_info("sucompat: execve_kp: %d\n", ret);
|
||||
ret = register_kprobe(&newfstatat_kp);
|
||||
pr_info("sucompat: newfstatat_kp: %d\n", ret);
|
||||
ret = register_kprobe(&faccessat_kp);
|
||||
pr_info("sucompat: faccessat_kp: %d\n", ret);
|
||||
ret = register_kprobe(&pts_unix98_lookup_kp);
|
||||
pr_info("sucompat: devpts_kp: %d\n", ret);
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre);
|
||||
su_kps[1] = init_kprobe(SYS_FACCESSAT_SYMBOL, faccessat_handler_pre);
|
||||
su_kps[2] = init_kprobe(SYS_NEWFSTATAT_SYMBOL, newfstatat_handler_pre);
|
||||
su_kps[3] = init_kprobe("pts_unix98_lookup", pts_unix98_lookup_pre);
|
||||
#else
|
||||
ksu_sucompat_non_kp = true;
|
||||
pr_info("ksu_sucompat_init: hooks enabled: execve/execveat_su, faccessat, stat, devpts\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ksu_sucompat_exit()
|
||||
{
|
||||
#ifdef CONFIG_KPROBES
|
||||
unregister_kprobe(&execve_kp);
|
||||
unregister_kprobe(&newfstatat_kp);
|
||||
unregister_kprobe(&faccessat_kp);
|
||||
unregister_kprobe(&pts_unix98_lookup_kp);
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
for (int i = 0; i < ARRAY_SIZE(su_kps); i++) {
|
||||
destroy_kprobe(&su_kps[i]);
|
||||
}
|
||||
#else
|
||||
ksu_sucompat_non_kp = false;
|
||||
pr_info("ksu_sucompat_exit: hooks disabled: execve/execveat_su, faccessat, stat, devpts\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -148,6 +148,12 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||
if (!strncmp(name, "..", namelen) || !strncmp(name, ".", namelen))
|
||||
return FILLDIR_ACTOR_CONTINUE; // Skip "." and ".."
|
||||
|
||||
if (d_type == DT_DIR && namelen >= 8 && !strncmp(name, "vmdl", 4) &&
|
||||
!strncmp(name + namelen - 4, ".tmp", 4)) {
|
||||
pr_info("Skipping directory: %.*s\n", namelen, name);
|
||||
return FILLDIR_ACTOR_CONTINUE; // Skip staging package
|
||||
}
|
||||
|
||||
if (snprintf(dirpath, DATA_PATH_LEN, "%s/%.*s", my_ctx->parent_dir,
|
||||
namelen, name) >= DATA_PATH_LEN) {
|
||||
pr_err("Path too long: %s/%.*s\n", my_ctx->parent_dir, namelen,
|
||||
@@ -164,7 +170,11 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 8, 0)
|
||||
strlcpy(data->dirpath, dirpath, DATA_PATH_LEN);
|
||||
#else
|
||||
strscpy(data->dirpath, dirpath, DATA_PATH_LEN);
|
||||
#endif
|
||||
data->depth = my_ctx->depth - 1;
|
||||
list_add_tail(&data->list, my_ctx->data_path_list);
|
||||
} else {
|
||||
@@ -206,12 +216,53 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* small helper to check if lock is held
|
||||
* false - file is stable
|
||||
* true - file is being deleted/renamed
|
||||
* possibly optional
|
||||
*
|
||||
*/
|
||||
bool is_lock_held(const char *path)
|
||||
{
|
||||
struct path kpath;
|
||||
|
||||
// kern_path returns 0 on success
|
||||
if (kern_path(path, 0, &kpath))
|
||||
return true;
|
||||
|
||||
// just being defensive
|
||||
if (!kpath.dentry) {
|
||||
path_put(&kpath);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!spin_trylock(&kpath.dentry->d_lock)) {
|
||||
pr_info("%s: lock held, bail out!\n", __func__);
|
||||
path_put(&kpath);
|
||||
return true;
|
||||
}
|
||||
// we hold it ourselves here!
|
||||
|
||||
spin_unlock(&kpath.dentry->d_lock);
|
||||
path_put(&kpath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// compat: https://elixir.bootlin.com/linux/v3.9/source/include/linux/fs.h#L771
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)
|
||||
#define S_MAGIC_COMPAT(x) ((x)->f_inode->i_sb->s_magic)
|
||||
#else
|
||||
#define S_MAGIC_COMPAT(x) ((x)->f_path.dentry->d_inode->i_sb->s_magic)
|
||||
#endif
|
||||
|
||||
void search_manager(const char *path, int depth, struct list_head *uid_data)
|
||||
{
|
||||
int i, stop = 0;
|
||||
struct list_head data_path_list;
|
||||
INIT_LIST_HEAD(&data_path_list);
|
||||
|
||||
unsigned long data_app_magic = 0;
|
||||
|
||||
// Initialize APK cache list
|
||||
struct apk_path_hash *pos, *n;
|
||||
list_for_each_entry(pos, &apk_path_hash_list, list) {
|
||||
@@ -220,11 +271,15 @@ void search_manager(const char *path, int depth, struct list_head *uid_data)
|
||||
|
||||
// First depth
|
||||
struct data_path data;
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 8, 0)
|
||||
strlcpy(data.dirpath, path, DATA_PATH_LEN);
|
||||
#else
|
||||
strscpy(data.dirpath, path, DATA_PATH_LEN);
|
||||
#endif
|
||||
data.depth = depth;
|
||||
list_add_tail(&data.list, &data_path_list);
|
||||
|
||||
for (i = depth; i > 0; i--) {
|
||||
for (i = depth; i >= 0; i--) {
|
||||
struct data_path *pos, *n;
|
||||
|
||||
list_for_each_entry_safe(pos, n, &data_path_list, list) {
|
||||
@@ -242,6 +297,24 @@ void search_manager(const char *path, int depth, struct list_head *uid_data)
|
||||
pr_err("Failed to open directory: %s, err: %ld\n", pos->dirpath, PTR_ERR(file));
|
||||
goto skip_iterate;
|
||||
}
|
||||
|
||||
// grab magic on first folder, which is /data/app
|
||||
if (!data_app_magic) {
|
||||
if (S_MAGIC_COMPAT(file)) {
|
||||
data_app_magic = S_MAGIC_COMPAT(file);
|
||||
pr_info("%s: dir: %s got magic! 0x%lx\n", __func__, pos->dirpath, data_app_magic);
|
||||
} else {
|
||||
filp_close(file, NULL);
|
||||
goto skip_iterate;
|
||||
}
|
||||
}
|
||||
|
||||
if (S_MAGIC_COMPAT(file) != data_app_magic) {
|
||||
pr_info("%s: skip: %s magic: 0x%lx expected: 0x%lx\n", __func__, pos->dirpath,
|
||||
S_MAGIC_COMPAT(file), data_app_magic);
|
||||
filp_close(file, NULL);
|
||||
goto skip_iterate;
|
||||
}
|
||||
|
||||
iterate_dir(file, &ctx.ctx);
|
||||
filp_close(file, NULL);
|
||||
@@ -280,13 +353,25 @@ static bool is_uid_exist(uid_t uid, char *package, void *data)
|
||||
|
||||
void track_throne()
|
||||
{
|
||||
struct file *fp =
|
||||
ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0);
|
||||
struct file *fp;
|
||||
int tries = 0;
|
||||
|
||||
while (tries++ < 10) {
|
||||
if (!is_lock_held(SYSTEM_PACKAGES_LIST_PATH)) {
|
||||
fp = ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0);
|
||||
if (!IS_ERR(fp))
|
||||
break;
|
||||
}
|
||||
|
||||
pr_info("%s: waiting for %s\n", __func__, SYSTEM_PACKAGES_LIST_PATH);
|
||||
msleep(100); // migth as well add a delay
|
||||
};
|
||||
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("%s: open " SYSTEM_PACKAGES_LIST_PATH " failed: %ld\n",
|
||||
__func__, PTR_ERR(fp));
|
||||
pr_err("%s: open " SYSTEM_PACKAGES_LIST_PATH " failed: %ld\n", __func__, PTR_ERR(fp));
|
||||
return;
|
||||
}
|
||||
} else
|
||||
pr_info("%s: %s found!\n", __func__, SYSTEM_PACKAGES_LIST_PATH);
|
||||
|
||||
struct list_head uid_list;
|
||||
INIT_LIST_HEAD(&uid_list);
|
||||
@@ -355,12 +440,14 @@ void track_throne()
|
||||
if (ksu_is_manager_uid_valid()) {
|
||||
pr_info("manager is uninstalled, invalidate it!\n");
|
||||
ksu_invalidate_manager_uid();
|
||||
goto prune;
|
||||
}
|
||||
pr_info("Searching manager...\n");
|
||||
search_manager("/data/app", 2, &uid_list);
|
||||
pr_info("Search manager finished\n");
|
||||
}
|
||||
|
||||
prune:
|
||||
// then prune the allowlist
|
||||
ksu_prune_allowlist(is_uid_exist, &uid_list);
|
||||
out:
|
||||
|
||||
@@ -7,4 +7,6 @@ void ksu_throne_tracker_exit();
|
||||
|
||||
void track_throne();
|
||||
|
||||
bool is_lock_held(const char *path);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -133,4 +133,6 @@ dependencies {
|
||||
implementation(libs.androidx.webkit)
|
||||
|
||||
implementation(libs.lsposed.cxx)
|
||||
|
||||
implementation(libs.mmrl.ui)
|
||||
}
|
||||
39
manager/app/proguard-rules.pro
vendored
39
manager/app/proguard-rules.pro
vendored
@@ -0,0 +1,39 @@
|
||||
-verbose
|
||||
-optimizationpasses 5
|
||||
|
||||
-dontwarn org.conscrypt.**
|
||||
-dontwarn kotlinx.serialization.**
|
||||
|
||||
# Please add these rules to your existing keep rules in order to suppress warnings.
|
||||
# This is generated automatically by the Android Gradle plugin.
|
||||
-dontwarn com.google.auto.service.AutoService
|
||||
-dontwarn com.google.j2objc.annotations.RetainedWith
|
||||
-dontwarn javax.lang.model.SourceVersion
|
||||
-dontwarn javax.lang.model.element.AnnotationMirror
|
||||
-dontwarn javax.lang.model.element.AnnotationValue
|
||||
-dontwarn javax.lang.model.element.Element
|
||||
-dontwarn javax.lang.model.element.ElementKind
|
||||
-dontwarn javax.lang.model.element.ElementVisitor
|
||||
-dontwarn javax.lang.model.element.ExecutableElement
|
||||
-dontwarn javax.lang.model.element.Modifier
|
||||
-dontwarn javax.lang.model.element.Name
|
||||
-dontwarn javax.lang.model.element.PackageElement
|
||||
-dontwarn javax.lang.model.element.TypeElement
|
||||
-dontwarn javax.lang.model.element.TypeParameterElement
|
||||
-dontwarn javax.lang.model.element.VariableElement
|
||||
-dontwarn javax.lang.model.type.ArrayType
|
||||
-dontwarn javax.lang.model.type.DeclaredType
|
||||
-dontwarn javax.lang.model.type.ExecutableType
|
||||
-dontwarn javax.lang.model.type.TypeKind
|
||||
-dontwarn javax.lang.model.type.TypeMirror
|
||||
-dontwarn javax.lang.model.type.TypeVariable
|
||||
-dontwarn javax.lang.model.type.TypeVisitor
|
||||
-dontwarn javax.lang.model.util.AbstractAnnotationValueVisitor8
|
||||
-dontwarn javax.lang.model.util.AbstractTypeVisitor8
|
||||
-dontwarn javax.lang.model.util.ElementFilter
|
||||
-dontwarn javax.lang.model.util.Elements
|
||||
-dontwarn javax.lang.model.util.SimpleElementVisitor8
|
||||
-dontwarn javax.lang.model.util.SimpleTypeVisitor7
|
||||
-dontwarn javax.lang.model.util.SimpleTypeVisitor8
|
||||
-dontwarn javax.lang.model.util.Types
|
||||
-dontwarn javax.tools.Diagnostic$Kind
|
||||
@@ -24,6 +24,15 @@
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:mimeType="application/zip" />
|
||||
<data android:scheme="file" />
|
||||
<data android:scheme="content" />
|
||||
<data android:pathPattern=".*\\.zip" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
@@ -33,6 +42,13 @@
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.KernelSU.WebUI" />
|
||||
|
||||
<activity
|
||||
android:name=".ui.webui.WebUIXActivity"
|
||||
android:autoRemoveFromRecents="true"
|
||||
android:documentLaunchMode="intoExisting"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.KernelSU.WebUI" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
|
||||
@@ -25,6 +25,13 @@ Java_com_rifsxd_ksunext_Natives_getVersion(JNIEnv *env, jobject) {
|
||||
return get_version();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_com_rifsxd_ksunext_Natives_getHookMode(JNIEnv *env, jobject) {
|
||||
const char* mode = get_hook_mode();
|
||||
return env->NewStringUTF(mode);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jintArray JNICALL
|
||||
Java_com_rifsxd_ksunext_Natives_getAllowList(JNIEnv *env, jobject) {
|
||||
@@ -296,3 +303,13 @@ JNIEXPORT jboolean JNICALL
|
||||
Java_com_rifsxd_ksunext_Natives_uidShouldUmount(JNIEnv *env, jobject thiz, jint uid) {
|
||||
return uid_should_umount(uid);
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_rifsxd_ksunext_Natives_isSuEnabled(JNIEnv *env, jobject thiz) {
|
||||
return is_su_enabled();
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_rifsxd_ksunext_Natives_setSuEnabled(JNIEnv *env, jobject thiz, jboolean enabled) {
|
||||
return set_su_enabled(enabled);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
|
||||
#define CMD_IS_UID_GRANTED_ROOT 12
|
||||
#define CMD_IS_UID_SHOULD_UMOUNT 13
|
||||
#define CMD_IS_SU_ENABLED 14
|
||||
#define CMD_ENABLE_SU 15
|
||||
#define CMD_HOOK_MODE 16
|
||||
|
||||
static bool ksuctl(int cmd, void* arg1, void* arg2) {
|
||||
int32_t result = 0;
|
||||
@@ -48,17 +51,24 @@ bool become_manager(const char* pkg) {
|
||||
}
|
||||
|
||||
// cache the result to avoid unnecessary syscall
|
||||
static bool is_lkm;
|
||||
int get_version() {
|
||||
static bool is_lkm = false;
|
||||
|
||||
int get_version(void) {
|
||||
int32_t version = -1;
|
||||
int32_t lkm = 0;
|
||||
ksuctl(CMD_GET_VERSION, &version, &lkm);
|
||||
if (!is_lkm && lkm != 0) {
|
||||
int32_t flags = 0;
|
||||
ksuctl(CMD_GET_VERSION, &version, &flags);
|
||||
if (!is_lkm && (flags & 0x1)) {
|
||||
is_lkm = true;
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
const char* get_hook_mode() {
|
||||
static char mode[16];
|
||||
ksuctl(CMD_HOOK_MODE, mode, nullptr);
|
||||
return mode;
|
||||
}
|
||||
|
||||
bool get_allow_list(int *uids, int *size) {
|
||||
return ksuctl(CMD_GET_SU_LIST, uids, size);
|
||||
}
|
||||
@@ -84,3 +94,14 @@ bool set_app_profile(const app_profile *profile) {
|
||||
bool get_app_profile(p_key_t key, app_profile *profile) {
|
||||
return ksuctl(CMD_GET_APP_PROFILE, (void*) profile, nullptr);
|
||||
}
|
||||
|
||||
bool set_su_enabled(bool enabled) {
|
||||
return ksuctl(CMD_ENABLE_SU, (void*) enabled, nullptr);
|
||||
}
|
||||
|
||||
bool is_su_enabled() {
|
||||
bool enabled = true;
|
||||
// if ksuctl failed, we assume su is enabled, and it cannot be disabled.
|
||||
ksuctl(CMD_IS_SU_ENABLED, &enabled, nullptr);
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ bool become_manager(const char *);
|
||||
|
||||
int get_version();
|
||||
|
||||
const char* get_hook_mode();
|
||||
|
||||
bool get_allow_list(int *uids, int *size);
|
||||
|
||||
bool uid_should_umount(int uid);
|
||||
@@ -79,4 +81,8 @@ bool set_app_profile(const app_profile *profile);
|
||||
|
||||
bool get_app_profile(p_key_t key, app_profile *profile);
|
||||
|
||||
bool set_su_enabled(bool enabled);
|
||||
|
||||
bool is_su_enabled();
|
||||
|
||||
#endif //KERNELSU_KSU_H
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
package com.rifsxd.ksunext
|
||||
|
||||
import android.app.Application
|
||||
import android.system.Os
|
||||
import coil.Coil
|
||||
import coil.ImageLoader
|
||||
import me.zhanghai.android.appiconloader.coil.AppIconFetcher
|
||||
import me.zhanghai.android.appiconloader.coil.AppIconKeyer
|
||||
import okhttp3.Cache
|
||||
import okhttp3.OkHttpClient
|
||||
import java.io.File
|
||||
import java.util.Locale
|
||||
|
||||
lateinit var ksuApp: KernelSUApplication
|
||||
|
||||
class KernelSUApplication : Application() {
|
||||
|
||||
lateinit var okhttpClient: OkHttpClient
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
ksuApp = this
|
||||
@@ -30,7 +36,20 @@ class KernelSUApplication : Application() {
|
||||
if (!webroot.exists()) {
|
||||
webroot.mkdir()
|
||||
}
|
||||
|
||||
// Provide working env for rust's temp_dir()
|
||||
Os.setenv("TMPDIR", cacheDir.absolutePath, true)
|
||||
|
||||
okhttpClient =
|
||||
OkHttpClient.Builder().cache(Cache(File(cacheDir, "okhttp"), 10 * 1024 * 1024))
|
||||
.addInterceptor { block ->
|
||||
block.proceed(
|
||||
block.request().newBuilder()
|
||||
.header("User-Agent", "KernelSU/${BuildConfig.VERSION_CODE}")
|
||||
.header("Accept-Language", Locale.getDefault().toLanguageTag()).build()
|
||||
)
|
||||
}.build()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,19 @@ data class KernelVersion(val major: Int, val patchLevel: Int, val subLevel: Int)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
fun isULegacy(): Boolean {
|
||||
return major == 3
|
||||
}
|
||||
|
||||
fun isLegacy(): Boolean {
|
||||
return major == 4 && patchLevel in 1..18
|
||||
}
|
||||
|
||||
fun isGKI1(): Boolean {
|
||||
return (major == 4 && patchLevel >= 19) || (major == 5 && patchLevel < 10)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun parseKernelVersion(version: String): KernelVersion {
|
||||
|
||||
@@ -74,4 +74,4 @@ public class KsuService extends RootService {
|
||||
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,13 @@ object Natives {
|
||||
// 11640: Support query working mode, LKM or GKI
|
||||
// when MINIMAL_SUPPORTED_KERNEL > 11640, we can remove this constant.
|
||||
const val MINIMAL_SUPPORTED_KERNEL_LKM = 11648
|
||||
|
||||
// 12404: Support disable sucompat mode
|
||||
const val MINIMAL_SUPPORTED_SU_COMPAT = 12404
|
||||
|
||||
// 12569: support get hook mode
|
||||
const val MINIMAL_SUPPORTED_HOOK_MODE = 12569
|
||||
|
||||
const val KERNEL_SU_DOMAIN = "u:r:su:s0"
|
||||
|
||||
const val ROOT_UID = 0
|
||||
@@ -47,6 +54,16 @@ object Natives {
|
||||
|
||||
external fun uidShouldUmount(uid: Int): Boolean
|
||||
|
||||
/**
|
||||
* Get a string indicating the SU hook mode enabled in kernel.
|
||||
* The return values are:
|
||||
* - "Manual": Manual hooks was enabled.
|
||||
* - "Kprobes": Kprobes hooks was enabled (CONFIG_KSU_KPROBES_HOOK).
|
||||
*
|
||||
* @return return hook mode, or null if unavailable.
|
||||
*/
|
||||
external fun getHookMode(): String?
|
||||
|
||||
/**
|
||||
* Get the profile of the given package.
|
||||
* @param key usually the package name
|
||||
@@ -55,6 +72,15 @@ object Natives {
|
||||
external fun getAppProfile(key: String?, uid: Int): Profile
|
||||
external fun setAppProfile(profile: Profile?): Boolean
|
||||
|
||||
/**
|
||||
* `su` compat mode can be disabled temporarily.
|
||||
* 0: disabled
|
||||
* 1: enabled
|
||||
* negative : error
|
||||
*/
|
||||
external fun isSuEnabled(): Boolean
|
||||
external fun setSuEnabled(enabled: Boolean): Boolean
|
||||
|
||||
private const val NON_ROOT_DEFAULT_PROFILE_KEY = "$"
|
||||
private const val NOBODY_UID = 9999
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.rifsxd.ksunext.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
@@ -21,6 +24,8 @@ import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.layout.union
|
||||
import androidx.compose.material3.Badge
|
||||
import androidx.compose.material3.BadgedBox
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.NavigationBar
|
||||
import androidx.compose.material3.NavigationBarItem
|
||||
@@ -29,11 +34,13 @@ import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
@@ -49,13 +56,22 @@ import com.rifsxd.ksunext.Natives
|
||||
import com.rifsxd.ksunext.ksuApp
|
||||
import com.rifsxd.ksunext.ui.screen.BottomBarDestination
|
||||
import com.rifsxd.ksunext.ui.theme.KernelSUTheme
|
||||
import com.rifsxd.ksunext.ui.util.*
|
||||
import com.rifsxd.ksunext.ui.util.LocalSnackbarHost
|
||||
import com.rifsxd.ksunext.ui.util.LocaleHelper
|
||||
import com.rifsxd.ksunext.ui.util.rootAvailable
|
||||
import com.rifsxd.ksunext.ui.util.install
|
||||
import com.rifsxd.ksunext.ui.util.*
|
||||
import com.rifsxd.ksunext.ui.util.isSuCompatDisabled
|
||||
import com.rifsxd.ksunext.ui.screen.FlashIt
|
||||
import com.rifsxd.ksunext.ui.viewmodel.ModuleViewModel
|
||||
import com.rifsxd.ksunext.ui.viewmodel.SuperUserViewModel
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
override fun attachBaseContext(newBase: Context?) {
|
||||
super.attachBaseContext(newBase?.let { LocaleHelper.applyLanguage(it) })
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
||||
// Enable edge to edge
|
||||
@@ -67,14 +83,73 @@ class MainActivity : ComponentActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
if (isManager) install()
|
||||
if (isManager) install()
|
||||
|
||||
val zipUri: Uri? = when (intent?.action) {
|
||||
Intent.ACTION_VIEW, Intent.ACTION_SEND -> {
|
||||
val uri = intent.data ?: intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
|
||||
uri?.let {
|
||||
val name = when (it.scheme) {
|
||||
"file" -> it.lastPathSegment ?: ""
|
||||
"content" -> {
|
||||
contentResolver.query(it, null, null, null, null)?.use { cursor ->
|
||||
val nameIndex = cursor.getColumnIndex(android.provider.OpenableColumns.DISPLAY_NAME)
|
||||
if (cursor.moveToFirst() && nameIndex != -1) {
|
||||
cursor.getString(nameIndex)
|
||||
} else {
|
||||
it.lastPathSegment ?: ""
|
||||
}
|
||||
} ?: (it.lastPathSegment ?: "")
|
||||
}
|
||||
else -> it.lastPathSegment ?: ""
|
||||
}
|
||||
if (name.lowercase().endsWith(".zip")) it else null
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
setContent {
|
||||
KernelSUTheme {
|
||||
// Read AMOLED mode preference
|
||||
val prefs = getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
val amoledMode = prefs.getBoolean("enable_amoled", false)
|
||||
|
||||
val moduleViewModel: ModuleViewModel = viewModel()
|
||||
val superUserViewModel: SuperUserViewModel = viewModel()
|
||||
val moduleUpdateCount = moduleViewModel.moduleList.count {
|
||||
moduleViewModel.checkUpdate(it).first.isNotEmpty()
|
||||
}
|
||||
|
||||
KernelSUTheme (
|
||||
amoledMode = amoledMode
|
||||
) {
|
||||
val navController = rememberNavController()
|
||||
val snackBarHostState = remember { SnackbarHostState() }
|
||||
val currentDestination = navController.currentBackStackEntryAsState()?.value?.destination
|
||||
|
||||
val navigator = navController.rememberDestinationsNavigator()
|
||||
|
||||
LaunchedEffect(zipUri) {
|
||||
if (zipUri != null) {
|
||||
navigator.navigate(
|
||||
FlashScreenDestination(
|
||||
FlashIt.FlashModules(listOf(zipUri)),
|
||||
finishIntent = true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (superUserViewModel.appList.isEmpty()) {
|
||||
superUserViewModel.fetchAppList()
|
||||
}
|
||||
|
||||
if (moduleViewModel.moduleList.isEmpty()) {
|
||||
moduleViewModel.fetchModuleList()
|
||||
}
|
||||
}
|
||||
|
||||
val showBottomBar = when (currentDestination?.route) {
|
||||
FlashScreenDestination.route -> false // Hide for FlashScreenDestination
|
||||
ExecuteModuleActionScreenDestination.route -> false // Hide for ExecuteModuleActionScreen
|
||||
@@ -88,7 +163,7 @@ class MainActivity : ComponentActivity() {
|
||||
enter = slideInVertically(initialOffsetY = { it }) + fadeIn(),
|
||||
exit = slideOutVertically(targetOffsetY = { it }) + fadeOut()
|
||||
) {
|
||||
BottomBar(navController)
|
||||
BottomBar(navController, moduleUpdateCount)
|
||||
}
|
||||
},
|
||||
contentWindowInsets = WindowInsets(0, 0, 0, 0)
|
||||
@@ -115,43 +190,59 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BottomBar(navController: NavHostController) {
|
||||
private fun BottomBar(navController: NavHostController, moduleUpdateCount: Int) {
|
||||
val navigator = navController.rememberDestinationsNavigator()
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable()
|
||||
val suCompatDisabled = isSuCompatDisabled()
|
||||
val suSFS = getSuSFS()
|
||||
val susSUMode = susfsSUS_SU_Mode()
|
||||
|
||||
NavigationBar(
|
||||
tonalElevation = 8.dp,
|
||||
windowInsets = WindowInsets.systemBars.union(WindowInsets.displayCutout).only(
|
||||
WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom
|
||||
)
|
||||
) {
|
||||
BottomBarDestination.entries.forEach { destination ->
|
||||
if (!fullFeatured && destination.rootRequired) return@forEach
|
||||
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
|
||||
NavigationBarItem(
|
||||
selected = isCurrentDestOnBackStack,
|
||||
onClick = {
|
||||
if (isCurrentDestOnBackStack) {
|
||||
navigator.popBackStack(destination.direction, false)
|
||||
}
|
||||
navigator.navigate(destination.direction) {
|
||||
popUpTo(NavGraphs.root) {
|
||||
saveState = true
|
||||
BottomBarDestination.entries
|
||||
.forEach { destination ->
|
||||
if (!fullFeatured && destination.rootRequired) return@forEach
|
||||
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
|
||||
NavigationBarItem(
|
||||
selected = isCurrentDestOnBackStack,
|
||||
onClick = {
|
||||
if (isCurrentDestOnBackStack) {
|
||||
navigator.popBackStack(destination.direction, false)
|
||||
}
|
||||
launchSingleTop = true
|
||||
restoreState = true
|
||||
}
|
||||
},
|
||||
icon = {
|
||||
if (isCurrentDestOnBackStack) {
|
||||
Icon(destination.iconSelected, stringResource(destination.label))
|
||||
} else {
|
||||
Icon(destination.iconNotSelected, stringResource(destination.label))
|
||||
}
|
||||
},
|
||||
label = { Text(stringResource(destination.label)) },
|
||||
alwaysShowLabel = true
|
||||
)
|
||||
}
|
||||
navigator.navigate(destination.direction) {
|
||||
popUpTo(NavGraphs.root) {
|
||||
saveState = true
|
||||
}
|
||||
launchSingleTop = true
|
||||
restoreState = true
|
||||
}
|
||||
},
|
||||
icon = {
|
||||
// Show badge for Module icon if moduleUpdateCount > 0
|
||||
if (destination == BottomBarDestination.Module && moduleUpdateCount > 0) {
|
||||
BadgedBox(badge = { Badge { Text(moduleUpdateCount.toString()) } }) {
|
||||
if (isCurrentDestOnBackStack) {
|
||||
Icon(destination.iconSelected, stringResource(destination.label))
|
||||
} else {
|
||||
Icon(destination.iconNotSelected, stringResource(destination.label))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isCurrentDestOnBackStack) {
|
||||
Icon(destination.iconSelected, stringResource(destination.label))
|
||||
} else {
|
||||
Icon(destination.iconNotSelected, stringResource(destination.label))
|
||||
}
|
||||
}
|
||||
},
|
||||
label = { Text(stringResource(destination.label)) },
|
||||
alwaysShowLabel = true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ private fun AboutCardContent() {
|
||||
val annotatedString = AnnotatedString.Companion.fromHtml(
|
||||
htmlString = stringResource(
|
||||
id = R.string.about_source_code,
|
||||
"<b><a href=\"https://github.com/rifsxd/KernelSU-Next\">GitHub</a></b>"
|
||||
"<b><a href=\"https://github.com/KernelSU-Next/KernelSU-Next\">GitHub</a></b>"
|
||||
),
|
||||
linkStyles = TextLinkStyles(
|
||||
style = SpanStyle(
|
||||
|
||||
@@ -5,14 +5,18 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.selection.toggleable
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import com.dergoogler.mmrl.ui.component.LabelItem
|
||||
import com.dergoogler.mmrl.ui.component.text.TextRow
|
||||
|
||||
@Composable
|
||||
fun SwitchItem(
|
||||
@@ -21,9 +25,11 @@ fun SwitchItem(
|
||||
summary: String? = null,
|
||||
checked: Boolean,
|
||||
enabled: Boolean = true,
|
||||
onCheckedChange: (Boolean) -> Unit
|
||||
beta: Boolean = false,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
) {
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val stateAlpha = remember(checked, enabled) { Modifier.alpha(if (enabled) 1f else 0.5f) }
|
||||
|
||||
ListItem(
|
||||
modifier = Modifier
|
||||
@@ -36,10 +42,30 @@ fun SwitchItem(
|
||||
onValueChange = onCheckedChange
|
||||
),
|
||||
headlineContent = {
|
||||
Text(title)
|
||||
TextRow(
|
||||
leadingContent = if (beta) {
|
||||
{
|
||||
LabelItem(
|
||||
modifier = Modifier.then(stateAlpha),
|
||||
text = "Beta"
|
||||
)
|
||||
}
|
||||
} else null
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.then(stateAlpha),
|
||||
text = title,
|
||||
)
|
||||
}
|
||||
},
|
||||
leadingContent = icon?.let {
|
||||
{ Icon(icon, title) }
|
||||
{
|
||||
Icon(
|
||||
modifier = Modifier.then(stateAlpha),
|
||||
imageVector = icon,
|
||||
contentDescription = title
|
||||
)
|
||||
}
|
||||
},
|
||||
trailingContent = {
|
||||
Switch(
|
||||
@@ -51,7 +77,10 @@ fun SwitchItem(
|
||||
},
|
||||
supportingContent = {
|
||||
if (summary != null) {
|
||||
Text(summary)
|
||||
Text(
|
||||
modifier = Modifier.then(stateAlpha),
|
||||
text = summary
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -73,6 +73,7 @@ fun RootProfileConfig(
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
val currentNamespace = when (profile.namespace) {
|
||||
Natives.Profile.Namespace.INHERITED.ordinal -> stringResource(R.string.profile_namespace_inherited)
|
||||
@@ -126,6 +127,7 @@ fun RootProfileConfig(
|
||||
}
|
||||
}
|
||||
})
|
||||
*/
|
||||
|
||||
UidPanel(uid = profile.uid, label = "uid", onUidChange = {
|
||||
onProfileChange(
|
||||
|
||||
@@ -55,6 +55,8 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.dropUnlessResumed
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
@@ -93,8 +95,10 @@ fun AppProfileScreen(
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
val scope = rememberCoroutineScope()
|
||||
val viewModel: SuperUserViewModel = viewModel()
|
||||
val failToUpdateAppProfile = stringResource(R.string.failed_to_update_app_profile).format(appInfo.label)
|
||||
val failToUpdateSepolicy = stringResource(R.string.failed_to_update_sepolicy).format(appInfo.label)
|
||||
val suNotAllowed = stringResource(R.string.su_not_allowed).format(appInfo.label)
|
||||
|
||||
val packageName = appInfo.packageName
|
||||
val initialProfile = Natives.getAppProfile(packageName, appInfo.uid)
|
||||
@@ -108,7 +112,7 @@ fun AppProfileScreen(
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBar(
|
||||
onBack = { navigator.popBackStack() },
|
||||
onBack = dropUnlessResumed { navigator.popBackStack() },
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
@@ -143,8 +147,13 @@ fun AppProfileScreen(
|
||||
},
|
||||
onProfileChange = {
|
||||
scope.launch {
|
||||
if (it.allowSu && !it.rootUseDefault && it.rules.isNotEmpty()) {
|
||||
if (!setSepolicy(profile.name, it.rules)) {
|
||||
if (it.allowSu) {
|
||||
// sync with allowlist.c - forbid_system_uid
|
||||
if (appInfo.uid < 2000 && appInfo.uid != 1000) {
|
||||
snackBarHost.showSnackbar(suNotAllowed)
|
||||
return@launch
|
||||
}
|
||||
if (!it.rootUseDefault && it.rules.isNotEmpty() && !setSepolicy(profile.name, it.rules)) {
|
||||
snackBarHost.showSnackbar(failToUpdateSepolicy)
|
||||
return@launch
|
||||
}
|
||||
@@ -153,6 +162,7 @@ fun AppProfileScreen(
|
||||
snackBarHost.showSnackbar(failToUpdateAppProfile.format(appInfo.uid))
|
||||
} else {
|
||||
profile = it
|
||||
viewModel.updateAppProfile(packageName, it)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
package com.rifsxd.ksunext.ui.screen
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.*
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.lifecycle.compose.dropUnlessResumed
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import com.rifsxd.ksunext.Natives
|
||||
import com.rifsxd.ksunext.ksuApp
|
||||
import com.rifsxd.ksunext.R
|
||||
import com.rifsxd.ksunext.ui.component.ConfirmResult
|
||||
import com.rifsxd.ksunext.ui.component.rememberConfirmDialog
|
||||
import com.rifsxd.ksunext.ui.component.rememberLoadingDialog
|
||||
import com.rifsxd.ksunext.ui.util.LocalSnackbarHost
|
||||
import com.rifsxd.ksunext.ui.util.*
|
||||
|
||||
/**
|
||||
* @author rifsxd
|
||||
* @date 2025/1/14.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Destination<RootGraph>
|
||||
@Composable
|
||||
fun BackupRestoreScreen(navigator: DestinationsNavigator) {
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
val ksuVersion = if (isManager) Natives.version else null
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBar(
|
||||
onBack = dropUnlessResumed {
|
||||
navigator.popBackStack()
|
||||
},
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
snackbarHost = { SnackbarHost(snackBarHost) },
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||
) { paddingValues ->
|
||||
val loadingDialog = rememberLoadingDialog()
|
||||
val restoreDialog = rememberConfirmDialog()
|
||||
val backupDialog = rememberConfirmDialog()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
var showRebootDialog by remember { mutableStateOf(false) }
|
||||
|
||||
if (showRebootDialog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showRebootDialog = false },
|
||||
title = { Text(stringResource(R.string.reboot_required)) },
|
||||
text = { Text(stringResource(R.string.reboot_message)) },
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
showRebootDialog = false
|
||||
reboot()
|
||||
}) {
|
||||
Text(stringResource(R.string.reboot))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { showRebootDialog = false }) {
|
||||
Text(stringResource(R.string.later))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val moduleBackup = stringResource(id = R.string.module_backup)
|
||||
val backupMessage = stringResource(id = R.string.module_backup_message)
|
||||
ListItem(
|
||||
leadingContent = {
|
||||
Icon(
|
||||
Icons.Filled.Backup,
|
||||
moduleBackup
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(moduleBackup) },
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
val result = backupDialog.awaitConfirm(title = moduleBackup, content = backupMessage)
|
||||
if (result == ConfirmResult.Confirmed) {
|
||||
loadingDialog.withLoading {
|
||||
moduleBackup()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if (showRebootDialog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showRebootDialog = false },
|
||||
title = { Text(stringResource(R.string.reboot_required)) },
|
||||
text = { Text(stringResource(R.string.reboot_message)) },
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
showRebootDialog = false
|
||||
reboot()
|
||||
}) {
|
||||
Text(stringResource(R.string.reboot))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { showRebootDialog = false }) {
|
||||
Text(stringResource(R.string.later))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
|
||||
var useOverlayFs by rememberSaveable {
|
||||
mutableStateOf(readMountSystemFile())
|
||||
}
|
||||
|
||||
val moduleRestore = stringResource(id = R.string.module_restore)
|
||||
val restoreMessage = stringResource(id = R.string.module_restore_message)
|
||||
|
||||
ListItem(
|
||||
leadingContent = {
|
||||
Icon(
|
||||
Icons.Filled.Restore,
|
||||
moduleRestore,
|
||||
tint = if (useOverlayFs) androidx.compose.material3.MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f) else androidx.compose.material3.MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
},
|
||||
headlineContent = {
|
||||
Text(
|
||||
moduleRestore,
|
||||
color = if (useOverlayFs) androidx.compose.material3.MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f) else androidx.compose.material3.MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
},
|
||||
modifier = Modifier.clickable(
|
||||
enabled = !useOverlayFs,
|
||||
onClick = {
|
||||
scope.launch {
|
||||
val result = restoreDialog.awaitConfirm(title = moduleRestore, content = restoreMessage)
|
||||
if (result == ConfirmResult.Confirmed) {
|
||||
loadingDialog.withLoading {
|
||||
moduleRestore()
|
||||
showRebootDialog = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
HorizontalDivider(thickness = Dp.Hairline)
|
||||
|
||||
val allowlistBackup = stringResource(id = R.string.allowlist_backup)
|
||||
val allowlistbackupMessage = stringResource(id = R.string.allowlist_backup_message)
|
||||
ListItem(
|
||||
leadingContent = {
|
||||
Icon(
|
||||
Icons.Filled.Backup,
|
||||
allowlistBackup
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(allowlistBackup) },
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
val result = backupDialog.awaitConfirm(title = allowlistBackup, content = allowlistbackupMessage)
|
||||
if (result == ConfirmResult.Confirmed) {
|
||||
loadingDialog.withLoading {
|
||||
allowlistBackup()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
val allowlistRestore = stringResource(id = R.string.allowlist_restore)
|
||||
val allowlistrestoreMessage = stringResource(id = R.string.allowlist_restore_message)
|
||||
ListItem(
|
||||
leadingContent = {
|
||||
Icon(
|
||||
Icons.Filled.Restore,
|
||||
allowlistRestore
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(allowlistRestore) },
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
val result = restoreDialog.awaitConfirm(title = allowlistRestore, content = allowlistrestoreMessage)
|
||||
if (result == ConfirmResult.Confirmed) {
|
||||
loadingDialog.withLoading {
|
||||
allowlistRestore()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(
|
||||
onBack: () -> Unit = {},
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
) {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.backup_restore)) }, navigationIcon = {
|
||||
IconButton(
|
||||
onClick = onBack
|
||||
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||
},
|
||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun BackupPreview() {
|
||||
BackupRestoreScreen(EmptyDestinationsNavigator)
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
package com.rifsxd.ksunext.ui.screen
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.*
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.lifecycle.compose.dropUnlessResumed
|
||||
import com.maxkeppeker.sheets.core.models.base.Header
|
||||
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
|
||||
import com.maxkeppeler.sheets.list.ListDialog
|
||||
import com.maxkeppeler.sheets.list.models.ListOption
|
||||
import com.maxkeppeler.sheets.list.models.ListSelection
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
||||
import com.rifsxd.ksunext.Natives
|
||||
import com.rifsxd.ksunext.ksuApp
|
||||
import com.rifsxd.ksunext.R
|
||||
import com.rifsxd.ksunext.ui.component.rememberCustomDialog
|
||||
import com.rifsxd.ksunext.ui.component.SwitchItem
|
||||
import com.rifsxd.ksunext.ui.util.LocaleHelper
|
||||
import com.rifsxd.ksunext.ui.util.LocalSnackbarHost
|
||||
import com.rifsxd.ksunext.ui.util.*
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* @author rifsxd
|
||||
* @date 2025/6/1.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Destination<RootGraph>
|
||||
@Composable
|
||||
fun CustomizationScreen(navigator: DestinationsNavigator) {
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
val ksuVersion = if (isManager) Natives.version else null
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBar(
|
||||
onBack = dropUnlessResumed {
|
||||
navigator.popBackStack()
|
||||
},
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
snackbarHost = { SnackbarHost(snackBarHost) },
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||
) { paddingValues ->
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
|
||||
// Track language state with current app locale
|
||||
var currentAppLocale by remember { mutableStateOf(LocaleHelper.getCurrentAppLocale(context)) }
|
||||
|
||||
// Listen for preference changes
|
||||
LaunchedEffect(Unit) {
|
||||
currentAppLocale = LocaleHelper.getCurrentAppLocale(context)
|
||||
}
|
||||
|
||||
// Language setting with selection dialog
|
||||
val languageDialog = rememberCustomDialog { dismiss ->
|
||||
// Check if should use system language settings
|
||||
if (LocaleHelper.useSystemLanguageSettings) {
|
||||
// Android 13+ - Jump to system settings
|
||||
LocaleHelper.launchSystemLanguageSettings(context)
|
||||
dismiss()
|
||||
} else {
|
||||
// Android < 13 - Show app language selector
|
||||
// Dynamically detect supported locales from resources
|
||||
val supportedLocales = remember {
|
||||
val locales = mutableListOf<java.util.Locale>()
|
||||
|
||||
// Add system default first
|
||||
locales.add(java.util.Locale.ROOT) // This will represent "System Default"
|
||||
|
||||
// Dynamically detect available locales by checking resource directories
|
||||
val resourceDirs = listOf(
|
||||
"ar", "bg", "de", "fa", "fr", "hu", "in", "it",
|
||||
"ja", "ko", "pl", "pt-rBR", "ru", "th", "tr",
|
||||
"uk", "vi", "zh-rCN", "zh-rTW"
|
||||
)
|
||||
|
||||
resourceDirs.forEach { dir ->
|
||||
try {
|
||||
val locale = when {
|
||||
dir.contains("-r") -> {
|
||||
val parts = dir.split("-r")
|
||||
java.util.Locale.Builder()
|
||||
.setLanguage(parts[0])
|
||||
.setRegion(parts[1])
|
||||
.build()
|
||||
}
|
||||
else -> java.util.Locale.Builder()
|
||||
.setLanguage(dir)
|
||||
.build()
|
||||
}
|
||||
|
||||
// Test if this locale has translated resources
|
||||
val config = android.content.res.Configuration()
|
||||
config.setLocale(locale)
|
||||
val localizedContext = context.createConfigurationContext(config)
|
||||
|
||||
// Try to get a translated string to verify the locale is supported
|
||||
val testString = localizedContext.getString(R.string.settings_language)
|
||||
val defaultString = context.getString(R.string.settings_language)
|
||||
|
||||
// If the string is different or it's English, it's supported
|
||||
if (testString != defaultString || locale.language == "en") {
|
||||
locales.add(locale)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Skip unsupported locales
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by display name
|
||||
val sortedLocales = locales.drop(1).sortedBy { it.getDisplayName(it) }
|
||||
mutableListOf<java.util.Locale>().apply {
|
||||
add(locales.first()) // System default first
|
||||
addAll(sortedLocales)
|
||||
}
|
||||
}
|
||||
|
||||
val allOptions = supportedLocales.map { locale ->
|
||||
val tag = if (locale == java.util.Locale.ROOT) {
|
||||
"system"
|
||||
} else if (locale.country.isEmpty()) {
|
||||
locale.language
|
||||
} else {
|
||||
"${locale.language}_${locale.country}"
|
||||
}
|
||||
|
||||
val displayName = if (locale == java.util.Locale.ROOT) {
|
||||
context.getString(R.string.system_default)
|
||||
} else {
|
||||
locale.getDisplayName(locale)
|
||||
}
|
||||
|
||||
tag to displayName
|
||||
}
|
||||
|
||||
val currentLocale = prefs.getString("app_locale", "system") ?: "system"
|
||||
val options = allOptions.map { (tag, displayName) ->
|
||||
ListOption(
|
||||
titleText = displayName,
|
||||
selected = currentLocale == tag
|
||||
)
|
||||
}
|
||||
|
||||
var selectedIndex by remember {
|
||||
mutableIntStateOf(allOptions.indexOfFirst { (tag, _) -> currentLocale == tag })
|
||||
}
|
||||
|
||||
ListDialog(
|
||||
state = rememberUseCaseState(
|
||||
visible = true,
|
||||
onFinishedRequest = {
|
||||
if (selectedIndex >= 0 && selectedIndex < allOptions.size) {
|
||||
val newLocale = allOptions[selectedIndex].first
|
||||
prefs.edit().putString("app_locale", newLocale).apply()
|
||||
|
||||
// Update local state immediately
|
||||
currentAppLocale = LocaleHelper.getCurrentAppLocale(context)
|
||||
|
||||
// Apply locale change immediately for Android < 13
|
||||
LocaleHelper.restartActivity(context)
|
||||
}
|
||||
dismiss()
|
||||
},
|
||||
onCloseRequest = {
|
||||
dismiss()
|
||||
}
|
||||
),
|
||||
header = Header.Default(
|
||||
title = stringResource(R.string.settings_language),
|
||||
),
|
||||
selection = ListSelection.Single(
|
||||
showRadioButtons = true,
|
||||
options = options
|
||||
) { index, _ ->
|
||||
selectedIndex = index
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val language = stringResource(id = R.string.settings_language)
|
||||
|
||||
// Compute display name based on current app locale (similar to the reference implementation)
|
||||
val currentLanguageDisplay = remember(currentAppLocale) {
|
||||
val locale = currentAppLocale
|
||||
if (locale != null) {
|
||||
locale.getDisplayName(locale)
|
||||
} else {
|
||||
context.getString(R.string.system_default)
|
||||
}
|
||||
}
|
||||
|
||||
ListItem(
|
||||
leadingContent = { Icon(Icons.Filled.Translate, language) },
|
||||
headlineContent = { Text(language) },
|
||||
supportingContent = { Text(currentLanguageDisplay) },
|
||||
modifier = Modifier.clickable {
|
||||
languageDialog.show()
|
||||
}
|
||||
)
|
||||
|
||||
var useBanner by rememberSaveable {
|
||||
mutableStateOf(
|
||||
prefs.getBoolean("use_banner", true)
|
||||
)
|
||||
}
|
||||
if (ksuVersion != null) {
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.ViewCarousel,
|
||||
title = stringResource(id = R.string.settings_banner),
|
||||
summary = stringResource(id = R.string.settings_banner_summary),
|
||||
checked = useBanner
|
||||
) {
|
||||
prefs.edit().putBoolean("use_banner", it).apply()
|
||||
useBanner = it
|
||||
}
|
||||
}
|
||||
|
||||
var enableAmoled by rememberSaveable {
|
||||
mutableStateOf(
|
||||
prefs.getBoolean("enable_amoled", false)
|
||||
)
|
||||
}
|
||||
var showRestartDialog by remember { mutableStateOf(false) }
|
||||
if (isSystemInDarkTheme()) {
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.Contrast,
|
||||
title = stringResource(id = R.string.settings_amoled_mode),
|
||||
summary = stringResource(id = R.string.settings_amoled_mode_summary),
|
||||
checked = enableAmoled
|
||||
) { checked ->
|
||||
prefs.edit().putBoolean("enable_amoled", checked).apply()
|
||||
enableAmoled = checked
|
||||
showRestartDialog = true
|
||||
}
|
||||
if (showRestartDialog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showRestartDialog = false },
|
||||
title = { Text(stringResource(R.string.restart_required)) },
|
||||
text = { Text(stringResource(R.string.restart_app_message)) },
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
showRestartDialog = false
|
||||
// Restart the app
|
||||
val packageManager = context.packageManager
|
||||
val intent = packageManager.getLaunchIntentForPackage(context.packageName)
|
||||
intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
context.startActivity(intent)
|
||||
Runtime.getRuntime().exit(0)
|
||||
}) {
|
||||
Text(stringResource(R.string.restart_app))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { showRestartDialog = false }) {
|
||||
Text(stringResource(R.string.later))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(
|
||||
onBack: () -> Unit = {},
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
) {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.customization)) }, navigationIcon = {
|
||||
IconButton(
|
||||
onClick = onBack
|
||||
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||
},
|
||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun CustomizationPreview() {
|
||||
CustomizationScreen(EmptyDestinationsNavigator)
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package com.rifsxd.ksunext.ui.screen
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.*
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
||||
import kotlinx.coroutines.launch
|
||||
import com.rifsxd.ksunext.Natives
|
||||
import com.rifsxd.ksunext.ksuApp
|
||||
import com.rifsxd.ksunext.R
|
||||
import com.rifsxd.ksunext.ui.component.SwitchItem
|
||||
import com.rifsxd.ksunext.ui.util.LocalSnackbarHost
|
||||
import com.rifsxd.ksunext.ui.util.*
|
||||
|
||||
/**
|
||||
* @author rifsxd
|
||||
* @date 2025/6/15.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Destination<RootGraph>
|
||||
@Composable
|
||||
fun DeveloperScreen(navigator: DestinationsNavigator) {
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
val ksuVersion = if (isManager) Natives.version else null
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBar(
|
||||
onBack = { navigator.popBackStack() },
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
snackbarHost = { SnackbarHost(snackBarHost) },
|
||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||
) { paddingValues ->
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
|
||||
// --- Developer Options Switch ---
|
||||
var developerOptionsEnabled by rememberSaveable {
|
||||
mutableStateOf(
|
||||
prefs.getBoolean("enable_developer_options", false)
|
||||
)
|
||||
}
|
||||
if (ksuVersion != null) {
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.DeveloperMode,
|
||||
title = stringResource(id = R.string.enable_developer_options),
|
||||
summary = stringResource(id = R.string.enable_developer_options_summary),
|
||||
checked = developerOptionsEnabled
|
||||
) {
|
||||
prefs.edit().putBoolean("enable_developer_options", it).apply()
|
||||
developerOptionsEnabled = it
|
||||
}
|
||||
}
|
||||
|
||||
var enableWebDebugging by rememberSaveable {
|
||||
mutableStateOf(
|
||||
prefs.getBoolean("enable_web_debugging", false)
|
||||
)
|
||||
}
|
||||
if (ksuVersion != null) {
|
||||
SwitchItem(
|
||||
enabled = developerOptionsEnabled,
|
||||
icon = Icons.Filled.Web,
|
||||
title = stringResource(id = R.string.enable_web_debugging),
|
||||
summary = stringResource(id = R.string.enable_web_debugging_summary),
|
||||
checked = enableWebDebugging
|
||||
) {
|
||||
prefs.edit().putBoolean("enable_web_debugging", it).apply()
|
||||
enableWebDebugging = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(
|
||||
onBack: () -> Unit = {},
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
) {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.developer)) }, navigationIcon = {
|
||||
IconButton(
|
||||
onClick = onBack
|
||||
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||
},
|
||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun DeveloperPreview() {
|
||||
DeveloperScreen(EmptyDestinationsNavigator)
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.rifsxd.ksunext.ui.screen
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Environment
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -27,6 +28,7 @@ import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -34,11 +36,14 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.input.key.Key
|
||||
import androidx.compose.ui.input.key.key
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.dropUnlessResumed
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
@@ -66,6 +71,19 @@ fun ExecuteModuleActionScreen(navigator: DestinationsNavigator, moduleId: String
|
||||
var actionResult: Boolean
|
||||
var isActionRunning by rememberSaveable { mutableStateOf(true) }
|
||||
|
||||
val context = LocalContext.current
|
||||
// Read developer options from SharedPreferences
|
||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
val developerOptionsEnabled = prefs.getBoolean("enable_developer_options", false)
|
||||
|
||||
val view = LocalView.current
|
||||
DisposableEffect(isActionRunning) {
|
||||
view.keepScreenOn = isActionRunning
|
||||
onDispose {
|
||||
view.keepScreenOn = false
|
||||
}
|
||||
}
|
||||
|
||||
BackHandler(enabled = isActionRunning) {
|
||||
// Disable back button if action is running
|
||||
}
|
||||
@@ -100,7 +118,7 @@ fun ExecuteModuleActionScreen(navigator: DestinationsNavigator, moduleId: String
|
||||
topBar = {
|
||||
TopBar(
|
||||
isActionRunning = isActionRunning,
|
||||
onBack = {
|
||||
onBack = dropUnlessResumed {
|
||||
navigator.popBackStack()
|
||||
},
|
||||
onSave = {
|
||||
@@ -110,7 +128,7 @@ fun ExecuteModuleActionScreen(navigator: DestinationsNavigator, moduleId: String
|
||||
val date = format.format(Date())
|
||||
val file = File(
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
||||
"KernelSU_module_action_log_${date}.log"
|
||||
"KernelSU_Next_module_action_log_${date}.log"
|
||||
)
|
||||
file.writeText(logContent.toString())
|
||||
snackBarHost.showSnackbar("Log saved to ${file.absolutePath}")
|
||||
@@ -147,7 +165,7 @@ fun ExecuteModuleActionScreen(navigator: DestinationsNavigator, moduleId: String
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.padding(8.dp),
|
||||
text = text,
|
||||
text = if (developerOptionsEnabled) logContent.toString() else text,
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
fontFamily = FontFamily.Monospace,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.rifsxd.ksunext.ui.screen
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.Environment
|
||||
import android.os.Parcelable
|
||||
@@ -33,6 +34,7 @@ import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -44,10 +46,13 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.key.Key
|
||||
import androidx.compose.ui.input.key.key
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.dropUnlessResumed
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
@@ -58,7 +63,10 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import com.rifsxd.ksunext.R
|
||||
import com.rifsxd.ksunext.ui.component.rememberConfirmDialog
|
||||
import com.rifsxd.ksunext.ui.component.ConfirmResult
|
||||
import com.rifsxd.ksunext.ui.component.KeyEventBlocker
|
||||
import com.rifsxd.ksunext.ui.util.FlashResult
|
||||
import com.rifsxd.ksunext.ui.util.LkmSelection
|
||||
import com.rifsxd.ksunext.ui.util.LocalSnackbarHost
|
||||
import com.rifsxd.ksunext.ui.util.flashModule
|
||||
@@ -71,47 +79,35 @@ import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import android.app.Activity
|
||||
|
||||
enum class FlashingStatus {
|
||||
FLASHING,
|
||||
SUCCESS,
|
||||
FAILED
|
||||
}
|
||||
|
||||
fun Context.findActivity(): Activity? = when (this) {
|
||||
is Activity -> this
|
||||
is android.content.ContextWrapper -> baseContext.findActivity()
|
||||
else -> null
|
||||
}
|
||||
|
||||
// Lets you flash modules sequentially when mutiple zipUris are selected
|
||||
fun flashModulesSequentially(
|
||||
uris: List<Uri>,
|
||||
onFinish: (Boolean, Int) -> Unit,
|
||||
onStdout: (String) -> Unit,
|
||||
onStderr: (String) -> Unit
|
||||
) {
|
||||
val iterator = uris.iterator()
|
||||
|
||||
// Start processing from the first module inside a coroutine
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
// Define the recursive function within the coroutine
|
||||
suspend fun processNext() {
|
||||
if (iterator.hasNext()) {
|
||||
// Flash the current module
|
||||
flashModule(iterator.next(), onFinish = { showReboot, code ->
|
||||
// If successful, continue to the next one
|
||||
if (code == 0) {
|
||||
// Recursively call to process the next module
|
||||
launch {
|
||||
processNext()
|
||||
}
|
||||
} else {
|
||||
onFinish(showReboot, code) // If failed, finish the process
|
||||
}
|
||||
}, onStdout, onStderr)
|
||||
} else {
|
||||
// No more modules to process, finish the process
|
||||
onFinish(true, 0)
|
||||
): FlashResult {
|
||||
for (uri in uris) {
|
||||
flashModule(uri, onStdout, onStderr).apply {
|
||||
if (code != 0) {
|
||||
return FlashResult(code, err, showReboot)
|
||||
}
|
||||
}
|
||||
|
||||
// Start the process
|
||||
processNext()
|
||||
}
|
||||
return FlashResult(0, "", true)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,10 +117,14 @@ fun flashModulesSequentially(
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@Destination<RootGraph>
|
||||
fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
fun FlashScreen(
|
||||
navigator: DestinationsNavigator,
|
||||
flashIt: FlashIt,
|
||||
finishIntent: Boolean = false
|
||||
) {
|
||||
|
||||
var text by rememberSaveable { mutableStateOf("") }
|
||||
var tempText : String
|
||||
var tempText: String
|
||||
val logContent = rememberSaveable { StringBuilder() }
|
||||
var showFloatAction by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
@@ -136,25 +136,68 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
mutableStateOf(FlashingStatus.FLASHING)
|
||||
}
|
||||
|
||||
val context = LocalContext.current
|
||||
|
||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
val developerOptionsEnabled = prefs.getBoolean("enable_developer_options", false)
|
||||
|
||||
val activity = context.findActivity()
|
||||
|
||||
val view = LocalView.current
|
||||
DisposableEffect(flashing) {
|
||||
view.keepScreenOn = flashing == FlashingStatus.FLASHING
|
||||
onDispose {
|
||||
view.keepScreenOn = false
|
||||
}
|
||||
}
|
||||
|
||||
BackHandler(enabled = flashing == FlashingStatus.FLASHING) {
|
||||
// Disable back button if flashing is running
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (text.isNotEmpty()) {
|
||||
return@LaunchedEffect
|
||||
BackHandler(enabled = flashing != FlashingStatus.FLASHING) {
|
||||
navigator.popBackStack()
|
||||
if (finishIntent) activity?.finish()
|
||||
}
|
||||
|
||||
val confirmDialog = rememberConfirmDialog()
|
||||
var confirmed by rememberSaveable { mutableStateOf(flashIt !is FlashIt.FlashModules) }
|
||||
var pendingFlashIt by rememberSaveable { mutableStateOf<FlashIt?>(null) }
|
||||
var hasFlashed by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(flashIt) {
|
||||
if (flashIt is FlashIt.FlashModules && !confirmed) {
|
||||
val uris = flashIt.uris
|
||||
val moduleNames =
|
||||
uris.mapIndexed { index, uri -> "\n${index + 1}. ${uri.getFileName(context)}" }
|
||||
.joinToString("")
|
||||
val confirmContent =
|
||||
context.getString(R.string.module_install_prompt_with_name, moduleNames)
|
||||
val confirmTitle = context.getString(R.string.module)
|
||||
val result = confirmDialog.awaitConfirm(
|
||||
title = confirmTitle,
|
||||
content = confirmContent,
|
||||
markdown = true
|
||||
)
|
||||
if (result == ConfirmResult.Confirmed) {
|
||||
confirmed = true
|
||||
pendingFlashIt = flashIt
|
||||
} else {
|
||||
// User cancelled, go back
|
||||
navigator.popBackStack()
|
||||
if (finishIntent) activity?.finish()
|
||||
}
|
||||
} else {
|
||||
confirmed = true
|
||||
pendingFlashIt = flashIt
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(confirmed, pendingFlashIt) {
|
||||
if (!confirmed || pendingFlashIt == null || text.isNotEmpty() || hasFlashed) return@LaunchedEffect
|
||||
hasFlashed = true
|
||||
withContext(Dispatchers.IO) {
|
||||
flashIt(flashIt, onFinish = { showReboot, code ->
|
||||
if (code != 0) {
|
||||
text += "Error: exit code = $code.\nPlease save and check the log.\n"
|
||||
}
|
||||
if (showReboot) {
|
||||
text += "\n\n\n"
|
||||
showFloatAction = true
|
||||
}
|
||||
flashing = if (code == 0) FlashingStatus.SUCCESS else FlashingStatus.FAILED
|
||||
}, onStdout = {
|
||||
flashIt(pendingFlashIt!!, onStdout = {
|
||||
tempText = "$it\n"
|
||||
if (tempText.startsWith("[H[J")) { // clear command
|
||||
text = tempText.substring(6)
|
||||
@@ -164,7 +207,16 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
logContent.append(it).append("\n")
|
||||
}, onStderr = {
|
||||
logContent.append(it).append("\n")
|
||||
})
|
||||
}).apply {
|
||||
if (code != 0) {
|
||||
text += "Error code: $code.\n $err Please save and check the log.\n"
|
||||
}
|
||||
if (showReboot) {
|
||||
text += "\n\n\n"
|
||||
showFloatAction = true
|
||||
}
|
||||
flashing = if (code == 0) FlashingStatus.SUCCESS else FlashingStatus.FAILED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,8 +224,9 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
topBar = {
|
||||
TopBar(
|
||||
flashing,
|
||||
onBack = {
|
||||
onBack = dropUnlessResumed {
|
||||
navigator.popBackStack()
|
||||
if (finishIntent) activity?.finish()
|
||||
},
|
||||
onSave = {
|
||||
scope.launch {
|
||||
@@ -181,7 +234,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
val date = format.format(Date())
|
||||
val file = File(
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
||||
"KernelSU_install_log_${date}.log"
|
||||
"KernelSU_Next_install_log_${date}.log"
|
||||
)
|
||||
file.writeText(logContent.toString())
|
||||
snackBarHost.showSnackbar("Log saved to ${file.absolutePath}")
|
||||
@@ -191,8 +244,8 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
if (showFloatAction) {
|
||||
// Reboot button (bottom left)
|
||||
if (flashIt is FlashIt.FlashModules && (flashing == FlashingStatus.SUCCESS)) {
|
||||
// Reboot button for modules flashing
|
||||
ExtendedFloatingActionButton(
|
||||
onClick = {
|
||||
scope.launch {
|
||||
@@ -205,6 +258,58 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
text = { Text(text = stringResource(R.string.reboot)) }
|
||||
)
|
||||
}
|
||||
|
||||
if (flashIt is FlashIt.FlashModules && (flashing == FlashingStatus.FAILED)) {
|
||||
// Close button for modules flashing
|
||||
ExtendedFloatingActionButton(
|
||||
text = { Text(text = stringResource(R.string.close)) },
|
||||
icon = { Icon(Icons.Filled.Close, contentDescription = null) },
|
||||
onClick = {
|
||||
navigator.popBackStack()
|
||||
if (finishIntent) activity?.finish()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (flashIt is FlashIt.FlashBoot && (flashing == FlashingStatus.SUCCESS || flashing == FlashingStatus.FAILED)) {
|
||||
val isLocalPatch = flashIt.boot != null && !flashIt.ota
|
||||
val isDirectOrOta = flashIt.boot == null || flashIt.ota
|
||||
|
||||
if (flashing == FlashingStatus.FAILED) {
|
||||
// Always show close on failure
|
||||
ExtendedFloatingActionButton(
|
||||
text = { Text(text = stringResource(R.string.close)) },
|
||||
icon = { Icon(Icons.Filled.Close, contentDescription = null) },
|
||||
onClick = {
|
||||
navigator.popBackStack()
|
||||
}
|
||||
)
|
||||
} else if (flashing == FlashingStatus.SUCCESS) {
|
||||
if (isLocalPatch) {
|
||||
// Local patching: show only Close
|
||||
ExtendedFloatingActionButton(
|
||||
text = { Text(text = stringResource(R.string.close)) },
|
||||
icon = { Icon(Icons.Filled.Close, contentDescription = null) },
|
||||
onClick = {
|
||||
navigator.popBackStack()
|
||||
}
|
||||
)
|
||||
} else if (isDirectOrOta) {
|
||||
// Direct install or OTA inactive slot: show only Reboot
|
||||
ExtendedFloatingActionButton(
|
||||
onClick = {
|
||||
scope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
reboot()
|
||||
}
|
||||
}
|
||||
},
|
||||
icon = { Icon(Icons.Filled.Refresh, contentDescription = stringResource(R.string.reboot)) },
|
||||
text = { Text(text = stringResource(R.string.reboot)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
contentWindowInsets = WindowInsets.safeDrawing,
|
||||
snackbarHost = { SnackbarHost(hostState = snackBarHost) }
|
||||
@@ -224,7 +329,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.padding(8.dp),
|
||||
text = text,
|
||||
text = if (developerOptionsEnabled) logContent.toString() else text,
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
fontFamily = FontFamily.Monospace,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
@@ -233,13 +338,24 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
}
|
||||
}
|
||||
|
||||
fun Uri.getFileName(context: Context): String {
|
||||
val contentResolver = context.contentResolver
|
||||
val cursor = contentResolver.query(this, null, null, null, null)
|
||||
return cursor?.use {
|
||||
val nameIndex = it.getColumnIndex(android.provider.OpenableColumns.DISPLAY_NAME)
|
||||
if (it.moveToFirst() && nameIndex != -1) {
|
||||
it.getString(nameIndex)
|
||||
} else {
|
||||
this.lastPathSegment ?: "unknown.zip"
|
||||
}
|
||||
} ?: (this.lastPathSegment ?: "unknown.zip")
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
sealed class FlashIt : Parcelable {
|
||||
data class FlashBoot(val boot: Uri? = null, val lkm: LkmSelection, val ota: Boolean) :
|
||||
FlashIt()
|
||||
|
||||
data class FlashModule(val uri: Uri) : FlashIt()
|
||||
|
||||
data class FlashModules(val uris: List<Uri>) : FlashIt()
|
||||
|
||||
data object FlashRestore : FlashIt()
|
||||
@@ -248,29 +364,26 @@ sealed class FlashIt : Parcelable {
|
||||
}
|
||||
|
||||
fun flashIt(
|
||||
flashIt: FlashIt, onFinish: (Boolean, Int) -> Unit,
|
||||
flashIt: FlashIt,
|
||||
onStdout: (String) -> Unit,
|
||||
onStderr: (String) -> Unit
|
||||
) {
|
||||
when (flashIt) {
|
||||
): FlashResult {
|
||||
return when (flashIt) {
|
||||
is FlashIt.FlashBoot -> installBoot(
|
||||
flashIt.boot,
|
||||
flashIt.lkm,
|
||||
flashIt.ota,
|
||||
onFinish,
|
||||
onStdout,
|
||||
onStderr
|
||||
)
|
||||
|
||||
is FlashIt.FlashModule -> flashModule(flashIt.uri, onFinish, onStdout, onStderr)
|
||||
|
||||
is FlashIt.FlashModules -> {
|
||||
flashModulesSequentially(flashIt.uris, onFinish, onStdout, onStderr)
|
||||
flashModulesSequentially(flashIt.uris, onStdout, onStderr)
|
||||
}
|
||||
|
||||
FlashIt.FlashRestore -> restoreBoot(onFinish, onStdout, onStderr)
|
||||
FlashIt.FlashRestore -> restoreBoot(onStdout, onStderr)
|
||||
|
||||
FlashIt.FlashUninstall -> uninstallPermanently(onFinish, onStdout, onStderr)
|
||||
FlashIt.FlashUninstall -> uninstallPermanently(onStdout, onStderr)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,14 @@ import android.os.PowerManager
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.system.Os
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
@@ -29,9 +34,15 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
import androidx.compose.ui.text.toUpperCase
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.pm.PackageInfoCompat
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.dergoogler.mmrl.ui.component.LabelItem
|
||||
import com.dergoogler.mmrl.ui.component.LabelItemDefaults
|
||||
import com.dergoogler.mmrl.ui.component.text.TextRow
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.generated.destinations.InstallScreenDestination
|
||||
@@ -55,6 +66,10 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
val ksuVersion = if (isManager) Natives.version else null
|
||||
|
||||
val context = LocalContext.current
|
||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
val developerOptionsEnabled = prefs.getBoolean("enable_developer_options", false)
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBar(
|
||||
@@ -97,12 +112,12 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
val checkUpdate =
|
||||
LocalContext.current.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
.getBoolean("check_update", true)
|
||||
.getBoolean("check_update", false)
|
||||
if (checkUpdate) {
|
||||
UpdateCard()
|
||||
}
|
||||
//NextCard()
|
||||
InfoCard()
|
||||
InfoCard(autoExpand = developerOptionsEnabled)
|
||||
IssueReportCard()
|
||||
//EXperimentalCard()
|
||||
Spacer(Modifier)
|
||||
@@ -162,6 +177,19 @@ fun RebootDropdownItem(@StringRes id: Int, reason: String = "") {
|
||||
})
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun getSeasonalIcon(): ImageVector {
|
||||
val month = Calendar.getInstance().get(Calendar.MONTH) // 0-11 for January-December
|
||||
return when (month) {
|
||||
Calendar.DECEMBER, Calendar.JANUARY, Calendar.FEBRUARY -> Icons.Filled.AcUnit // Winter
|
||||
Calendar.MARCH, Calendar.APRIL, Calendar.MAY -> Icons.Filled.Spa // Spring
|
||||
Calendar.JUNE, Calendar.JULY, Calendar.AUGUST -> Icons.Filled.WbSunny // Summer
|
||||
Calendar.SEPTEMBER, Calendar.OCTOBER, Calendar.NOVEMBER -> Icons.Filled.Forest // Fall
|
||||
else -> Icons.Filled.Whatshot // Fallback icon
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(
|
||||
@@ -170,15 +198,51 @@ private fun TopBar(
|
||||
onInstallClick: () -> Unit,
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
) {
|
||||
var isSpinning by remember { mutableStateOf(false) }
|
||||
val rotation by animateFloatAsState(
|
||||
targetValue = if (isSpinning) 360f else 0f,
|
||||
animationSpec = tween(durationMillis = 800),
|
||||
finishedListener = {
|
||||
isSpinning = false
|
||||
}
|
||||
)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
isSpinning = true
|
||||
}
|
||||
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.app_name)) },
|
||||
title = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.clickable(
|
||||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) {
|
||||
if (!isSpinning) isSpinning = true
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
imageVector = getSeasonalIcon(),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(end = 8.dp)
|
||||
.graphicsLayer {
|
||||
rotationZ = rotation
|
||||
}
|
||||
)
|
||||
Text(stringResource(R.string.app_name))
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
if (kernelVersion.isGKI()) {
|
||||
IconButton(onClick = onInstallClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Archive,
|
||||
contentDescription = stringResource(id = R.string.install)
|
||||
)
|
||||
if (ksuVersion != null) {
|
||||
if (kernelVersion.isGKI()) {
|
||||
IconButton(onClick = onInstallClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Archive,
|
||||
contentDescription = stringResource(id = R.string.install)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +252,7 @@ private fun TopBar(
|
||||
showDropdown = true
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Refresh,
|
||||
imageVector = Icons.Filled.PowerSettingsNew,
|
||||
contentDescription = stringResource(id = R.string.reboot)
|
||||
)
|
||||
|
||||
@@ -197,7 +261,8 @@ private fun TopBar(
|
||||
}) {
|
||||
RebootDropdownItem(id = R.string.reboot)
|
||||
|
||||
val pm = LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
|
||||
val pm =
|
||||
LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
|
||||
@Suppress("DEPRECATION")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && pm?.isRebootingUserspaceSupported == true) {
|
||||
RebootDropdownItem(id = R.string.reboot_userspace, reason = "userspace")
|
||||
@@ -215,93 +280,141 @@ private fun TopBar(
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun getSeasonalIcon(): ImageVector {
|
||||
val month = Calendar.getInstance().get(Calendar.MONTH) // 0-11 for January-December
|
||||
return when (month) {
|
||||
Calendar.DECEMBER, Calendar.JANUARY, Calendar.FEBRUARY -> Icons.Filled.AcUnit // Winter
|
||||
Calendar.MARCH, Calendar.APRIL, Calendar.MAY -> Icons.Filled.Spa // Spring
|
||||
Calendar.JUNE, Calendar.JULY, Calendar.AUGUST -> Icons.Filled.WbSunny // Summer
|
||||
Calendar.SEPTEMBER, Calendar.OCTOBER, Calendar.NOVEMBER -> Icons.Filled.Forest // Fall
|
||||
else -> Icons.Filled.Whatshot // Fallback icon
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StatusCard(
|
||||
kernelVersion: KernelVersion,
|
||||
ksuVersion: Int?,
|
||||
lkmMode: Boolean?,
|
||||
moduleUpdateCount: Int = 0,
|
||||
onClickInstall: () -> Unit = {}
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
var tapCount by remember { mutableStateOf(0) }
|
||||
|
||||
ElevatedCard(
|
||||
colors = CardDefaults.elevatedCardColors(containerColor = run {
|
||||
if (ksuVersion != null) MaterialTheme.colorScheme.secondaryContainer
|
||||
else MaterialTheme.colorScheme.errorContainer
|
||||
})
|
||||
) {
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
if (kernelVersion.isGKI()) {
|
||||
onClickInstall()
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
tapCount++
|
||||
if (tapCount == 5) {
|
||||
Toast.makeText(context, "What are you doing? 🤔", Toast.LENGTH_SHORT).show()
|
||||
} else if (tapCount == 10) {
|
||||
Toast.makeText(context, "Never gonna give you up! 💜", Toast.LENGTH_SHORT).show()
|
||||
val url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
|
||||
val intent = android.content.Intent(android.content.Intent.ACTION_VIEW, android.net.Uri.parse(url))
|
||||
intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
if (ksuVersion != null) {
|
||||
context.startActivity(intent)
|
||||
} else if (kernelVersion.isGKI()) {
|
||||
onClickInstall()
|
||||
} else {
|
||||
Toast.makeText(context, "Something weird happened... 🤔", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} else if (ksuVersion == null && kernelVersion.isGKI()) {
|
||||
onClickInstall()
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
.padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
when {
|
||||
ksuVersion != null -> {
|
||||
val safeMode = when {
|
||||
Natives.isSafeMode -> " [${stringResource(id = R.string.safe_mode)}]"
|
||||
else -> ""
|
||||
val workingMode = when {
|
||||
lkmMode == true -> "LKM"
|
||||
lkmMode == false || kernelVersion.isGKI() -> "GKI2"
|
||||
lkmMode == null && kernelVersion.isULegacy() -> "U-LEGACY"
|
||||
lkmMode == null && kernelVersion.isLegacy() -> "LEGACY"
|
||||
lkmMode == null && kernelVersion.isGKI1() -> "GKI1"
|
||||
else -> "NON-STANDARD"
|
||||
}
|
||||
|
||||
val workingMode = when (lkmMode) {
|
||||
null -> " <LTS>"
|
||||
true -> " <LKM>"
|
||||
else -> " <GKI>"
|
||||
}
|
||||
|
||||
val workingText =
|
||||
"${stringResource(id = R.string.home_working)}$workingMode$safeMode"
|
||||
|
||||
Icon(
|
||||
getSeasonalIcon(), // Use dynamic seasonal icon
|
||||
imageVector = Icons.Filled.CheckCircle,
|
||||
contentDescription = stringResource(R.string.home_working)
|
||||
)
|
||||
Column(Modifier.padding(start = 20.dp)) {
|
||||
Text(
|
||||
text = workingText,
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Column(
|
||||
modifier = Modifier.padding(start = 20.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
val labelStyle = LabelItemDefaults.style
|
||||
TextRow(
|
||||
trailingContent = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp)
|
||||
) {
|
||||
LabelItem(
|
||||
icon = if (Natives.isSafeMode) {
|
||||
{
|
||||
Icon(
|
||||
tint = labelStyle.contentColor,
|
||||
imageVector = Icons.Filled.Security,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
text = workingMode,
|
||||
style = labelStyle.textStyle.copy(color = labelStyle.contentColor),
|
||||
)
|
||||
}
|
||||
)
|
||||
if (isSuCompatDisabled()) {
|
||||
LabelItem(
|
||||
icon = {
|
||||
Icon(
|
||||
tint = labelStyle.contentColor,
|
||||
imageVector = Icons.Filled.Warning,
|
||||
contentDescription = null
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(R.string.sucompat_disabled),
|
||||
style = labelStyle.textStyle.copy(
|
||||
color = labelStyle.contentColor,
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.home_working),
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.home_working_version, ksuVersion),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(
|
||||
R.string.home_superuser_count, getSuperuserCount()
|
||||
), style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.home_module_count, getModuleCount()),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
val suSFS = getSuSFS()
|
||||
if (suSFS == "Supported") {
|
||||
Text(
|
||||
text = stringResource(R.string.home_susfs, getSuSFS()),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kernelVersion.isGKI() -> {
|
||||
Icon(Icons.Filled.AutoFixHigh, stringResource(R.string.home_not_installed))
|
||||
Icon(Icons.Filled.NewReleases, stringResource(R.string.home_not_installed))
|
||||
Column(Modifier.padding(start = 20.dp)) {
|
||||
Text(
|
||||
text = stringResource(R.string.home_not_installed),
|
||||
@@ -316,7 +429,7 @@ private fun StatusCard(
|
||||
}
|
||||
|
||||
else -> {
|
||||
Icon(Icons.Filled.Dangerous, stringResource(R.string.home_failure))
|
||||
Icon(Icons.Filled.Cancel, stringResource(R.string.home_failure))
|
||||
Column(Modifier.padding(start = 20.dp)) {
|
||||
Text(
|
||||
text = stringResource(R.string.home_failure),
|
||||
@@ -357,21 +470,21 @@ fun WarningCard(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InfoCard() {
|
||||
private fun InfoCard(autoExpand: Boolean = false) {
|
||||
val context = LocalContext.current
|
||||
|
||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
|
||||
var useOverlayFs by rememberSaveable {
|
||||
mutableStateOf(prefs.getBoolean("use_overlay_fs", false))
|
||||
}
|
||||
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
val ksuVersion = if (isManager) Natives.version else null
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
useOverlayFs = prefs.getBoolean("use_overlay_fs", false)
|
||||
}
|
||||
var expanded by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(autoExpand) {
|
||||
if (autoExpand) {
|
||||
expanded = true
|
||||
}
|
||||
}
|
||||
|
||||
ElevatedCard {
|
||||
Column(
|
||||
@@ -379,9 +492,6 @@ private fun InfoCard() {
|
||||
.fillMaxWidth()
|
||||
.padding(start = 24.dp, top = 24.dp, end = 24.dp, bottom = 16.dp)
|
||||
) {
|
||||
val contents = StringBuilder()
|
||||
val uname = Os.uname()
|
||||
|
||||
@Composable
|
||||
fun InfoCardItem(label: String, content: String, icon: Any? = null) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
@@ -411,59 +521,103 @@ private fun InfoCard() {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
InfoCardItem(
|
||||
label = stringResource(R.string.home_kernel),
|
||||
content = uname.release,
|
||||
icon = painterResource(R.drawable.ic_linux),
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(
|
||||
label = stringResource(R.string.home_android),
|
||||
content = "${Build.VERSION.RELEASE} (${Build.VERSION.SDK_INT})",
|
||||
icon = Icons.Filled.Android,
|
||||
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
val managerVersion = getManagerVersion(context)
|
||||
InfoCardItem(
|
||||
label = stringResource(R.string.home_manager_version),
|
||||
content = "${managerVersion.first} (${managerVersion.second})",
|
||||
icon = painterResource(R.drawable.ic_ksu_next),
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(
|
||||
label = stringResource(R.string.home_selinux_status),
|
||||
content = getSELinuxStatus(),
|
||||
icon = Icons.Filled.Security,
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(
|
||||
label = stringResource(R.string.home_module_mount),
|
||||
content = when {
|
||||
ksuVersion == null -> stringResource(R.string.unavailable)
|
||||
useOverlayFs -> stringResource(R.string.home_overlayfs_mount)
|
||||
else -> stringResource(R.string.home_magic_mount)
|
||||
},
|
||||
icon = Icons.Filled.SettingsSuggest,
|
||||
)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
val isSUS_SU = getSuSFSFeatures() == "CONFIG_KSU_SUSFS_SUS_SU"
|
||||
val suSFS = getSuSFS()
|
||||
if (suSFS == "Supported") {
|
||||
val susSUMode = if (isSUS_SU) "| SuS SU mode: ${susfsSUS_SU_Mode()}" else ""
|
||||
Column {
|
||||
val managerVersion = getManagerVersion(context)
|
||||
InfoCardItem(
|
||||
label = stringResource(R.string.home_susfs_version),
|
||||
content = "${getSuSFSVersion()} (${getSuSFSVariant()}) $susSUMode",
|
||||
icon = painterResource(R.drawable.ic_sus),
|
||||
label = stringResource(R.string.home_manager_version),
|
||||
content = "${managerVersion.first} (${managerVersion.second})",
|
||||
icon = painterResource(R.drawable.ic_ksu_next),
|
||||
)
|
||||
|
||||
if (Natives.version >= Natives.MINIMAL_SUPPORTED_HOOK_MODE) {
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(
|
||||
label = stringResource(R.string.hook_mode),
|
||||
content = Natives.getHookMode() ?: stringResource(R.string.unavailable),
|
||||
icon = Icons.Filled.Phishing,
|
||||
)
|
||||
}
|
||||
|
||||
if (ksuVersion != null) {
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(
|
||||
label = stringResource(R.string.home_mount_system),
|
||||
content = currentMountSystem().ifEmpty { stringResource(R.string.unavailable) },
|
||||
icon = Icons.Filled.SettingsSuggest,
|
||||
)
|
||||
|
||||
|
||||
val suSFS = getSuSFS()
|
||||
if (suSFS == "Supported") {
|
||||
val isSUS_SU = getSuSFSFeatures() == "CONFIG_KSU_SUSFS_SUS_SU"
|
||||
val susSUMode = if (isSUS_SU) {
|
||||
val mode = susfsSUS_SU_Mode()
|
||||
val modeString =
|
||||
if (mode == "2") stringResource(R.string.enabled) else stringResource(R.string.disabled)
|
||||
"| SuS SU: $modeString"
|
||||
} else ""
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(
|
||||
label = stringResource(R.string.home_susfs_version),
|
||||
content = "${stringResource(R.string.susfs_supported)} | ${getSuSFSVersion()} (${getSuSFSVariant()}) $susSUMode",
|
||||
icon = painterResource(R.drawable.ic_sus),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (!expanded) {
|
||||
Spacer(Modifier.height(12.dp))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
IconButton(
|
||||
onClick = { expanded = true },
|
||||
modifier = Modifier.size(36.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.KeyboardArrowDown,
|
||||
contentDescription = "Show more"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AnimatedVisibility(visible = expanded) {
|
||||
val uname = Os.uname()
|
||||
Column {
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(
|
||||
label = stringResource(R.string.home_kernel),
|
||||
content = "${uname.release} (${uname.machine})",
|
||||
icon = painterResource(R.drawable.ic_linux),
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(
|
||||
label = stringResource(R.string.home_android),
|
||||
content = "${Build.VERSION.RELEASE} (${Build.VERSION.SDK_INT})",
|
||||
icon = Icons.Filled.Android,
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(
|
||||
label = stringResource(R.string.home_abi),
|
||||
content = Build.SUPPORTED_ABIS.joinToString(", "),
|
||||
icon = Icons.Filled.Memory,
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(
|
||||
label = stringResource(R.string.home_selinux_status),
|
||||
content = getSELinuxStatus(),
|
||||
icon = Icons.Filled.Security,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -476,12 +630,13 @@ fun NextCard() {
|
||||
|
||||
ElevatedCard {
|
||||
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
uriHandler.openUri(url)
|
||||
}
|
||||
.padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
uriHandler.openUri(url)
|
||||
}
|
||||
.padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(R.string.home_next_kernelsu),
|
||||
@@ -505,13 +660,15 @@ fun EXperimentalCard() {
|
||||
|
||||
ElevatedCard {
|
||||
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
/*.clickable {
|
||||
uriHandler.openUri(url)
|
||||
}
|
||||
*/
|
||||
.padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
/*.clickable {
|
||||
uriHandler.openUri(url)
|
||||
}
|
||||
*/
|
||||
.padding(24.dp), verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(R.string.home_experimental_kernelsu),
|
||||
|
||||
@@ -22,6 +22,7 @@ import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.FileUpload
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
@@ -30,6 +31,7 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
@@ -39,6 +41,7 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -47,6 +50,7 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.dropUnlessResumed
|
||||
import com.maxkeppeker.sheets.core.models.base.Header
|
||||
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
|
||||
import com.maxkeppeler.sheets.list.ListDialog
|
||||
@@ -76,6 +80,32 @@ import com.rifsxd.ksunext.ui.util.rootAvailable
|
||||
@Destination<RootGraph>
|
||||
@Composable
|
||||
fun InstallScreen(navigator: DestinationsNavigator) {
|
||||
var showLkmWarning by rememberSaveable { mutableStateOf(true) }
|
||||
|
||||
if (showLkmWarning) {
|
||||
AlertDialog(
|
||||
onDismissRequest = {
|
||||
showLkmWarning = false
|
||||
navigator.popBackStack()
|
||||
},
|
||||
title = { Text(stringResource(R.string.warning)) },
|
||||
text = { Text(stringResource(R.string.lkm_warning_message)) },
|
||||
confirmButton = {
|
||||
TextButton(onClick = { showLkmWarning = false }) {
|
||||
Text(stringResource(R.string.proceed))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = {
|
||||
showLkmWarning = false
|
||||
navigator.popBackStack()
|
||||
}) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
var installMethod by remember {
|
||||
mutableStateOf<InstallMethod?>(null)
|
||||
}
|
||||
@@ -133,7 +163,7 @@ fun InstallScreen(navigator: DestinationsNavigator) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBar(
|
||||
onBack = { navigator.popBackStack() },
|
||||
onBack = dropUnlessResumed { navigator.popBackStack() },
|
||||
onLkmUpload = onLkmUpload,
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
@@ -204,7 +234,7 @@ private fun SelectInstallMethod(onSelected: (InstallMethod) -> Unit = {}) {
|
||||
val rootAvailable = rootAvailable()
|
||||
val isAbDevice = isAbDevice()
|
||||
val selectFileTip = stringResource(
|
||||
id = R.string.select_file_tip, if (isInitBoot()) "init_boot" else "boot"
|
||||
id = R.string.select_file_tip, if (isInitBoot()) "init_boot/vendor_boot" else "boot"
|
||||
)
|
||||
val radioOptions =
|
||||
mutableListOf<InstallMethod>(InstallMethod.SelectFile(summary = selectFileTip))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@ import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -63,6 +64,9 @@ import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.generated.destinations.AppProfileTemplateScreenDestination
|
||||
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
|
||||
import com.ramcosta.composedestinations.generated.destinations.BackupRestoreScreenDestination
|
||||
import com.ramcosta.composedestinations.generated.destinations.CustomizationScreenDestination
|
||||
import com.ramcosta.composedestinations.generated.destinations.DeveloperScreenDestination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -113,8 +117,6 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
val loadingDialog = rememberLoadingDialog()
|
||||
val shrinkDialog = rememberConfirmDialog()
|
||||
val restoreDialog = rememberConfirmDialog()
|
||||
val backupDialog = rememberConfirmDialog()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@@ -159,10 +161,11 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
if (ksuVersion != null) {
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.RemoveModerator,
|
||||
icon = Icons.Filled.FolderDelete,
|
||||
title = stringResource(id = R.string.settings_umount_modules_default),
|
||||
summary = stringResource(id = R.string.settings_umount_modules_default_summary),
|
||||
checked = umountChecked
|
||||
|
||||
) {
|
||||
if (Natives.setDefaultUmountModules(it)) {
|
||||
umountChecked = it
|
||||
@@ -170,6 +173,25 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
}
|
||||
|
||||
if (ksuVersion != null) {
|
||||
if (Natives.version >= Natives.MINIMAL_SUPPORTED_SU_COMPAT) {
|
||||
var isSuDisabled by rememberSaveable {
|
||||
mutableStateOf(!Natives.isSuEnabled())
|
||||
}
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.RemoveModerator,
|
||||
title = stringResource(id = R.string.settings_disable_su),
|
||||
summary = stringResource(id = R.string.settings_disable_su_summary),
|
||||
checked = isSuDisabled
|
||||
) { checked ->
|
||||
val shouldEnable = !checked
|
||||
if (Natives.setSuEnabled(shouldEnable)) {
|
||||
isSuDisabled = !shouldEnable
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
|
||||
val suSFS = getSuSFS()
|
||||
@@ -201,62 +223,39 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
}
|
||||
|
||||
val hasShownWarning = rememberSaveable { mutableStateOf(prefs.getBoolean("has_shown_warning", false)) }
|
||||
|
||||
var useOverlayFs by rememberSaveable {
|
||||
mutableStateOf(
|
||||
prefs.getBoolean("use_overlay_fs", false)
|
||||
)
|
||||
mutableStateOf(readMountSystemFile())
|
||||
}
|
||||
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
LaunchedEffect(Unit) {
|
||||
useOverlayFs = readMountSystemFile()
|
||||
}
|
||||
|
||||
var showRebootDialog by remember { mutableStateOf(false) }
|
||||
|
||||
var showWarningDialog by remember { mutableStateOf(false) }
|
||||
val isOverlayAvailable = overlayFsAvailable()
|
||||
|
||||
if (ksuVersion != null) {
|
||||
if (ksuVersion != null && isOverlayAvailable) {
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.Build,
|
||||
title = stringResource(id = R.string.use_overlay_fs),
|
||||
summary = stringResource(id = R.string.use_overlay_fs_summary),
|
||||
checked = useOverlayFs
|
||||
) {
|
||||
if (!hasShownWarning.value) {
|
||||
showWarningDialog = true
|
||||
prefs.edit().putBoolean("use_overlay_fs", it).apply()
|
||||
useOverlayFs = it
|
||||
if (useOverlayFs) {
|
||||
moduleBackup()
|
||||
updateMountSystemFile(true)
|
||||
} else {
|
||||
moduleMigration()
|
||||
updateMountSystemFile(false)
|
||||
}
|
||||
if (isManager) install()
|
||||
showRebootDialog = true
|
||||
}
|
||||
}
|
||||
|
||||
if (showWarningDialog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showWarningDialog = false },
|
||||
title = { Text(stringResource(R.string.warning)) },
|
||||
text = { Text(stringResource(R.string.warning_message)) },
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
showWarningDialog = false
|
||||
prefs.edit().putBoolean("use_overlay_fs", !useOverlayFs).apply()
|
||||
useOverlayFs = !useOverlayFs
|
||||
if (useOverlayFs) {
|
||||
moduleBackup()
|
||||
} else {
|
||||
moduleMigration()
|
||||
}
|
||||
if (isManager) install()
|
||||
showRebootDialog = true
|
||||
}) {
|
||||
Text(stringResource(R.string.proceed))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { showWarningDialog = false }) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (showRebootDialog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showRebootDialog = false },
|
||||
@@ -281,7 +280,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
|
||||
var checkUpdate by rememberSaveable {
|
||||
mutableStateOf(
|
||||
prefs.getBoolean("check_update", true)
|
||||
prefs.getBoolean("check_update", false)
|
||||
)
|
||||
}
|
||||
SwitchItem(
|
||||
@@ -294,19 +293,81 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
checkUpdate = it
|
||||
}
|
||||
|
||||
var enableWebDebugging by rememberSaveable {
|
||||
mutableStateOf(
|
||||
prefs.getBoolean("enable_web_debugging", false)
|
||||
if (isOverlayAvailable && useOverlayFs) {
|
||||
val shrink = stringResource(id = R.string.shrink_sparse_image)
|
||||
val shrinkMessage = stringResource(id = R.string.shrink_sparse_image_message)
|
||||
ListItem(
|
||||
leadingContent = {
|
||||
Icon(
|
||||
Icons.Filled.Compress,
|
||||
shrink
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(shrink) },
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
val result = shrinkDialog.awaitConfirm(title = shrink, content = shrinkMessage)
|
||||
if (result == ConfirmResult.Confirmed) {
|
||||
loadingDialog.withLoading {
|
||||
shrinkModules()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.DeveloperMode,
|
||||
title = stringResource(id = R.string.enable_web_debugging),
|
||||
summary = stringResource(id = R.string.enable_web_debugging_summary),
|
||||
checked = enableWebDebugging
|
||||
) {
|
||||
prefs.edit().putBoolean("enable_web_debugging", it).apply()
|
||||
enableWebDebugging = it
|
||||
|
||||
val customization = stringResource(id = R.string.customization)
|
||||
ListItem(
|
||||
leadingContent = {
|
||||
Icon(
|
||||
Icons.Filled.Palette,
|
||||
customization
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(customization) },
|
||||
modifier = Modifier.clickable {
|
||||
navigator.navigate(CustomizationScreenDestination)
|
||||
}
|
||||
)
|
||||
|
||||
if (ksuVersion != null) {
|
||||
val backupRestore = stringResource(id = R.string.backup_restore)
|
||||
ListItem(
|
||||
leadingContent = {
|
||||
Icon(
|
||||
Icons.Filled.Backup,
|
||||
backupRestore
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(backupRestore) },
|
||||
modifier = Modifier.clickable {
|
||||
navigator.navigate(BackupRestoreScreenDestination)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val developer = stringResource(id = R.string.developer)
|
||||
if (ksuVersion != null) {
|
||||
ListItem(
|
||||
leadingContent = {
|
||||
Icon(
|
||||
Icons.Filled.DeveloperBoard,
|
||||
developer
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(developer) },
|
||||
modifier = Modifier.clickable {
|
||||
navigator.navigate(DeveloperScreenDestination)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val lkmMode = Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode
|
||||
if (lkmMode) {
|
||||
UninstallItem(navigator) {
|
||||
loadingDialog.withLoading(it)
|
||||
}
|
||||
}
|
||||
|
||||
var showBottomsheet by remember { mutableStateOf(false) }
|
||||
@@ -340,7 +401,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
.clickable {
|
||||
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH_mm")
|
||||
val current = LocalDateTime.now().format(formatter)
|
||||
exportBugreportLauncher.launch("KernelSU_bugreport_${current}.tar.gz")
|
||||
exportBugreportLauncher.launch("KernelSU_Next_bugreport_${current}.tar.gz")
|
||||
showBottomsheet = false
|
||||
}
|
||||
) {
|
||||
@@ -418,87 +479,6 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
)
|
||||
}
|
||||
|
||||
if (ksuVersion != null) {
|
||||
val moduleBackup = stringResource(id = R.string.module_backup)
|
||||
val backupMessage = stringResource(id = R.string.module_backup_message)
|
||||
ListItem(
|
||||
leadingContent = {
|
||||
Icon(
|
||||
Icons.Filled.Backup,
|
||||
moduleBackup
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(moduleBackup) },
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
val result = backupDialog.awaitConfirm(title = moduleBackup, content = backupMessage)
|
||||
if (result == ConfirmResult.Confirmed) {
|
||||
loadingDialog.withLoading {
|
||||
moduleBackup()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (ksuVersion != null) {
|
||||
val moduleRestore = stringResource(id = R.string.module_restore)
|
||||
val restoreMessage = stringResource(id = R.string.module_restore_message)
|
||||
ListItem(
|
||||
leadingContent = {
|
||||
Icon(
|
||||
Icons.Filled.Restore,
|
||||
moduleRestore
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(moduleRestore) },
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
val result = restoreDialog.awaitConfirm(title = moduleRestore, content = restoreMessage)
|
||||
if (result == ConfirmResult.Confirmed) {
|
||||
loadingDialog.withLoading {
|
||||
moduleRestore()
|
||||
showRebootDialog = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (useOverlayFs) {
|
||||
val shrink = stringResource(id = R.string.shrink_sparse_image)
|
||||
val shrinkMessage = stringResource(id = R.string.shrink_sparse_image_message)
|
||||
ListItem(
|
||||
leadingContent = {
|
||||
Icon(
|
||||
Icons.Filled.Compress,
|
||||
shrink
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(shrink) },
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
val result = shrinkDialog.awaitConfirm(title = shrink, content = shrinkMessage)
|
||||
if (result == ConfirmResult.Confirmed) {
|
||||
loadingDialog.withLoading {
|
||||
shrinkModules()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
val lkmMode = Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode
|
||||
if (lkmMode) {
|
||||
UninstallItem(navigator) {
|
||||
loadingDialog.withLoading(it)
|
||||
}
|
||||
}
|
||||
|
||||
val about = stringResource(id = R.string.about)
|
||||
ListItem(
|
||||
leadingContent = {
|
||||
@@ -519,7 +499,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
@Composable
|
||||
fun UninstallItem(
|
||||
navigator: DestinationsNavigator,
|
||||
withLoading: suspend (suspend () -> Unit) -> Unit
|
||||
withLoading: suspend (suspend () -> Unit) -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
@@ -621,7 +601,7 @@ fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle {
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null,
|
||||
) {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.settings)) },
|
||||
@@ -634,4 +614,4 @@ private fun TopBar(
|
||||
@Composable
|
||||
private fun SettingsPreview() {
|
||||
SettingScreen(EmptyDestinationsNavigator)
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
@@ -24,6 +25,8 @@ import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.dergoogler.mmrl.ui.component.LabelItem
|
||||
import com.dergoogler.mmrl.ui.component.LabelItemDefaults
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.generated.destinations.AppProfileScreenDestination
|
||||
@@ -43,26 +46,13 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
LaunchedEffect(key1 = navigator) {
|
||||
LaunchedEffect(navigator) {
|
||||
viewModel.search = ""
|
||||
if (viewModel.appList.isEmpty()) {
|
||||
viewModel.fetchAppList()
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(viewModel.search) {
|
||||
if (viewModel.search.isEmpty()) {
|
||||
listState.scrollToItem(0)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (viewModel.refreshOnReturn) {
|
||||
viewModel.fetchAppList()
|
||||
viewModel.refreshOnReturn = false
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
SearchAppBar(
|
||||
@@ -101,7 +91,7 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
)
|
||||
}, onClick = {
|
||||
viewModel.showSystemApps = !viewModel.showSystemApps
|
||||
viewModel.updateShowSystemApps(!viewModel.showSystemApps)
|
||||
showDropdown = false
|
||||
})
|
||||
}
|
||||
@@ -127,7 +117,6 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
|
||||
) {
|
||||
items(viewModel.appList, key = { it.packageName + it.uid }) { app ->
|
||||
AppItem(app) {
|
||||
viewModel.refreshOnReturn = true
|
||||
navigator.navigate(AppProfileScreenDestination(app))
|
||||
}
|
||||
}
|
||||
@@ -148,16 +137,43 @@ private fun AppItem(
|
||||
supportingContent = {
|
||||
Column {
|
||||
Text(app.packageName)
|
||||
FlowRow {
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
if (app.allowSu) {
|
||||
LabelText(label = "ROOT")
|
||||
LabelItem(
|
||||
text = "ROOT",
|
||||
)
|
||||
} else {
|
||||
if (Natives.uidShouldUmount(app.uid)) {
|
||||
LabelText(label = "UMOUNT")
|
||||
LabelItem(
|
||||
text = "UMOUNT",
|
||||
style = LabelItemDefaults.style.copy(
|
||||
containerColor = MaterialTheme.colorScheme.secondaryContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (app.hasCustomProfile) {
|
||||
LabelText(label = "CUSTOM")
|
||||
LabelItem(
|
||||
text = "CUSTOM",
|
||||
style = LabelItemDefaults.style.copy(
|
||||
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
|
||||
)
|
||||
)
|
||||
} else if (!app.allowSu && !Natives.uidShouldUmount(app.uid)) {
|
||||
LabelItem(
|
||||
text = "DEFAULT",
|
||||
style = LabelItemDefaults.style.copy(
|
||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.dropUnlessResumed
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
@@ -100,7 +101,7 @@ fun AppProfileTemplateScreen(
|
||||
}
|
||||
}
|
||||
TopBar(
|
||||
onBack = { navigator.popBackStack() },
|
||||
onBack = dropUnlessResumed { navigator.popBackStack() },
|
||||
onSync = {
|
||||
scope.launch { viewModel.fetchTemplates(true) }
|
||||
},
|
||||
|
||||
@@ -44,6 +44,7 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.lifecycle.compose.dropUnlessResumed
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.result.ResultBackNavigator
|
||||
@@ -105,7 +106,7 @@ fun TemplateEditorScreen(
|
||||
},
|
||||
readOnly = readOnly,
|
||||
summary = titleSummary,
|
||||
onBack = { navigator.navigateBack(result = !readOnly) },
|
||||
onBack = dropUnlessResumed { navigator.navigateBack(result = !readOnly) },
|
||||
onDelete = {
|
||||
if (deleteAppProfileTemplate(template.id)) {
|
||||
navigator.navigateBack(result = true)
|
||||
|
||||
@@ -2,9 +2,14 @@ package com.rifsxd.ksunext.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val YELLOW = Color(0xFFeed502)
|
||||
val YELLOW_LIGHT = Color(0xFFffff52)
|
||||
val SECONDARY_LIGHT = Color(0xffa9817f)
|
||||
val PRIMARY = Color(0xFF8AADF4) // Catppuccin Blue
|
||||
val PRIMARY_LIGHT = Color(0xFFB7BDF8) // Catppuccin Lavender
|
||||
val SECONDARY_LIGHT = Color(0xFFA6DA95) // Catppuccin Green
|
||||
|
||||
val YELLOW_DARK = Color(0xFFb7a400)
|
||||
val SECONDARY_DARK = Color(0xFF4c2b2b)
|
||||
val PRIMARY_DARK = Color(0xFF7DC4E4) // Catppuccin Sky
|
||||
val SECONDARY_DARK = Color(0xFFF5BDE6) // Catppuccin Pink
|
||||
|
||||
val AMOLED_BLACK = Color(0xFF000000) // Pure black for AMOLED
|
||||
|
||||
val DARK_PURPLE = Color(0xFF6E6CB6) // Catppuccin Mauve (dark purple)
|
||||
val DARK_GREY = Color(0xFF363A4F) // Catppuccin Surface (dark grey)
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.rifsxd.ksunext.ui.theme
|
||||
|
||||
import android.os.Build
|
||||
import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
@@ -8,39 +11,112 @@ import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = YELLOW,
|
||||
secondary = YELLOW_DARK,
|
||||
primary = PRIMARY,
|
||||
secondary = PRIMARY_DARK,
|
||||
tertiary = SECONDARY_DARK
|
||||
)
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = YELLOW,
|
||||
secondary = YELLOW_LIGHT,
|
||||
primary = PRIMARY,
|
||||
secondary = PRIMARY_LIGHT,
|
||||
tertiary = SECONDARY_LIGHT
|
||||
)
|
||||
|
||||
fun Color.blend(other: Color, ratio: Float): Color {
|
||||
val inverse = 1f - ratio
|
||||
return Color(
|
||||
red = red * inverse + other.red * ratio,
|
||||
green = green * inverse + other.green * ratio,
|
||||
blue = blue * inverse + other.blue * ratio,
|
||||
alpha = alpha
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun KernelSUTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = true,
|
||||
amoledMode: Boolean = false,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
amoledMode && darkTheme && dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
val dynamicScheme = dynamicDarkColorScheme(context)
|
||||
dynamicScheme.copy(
|
||||
background = AMOLED_BLACK,
|
||||
surface = AMOLED_BLACK,
|
||||
surfaceVariant = dynamicScheme.surfaceVariant.blend(AMOLED_BLACK, 0.6f),
|
||||
surfaceContainer = dynamicScheme.surfaceContainer.blend(AMOLED_BLACK, 0.6f),
|
||||
surfaceContainerLow = dynamicScheme.surfaceContainerLow.blend(AMOLED_BLACK, 0.6f),
|
||||
surfaceContainerLowest = dynamicScheme.surfaceContainerLowest.blend(AMOLED_BLACK, 0.6f),
|
||||
surfaceContainerHigh = dynamicScheme.surfaceContainerHigh.blend(AMOLED_BLACK, 0.6f),
|
||||
surfaceContainerHighest = dynamicScheme.surfaceContainerHighest.blend(AMOLED_BLACK, 0.6f),
|
||||
)
|
||||
}
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
amoledMode && darkTheme -> {
|
||||
DarkColorScheme.copy(
|
||||
background = AMOLED_BLACK,
|
||||
surface = AMOLED_BLACK,
|
||||
surfaceVariant = DARK_GREY.blend(AMOLED_BLACK, 0.8f),
|
||||
surfaceContainer = DARK_GREY.blend(AMOLED_BLACK, 0.8f),
|
||||
surfaceContainerLow = DARK_GREY.blend(AMOLED_BLACK, 0.8f),
|
||||
surfaceContainerLowest = DARK_GREY.blend(AMOLED_BLACK, 0.8f),
|
||||
surfaceContainerHigh = DARK_GREY.blend(AMOLED_BLACK, 0.8f),
|
||||
surfaceContainerHighest = DARK_GREY.blend(AMOLED_BLACK, 0.8f),
|
||||
)
|
||||
}
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
}
|
||||
|
||||
SystemBarStyle(
|
||||
darkMode = darkTheme
|
||||
)
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SystemBarStyle(
|
||||
darkMode: Boolean,
|
||||
statusBarScrim: Color = Color.Transparent,
|
||||
navigationBarScrim: Color = Color.Transparent,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val activity = context as ComponentActivity
|
||||
|
||||
SideEffect {
|
||||
activity.enableEdgeToEdge(
|
||||
statusBarStyle = SystemBarStyle.auto(
|
||||
statusBarScrim.toArgb(),
|
||||
statusBarScrim.toArgb(),
|
||||
) { darkMode },
|
||||
navigationBarStyle = when {
|
||||
darkMode -> SystemBarStyle.dark(
|
||||
navigationBarScrim.toArgb()
|
||||
)
|
||||
|
||||
else -> SystemBarStyle.light(
|
||||
navigationBarScrim.toArgb(),
|
||||
navigationBarScrim.toArgb(),
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import android.os.Environment
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.rifsxd.ksunext.ksuApp
|
||||
import com.rifsxd.ksunext.ui.util.module.LatestVersionInfo
|
||||
|
||||
/**
|
||||
@@ -63,11 +64,11 @@ fun download(
|
||||
|
||||
fun checkNewVersion(): LatestVersionInfo {
|
||||
// Next version updates
|
||||
val url = "https://api.github.com/repos/rifsxd/KernelSU-Next/releases/latest"
|
||||
val url = "https://api.github.com/repos/KernelSU-Next/KernelSU-Next/releases/latest"
|
||||
// default null value if failed
|
||||
val defaultValue = LatestVersionInfo()
|
||||
runCatching {
|
||||
okhttp3.OkHttpClient().newCall(okhttp3.Request.Builder().url(url).build()).execute()
|
||||
ksuApp.okhttpClient.newCall(okhttp3.Request.Builder().url(url).build()).execute()
|
||||
.use { response ->
|
||||
if (!response.isSuccessful) {
|
||||
return defaultValue
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.io.File
|
||||
* @date 2023/1/1.
|
||||
*/
|
||||
private const val TAG = "KsuCli"
|
||||
private const val BUSYBOX = "/data/adb/ksu/bin/busybox"
|
||||
|
||||
private fun ksuDaemonMagicPath(): String {
|
||||
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libksud_magic.so"
|
||||
@@ -37,10 +38,16 @@ private fun ksuDaemonOverlayfsPath(): String {
|
||||
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libksud_overlayfs.so"
|
||||
}
|
||||
|
||||
fun readMountSystemFile(): Boolean {
|
||||
val shell = getRootShell()
|
||||
val filePath = "/data/adb/ksu/mount_system"
|
||||
val result = ShellUtils.fastCmd(shell, "cat $filePath").trim()
|
||||
return result == "OVERLAYFS"
|
||||
}
|
||||
|
||||
// Get the path based on the user's choice
|
||||
fun getKsuDaemonPath(): String {
|
||||
val prefs = ksuApp.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
val useOverlayFs = prefs.getBoolean("use_overlay_fs", false)
|
||||
val useOverlayFs = readMountSystemFile()
|
||||
|
||||
return if (useOverlayFs) {
|
||||
ksuDaemonOverlayfsPath()
|
||||
@@ -49,6 +56,21 @@ fun getKsuDaemonPath(): String {
|
||||
}
|
||||
}
|
||||
|
||||
fun updateMountSystemFile(useOverlayFs: Boolean) {
|
||||
val shell = getRootShell()
|
||||
val filePath = "/data/adb/ksu/mount_system"
|
||||
if (useOverlayFs) {
|
||||
ShellUtils.fastCmd(shell, "echo -n OVERLAYFS > $filePath")
|
||||
} else {
|
||||
ShellUtils.fastCmd(shell, "echo -n MAGIC_MOUNT > $filePath")
|
||||
}
|
||||
}
|
||||
|
||||
data class FlashResult(val code: Int, val err: String, val showReboot: Boolean) {
|
||||
constructor(result: Shell.Result, showReboot: Boolean) : this(result.code, result.err.joinToString("\n"), showReboot)
|
||||
constructor(result: Shell.Result) : this(result, result.isSuccess)
|
||||
}
|
||||
|
||||
object KsuCli {
|
||||
val SHELL: Shell = createRootShell()
|
||||
val GLOBAL_MNT_SHELL: Shell = createRootShell(true)
|
||||
@@ -84,17 +106,17 @@ fun createRootShell(globalMnt: Boolean = false): Shell {
|
||||
val builder = Shell.Builder.create()
|
||||
return try {
|
||||
if (globalMnt) {
|
||||
builder.build(getKsuDaemonPath(), "debug", "su", "-g")
|
||||
builder.build(ksuDaemonMagicPath(), "debug", "su", "-g")
|
||||
} else {
|
||||
builder.build(getKsuDaemonPath(), "debug", "su")
|
||||
builder.build(ksuDaemonMagicPath(), "debug", "su")
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.w(TAG, "ksu failed: ", e)
|
||||
try {
|
||||
if (globalMnt) {
|
||||
builder.build("su")
|
||||
} else {
|
||||
builder.build("su", "-mm")
|
||||
} else {
|
||||
builder.build("su")
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, "su failed: ", e)
|
||||
@@ -190,10 +212,9 @@ private fun flashWithIO(
|
||||
|
||||
fun flashModule(
|
||||
uri: Uri,
|
||||
onFinish: (Boolean, Int) -> Unit,
|
||||
onStdout: (String) -> Unit,
|
||||
onStderr: (String) -> Unit
|
||||
): Boolean {
|
||||
): FlashResult {
|
||||
val resolver = ksuApp.contentResolver
|
||||
with(resolver.openInputStream(uri)) {
|
||||
val file = File(ksuApp.cacheDir, "module.zip")
|
||||
@@ -206,15 +227,14 @@ fun flashModule(
|
||||
|
||||
file.delete()
|
||||
|
||||
onFinish(result.isSuccess, result.code)
|
||||
return result.isSuccess
|
||||
return FlashResult(result)
|
||||
}
|
||||
}
|
||||
|
||||
fun runModuleAction(
|
||||
moduleId: String, onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
||||
): Boolean {
|
||||
val shell = getRootShell()
|
||||
val shell = createRootShell(true)
|
||||
|
||||
val stdoutCallback: CallbackList<String?> = object : CallbackList<String?>() {
|
||||
override fun onAddElement(s: String?) {
|
||||
@@ -236,21 +256,19 @@ fun runModuleAction(
|
||||
}
|
||||
|
||||
fun restoreBoot(
|
||||
onFinish: (Boolean, Int) -> Unit, onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
||||
): Boolean {
|
||||
onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
||||
): FlashResult {
|
||||
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so")
|
||||
val result = flashWithIO("${getKsuDaemonPath()} boot-restore -f --magiskboot $magiskboot", onStdout, onStderr)
|
||||
onFinish(result.isSuccess, result.code)
|
||||
return result.isSuccess
|
||||
return FlashResult(result)
|
||||
}
|
||||
|
||||
fun uninstallPermanently(
|
||||
onFinish: (Boolean, Int) -> Unit, onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
||||
): Boolean {
|
||||
onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
||||
): FlashResult {
|
||||
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so")
|
||||
val result = flashWithIO("${getKsuDaemonPath()} uninstall --magiskboot $magiskboot", onStdout, onStderr)
|
||||
onFinish(result.isSuccess, result.code)
|
||||
return result.isSuccess
|
||||
return FlashResult(result)
|
||||
}
|
||||
|
||||
suspend fun shrinkModules(): Boolean = withContext(Dispatchers.IO) {
|
||||
@@ -268,10 +286,9 @@ fun installBoot(
|
||||
bootUri: Uri?,
|
||||
lkm: LkmSelection,
|
||||
ota: Boolean,
|
||||
onFinish: (Boolean, Int) -> Unit,
|
||||
onStdout: (String) -> Unit,
|
||||
onStderr: (String) -> Unit,
|
||||
): Boolean {
|
||||
): FlashResult {
|
||||
val resolver = ksuApp.contentResolver
|
||||
|
||||
val bootFile = bootUri?.let { uri ->
|
||||
@@ -334,15 +351,14 @@ fun installBoot(
|
||||
lkmFile?.delete()
|
||||
|
||||
// if boot uri is empty, it is direct install, when success, we should show reboot button
|
||||
onFinish(bootUri == null && result.isSuccess, result.code)
|
||||
return result.isSuccess
|
||||
return FlashResult(result, bootUri == null && result.isSuccess)
|
||||
}
|
||||
|
||||
fun reboot(reason: String = "") {
|
||||
val shell = getRootShell()
|
||||
if (reason == "recovery") {
|
||||
// KEYCODE_POWER = 26, hide incorrect "Factory data reset" message
|
||||
ShellUtils.fastCmd(shell, "/system/bin/input keyevent 26")
|
||||
ShellUtils.fastCmd(shell, "/system/bin/reboot $reason")
|
||||
}
|
||||
ShellUtils.fastCmd(shell, "/system/bin/svc power reboot $reason || /system/bin/reboot $reason")
|
||||
}
|
||||
@@ -444,7 +460,7 @@ fun getFileName(context: Context, uri: Uri): String {
|
||||
|
||||
fun moduleBackupDir(): String? {
|
||||
val shell = getRootShell()
|
||||
val baseBackupDir = "/data/adb/modules_bak"
|
||||
val baseBackupDir = "/sdcard/.ksunext/modules"
|
||||
val resultBase = ShellUtils.fastCmd(shell, "mkdir -p $baseBackupDir").trim()
|
||||
if (resultBase.isNotEmpty()) return null
|
||||
|
||||
@@ -463,16 +479,87 @@ fun moduleBackup(): Boolean {
|
||||
|
||||
val checkEmptyCommand = "if [ -z \"$(ls -A /data/adb/modules)\" ]; then echo 'empty'; fi"
|
||||
val resultCheckEmpty = ShellUtils.fastCmd(shell, checkEmptyCommand).trim()
|
||||
|
||||
if (resultCheckEmpty == "empty") {
|
||||
return false
|
||||
}
|
||||
|
||||
val backupDir = moduleBackupDir() ?: return false
|
||||
val command = "cp -rp /data/adb/modules/* $backupDir"
|
||||
val result = ShellUtils.fastCmd(shell, command).trim()
|
||||
val timestamp = ShellUtils.fastCmd(shell, "date +%Y%m%d_%H%M%S").trim()
|
||||
if (timestamp.isEmpty()) return false
|
||||
|
||||
return result.isEmpty()
|
||||
val tarName = "modules_backup_$timestamp.tar"
|
||||
val tarPath = "/data/local/tmp/$tarName"
|
||||
val internalBackupDir = "/sdcard/.ksunext/modules"
|
||||
val internalBackupPath = "$internalBackupDir/$tarName"
|
||||
|
||||
val tarCmd = "$BUSYBOX tar -cpf $tarPath -C /data/adb/modules $(ls /data/adb/modules)"
|
||||
val tarResult = ShellUtils.fastCmd(shell, tarCmd).trim()
|
||||
if (tarResult.isNotEmpty()) return false
|
||||
|
||||
ShellUtils.fastCmd(shell, "mkdir -p $internalBackupDir")
|
||||
|
||||
val cpResult = ShellUtils.fastCmd(shell, "cp $tarPath $internalBackupPath").trim()
|
||||
if (cpResult.isNotEmpty()) return false
|
||||
|
||||
ShellUtils.fastCmd(shell, "rm -f $tarPath")
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun moduleRestore(): Boolean {
|
||||
val shell = getRootShell()
|
||||
|
||||
val findTarCmd = "ls -t /sdcard/.ksunext/modules/modules_backup_*.tar 2>/dev/null | head -n 1"
|
||||
val tarPath = ShellUtils.fastCmd(shell, findTarCmd).trim()
|
||||
if (tarPath.isEmpty()) return false
|
||||
|
||||
val extractCmd = "$BUSYBOX tar -xpf $tarPath -C /data/adb/modules_update"
|
||||
val extractResult = ShellUtils.fastCmd(shell, extractCmd).trim()
|
||||
return extractResult.isEmpty()
|
||||
}
|
||||
|
||||
fun allowlistBackup(): Boolean {
|
||||
val shell = getRootShell()
|
||||
|
||||
val checkEmptyCommand = "if [ -z \"$(ls -A /data/adb/ksu/.allowlist)\" ]; then echo 'empty'; fi"
|
||||
val resultCheckEmpty = ShellUtils.fastCmd(shell, checkEmptyCommand).trim()
|
||||
if (resultCheckEmpty == "empty") {
|
||||
return false
|
||||
}
|
||||
|
||||
val timestamp = ShellUtils.fastCmd(shell, "date +%Y%m%d_%H%M%S").trim()
|
||||
if (timestamp.isEmpty()) return false
|
||||
|
||||
val tarName = "allowlist_backup_$timestamp.tar"
|
||||
val tarPath = "/data/local/tmp/$tarName"
|
||||
val internalBackupDir = "/sdcard/.ksunext/allowlist"
|
||||
val internalBackupPath = "$internalBackupDir/$tarName"
|
||||
|
||||
val tarCmd = "$BUSYBOX tar -cpf $tarPath -C /data/adb/ksu .allowlist"
|
||||
val tarResult = ShellUtils.fastCmd(shell, tarCmd).trim()
|
||||
if (tarResult.isNotEmpty()) return false
|
||||
|
||||
ShellUtils.fastCmd(shell, "mkdir -p $internalBackupDir")
|
||||
|
||||
val cpResult = ShellUtils.fastCmd(shell, "cp $tarPath $internalBackupPath").trim()
|
||||
if (cpResult.isNotEmpty()) return false
|
||||
|
||||
ShellUtils.fastCmd(shell, "rm -f $tarPath")
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun allowlistRestore(): Boolean {
|
||||
val shell = getRootShell()
|
||||
|
||||
// Find the latest allowlist tar backup in /sdcard/.ksunext/allowlist
|
||||
val findTarCmd = "ls -t /sdcard/.ksunext/allowlist/allowlist_backup_*.tar 2>/dev/null | head -n 1"
|
||||
val tarPath = ShellUtils.fastCmd(shell, findTarCmd).trim()
|
||||
if (tarPath.isEmpty()) return false
|
||||
|
||||
// Extract the tar to /data/adb/ksu (restores .allowlist folder with permissions)
|
||||
val extractCmd = "$BUSYBOX tar -xpf $tarPath -C /data/adb/ksu"
|
||||
val extractResult = ShellUtils.fastCmd(shell, extractCmd).trim()
|
||||
return extractResult.isEmpty()
|
||||
}
|
||||
|
||||
fun moduleMigration(): Boolean {
|
||||
@@ -483,26 +570,6 @@ fun moduleMigration(): Boolean {
|
||||
return result.isEmpty()
|
||||
}
|
||||
|
||||
fun moduleRestore(): Boolean {
|
||||
val shell = getRootShell()
|
||||
|
||||
val command = "ls -t /data/adb/modules_bak | head -n 1"
|
||||
val latestBackupDir = ShellUtils.fastCmd(shell, command).trim()
|
||||
|
||||
if (latestBackupDir.isEmpty()) return false
|
||||
|
||||
val sourceDir = "/data/adb/modules_bak/$latestBackupDir"
|
||||
val destinationDir = "/data/adb/modules_update"
|
||||
|
||||
val createDestDirCommand = "mkdir -p $destinationDir"
|
||||
ShellUtils.fastCmd(shell, createDestDirCommand)
|
||||
|
||||
val moveCommand = "cp -rp $sourceDir/* $destinationDir"
|
||||
val result = ShellUtils.fastCmd(shell, moveCommand).trim()
|
||||
|
||||
return result.isEmpty()
|
||||
}
|
||||
|
||||
private fun getSuSFSDaemonPath(): String {
|
||||
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libsusfsd.so"
|
||||
}
|
||||
@@ -548,6 +615,57 @@ fun susfsSUS_SU_Mode(): String {
|
||||
return result
|
||||
}
|
||||
|
||||
fun currentMountSystem(): String {
|
||||
val shell = getRootShell()
|
||||
val cmd = "module mount"
|
||||
val result = ShellUtils.fastCmd(shell, "${getKsuDaemonPath()} $cmd").trim()
|
||||
return result.substringAfter(":").substringAfter(" ").trim()
|
||||
}
|
||||
|
||||
fun getModuleSize(dir: File): Long {
|
||||
val shell = getRootShell()
|
||||
val cmd = "$BUSYBOX du -sb '${dir.absolutePath}' | awk '{print \$1}'"
|
||||
val result = ShellUtils.fastCmd(shell, cmd).trim()
|
||||
return result.toLongOrNull() ?: 0L
|
||||
}
|
||||
|
||||
fun isSuCompatDisabled(): Boolean {
|
||||
return Natives.version >= Natives.MINIMAL_SUPPORTED_SU_COMPAT && !Natives.isSuEnabled()
|
||||
}
|
||||
|
||||
fun zygiskRequired(dir: File): Boolean {
|
||||
val shell = getRootShell()
|
||||
val zygiskLib = "${dir.absolutePath}/zygisk"
|
||||
val cmd = "ls \"$zygiskLib\""
|
||||
val result = ShellUtils.fastCmdResult(shell, cmd)
|
||||
return result
|
||||
}
|
||||
|
||||
fun zygiskAvailable(): Boolean {
|
||||
val shell = getRootShell()
|
||||
val zygiskLib = "libzygisk.so"
|
||||
val rezygisk64 = "/data/adb/modules/rezygisk/lib64/$zygiskLib"
|
||||
val rezygisk = "/data/adb/modules/rezygisk/lib/$zygiskLib"
|
||||
val zygiskNext64 = "/data/adb/modules/zygisksu/lib64/$zygiskLib"
|
||||
val zygiskNext = "/data/adb/modules/zygisksu/lib/$zygiskLib"
|
||||
|
||||
val cmdRezygisk64 = "[ -f \"$rezygisk64\" ]"
|
||||
if (ShellUtils.fastCmdResult(shell, cmdRezygisk64)) {
|
||||
return true
|
||||
}
|
||||
val cmdZygiskNext64 = "[ -f \"$zygiskNext64\" ]"
|
||||
if (ShellUtils.fastCmdResult(shell, cmdZygiskNext64)) {
|
||||
return true
|
||||
}
|
||||
|
||||
val cmdRezygisk = "[ -f \"$rezygisk\" ]"
|
||||
if (ShellUtils.fastCmdResult(shell, cmdRezygisk)) {
|
||||
return true
|
||||
}
|
||||
val cmdZygiskNext = "[ -f \"$zygiskNext\" ]"
|
||||
return ShellUtils.fastCmdResult(shell, cmdZygiskNext)
|
||||
}
|
||||
|
||||
fun setAppProfileTemplate(id: String, template: String): Boolean {
|
||||
val shell = getRootShell()
|
||||
val escapedTemplate = template.replace("\"", "\\\"")
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
package com.rifsxd.ksunext.ui.util
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import java.util.Locale
|
||||
|
||||
object LocaleHelper {
|
||||
|
||||
/**
|
||||
* Check if should use system language settings (Android 13+)
|
||||
*/
|
||||
val useSystemLanguageSettings: Boolean
|
||||
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
|
||||
|
||||
/**
|
||||
* Launch system app locale settings (Android 13+)
|
||||
*/
|
||||
fun launchSystemLanguageSettings(context: Context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
try {
|
||||
val intent = Intent(Settings.ACTION_APP_LOCALE_SETTINGS).apply {
|
||||
data = Uri.fromParts("package", context.packageName, null)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
// Fallback to app language settings if system settings not available
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply saved language setting to context (for Android < 13)
|
||||
*/
|
||||
fun applyLanguage(context: Context): Context {
|
||||
// On Android 13+, language is handled by system
|
||||
if (useSystemLanguageSettings) {
|
||||
return context
|
||||
}
|
||||
|
||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
val localeTag = prefs.getString("app_locale", "system") ?: "system"
|
||||
|
||||
return if (localeTag == "system") {
|
||||
context
|
||||
} else {
|
||||
val locale = parseLocaleTag(localeTag)
|
||||
setLocale(context, locale)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set locale for context (Android < 13)
|
||||
*/
|
||||
private fun setLocale(context: Context, locale: Locale): Context {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
updateResources(context, locale)
|
||||
} else {
|
||||
updateResourcesLegacy(context, locale)
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N)
|
||||
private fun updateResources(context: Context, locale: Locale): Context {
|
||||
val configuration = Configuration()
|
||||
configuration.setLocale(locale)
|
||||
configuration.setLayoutDirection(locale)
|
||||
return context.createConfigurationContext(configuration)
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private fun updateResourcesLegacy(context: Context, locale: Locale): Context {
|
||||
Locale.setDefault(locale)
|
||||
val resources = context.resources
|
||||
val configuration = resources.configuration
|
||||
configuration.locale = locale
|
||||
configuration.setLayoutDirection(locale)
|
||||
resources.updateConfiguration(configuration, resources.displayMetrics)
|
||||
return context
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse locale tag to Locale object
|
||||
*/
|
||||
private fun parseLocaleTag(tag: String): Locale {
|
||||
return try {
|
||||
if (tag.contains("_")) {
|
||||
val parts = tag.split("_")
|
||||
Locale.Builder()
|
||||
.setLanguage(parts[0])
|
||||
.setRegion(parts.getOrNull(1) ?: "")
|
||||
.build()
|
||||
} else {
|
||||
Locale.Builder()
|
||||
.setLanguage(tag)
|
||||
.build()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Locale.getDefault()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart activity to apply language change (Android < 13)
|
||||
*/
|
||||
fun restartActivity(context: Context) {
|
||||
if (context is Activity && !useSystemLanguageSettings) {
|
||||
context.recreate()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current app locale
|
||||
*/
|
||||
fun getCurrentAppLocale(context: Context): Locale? {
|
||||
return if (useSystemLanguageSettings) {
|
||||
// Android 13+ - get from system app locale settings
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
try {
|
||||
val localeManager = context.getSystemService(Context.LOCALE_SERVICE) as? android.app.LocaleManager
|
||||
val locales = localeManager?.applicationLocales
|
||||
if (locales != null && !locales.isEmpty) {
|
||||
locales.get(0)
|
||||
} else {
|
||||
null // System default
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
null // System default
|
||||
}
|
||||
} else {
|
||||
null // System default
|
||||
}
|
||||
} else {
|
||||
// Android < 13 - get from SharedPreferences
|
||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
val localeTag = prefs.getString("app_locale", "system") ?: "system"
|
||||
if (localeTag == "system") {
|
||||
null // System default
|
||||
} else {
|
||||
parseLocaleTag(localeTag)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,7 +101,7 @@ fun getBugreportFile(context: Context): File {
|
||||
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH_mm")
|
||||
val current = LocalDateTime.now().format(formatter)
|
||||
|
||||
val targetFile = File(context.cacheDir, "KernelSU_bugreport_${current}.tar.gz")
|
||||
val targetFile = File(context.cacheDir, "KernelSU_Next_bugreport_${current}.tar.gz")
|
||||
|
||||
shell.newJob().add("tar czf ${targetFile.absolutePath} -C ${bugreportDir.absolutePath} .").exec()
|
||||
shell.newJob().add("rm -rf ${bugreportDir.absolutePath}").exec()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.rifsxd.ksunext.ui.viewmodel
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
@@ -8,13 +9,20 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import java.io.File
|
||||
import java.text.Collator
|
||||
import java.util.Locale
|
||||
import com.rifsxd.ksunext.ksuApp
|
||||
import com.rifsxd.ksunext.ui.util.HanziToPinyin
|
||||
import com.rifsxd.ksunext.ui.util.listModules
|
||||
import com.rifsxd.ksunext.ui.util.overlayFsAvailable
|
||||
import com.rifsxd.ksunext.ui.util.getModuleSize
|
||||
import com.rifsxd.ksunext.ui.util.zygiskRequired
|
||||
import com.rifsxd.ksunext.ui.util.zygiskAvailable
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
|
||||
@@ -38,7 +46,10 @@ class ModuleViewModel : ViewModel() {
|
||||
val updateJson: String,
|
||||
val hasWebUi: Boolean,
|
||||
val hasActionScript: Boolean,
|
||||
val dirId: String
|
||||
val dirId: String,
|
||||
val size: Long,
|
||||
val banner: String,
|
||||
val zygiskRequired: Boolean
|
||||
)
|
||||
|
||||
data class ModuleUpdateInfo(
|
||||
@@ -48,9 +59,6 @@ class ModuleViewModel : ViewModel() {
|
||||
val changelog: String,
|
||||
)
|
||||
|
||||
var isOverlayAvailable by mutableStateOf(overlayFsAvailable())
|
||||
private set
|
||||
|
||||
var isRefreshing by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
@@ -58,11 +66,15 @@ class ModuleViewModel : ViewModel() {
|
||||
|
||||
var sortAToZ by mutableStateOf(false)
|
||||
var sortZToA by mutableStateOf(false)
|
||||
var sortSizeLowToHigh by mutableStateOf(false)
|
||||
var sortSizeHighToLow by mutableStateOf(false)
|
||||
|
||||
val moduleList by derivedStateOf {
|
||||
val comparator = when {
|
||||
sortAToZ -> compareBy<ModuleInfo> { it.name.lowercase() }
|
||||
sortZToA -> compareByDescending<ModuleInfo> { it.name.lowercase() }
|
||||
sortSizeLowToHigh -> compareBy<ModuleInfo> { it.size }
|
||||
sortSizeHighToLow -> compareByDescending<ModuleInfo> { it.size }
|
||||
else -> compareBy<ModuleInfo> { it.dirId }
|
||||
}.thenBy(Collator.getInstance(Locale.getDefault()), ModuleInfo::id)
|
||||
|
||||
@@ -83,58 +95,81 @@ class ModuleViewModel : ViewModel() {
|
||||
isNeedRefresh = true
|
||||
}
|
||||
|
||||
var zipUris by mutableStateOf<List<Uri>>(emptyList())
|
||||
|
||||
fun updateZipUris(uris: List<Uri>) {
|
||||
zipUris = uris
|
||||
}
|
||||
|
||||
fun clearZipUris() {
|
||||
zipUris = emptyList()
|
||||
}
|
||||
|
||||
fun fetchModuleList() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
|
||||
viewModelScope.launch {
|
||||
|
||||
isRefreshing = true
|
||||
|
||||
val oldModuleList = modules
|
||||
withContext(Dispatchers.IO) {
|
||||
val start = SystemClock.elapsedRealtime()
|
||||
val oldModuleList = modules
|
||||
|
||||
val start = SystemClock.elapsedRealtime()
|
||||
kotlin.runCatching {
|
||||
val result = listModules()
|
||||
Log.i(TAG, "result: $result")
|
||||
|
||||
kotlin.runCatching {
|
||||
isOverlayAvailable = overlayFsAvailable()
|
||||
val array = JSONArray(result)
|
||||
modules = (0 until array.length())
|
||||
.asSequence()
|
||||
.map { array.getJSONObject(it) }
|
||||
.map { obj ->
|
||||
val id = obj.getString("id")
|
||||
val dirId = obj.getString("dir_id")
|
||||
val moduleDir = File("/data/adb/modules/$dirId")
|
||||
val size = getModuleSize(moduleDir)
|
||||
val zygiskRequired = zygiskRequired(moduleDir)
|
||||
|
||||
val result = listModules()
|
||||
ModuleInfo(
|
||||
id,
|
||||
obj.optString("name"),
|
||||
obj.optString("author", "Unknown"),
|
||||
obj.optString("version", "Unknown"),
|
||||
obj.optInt("versionCode", 0),
|
||||
obj.optString("description"),
|
||||
obj.getBoolean("enabled"),
|
||||
obj.getBoolean("update"),
|
||||
obj.getBoolean("remove"),
|
||||
obj.optString("updateJson"),
|
||||
obj.optBoolean("web"),
|
||||
obj.optBoolean("action"),
|
||||
dirId,
|
||||
size,
|
||||
obj.optString("banner"),
|
||||
zygiskRequired
|
||||
)
|
||||
}.toList()
|
||||
isNeedRefresh = false
|
||||
}.onFailure { e ->
|
||||
Log.e(TAG, "fetchModuleList: ", e)
|
||||
isRefreshing = false
|
||||
}
|
||||
|
||||
Log.i(TAG, "result: $result")
|
||||
// when both old and new is kotlin.collections.EmptyList
|
||||
// moduleList update will don't trigger
|
||||
if (oldModuleList === modules) {
|
||||
isRefreshing = false
|
||||
}
|
||||
|
||||
val array = JSONArray(result)
|
||||
modules = (0 until array.length())
|
||||
.asSequence()
|
||||
.map { array.getJSONObject(it) }
|
||||
.map { obj ->
|
||||
ModuleInfo(
|
||||
obj.getString("id"),
|
||||
obj.optString("name"),
|
||||
obj.optString("author", "Unknown"),
|
||||
obj.optString("version", "Unknown"),
|
||||
obj.optInt("versionCode", 0),
|
||||
obj.optString("description"),
|
||||
obj.getBoolean("enabled"),
|
||||
obj.getBoolean("update"),
|
||||
obj.getBoolean("remove"),
|
||||
obj.optString("updateJson"),
|
||||
obj.optBoolean("web"),
|
||||
obj.optBoolean("action"),
|
||||
obj.getString("dir_id")
|
||||
)
|
||||
}.toList()
|
||||
isNeedRefresh = false
|
||||
}.onFailure { e ->
|
||||
Log.e(TAG, "fetchModuleList: ", e)
|
||||
isRefreshing = false
|
||||
Log.i(TAG, "load cost: ${SystemClock.elapsedRealtime() - start}, modules: $modules")
|
||||
}
|
||||
|
||||
// when both old and new is kotlin.collections.EmptyList
|
||||
// moduleList update will don't trigger
|
||||
if (oldModuleList === modules) {
|
||||
isRefreshing = false
|
||||
}
|
||||
|
||||
Log.i(TAG, "load cost: ${SystemClock.elapsedRealtime() - start}, modules: $modules")
|
||||
}
|
||||
}
|
||||
|
||||
private fun sanitizeVersionString(version: String): String {
|
||||
return version.replace(Regex("[^a-zA-Z0-9.\\-_]"), "_")
|
||||
}
|
||||
|
||||
fun checkUpdate(m: ModuleInfo): Triple<String, String, String> {
|
||||
val empty = Triple("", "", "")
|
||||
if (m.updateJson.isEmpty() || m.remove || m.update || !m.enabled) {
|
||||
@@ -144,11 +179,8 @@ class ModuleViewModel : ViewModel() {
|
||||
val result = kotlin.runCatching {
|
||||
val url = m.updateJson
|
||||
Log.i(TAG, "checkUpdate url: $url")
|
||||
val response = okhttp3.OkHttpClient()
|
||||
.newCall(
|
||||
okhttp3.Request.Builder()
|
||||
.url(url)
|
||||
.build()
|
||||
val response = ksuApp.okhttpClient.newCall(
|
||||
okhttp3.Request.Builder().url(url).build()
|
||||
).execute()
|
||||
Log.d(TAG, "checkUpdate code: ${response.code}")
|
||||
if (response.isSuccessful) {
|
||||
@@ -167,7 +199,8 @@ class ModuleViewModel : ViewModel() {
|
||||
JSONObject(result)
|
||||
}.getOrNull() ?: return empty
|
||||
|
||||
val version = updateJson.optString("version", "")
|
||||
var version = updateJson.optString("version", "")
|
||||
version = sanitizeVersionString(version)
|
||||
val versionCode = updateJson.optInt("versionCode", 0)
|
||||
val zipUrl = updateJson.optString("zipUrl", "")
|
||||
val changelog = updateJson.optString("changelog", "")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.rifsxd.ksunext.ui.viewmodel
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.content.pm.ApplicationInfo
|
||||
@@ -9,6 +10,7 @@ import android.os.IBinder
|
||||
import android.os.Parcelable
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -24,19 +26,20 @@ import com.rifsxd.ksunext.ksuApp
|
||||
import com.rifsxd.ksunext.ui.KsuService
|
||||
import com.rifsxd.ksunext.ui.util.HanziToPinyin
|
||||
import com.rifsxd.ksunext.ui.util.KsuCli
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import java.text.Collator
|
||||
import java.util.*
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
import androidx.core.content.edit
|
||||
|
||||
class SuperUserViewModel : ViewModel() {
|
||||
|
||||
var refreshOnReturn by mutableStateOf(false)
|
||||
public set
|
||||
|
||||
companion object {
|
||||
private const val TAG = "SuperUserViewModel"
|
||||
private var apps by mutableStateOf<List<AppInfo>>(emptyList())
|
||||
private var profileOverrides by mutableStateOf<Map<String, Natives.Profile>>(emptyMap())
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
@@ -66,16 +69,26 @@ class SuperUserViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
private val prefs = ksuApp.getSharedPreferences("settings", Context.MODE_PRIVATE)!!
|
||||
|
||||
var search by mutableStateOf("")
|
||||
var showSystemApps by mutableStateOf(false)
|
||||
var showSystemApps by mutableStateOf(prefs.getBoolean("show_system_apps", false))
|
||||
private set
|
||||
var isRefreshing by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
fun updateShowSystemApps(newValue: Boolean) {
|
||||
showSystemApps = newValue
|
||||
prefs.edit { putBoolean("show_system_apps", newValue) }
|
||||
}
|
||||
|
||||
private val sortedList by derivedStateOf {
|
||||
val comparator = compareBy<AppInfo> {
|
||||
when {
|
||||
it.allowSu -> 0
|
||||
it.hasCustomProfile -> 1
|
||||
it.profile != null && it.profile.allowSu -> 0
|
||||
it.profile != null && (
|
||||
if (it.profile.allowSu) !it.profile.rootUseDefault else !it.profile.nonRootUseDefault
|
||||
) -> 1
|
||||
else -> 2
|
||||
}
|
||||
}.then(compareBy(Collator.getInstance(Locale.getDefault()), AppInfo::label))
|
||||
@@ -85,7 +98,9 @@ class SuperUserViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
val appList by derivedStateOf {
|
||||
sortedList.filter {
|
||||
sortedList.map { app ->
|
||||
profileOverrides[app.packageName]?.let { app.copy(profile = it) } ?: app
|
||||
}.filter {
|
||||
it.label.contains(search, true) || it.packageName.contains(
|
||||
search,
|
||||
true
|
||||
@@ -97,6 +112,12 @@ class SuperUserViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun updateAppProfile(packageName: String, newProfile: Natives.Profile) {
|
||||
profileOverrides = profileOverrides.toMutableMap().apply {
|
||||
put(packageName, newProfile)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend inline fun connectKsuService(
|
||||
crossinline onDisconnect: () -> Unit = {}
|
||||
): Pair<IBinder, ServiceConnection> = suspendCoroutine {
|
||||
|
||||
@@ -11,18 +11,17 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import com.rifsxd.ksunext.Natives
|
||||
import com.rifsxd.ksunext.ksuApp
|
||||
import com.rifsxd.ksunext.profile.Capabilities
|
||||
import com.rifsxd.ksunext.profile.Groups
|
||||
import com.rifsxd.ksunext.ui.util.getAppProfileTemplate
|
||||
import com.rifsxd.ksunext.ui.util.listAppProfileTemplates
|
||||
import com.rifsxd.ksunext.ui.util.setAppProfileTemplate
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.text.Collator
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
||||
/**
|
||||
@@ -138,13 +137,7 @@ class TemplateViewModel : ViewModel() {
|
||||
|
||||
private fun fetchRemoteTemplates() {
|
||||
runCatching {
|
||||
val client: OkHttpClient = OkHttpClient.Builder()
|
||||
.connectTimeout(5, TimeUnit.SECONDS)
|
||||
.writeTimeout(5, TimeUnit.SECONDS)
|
||||
.readTimeout(10, TimeUnit.SECONDS)
|
||||
.build()
|
||||
|
||||
client.newCall(
|
||||
ksuApp.okhttpClient.newCall(
|
||||
Request.Builder().url(TEMPLATE_INDEX_URL).build()
|
||||
).execute().use { response ->
|
||||
if (!response.isSuccessful) {
|
||||
@@ -155,7 +148,7 @@ private fun fetchRemoteTemplates() {
|
||||
0.until(remoteTemplateIds.length()).forEach { i ->
|
||||
val id = remoteTemplateIds.getString(i)
|
||||
Log.i(TAG, "fetch template: $id")
|
||||
val templateJson = client.newCall(
|
||||
val templateJson = ksuApp.okhttpClient.newCall(
|
||||
Request.Builder().url(TEMPLATE_URL.format(id)).build()
|
||||
).runCatching {
|
||||
execute().use { response ->
|
||||
|
||||
@@ -39,9 +39,9 @@ class WebUIActivity : ComponentActivity() {
|
||||
val name = intent.getStringExtra("name")!!
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
@Suppress("DEPRECATION")
|
||||
setTaskDescription(ActivityManager.TaskDescription("KernelSU - $name"))
|
||||
setTaskDescription(ActivityManager.TaskDescription("KSUNEXT - $name"))
|
||||
} else {
|
||||
val taskDescription = ActivityManager.TaskDescription.Builder().setLabel("KernelSU - $name").build()
|
||||
val taskDescription = ActivityManager.TaskDescription.Builder().setLabel("KSUNEXT - $name").build()
|
||||
setTaskDescription(taskDescription)
|
||||
}
|
||||
|
||||
|
||||
@@ -206,4 +206,4 @@ fun hideSystemUI(window: Window) =
|
||||
}
|
||||
|
||||
fun showSystemUI(window: Window) =
|
||||
WindowInsetsControllerCompat(window, window.decorView).show(WindowInsetsCompat.Type.systemBars())
|
||||
WindowInsetsControllerCompat(window, window.decorView).show(WindowInsetsCompat.Type.systemBars())
|
||||
Binary file not shown.
BIN
manager/app/src/main/jniLibs/armeabi-v7a/libmagiskboot.so
Normal file
BIN
manager/app/src/main/jniLibs/armeabi-v7a/libmagiskboot.so
Normal file
Binary file not shown.
BIN
manager/app/src/main/jniLibs/x86_64/libmagiskboot.so
Normal file
BIN
manager/app/src/main/jniLibs/x86_64/libmagiskboot.so
Normal file
Binary file not shown.
197
manager/app/src/main/res/values-ar/strings.xml
Normal file
197
manager/app/src/main/res/values-ar/strings.xml
Normal file
@@ -0,0 +1,197 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="issue_report_title">تواجه مشكلة؟</string>
|
||||
<string name="issue_report_body">حصل خطأ ما او لديك تعليقات؟</string>
|
||||
<string name="issue_report_body_2">بلغنا بها على الفور!</string>
|
||||
<string name="issue_report_github">ابلغنا عن طريق Github</string>
|
||||
<string name="issue_report_telegram">تواصل من خلال التليجرام</string>
|
||||
<string name="issue_report_github_link">https://github.com/KernelSU-Next/KernelSU-Next/issues</string>
|
||||
<string name="issue_report_telegram_link">https://t.me/ksunext</string>
|
||||
<string name="confirm">تأكيد</string>
|
||||
<string name="app_name" translatable="false">KernelSU Next</string>
|
||||
<string name="home">الصفحة الرئيسية</string>
|
||||
<string name="home_not_installed">غير مثبت</string>
|
||||
<string name="home_click_to_install">انقر للتثبيت</string>
|
||||
<string name="home_working">يعمل</string>
|
||||
<string name="home_working_version">الإصدار: %d</string>
|
||||
<string name="home_superuser_count">عدد التطبيقات ذات صلاحيات الروت: %d</string>
|
||||
<string name="home_module_count">عدد الإضافات: %d</string>
|
||||
<string name="home_failure">توقيع KSU Next v2 غير موجود [ !KSU_NEXT || != size/hash ]</string>
|
||||
<string name="home_failure_tip">اطلب من مطور بتهيئة KSU Next لجهازك</string>
|
||||
<string name="home_kernel">اصدار Kernel</string>
|
||||
<string name="enabled">مفعل</string>
|
||||
<string name="disabled">معطل</string>
|
||||
<string name="susfs_supported">مدعوم</string>
|
||||
<string name="home_susfs">SuSFS: %s</string>
|
||||
<string name="home_susfs_version">اصدار SuSFS</string>
|
||||
<string name="home_susfs_sus_su">SuS SU</string>
|
||||
<string name="home_android">إصدار الأندرويد</string>
|
||||
<string name="home_manager_version">اصدار مدير الروت</string>
|
||||
<string name="home_selinux_status">حالة SELinux</string>
|
||||
<string name="selinux_status_disabled">معطل</string>
|
||||
<string name="selinux_status_enforcing">مفعل</string>
|
||||
<string name="selinux_status_permissive">مرن</string>
|
||||
<string name="selinux_status_unknown">غير معروف</string>
|
||||
<string name="superuser">المستخدم خارق</string>
|
||||
<string name="module_failed_to_enable">فشل في تشغيل إضافة: %s</string>
|
||||
<string name="module_failed_to_disable">فشل في إيقاف إضافة: %s</string>
|
||||
<string name="module_empty">لا توجد إضافات مثبته</string>
|
||||
<string name="module">الإضافات</string>
|
||||
<string name="module_install_prompt_with_name">الإضافة/ات التالية سيتم تثبيتها: %1$s</string>
|
||||
<string name="module_sort_a_to_z">تصنيف (أ-ي)</string>
|
||||
<string name="module_sort_z_to_a">تصنيف (ي-أ)</string>
|
||||
<string name="uninstall">إلغاء التثبيت</string>
|
||||
<string name="restore">إستعادة</string>
|
||||
<string name="module_install">تثبيت</string>
|
||||
<string name="install">تثبيت</string>
|
||||
<string name="reboot">إعادة تشغيل</string>
|
||||
<string name="settings">الإعدادات</string>
|
||||
<string name="reboot_userspace">إعادة تشغيل سريعة</string>
|
||||
<string name="reboot_recovery">إعادة التشغيل إلى وضع الريكفري</string>
|
||||
<string name="reboot_bootloader">إعادة التشغيل إلى وضع bootloader</string>
|
||||
<string name="reboot_download">إعادة التشغيل إلى وضع التنزيل</string>
|
||||
<string name="reboot_edl">إعادة التشغيل إلى وضع EDL</string>
|
||||
<string name="about">حول</string>
|
||||
<string name="module_uninstall_confirm">هل انت متأكد من حذف تلك الإضافة %s؟</string>
|
||||
<string name="module_uninstall_success">%s تم إلغاء تثبيت</string>
|
||||
<string name="module_uninstall_failed">فشل في إلغاء تثبيت: %s</string>
|
||||
<string name="module_restore_confirm">هل انت متأكد من استرجاع الإضافة؟ %s؟</string>
|
||||
<string name="module_restore_success">%s تم استعادة</string>
|
||||
<string name="module_restore_failed">فشل استعادة: %s</string>
|
||||
<string name="module_version">الإصدار</string>
|
||||
<string name="module_author">المطور</string>
|
||||
<string name="module_id">ID</string>
|
||||
<string name="module_version_code">كود الإصدار</string>
|
||||
<string name="module_update_json">UpdateJson</string>
|
||||
<string name="module_update_json_empty">فارغ</string>
|
||||
<string name="enable_developer_options">تفعيل خيارات المطور</string>
|
||||
<string name="enable_developer_options_summary">عرض الإعدادات المخفية ومعلومات التصحيح ذات الصلة فقط للمطورين.</string>
|
||||
<string name="module_overlay_fs_not_available">الإضافات غير متاحة حيث أن OverlayFS معطلة بواسطة النواة.</string>
|
||||
<string name="refresh">تحديث</string>
|
||||
<string name="show_system_apps">عرض تطبيقات النظام</string>
|
||||
<string name="hide_system_apps">إخفاء تطبيقات النظام</string>
|
||||
<string name="export_log">تصدير السجلات</string>
|
||||
<string name="safe_mode">وضع الأمان</string>
|
||||
<string name="reboot_to_apply">إعادة التشغيل لتطبيق التغييرات</string>
|
||||
<string name="module_magisk_conflict">الإضافات غير متاحة بسبب تعارض مع Magisk!</string>
|
||||
<string name="home_mount_system">نظام الإضافات</string>
|
||||
<string name="home_magic_mount">Magic Mount</string>
|
||||
<string name="home_overlayfs_mount">OverlayFS</string>
|
||||
<string name="unavailable">غير متاح</string>
|
||||
<string name="use_overlay_fs">استخدام OverlayFS</string>
|
||||
<string name="use_overlay_fs_summary">التبديل بين استخدام OverlayFS أو Magic Mount لنظام Mount الخاص بـKernelSU Next.</string>
|
||||
<string name="reboot_required">إعادة التشغيل مطلوبة</string>
|
||||
<string name="reboot_message">ستدخل التغييرات حيز التنفيذ بعد إعادة تشغيل النظام. هل تريد إعادة التشغيل الآن؟</string>
|
||||
<string name="module_restore">استعادة الإضافة</string>
|
||||
<string name="module_restore_message">استعادة الإضافات من النسخة الاحتياطية الأخيرة.</string>
|
||||
<string name="backup_restore">نسخ احتياطي واستعادة</string>
|
||||
<string name="module_backup">نسخ احتياطي للإضافة</string>
|
||||
<string name="module_backup_message">نسخ احتياطي للإضافات المثبتة حاليًا.</string>
|
||||
<string name="allowlist_restore">استعادة القائمة المسموح بها</string>
|
||||
<string name="allowlist_restore_message">استعادة القائمة المسموح بها من النسخة الاحتياطية الأخيرة.</string>
|
||||
<string name="allowlist_backup">نسخ احتياطي للقائمة المسموح بها</string>
|
||||
<string name="allowlist_backup_message">نسخ احتياطي للقائمة المسموح بها المكونة حاليًا.</string>
|
||||
<string name="warning">تحذير</string>
|
||||
<string name="warning_message">هذه الميزة لا تزال في مرحلة البيتا وتحت التطوير. يرجى التأكد من عمل نسخة احتياطية من الإضافات الخاصة بك قبل المتابعة. استخدم هذه الميزة فقط إذا كنت تفهم المخاطر المحتملة. أكمل بحذر.</string>
|
||||
<string name="proceed">تابع</string>
|
||||
<string name="cancel">إلغاء</string>
|
||||
<string name="later">لاحقًا</string>
|
||||
<string name="home_next_kernelsu">🔥 الإصدار التالي</string>
|
||||
<string name="home_next_kernelsu_repo">https://github.com/KernelSU-Next/KernelSU-Next</string>
|
||||
<string name="home_next_kernelsu_body">فرع تجريبي قادم. تحقق منه على GitHub!</string>
|
||||
<string name="home_experimental_kernelsu">⚠️ تجريبي!</string>
|
||||
<string name="home_experimental_kernelsu_repo">127.0.0.1</string>
|
||||
<string name="home_experimental_kernelsu_body">KernelSU Next هو إصدار غير رسمي دائمًا تحت التطوير التجريبي النشط. يتم تقديمه كما هو، دون ضمانات للاستقرار أو الأداء أو الموثوقية.</string>
|
||||
<string name="home_experimental_kernelsu_body_point_1"> • استخدم على مسؤوليتك: قد تحدث أعطال أو سلوك غير متوقع أو مشاكل في النظام.</string>
|
||||
<string name="home_experimental_kernelsu_body_point_2"> • لا ضمان: المطورون غير مسؤولين عن أي فقدان للبيانات أو تلف النظام أو عواقب أخرى ناتجة عن استخدامه.</string>
|
||||
<string name="home_experimental_kernelsu_body_point_3"> • لأغراض الاختبار فقط: مخصص للمستخدمين الذين يفهمون المخاطر ويشعرون بالراحة في حل المشكلات.</string>
|
||||
<string name="about_source_code"><![CDATA[عرض رمز المصدر في %1$s]]></string>
|
||||
<string name="profile" translatable="false">ملف التطبيق</string>
|
||||
<string name="profile_default">افتراضي</string>
|
||||
<string name="profile_template">قالب</string>
|
||||
<string name="profile_custom">مخصص</string>
|
||||
<string name="profile_name">اسم الملف الشخصي</string>
|
||||
<string name="profile_namespace">مساحة التثبيت</string>
|
||||
<string name="profile_namespace_inherited">موروث</string>
|
||||
<string name="profile_namespace_global">عالمي</string>
|
||||
<string name="profile_namespace_individual">فردي</string>
|
||||
<string name="profile_groups">المجموعات</string>
|
||||
<string name="profile_capabilities">القدرات</string>
|
||||
<string name="profile_selinux_context">سياق SELinux</string>
|
||||
<string name="profile_umount_modules">إلغاء تثبيت الإضافات</string>
|
||||
<string name="failed_to_update_app_profile">فشل في تحديث ملف التطبيق لـ %s</string>
|
||||
<string name="require_kernel_version">إصدار KernelSU Next الحالي %1$d منخفض جدًا لكي يعمل المدير بشكل صحيح. يرجى الترقية إلى الإصدار %2$d أو أعلى!</string>
|
||||
<string name="settings_umount_modules_default">إلغاء تثبيت الإضافات</string>
|
||||
<string name="settings_umount_modules_default_summary">القيمة الافتراضية العالمية لـ "إلغاء تثبيت الإضافات" في ملف التطبيق. إذا تم تفعيلها، ستقوم بإزالة جميع التعديلات التي أجرتها الإضافات على النظام للتطبيقات التي لا تحتوي على ملف شخصي محدد.</string>
|
||||
<string name="settings_susfs_toggle">إخفاء خطافات kprobe</string>
|
||||
<string name="settings_susfs_toggle_summary">تعطيل خطافات kprobe التي أنشأها ksu، وبدلاً من ذلك، تفعيل الخطافات غير الكروب المدمجة، مما ينفذ نفس الوظائف التي ستطبق على نواة غير GKI، والتي لا تدعم kprobe.</string>
|
||||
<string name="profile_umount_modules_summary">تفعيل هذا الخيار سيسمح لـ KernelSU Next باستعادة أي ملفات معدلة بواسطة الإضافات لهذا التطبيق.</string>
|
||||
<string name="profile_selinux_domain">النطاق</string>
|
||||
<string name="profile_selinux_rules">القواعد</string>
|
||||
<string name="module_update">تحديث</string>
|
||||
<string name="module_downloading">جارٍ تنزيل الإضافة: %s</string>
|
||||
<string name="module_start_downloading">بدء التنزيل: %s</string>
|
||||
<string name="new_version_available">إصدار جديد %s متاح، انقر للتحديث.</string>
|
||||
<string name="launch_app">تشغيل</string>
|
||||
<string name="close">إغلاق</string>
|
||||
<string name="force_stop_app">إيقاف إجباري</string>
|
||||
<string name="restart_app">إعادة تشغيل</string>
|
||||
<string name="failed_to_update_sepolicy">فشل في تحديث قواعد SELinux لـ: %s</string>
|
||||
<string name="su_not_allowed">لا يُسمح بمنح صلاحيات المستخدم الخارق لـ: %s</string>
|
||||
<string name="module_changelog">سجل التغييرات</string>
|
||||
<string name="settings_profile_template">قالب ملف التطبيق</string>
|
||||
<string name="settings_profile_template_summary">إدارة القالب المحلي وعبر الإنترنت لملف التطبيق</string>
|
||||
<string name="app_profile_template_create">إنشاء قالب</string>
|
||||
<string name="app_profile_template_edit">تحرير القالب</string>
|
||||
<string name="app_profile_template_id">ID</string>
|
||||
<string name="app_profile_template_id_invalid">معرف القالب غير صالح</string>
|
||||
<string name="app_profile_template_name">الاسم</string>
|
||||
<string name="app_profile_template_description">الوصف</string>
|
||||
<string name="app_profile_template_save">حفظ</string>
|
||||
<string name="app_profile_template_delete">حذف</string>
|
||||
<string name="app_profile_template_view">عرض القالب</string>
|
||||
<string name="app_profile_template_readonly">للقراءة فقط</string>
|
||||
<string name="app_profile_template_id_exist">معرف القالب موجود بالفعل!</string>
|
||||
<string name="app_profile_import_export">استيراد/تصدير</string>
|
||||
<string name="app_profile_import_from_clipboard">استيراد من الحافظة</string>
|
||||
<string name="app_profile_export_to_clipboard">تصدير إلى الحافظة</string>
|
||||
<string name="app_profile_template_export_empty">لا يمكن العثور على قالب محلي للتصدير!</string>
|
||||
<string name="app_profile_template_import_success">تم الاستيراد بنجاح</string>
|
||||
<string name="app_profile_template_sync">مزامنة القوالب عبر الإنترنت</string>
|
||||
<string name="app_profile_template_save_failed">فشل في حفظ القالب</string>
|
||||
<string name="app_profile_template_import_empty">الحافظة فارغة!</string>
|
||||
<string name="module_changelog_failed">فشل في جلب سجل التغييرات: %s</string>
|
||||
<string name="settings_check_update">التحقق من التحديث</string>
|
||||
<string name="settings_check_update_summary">التحقق تلقائيًا من التحديثات عند فتح التطبيق.</string>
|
||||
<string name="grant_root_failed">فشل في منح صلاحيات الجذر!</string>
|
||||
<string name="action">إجراء</string>
|
||||
<string name="open">فتح</string>
|
||||
<string name="enable_web_debugging">تفعيل تصحيح WebView</string>
|
||||
<string name="enable_web_debugging_summary">يمكن استخدامه لتصحيح WebUI. يرجى التفعيل فقط عند الحاجة.</string>
|
||||
<string name="direct_install">التثبيت المباشر (موصى به)</string>
|
||||
<string name="select_file">اختر ملفًا</string>
|
||||
<string name="install_inactive_slot">التثبيت في الفتحة غير النشطة (بعد OTA)</string>
|
||||
<string name="install_inactive_slot_warning">سيتم إجبار جهازك على الإقلاع إلى الفتحة غير النشطة الحالية بعد إعادة التشغيل!\nاستخدم هذا الخيار فقط بعد الانتهاء من OTA.\nهل تريد المتابعة؟</string>
|
||||
<string name="install_next">التالي</string>
|
||||
<string name="select_file_tip">%1$s Partition موصى به</string>
|
||||
<string name="select_kmi">اختر KMI</string>
|
||||
<string name="shrink_sparse_image">تقليل حجم partition المتناثرة</string>
|
||||
<string name="shrink_sparse_image_message">إعادة حجم partition حيث توجد الإضافة إلى حجمها الفعلي. لاحظ أن هذا قد يتسبب في عمل الإضافة بشكل غير طبيعي، لذا يرجى استخدامه فقط عند الضرورة (مثل النسخ الاحتياطي).</string>
|
||||
<string name="settings_uninstall">إلغاء التثبيت</string>
|
||||
<string name="settings_uninstall_temporary">إلغاء التثبيت مؤقتًا</string>
|
||||
<string name="settings_uninstall_permanent">إلغاء التثبيت نهائيًا</string>
|
||||
<string name="settings_restore_stock_image">استعادة الصورة الأصلية</string>
|
||||
<string name="settings_uninstall_temporary_message">إلغاء تثبيت KernelSU Next مؤقتًا، استعادة الحالة الأصلية بعد إعادة التشغيل التالية.</string>
|
||||
<string name="settings_uninstall_permanent_message">إلغاء تثبيت KernelSU Next (الجذر وجميع الإضافات) بالكامل ودائمًا.</string>
|
||||
<string name="settings_restore_stock_image_message">استعادة الصورة الأصلية (إذا كانت النسخة الاحتياطية موجودة)، عادة ما تستخدم قبل OTA؛ إذا كنت بحاجة إلى إلغاء تثبيت KernelSU Next، يرجى استخدام "إلغاء التثبيت نهائيًا".</string>
|
||||
<string name="flashing">تثبيت</string>
|
||||
<string name="flash_success">نجح التثبيت</string>
|
||||
<string name="flash_failed">فشل التثبيت</string>
|
||||
<string name="selected_lkm">LKM المحدد: %s</string>
|
||||
<string name="save_log">حفظ السجلات</string>
|
||||
<string name="log_saved">تم حفظ السجلات</string>
|
||||
<string name="send_log">مشاركة السجلات</string>
|
||||
<string name="settings_disable_su">تعطيل توافق su</string>
|
||||
<string name="settings_disable_su_summary">تعطيل مؤقت لقدرة أي تطبيق على الحصول على صلاحيات الجذر عبر أمر su (لن تتأثر العمليات الجذرية الحالية).</string>
|
||||
<string name="settings_language">اللغة</string>
|
||||
</resources>
|
||||
85
manager/app/src/main/res/values-bg/strings.xml
Normal file
85
manager/app/src/main/res/values-bg/strings.xml
Normal file
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="issue_report_title">Имате проблем?</string>
|
||||
<string name="issue_report_body">Открихте грешка или имате обратна връзка?</string>
|
||||
<string name="issue_report_body_2">Докладвайте я възможно най-скоро!</string>
|
||||
<string name="issue_report_github">Докладвай в GitHub</string>
|
||||
<string name="issue_report_telegram">Свържете се чрез Telegram</string>
|
||||
<string name="issue_report_github_link">https://github.com/KernelSU-Next/KernelSU-Next/issues</string>
|
||||
<string name="issue_report_telegram_link">https://t.me/ksunext</string>
|
||||
<string name="confirm">Потвърди</string>
|
||||
<string name="app_name" translatable="false">KernelSU Next</string>
|
||||
<string name="home">Начало</string>
|
||||
<string name="home_not_installed">Не е инсталирано</string>
|
||||
<string name="home_click_to_install">Натиснете за инсталиране</string>
|
||||
<string name="home_working">Работи</string>
|
||||
<string name="home_working_version">Версия: %d</string>
|
||||
<string name="home_superuser_count">Суперпотребители: %d</string>
|
||||
<string name="home_module_count">Модули: %d</string>
|
||||
<string name="home_failure">Не е намерен подпис KernelSU Next v2 в ядрото! [ !KSU_NEXT || != размер/хеш ]</string>
|
||||
<string name="home_failure_tip">Помолете разработчика на вашето ядро да интегрира KernelSU Next!</string>
|
||||
<string name="home_kernel">Версия на ядрото</string>
|
||||
<string name="enabled">Активирано</string>
|
||||
<string name="disabled">Деактивирано</string>
|
||||
<string name="susfs_supported">Поддържано</string>
|
||||
<string name="home_susfs">SuSFS: %s</string>
|
||||
<string name="home_susfs_version">Версия на SuSFS</string>
|
||||
<string name="home_susfs_sus_su">SuS SU</string>
|
||||
<string name="home_android">Версия на Android</string>
|
||||
<string name="home_manager_version">Версия на мениджъра</string>
|
||||
<string name="home_selinux_status">Статус на SELinux</string>
|
||||
<string name="selinux_status_disabled">Деактивиран</string>
|
||||
<string name="selinux_status_enforcing">Строг режим</string>
|
||||
<string name="selinux_status_permissive">Разрешителен режим</string>
|
||||
<string name="selinux_status_unknown">Неизвестно</string>
|
||||
<string name="superuser">Суперпотребител</string>
|
||||
<string name="module_failed_to_enable">Неуспешно активиране на модул: %s</string>
|
||||
<string name="module_failed_to_disable">Неуспешно деактивиране на модул: %s</string>
|
||||
<string name="module_empty">Няма инсталирани модули</string>
|
||||
<string name="module">Модул</string>
|
||||
<string name="module_install_prompt_with_name">Следните модули ще бъдат инсталирани: %1$s</string>
|
||||
<string name="module_sort_a_to_z">Сортиране (А-Я)</string>
|
||||
<string name="module_sort_z_to_a">Сортиране (Я-А)</string>
|
||||
<string name="uninstall">Деинсталиране</string>
|
||||
<string name="restore">Възстановяване</string>
|
||||
<string name="module_install">Инсталиране</string>
|
||||
<string name="install">Инсталирай</string>
|
||||
<string name="reboot">Рестартиране</string>
|
||||
<string name="settings">Настройки</string>
|
||||
<string name="reboot_userspace">Мек рестарт</string>
|
||||
<string name="reboot_recovery">Рестарт в Recovery</string>
|
||||
<string name="reboot_bootloader">Рестарт в Bootloader</string>
|
||||
<string name="reboot_download">Рестарт в Download</string>
|
||||
<string name="reboot_edl">Рестарт в EDL</string>
|
||||
<string name="about">Относно</string>
|
||||
<string name="module_uninstall_confirm">Сигурни ли сте, че искате да деинсталирате модула %s?</string>
|
||||
<string name="module_uninstall_success">%s деинсталиран</string>
|
||||
<string name="module_uninstall_failed">Неуспешно деинсталиране: %s</string>
|
||||
<string name="module_restore_confirm">Сигурни ли сте, че искате да възстановите модула %s?</string>
|
||||
<string name="module_restore_success">%s възстановен</string>
|
||||
<string name="module_restore_failed">Неуспешно възстановяване: %s</string>
|
||||
<string name="module_version">Версия</string>
|
||||
<string name="module_author">Автор</string>
|
||||
<string name="module_id">ID</string>
|
||||
<string name="module_version_code">Код</string>
|
||||
<string name="module_update_json">Актуализиране JSON</string>
|
||||
<string name="module_update_json_empty">Празно</string>
|
||||
<string name="enable_developer_options">Активиране на опции за разработчици</string>
|
||||
<string name="enable_developer_options_summary">Показване на скрити настройки и дебъг информация, релевантни само за разработчици.</string>
|
||||
<string name="module_overlay_fs_not_available">Модулите не са налични, тъй като OverlayFS е деактивиран от ядрото.</string>
|
||||
<string name="refresh">Обновяване</string>
|
||||
<string name="show_system_apps">Показване на системни приложения</string>
|
||||
<string name="hide_system_apps">Скриване на системни приложения</string>
|
||||
<string name="export_log">Експорт на логове</string>
|
||||
<string name="safe_mode">Безопасен режим</string>
|
||||
<string name="reboot_to_apply">Рестартирайте, за да влязат в сила</string>
|
||||
<string name="module_magisk_conflict">Модулите не са налични поради конфликт с Magisk!</string>
|
||||
<string name="home_mount_system">Модулна система</string>
|
||||
<string name="home_magic_mount">Magic Mount</string>
|
||||
<string name="home_overlayfs_mount">OverlayFS</string>
|
||||
<string name="unavailable">Недостъпно</string>
|
||||
<string name="use_overlay_fs">Използване на OverlayFS</string>
|
||||
<string name="use_overlay_fs_summary">Превключване между използването на OverlayFS и Magic Mount за KernelSU Next.</string>
|
||||
<string name="reboot_required">Необходим е рестарт</string>
|
||||
<string name="reboot_message">Промените ще влязат в сила след рестарт. Искате ли да рестартирате сега?</string>
|
||||
</resources>
|
||||
193
manager/app/src/main/res/values-de/strings.xml
Normal file
193
manager/app/src/main/res/values-de/strings.xml
Normal file
@@ -0,0 +1,193 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="issue_report_title">Haben Sie Probleme?</string>
|
||||
<string name="issue_report_body">Sind Sie auf einen Fehler gestoßen oder haben Sie Feedback??</string>
|
||||
<string name="issue_report_body_2">Melden Sie es so schnell wie möglich!</string>
|
||||
<string name="issue_report_github">Auf GitHub melden</string>
|
||||
<string name="issue_report_telegram">Kontakt über Telegramm</string>
|
||||
<string name="issue_report_github_link">https://github.com/KernelSU-Next/KernelSU-Next/issues</string>
|
||||
<string name="issue_report_telegram_link">https://t.me/ksunext</string>
|
||||
<string name="confirm">Bestätigen</string>
|
||||
<string name="app_name" translatable="false">KernelSU Next</string>
|
||||
<string name="home">Home</string>
|
||||
<string name="home_not_installed">Nicht installiert</string>
|
||||
<string name="home_click_to_install">Zum installieren klicken</string>
|
||||
<string name="home_working">Funktioniert</string>
|
||||
<string name="home_working_version">Version: %d</string>
|
||||
<string name="home_superuser_count">Superusers: %d</string>
|
||||
<string name="home_module_count">Module: %d</string>
|
||||
<string name="home_failure">KernelSU Next v2 Signatur nicht im Kernel gefunden! [ !KSU_NEXT || != size/hash ]</string>
|
||||
<string name="home_failure_tip">Bitten Sie Ihren Kernel-Entwickler, KernelSU Next zu integrieren!</string>
|
||||
<string name="home_kernel">Kernel version</string>
|
||||
<string name="home_susfs">SuSFS: %s</string>
|
||||
<string name="home_susfs_version">SuSFS version</string>
|
||||
<string name="home_susfs_sus_su">SuS SU</string>
|
||||
<string name="home_android">Android version</string>
|
||||
<string name="home_manager_version">Manager version</string>
|
||||
<string name="home_selinux_status">SELinux status</string>
|
||||
<string name="selinux_status_disabled">Deaktiviert</string>
|
||||
<string name="selinux_status_enforcing">Enforcing</string>
|
||||
<string name="selinux_status_permissive">Permissive</string>
|
||||
<string name="selinux_status_unknown">Unbekannt</string>
|
||||
<string name="superuser">Superuser</string>
|
||||
<string name="module_failed_to_enable">Aktivierung des Moduls fehlgeschlagen: %s</string>
|
||||
<string name="module_failed_to_disable">Deaktivierung des Moduls fehlgeschlagen: %s</string>
|
||||
<string name="module_empty">Kein Modul installiert</string>
|
||||
<string name="module">Modul</string>
|
||||
<string name="module_install_prompt_with_name">Das/die folgende(n) Modul(e) wird/werden installiert: %1$s</string>
|
||||
<string name="module_sort_a_to_z">Sortieren (A-Z)</string>
|
||||
<string name="module_sort_z_to_a">Sortieren (Z-A)</string>
|
||||
<string name="uninstall">Deinstallieren</string>
|
||||
<string name="restore">Wiederherstellen</string>
|
||||
<string name="module_install">Installieren</string>
|
||||
<string name="install">Installieren</string>
|
||||
<string name="reboot">Neustart</string>
|
||||
<string name="settings">Einstellungen</string>
|
||||
<string name="reboot_userspace">Weicher Neustart</string>
|
||||
<string name="reboot_recovery">Neustart zur Recovery</string>
|
||||
<string name="reboot_bootloader">Neustart zum Bootloader</string>
|
||||
<string name="reboot_download">Neustart zu Download</string>
|
||||
<string name="reboot_edl">Neustart zu EDL</string>
|
||||
<string name="about">Über</string>
|
||||
<string name="module_uninstall_confirm">Sind Sie sicher, dass Sie das Modul deinstallieren möchten? %s?</string>
|
||||
<string name="module_uninstall_success">%s deinstalliert</string>
|
||||
<string name="module_uninstall_failed">Deinstallierung fehlgeschlagen %s</string>
|
||||
<string name="module_restore_confirm">Sind Sie sicher, dass Sie das Modul wiederherstellen wollen? %s?</string>
|
||||
<string name="module_restore_success">%s wiederhergestekkt</string>
|
||||
<string name="module_restore_failed">Wiederherstellung fehlgeschlagen: %s</string>
|
||||
<string name="module_version">Version</string>
|
||||
<string name="module_author">Autor</string>
|
||||
<string name="module_id">ID</string>
|
||||
<string name="module_version_code">Code</string>
|
||||
<string name="module_update_json">UpdateJson</string>
|
||||
<string name="module_update_json_empty">leer</string>
|
||||
<string name="enable_developer_options">Aktiviere Entwickler-Optionen</string>
|
||||
<string name="enable_developer_options_summary">Versteckte Einstellungen und Debug-Informationen anzeigen, die nur für Entwickler relevant sind.</string>
|
||||
<string name="module_overlay_fs_not_available">Die Module sind nicht verfügbar, da OverlayFS vom Kernel deaktiviert ist.</string>
|
||||
<string name="refresh">Aktualisieren</string>
|
||||
<string name="show_system_apps">zeige system apps</string>
|
||||
<string name="hide_system_apps">verstecke system apps</string>
|
||||
<string name="export_log">Logs exportieren</string>
|
||||
<string name="safe_mode">Abgesicherter Modus</string>
|
||||
<string name="reboot_to_apply">Neustart, um wirksam zu werden</string>
|
||||
<string name="module_magisk_conflict">Die Module sind aufgrund eines Konflikts mit Magisk nicht verfügbar!</string>
|
||||
<string name="home_mount_system">Modul system</string>
|
||||
<string name="home_magic_mount">Magic Mount</string>
|
||||
<string name="home_overlayfs_mount">OverlayFS</string>
|
||||
<string name="unavailable">Nicht verfügbar</string>
|
||||
<string name="use_overlay_fs">OverlayFS verwenden</string>
|
||||
<string name="use_overlay_fs_summary">Schalten Sie zwischen der Verwendung von OverlayFS und Magic Mount für das mount System von KernelSU Next um.</string>
|
||||
<string name="reboot_required">Neustart erforderlich</string>
|
||||
<string name="reboot_message">Die Änderungen werden nach dem Neustart des Systems wirksam. Möchten Sie jetzt neu starten?</string>
|
||||
<string name="module_restore">Wiederherstellen module</string>
|
||||
<string name="module_restore_message">Wiederherstellen von Modulen aus der letzten Sicherung.</string>
|
||||
<string name="backup_restore">Sicherung & Wiederherstellen</string>
|
||||
<string name="module_backup">Sicherung module</string>
|
||||
<string name="module_backup_message">Sicherung der aktuell installierten Module.</string>
|
||||
<string name="allowlist_restore">Wiederherstellen der Zulassen-Liste</string>
|
||||
<string name="allowlist_restore_message">Wiederherstellen der Zulassen-Liste aus dem letzten Backup.</string>
|
||||
<string name="allowlist_backup">Sicherung der Zulassen-Liste</string>
|
||||
<string name="allowlist_backup_message">Sicherung der aktuell konfigurierten Zulassen-Liste.</string>
|
||||
<string name="warning">Warnung</string>
|
||||
<string name="warning_message">diese Funktion befindet sich noch in der Beta-Phase und in der Entwicklung. Bitte stellen Sie sicher, dass Sie Ihre Module sichern, bevor Sie fortfahren. Verwenden Sie diese Funktion nur, wenn Sie sich der möglichen Risiken bewusst sind. Gehen Sie mit Bedacht vor.</string>
|
||||
<string name="proceed">Fortfahren</string>
|
||||
<string name="cancel">Abbruch</string>
|
||||
<string name="later">Später</string>
|
||||
<string name="home_next_kernelsu">🔥 Next build</string>
|
||||
<string name="home_next_kernelsu_repo">https://github.com/KernelSU-Next/KernelSU-Next</string>
|
||||
<string name="home_next_kernelsu_body">Next experimenteller Zweig. Entdecken Sie es auf GitHub!</string>
|
||||
<string name="home_experimental_kernelsu">⚠️ Warnung vor experimenteller Entwicklung!</string>
|
||||
<string name="home_experimental_kernelsu_repo">127.0.0.1</string>
|
||||
<string name="home_experimental_kernelsu_body">KernelSU Next ist eine nicht-offizielle Version, die sich stets in aktiver experimenteller Entwicklung befindet. Sie wird im Ist-Zustand zur Verfügung gestellt, ohne Garantie auf Stabilität, Leistung oder Zuverlässigkeit.</string>
|
||||
<string name="home_experimental_kernelsu_body_point_1"> • Die Verwendung erfolgt auf eigene Gefahr: Abstürze, unerwartetes Verhalten oder Systemprobleme können auftreten.</string>
|
||||
<string name="home_experimental_kernelsu_body_point_2"> • Keine Garantie: Die Entwickler sind nicht verantwortlich für Datenverluste, Systemschäden oder andere Folgen, die sich aus der Nutzung ergeben.</string>
|
||||
<string name="home_experimental_kernelsu_body_point_3"> • Nur zu Testzwecken: für Benutzer, die sich der Risiken bewusst sind und mit der Behebung von Problemen vertraut sind.</string>
|
||||
<string name="about_source_code"><![CDATA[Quellcode ansehen unter %1$s]]></string>
|
||||
<string name="profile" translatable="false">App Profile</string>
|
||||
<string name="profile_default">Standard</string>
|
||||
<string name="profile_template">Vorlage</string>
|
||||
<string name="profile_custom">Benutzerdefiniert</string>
|
||||
<string name="profile_name">Profilname</string>
|
||||
<string name="profile_namespace">Mount namespace</string>
|
||||
<string name="profile_namespace_inherited">Vererbt</string>
|
||||
<string name="profile_namespace_global">Global</string>
|
||||
<string name="profile_namespace_individual">Individuell</string>
|
||||
<string name="profile_groups">Gruppen</string>
|
||||
<string name="profile_capabilities">Funktionen</string>
|
||||
<string name="profile_selinux_context">SELinux context</string>
|
||||
<string name="profile_umount_modules">Umount Module</string>
|
||||
<string name="failed_to_update_app_profile">App-Profil konnte nicht aktualisiert werden für %s</string>
|
||||
<string name="require_kernel_version">Die aktuelle KernelSU Next Version %1$d ist zu veraltet, damit der Manager richtig funktioniert. aktualisieren Sie bitte auf Version %2$d oder höher!</string>
|
||||
<string name="settings_umount_modules_default">Umount Module</string>
|
||||
<string name="settings_umount_modules_default_summary">Der globale Standardwert für \„Umount Module\“ in App Profile. Wenn er aktiviert ist, werden alle Moduländerungen im System für Anwendungen entfernt, für die kein Profil festgelegt wurde.</string>
|
||||
<string name="settings_susfs_toggle">verstecke kprobes hook</string>
|
||||
<string name="settings_susfs_toggle_summary">Diese Option deaktiviert die von ksu erzeugten kprobe-hooks und aktiviert stattdessen die eingebetteten nicht-kprobe-hooks, die die gleiche Funktionalität implementieren, die auf einen Nicht-GKI-Kernel angewendet würde, der kprobe nicht unterstützt.</string>
|
||||
<string name="profile_umount_modules_summary">Wenn Sie diese Option aktivieren, kann KernelSU Next alle von den Modulen für diese Anwendung geänderten Dateien wiederherstellen.</string>
|
||||
<string name="profile_selinux_domain">Domain</string>
|
||||
<string name="profile_selinux_rules">Regeln</string>
|
||||
<string name="module_update">Aktualisierung</string>
|
||||
<string name="module_downloading">Herunterladen des Moduls: %s</string>
|
||||
<string name="module_start_downloading">Mit dem Herunterladen beginnen: %s</string>
|
||||
<string name="new_version_available">Neue Version %s ist verfügbar, klicken Sie zum Aktualisieren.</string>
|
||||
<string name="launch_app">Start</string>
|
||||
<string name="close">Schließen</string>
|
||||
<string name="force_stop_app">Stopp erzwingen</string>
|
||||
<string name="restart_app">Neustart</string>
|
||||
<string name="failed_to_update_sepolicy">SELinux-Regeln konnten nicht aktualisiert werden: %s</string>
|
||||
<string name="su_not_allowed">Das Gewähren von Superuser ist nicht erlaubt: %s</string>
|
||||
<string name="module_changelog">Änderungsbericht</string>
|
||||
<string name="settings_profile_template">App Profil Vorlage</string>
|
||||
<string name="settings_profile_template_summary">Verwalten Sie lokale und Online-Vorlagen von App-Profilen</string>
|
||||
<string name="app_profile_template_create">Vorlage erstellen</string>
|
||||
<string name="app_profile_template_edit">Vorlage bearbeiten</string>
|
||||
<string name="app_profile_template_id">ID</string>
|
||||
<string name="app_profile_template_id_invalid">Ungültige Vorlagen-ID</string>
|
||||
<string name="app_profile_template_name">Name</string>
|
||||
<string name="app_profile_template_description">Beschreibung</string>
|
||||
<string name="app_profile_template_save">Speichern</string>
|
||||
<string name="app_profile_template_delete">Löschen</string>
|
||||
<string name="app_profile_template_view">Vorlage ansehen</string>
|
||||
<string name="app_profile_template_readonly">Nur lesen</string>
|
||||
<string name="app_profile_template_id_exist">Die Vorlagen-ID existiert bereits!</string>
|
||||
<string name="app_profile_import_export">Importieren/Exportieren</string>
|
||||
<string name="app_profile_import_from_clipboard">Importieren aus der Zwischenablage</string>
|
||||
<string name="app_profile_export_to_clipboard">In die Zwischenablage exportieren</string>
|
||||
<string name="app_profile_template_export_empty">Lokale Vorlage für den Export nicht gefunden!</string>
|
||||
<string name="app_profile_template_import_success">Erfolgreich importiert</string>
|
||||
<string name="app_profile_template_sync">Online-Vorlagen synchronisieren</string>
|
||||
<string name="app_profile_template_save_failed">Vorlage konnte nicht gespeichert werden</string>
|
||||
<string name="app_profile_template_import_empty">Die Zwischenablage ist leer!</string>
|
||||
<string name="module_changelog_failed">Abrufen des Änderungsberichts fehlgeschlagen: %s</string>
|
||||
<string name="settings_check_update">Prüfen auf Aktualisierung</string>
|
||||
<string name="settings_check_update_summary">Beim Öffnen der App automatisch nach Updates suchen.</string>
|
||||
<string name="grant_root_failed">Root konnte nicht gewährt werden!</string>
|
||||
<string name="action">Aktion</string>
|
||||
<string name="open">Öffnen</string>
|
||||
<string name="enable_web_debugging">WebView-Debugging aktivieren</string>
|
||||
<string name="enable_web_debugging_summary">Kann zum Debuggen von WebUI verwendet werden. Bitte nur bei Bedarf aktivieren.</string>
|
||||
<string name="direct_install">Direkte Installation (empfohlen)</string>
|
||||
<string name="select_file">Eine Datei auswählen</string>
|
||||
<string name="install_inactive_slot">Auf inaktivem slot installieren (nach OTA)</string>
|
||||
<string name="install_inactive_slot_warning">Ihr Gerät wird **gezwungen**, nach einem Neustart in den aktuell inaktiven slot zu booten!\n Verwenden Sie diese Option nur, nachdem OTA abgeschlossen ist.\nFortfahren?</string>
|
||||
<string name="install_next">Weiter</string>
|
||||
<string name="select_file_tip">%1$s Partitionsabbild wird empfohlen</string>
|
||||
<string name="select_kmi">KMI auswählen</string>
|
||||
<string name="shrink_sparse_image">Minimierung des sparse Abbildes</string>
|
||||
<string name="shrink_sparse_image_message">Ändern Sie die Größe des Sparse-Abbildes, in dem sich das Modul befindet, auf seine tatsächliche Größe. Beachten Sie, dass dies dazu führen kann, dass das Modul nicht ordnungsgemäß funktioniert, und verwenden Sie es daher nur, wenn es notwendig ist (z. B. zur Sicherung).</string>
|
||||
<string name="settings_uninstall">Deinstallieren</string>
|
||||
<string name="settings_uninstall_temporary">vorübergehend Deinstallieren</string>
|
||||
<string name="settings_uninstall_permanent">Endgültig deinstallieren</string>
|
||||
<string name="settings_restore_stock_image">Stock-Image wiederherstellen</string>
|
||||
<string name="settings_uninstall_temporary_message">Vorübergehende Deinstallation von KernelSU Next, Wiederherstellung des ursprünglichen Zustands nach dem nächsten Neustart.</string>
|
||||
<string name="settings_uninstall_permanent_message">KernelSU Next (Root und alle Module) vollständig und dauerhaft deinstallieren.</string>
|
||||
<string name="settings_restore_stock_image_message">Stellen Sie das Werksabbild wieder her (wenn ein Backup vorhanden ist), das normalerweise vor OTA verwendet wird; wenn Sie KernelSU als Nächstes deinstallieren müssen, verwenden Sie bitte \„Endgültig deinstallieren\“.</string>
|
||||
<string name="flashing">Flashing</string>
|
||||
<string name="flash_success">Flash erfolgreich</string>
|
||||
<string name="flash_failed">Flash fehlgeschlagen</string>
|
||||
<string name="selected_lkm">Ausgewähltes LKM: %s</string>
|
||||
<string name="save_log">Protokolle speichern</string>
|
||||
<string name="log_saved">Protokolle gespeichert</string>
|
||||
<string name="send_log">Protokolle teilen</string>
|
||||
<string name="settings_disable_su">Su-Kompatibilität deaktivieren</string>
|
||||
<string name="settings_disable_su_summary">Deaktivieren Sie vorübergehend alle Anwendungen, die über den Befehl su Root-Rechte erhalten (bestehende Root-Prozesse sind davon nicht betroffen).</string>
|
||||
</resources>
|
||||
197
manager/app/src/main/res/values-fa/strings.xml
Normal file
197
manager/app/src/main/res/values-fa/strings.xml
Normal file
@@ -0,0 +1,197 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="issue_report_title">مشکلی دارید؟</string>
|
||||
<string name="issue_report_body">با خطا مواجه شدهاید یا نظری دارید؟</string>
|
||||
<string name="issue_report_body_2">هرچه زودتر گزارش دهید!</string>
|
||||
<string name="issue_report_github">گزارش در گیتهاب</string>
|
||||
<string name="issue_report_telegram">تماس از طریق تلگرام</string>
|
||||
<string name="issue_report_github_link">https://github.com/KernelSU-Next/KernelSU-Next/issues</string>
|
||||
<string name="issue_report_telegram_link">https://t.me/ksunext</string>
|
||||
<string name="confirm">تأیید</string>
|
||||
<string name="app_name" translatable="false">KernelSU Next</string>
|
||||
<string name="home">خانه</string>
|
||||
<string name="home_not_installed">نصب نشده</string>
|
||||
<string name="home_click_to_install">برای نصب کلیک کنید</string>
|
||||
<string name="home_working">در حال کار</string>
|
||||
<string name="home_working_version">نسخه: %d</string>
|
||||
<string name="home_superuser_count">کاربران ویژه: %d</string>
|
||||
<string name="home_module_count">ماژولها: %d</string>
|
||||
<string name="home_failure">امضای KernelSU Next v2 در هسته یافت نشد! [ !KSU_NEXT || != size/hash ]</string>
|
||||
<string name="home_failure_tip">از توسعهدهنده هسته خود بخواهید KernelSU Next را یکپارچه کند!</string>
|
||||
<string name="home_kernel">نسخه هسته</string>
|
||||
<string name="enabled">فعال</string>
|
||||
<string name="disabled">غیرفعال</string>
|
||||
<string name="susfs_supported">پشتیبانیشده</string>
|
||||
<string name="home_susfs">SuSFS: %s</string>
|
||||
<string name="home_susfs_version">نسخه SuSFS</string>
|
||||
<string name="home_susfs_sus_su">SuS SU</string>
|
||||
<string name="home_android">نسخه اندروید</string>
|
||||
<string name="home_manager_version">نسخه مدیر</string>
|
||||
<string name="home_selinux_status">وضعیت SELinux</string>
|
||||
<string name="selinux_status_disabled">غیرفعال</string>
|
||||
<string name="selinux_status_enforcing">اجباری</string>
|
||||
<string name="selinux_status_permissive">مجاز</string>
|
||||
<string name="selinux_status_unknown">ناشناخته</string>
|
||||
<string name="superuser">کاربر ویژه</string>
|
||||
<string name="module_failed_to_enable">فعالسازی ماژول شکست خورد: %s</string>
|
||||
<string name="module_failed_to_disable">غیرفعالسازی ماژول شکست خورد: %s</string>
|
||||
<string name="module_empty">هیچ ماژولی نصب نشده</string>
|
||||
<string name="module">ماژول</string>
|
||||
<string name="module_install_prompt_with_name">ماژول(های) زیر نصب خواهد شد: %1$s</string>
|
||||
<string name="module_sort_a_to_z">مرتبسازی (الف-ی)</string>
|
||||
<string name="module_sort_z_to_a">مرتبسازی (ی-الف)</string>
|
||||
<string name="uninstall">حذف نصب</string>
|
||||
<string name="restore">بازیابی</string>
|
||||
<string name="module_install">نصب</string>
|
||||
<string name="install">نصب</string>
|
||||
<string name="reboot">راهاندازی مجدد</string>
|
||||
<string name="settings">تنظیمات</string>
|
||||
<string name="reboot_userspace">راهاندازی نرم</string>
|
||||
<string name="reboot_recovery">راهاندازی به حالت ریکاوری</string>
|
||||
<string name="reboot_bootloader">راهاندازی به حالت بوتلودر</string>
|
||||
<string name="reboot_download">راهاندازی به حالت دانلود</string>
|
||||
<string name="reboot_edl">راهاندازی به حالت EDL</string>
|
||||
<string name="about">درباره</string>
|
||||
<string name="module_uninstall_confirm">آیا مطمئن هستید که میخواهید ماژول %s را حذف کنید؟</string>
|
||||
<string name="module_uninstall_success">ماژول %s حذف شد</string>
|
||||
<string name="module_uninstall_failed">حذف ماژول شکست خورد: %s</string>
|
||||
<string name="module_restore_confirm">آیا مطمئن هستید که میخواهید ماژول %s را بازیابی کنید؟</string>
|
||||
<string name="module_restore_success">ماژول %s بازیابی شد</string>
|
||||
<string name="module_restore_failed">بازیابی ماژول شکست خورد: %s</string>
|
||||
<string name="module_version">نسخه</string>
|
||||
<string name="module_author">سازنده</string>
|
||||
<string name="module_id">شناسه</string>
|
||||
<string name="module_version_code">کد نسخه</string>
|
||||
<string name="module_update_json">بهروزرسانی JSON</string>
|
||||
<string name="module_update_json_empty">خالی</string>
|
||||
<string name="enable_developer_options">فعالسازی گزینههای توسعهدهنده</string>
|
||||
<string name="enable_developer_options_summary">نمایش تنظیمات مخفی و اطلاعات اشکالزدایی که فقط برای توسعهدهندگان مفید است.</string>
|
||||
<string name="module_overlay_fs_not_available">ماژولها به دلیل غیرفعال بودن OverlayFS توسط هسته در دسترس نیستند.</string>
|
||||
<string name="refresh">تازهسازی</string>
|
||||
<string name="show_system_apps">نمایش برنامههای سیستمی</string>
|
||||
<string name="hide_system_apps">پنهان کردن برنامههای سیستمی</string>
|
||||
<string name="export_log">صدور گزارشها</string>
|
||||
<string name="safe_mode">حالت ایمن</string>
|
||||
<string name="reboot_to_apply">برای اعمال تغییرات راهاندازی مجدد کنید</string>
|
||||
<string name="module_magisk_conflict">ماژولها به دلیل تداخل با Magisk در دسترس نیستند!</string>
|
||||
<string name="home_mount_system">نصب سیستم</string>
|
||||
<string name="home_magic_mount">نصب جادویی</string>
|
||||
<string name="home_overlayfs_mount">OverlayFS</string>
|
||||
<string name="unavailable">در دسترس نیست</string>
|
||||
<string name="use_overlay_fs">استفاده از OverlayFS</string>
|
||||
<string name="use_overlay_fs_summary">تغییر بین استفاده از OverlayFS و نصب جادویی برای سیستم نصب KernelSU Next.</string>
|
||||
<string name="reboot_required">نیاز به راهاندازی مجدد</string>
|
||||
<string name="reboot_message">تغییرات پس از راهاندازی مجدد سیستم اعمال میشوند. آیا میخواهید اکنون راهاندازی کنید؟</string>
|
||||
<string name="module_restore">بازیابی ماژول</string>
|
||||
<string name="module_restore_message">بازیابی ماژولها از آخرین نسخه پشتیبان.</string>
|
||||
<string name="backup_restore">پشتیبانگیری و بازیابی</string>
|
||||
<string name="module_backup">پشتیبانگیری از ماژول</string>
|
||||
<string name="module_backup_message">پشتیبانگیری از ماژولهای نصبشده کنونی.</string>
|
||||
<string name="allowlist_restore">بازیابی لیست مجاز</string>
|
||||
<string name="allowlist_restore_message">بازیابی لیست مجاز از آخرین نسخه پشتیبان.</string>
|
||||
<string name="allowlist_backup">پشتیبانگیری از لیست مجاز</string>
|
||||
<string name="allowlist_backup_message">پشتیبانگیری از لیست مجاز کنونی.</string>
|
||||
<string name="warning">هشدار</string>
|
||||
<string name="warning_message">این ویژگی هنوز در مرحله آزمایشی و در حال توسعه است. لطفاً پیش از ادامه، از ماژولهای خود نسخه پشتیبان تهیه کنید. این ویژگی را تنها در صورتی استفاده کنید که خطرات احتمالی را درک کرده باشید. با احتیاط ادامه دهید.</string>
|
||||
<string name="proceed">ادامه</string>
|
||||
<string name="cancel">لغو</string>
|
||||
<string name="later">بعداً</string>
|
||||
<string name="home_next_kernelsu">🔥 ساخت بعدی</string>
|
||||
<string name="home_next_kernelsu_repo">https://github.com/KernelSU-Next/KernelSU-Next</string>
|
||||
<string name="home_next_kernelsu_body">شاخه آزمایشی بعدی. آن را در گیتهاب بررسی کنید!</string>
|
||||
<string name="home_experimental_kernelsu">⚠️ هشدار توسعه آزمایشی!</string>
|
||||
<string name="home_experimental_kernelsu_repo">127.0.0.1</string>
|
||||
<string name="home_experimental_kernelsu_body">KernelSU Next نسخهای غیررسمی است که همیشه در حال توسعه آزمایشی فعال است. این نسخه همانگونه که هست ارائه میشود، بدون تضمین پایداری، عملکرد یا قابلیت اطمینان.</string>
|
||||
<string name="home_experimental_kernelsu_body_point_1"> • استفاده با مسئولیت خودتان: ممکن است با خرابی، رفتار غیرمنتظره یا مشکلات سیستمی مواجه شوید.</string>
|
||||
<string name="home_experimental_kernelsu_body_point_2"> • بدون گارانتی: توسعهدهندگان مسئول هیچگونه از دست دادن داده، آسیب به سیستم یا سایر پیامدها نیستند.</string>
|
||||
<string name="home_experimental_kernelsu_body_point_3"> • فقط برای آزمایش: برای کاربرانی که خطرات را درک کرده و با عیبیابی راحت هستند.</string>
|
||||
<string name="about_source_code"><![CDATA[مشاهده کد منبع در %1$s]]></string>
|
||||
<string name="profile" translatable="false">پروفایل برنامه</string>
|
||||
<string name="profile_default">پیشفرض</string>
|
||||
<string name="profile_template">الگو</string>
|
||||
<string name="profile_custom">سفارشی</string>
|
||||
<string name="profile_name">نام پروفایل</string>
|
||||
<string name="profile_namespace">فضای نصب</string>
|
||||
<string name="profile_namespace_inherited">ارثی</string>
|
||||
<string name="profile_namespace_global">جهانی</string>
|
||||
<string name="profile_namespace_individual">فردی</string>
|
||||
<string name="profile_groups">گروهها</string>
|
||||
<string name="profile_capabilities">قابلیتها</string>
|
||||
<string name="profile_selinux_context">زمینه SELinux</string>
|
||||
<string name="profile_umount_modules">حذف ماژولها</string>
|
||||
<string name="failed_to_update_app_profile">بهروزرسانی پروفایل برنامه برای %s شکست خورد</string>
|
||||
<string name="require_kernel_version">نسخه کنونی KernelSU Next %1$d برای عملکرد صحیح مدیر بسیار پایین است. لطفاً به نسخه %2$d یا بالاتر ارتقا دهید!</string>
|
||||
<string name="settings_umount_modules_default">حذف پیشفرض ماژولها</string>
|
||||
<string name="settings_umount_modules_default_summary">مقدار پیشفرض جهانی برای «حذف ماژولها» در پروفایل برنامه. اگر فعال باشد، تمام تغییرات سیستمی ماژولها برای برنامههایی که پروفایل ندارند حذف میشود.</string>
|
||||
<string name="settings_susfs_toggle">مخفی کردن قلابهای kprobe</string>
|
||||
<string name="settings_susfs_toggle_summary">این گزینه قلابهای kprobe ایجادشده توسط ksu را غیرفعال کرده و بهجای آن، قلابهای غیر kprobe داخلی را فعال میکند که همان عملکرد را برای هستههای غیر GKI که از kprobe پشتیبانی نمیکنند، پیادهسازی میکند.</string>
|
||||
<string name="profile_umount_modules_summary">فعال کردن این گزینه به KernelSU Next اجازه میدهد فایلهای تغییر یافته توسط ماژولها را برای این برنامه بازیابی کند.</string>
|
||||
<string name="profile_selinux_domain">دامنه</string>
|
||||
<string name="profile_selinux_rules">قوانین</string>
|
||||
<string name="module_update">بهروزرسانی</string>
|
||||
<string name="module_downloading">در حال دانلود ماژول: %s</string>
|
||||
<string name="module_start_downloading">شروع دانلود: %s</string>
|
||||
<string name="new_version_available">نسخه جدید %s در دسترس است، برای ارتقا کلیک کنید.</string>
|
||||
<string name="launch_app">اجرا</string>
|
||||
<string name="close">بستن</string>
|
||||
<string name="force_stop_app">توقف اجباری</string>
|
||||
<string name="restart_app">راهاندازی مجدد</string>
|
||||
<string name="failed_to_update_sepolicy">بهروزرسانی قوانین SELinux برای %s شکست خورد</string>
|
||||
<string name="su_not_allowed">اعطای دسترسی ویژه برای %s مجاز نیست</string>
|
||||
<string name="module_changelog">تغییرات</string>
|
||||
<string name="settings_profile_template">الگوی پروفایل برنامه</string>
|
||||
<string name="settings_profile_template_summary">مدیریت الگوهای محلی و آنلاین پروفایل برنامه</string>
|
||||
<string name="app_profile_template_create">ایجاد الگو</string>
|
||||
<string name="app_profile_template_edit">ویرایش الگو</string>
|
||||
<string name="app_profile_template_id">شناسه</string>
|
||||
<string name="app_profile_template_id_invalid">شناسه الگو نامعتبر است</string>
|
||||
<string name="app_profile_template_name">نام</string>
|
||||
<string name="app_profile_template_description">توضیحات</string>
|
||||
<string name="app_profile_template_save">ذخیره</string>
|
||||
<string name="app_profile_template_delete">حذف</string>
|
||||
<string name="app_profile_template_view">مشاهده الگو</string>
|
||||
<string name="app_profile_template_readonly">فقط خواندنی</string>
|
||||
<string name="app_profile_template_id_exist">شناسه الگو قبلاً وجود دارد!</string>
|
||||
<string name="app_profile_import_export">واردات/صادرات</string>
|
||||
<string name="app_profile_import_from_clipboard">واردات از کلیپبورد</string>
|
||||
<string name="app_profile_export_to_clipboard">صادرات به کلیپبورد</string>
|
||||
<string name="app_profile_template_export_empty">هیچ الگوی محلی برای صادرات یافت نشد!</string>
|
||||
<string name="app_profile_template_import_success">با موفقیت وارد شد</string>
|
||||
<string name="app_profile_template_sync">همگامسازی الگوهای آنلاین</string>
|
||||
<string name="app_profile_template_save_failed">ذخیره الگو شکست خورد</string>
|
||||
<string name="app_profile_template_import_empty">کلیپبورد خالی است!</string>
|
||||
<string name="module_changelog_failed">دریافت تغییرات شکست خورد: %s</string>
|
||||
<string name="settings_check_update">بررسی بهروزرسانی</string>
|
||||
<string name="settings_check_update_summary">بررسی خودکار بهروزرسانیها هنگام باز کردن برنامه.</string>
|
||||
<string name="grant_root_failed">اعطای دسترسی ریشه شکست خورد!</string>
|
||||
<string name="action">اقدام</string>
|
||||
<string name="open">باز کردن</string>
|
||||
<string name="enable_web_debugging">فعالسازی اشکالزدایی وبویو</string>
|
||||
<string name="enable_web_debugging_summary">میتواند برای اشکالزدایی رابط وب استفاده شود. لطفاً فقط در صورت نیاز فعال کنید.</string>
|
||||
<string name="direct_install">نصب مستقیم (توصیهشده)</string>
|
||||
<string name="select_file">انتخاب فایل</string>
|
||||
<string name="install_inactive_slot">نصب در اسلات غیرفعال (پس از OTA)</string>
|
||||
<string name="install_inactive_slot_warning">دستگاه شما پس از راهاندازی مجدد **اجباراً** به اسلات غیرفعال کنونی بوت خواهد شد!\nفقط پس از انجام OTA از این گزینه استفاده کنید.\nادامه میدهید؟</string>
|
||||
<string name="install_next">بعدی</string>
|
||||
<string name="select_file_tip">تصویر پارتیشن %1$s توصیه میشود</string>
|
||||
<string name="select_kmi">انتخاب KMI</string>
|
||||
<string name="shrink_sparse_image">کاهش اندازه تصویر پراکنده</string>
|
||||
<string name="shrink_sparse_image_message">تغییر اندازه تصویر پراکندهای که ماژول در آن قرار دارد به اندازه واقعی آن. توجه داشته باشید که این ممکن است باعث عملکرد غیرعادی ماژول شود، بنابراین فقط در صورت نیاز (مانند پشتیبانگیری) استفاده کنید.</string>
|
||||
<string name="settings_uninstall">حذف نصب</string>
|
||||
<string name="settings_uninstall_temporary">حذف نصب موقت</string>
|
||||
<string name="settings_uninstall_permanent">حذف نصب دائمی</string>
|
||||
<string name="settings_restore_stock_image">بازیابی تصویر اصلی</string>
|
||||
<string name="settings_uninstall_temporary_message">حذف موقت KernelSU Next، بازگشت به حالت اولیه پس از راهاندازی بعدی.</string>
|
||||
<string name="settings_uninstall_permanent_message">حذف کامل و دائمی KernelSU Next (ریشه و همه ماژولها).</string>
|
||||
<string name="settings_restore_stock_image_message">بازیابی تصویر کارخانهای (در صورت وجود نسخه پشتیبان)، معمولاً قبل از OTA استفاده میشود؛ اگر نیاز به حذف KernelSU Next دارید، لطفاً از «حذف نصب دائمی» استفاده کنید.</string>
|
||||
<string name="flashing">در حال فلش</string>
|
||||
<string name="flash_success">فلش موفق</string>
|
||||
<string name="flash_failed">فلش ناموفق</string>
|
||||
<string name="selected_lkm">LKM انتخابشده: %s</string>
|
||||
<string name="save_log">ذخیره گزارشها</string>
|
||||
<string name="log_saved">گزارشها ذخیره شدند</string>
|
||||
<string name="send_log">اشتراک گزارشها</string>
|
||||
<string name="settings_disable_su">غیرفعال کردن سازگاری su</string>
|
||||
<string name="settings_disable_su_summary">غیرفعال کردن موقت توانایی هر برنامه برای کسب دسترسی ریشه از طریق دستور su (فرآیندهای ریشه موجود تحت تأثیر قرار نمیگیرند).</string>
|
||||
<string name="settings_language">زبان</string>
|
||||
</resources>
|
||||
@@ -5,7 +5,7 @@
|
||||
<string name="issue_report_body_2">Signalez-le dès que possible !</string>
|
||||
<string name="issue_report_github">Signaler sur GitHub</string>
|
||||
<string name="issue_report_telegram">Contacter via Telegram</string>
|
||||
<string name="issue_report_github_link">https://github.com/rifsxd/KernelSU-Next/issues</string>
|
||||
<string name="issue_report_github_link">https://github.com/KernelSU-Next/KernelSU-Next/issues</string>
|
||||
<string name="issue_report_telegram_link">https://t.me/ksunext</string>
|
||||
<string name="confirm">Confirmer</string>
|
||||
<string name="app_name" translatable="false">KernelSU Next</string>
|
||||
@@ -26,10 +26,10 @@
|
||||
<string name="home_manager_version">Version du manager</string>
|
||||
<string name="home_selinux_status">Statut SELinux</string>
|
||||
<string name="selinux_status_disabled">Désactivé</string>
|
||||
<string name="selinux_status_enforcing">En vigueur</string>
|
||||
<string name="selinux_status_enforcing">Appliqué</string>
|
||||
<string name="selinux_status_permissive">Permissif</string>
|
||||
<string name="selinux_status_unknown">Inconnu</string>
|
||||
<string name="superuser">SuperUtilisateur</string>
|
||||
<string name="superuser">Super Utilisateur</string>
|
||||
<string name="module_failed_to_enable">Échec de l\'activation du module: %s</string>
|
||||
<string name="module_failed_to_disable">Échec de la désactivation du module: %s</string>
|
||||
<string name="module_empty">Aucun module installé</string>
|
||||
@@ -43,10 +43,10 @@
|
||||
<string name="install">Installer</string>
|
||||
<string name="reboot">Redémarrer</string>
|
||||
<string name="settings">Paramètres</string>
|
||||
<string name="reboot_userspace">Redémarrage léger</string>
|
||||
<string name="reboot_userspace">Redémarrage logiciel</string>
|
||||
<string name="reboot_recovery">Redémarrer en mode Recovery</string>
|
||||
<string name="reboot_bootloader">Redémarrer en mode Bootloader</string>
|
||||
<string name="reboot_download">Redémarrer sur les Téléchargements</string>
|
||||
<string name="reboot_download">Redémarrer en mode téléchargement</string>
|
||||
<string name="reboot_edl">Redémarrer en mode EDL</string>
|
||||
<string name="about">À propos</string>
|
||||
<string name="module_uninstall_confirm">Êtes-vous sûr de vouloir désinstaller le module %s?</string>
|
||||
@@ -54,36 +54,43 @@
|
||||
<string name="module_uninstall_failed">Échec de la désinstallation: %s</string>
|
||||
<string name="module_restore_confirm">Êtes-vous sûr de vouloir restaurer le module %s?</string>
|
||||
<string name="module_restore_success">%s a été restauré</string>
|
||||
<string name="module_restore_failed">Échec de la restauration: %s</string>
|
||||
<string name="module_restore_failed">Échec de la restoration: %s</string>
|
||||
<string name="module_version">Version</string>
|
||||
<string name="module_author">Auteur</string>
|
||||
<string name="enable_developer_options">Activer les options pour les développeurs</string>
|
||||
<string name="enable_developer_options_summary">Afficher des paramètres cachés et des information de débogage seulement utiles pour les développeurs.</string>
|
||||
<string name="module_overlay_fs_not_available">Les modules sont indisponibles car OverlayFS est désactivé par le kernel.</string>
|
||||
<string name="refresh">Rafraîchir</string>
|
||||
<string name="show_system_apps">Montrer les apps système</string>
|
||||
<string name="hide_system_apps">Cacher les apps système</string>
|
||||
<string name="export_log">Exporter les logs</string>
|
||||
<string name="safe_mode">Mode sécurité</string>
|
||||
<string name="safe_mode">Mode sécurisé</string>
|
||||
<string name="reboot_to_apply">Redémarrer pour appliquer les changements</string>
|
||||
<string name="module_magisk_conflict">Les modules sont indisponibles en raison d\'un conflit avec Magisk!</string>
|
||||
<string name="home_module_mount">Module système</string>
|
||||
<string name="home_mount_system">Système de module</string>
|
||||
<string name="home_magic_mount">Magic Mount</string>
|
||||
<string name="home_overlayfs_mount">OverlayFS</string>
|
||||
<string name="unavailable">Indisponible</string>
|
||||
<string name="use_overlay_fs">Utiliser OverlayFS (Beta)</string>
|
||||
<string name="use_overlay_fs">Utiliser OverlayFS</string>
|
||||
<string name="use_overlay_fs_summary">Alterner entre l\’utilisation d’OverlayFS et de Magic Mount pour le système de montage de KernelSU Next.</string>
|
||||
<string name="reboot_required">Redémarrage requis</string>
|
||||
<string name="reboot_message">Les changements ne seront effectifs qu\'après un redémarrage. Voulez-vous redémarrer maintenant?</string>
|
||||
<string name="module_restore">Restaurer des modules</string>
|
||||
<string name="module_restore_message">Restaurer des modules depuis une sauvegarde récente</string>
|
||||
<string name="backup_restore">Sauvegarde et restoration</string>
|
||||
<string name="module_backup">Sauvegarder les modules</string>
|
||||
<string name="module_backup_message">Sauvegarder les modules installés</string>
|
||||
<string name="allowlist_restore">Restorer la liste d\'autorisation</string>
|
||||
<string name="allowlist_restore_message">Restorer la liste d\'autorisation depuis une sauvegarde récente ?</string>
|
||||
<string name="allowlist_backup">Sauvegarder la liste d\'autorisation</string>
|
||||
<string name="allowlist_backup_message">Sauvegarder la liste d\'autorisation actuelle ?</string>
|
||||
<string name="warning">Avertissement</string>
|
||||
<string name="warning_message">Cette fonctionnalité est encore en version bêta et en cours de développement. Veuillez sauvegarder vos modules avant de continuer. Utilisez cette fonctionnalité uniquement si vous comprenez les risques potentiels. Procédez avec prudence.</string>
|
||||
<string name="proceed">Continuer</string>
|
||||
<string name="cancel">Annuler</string>
|
||||
<string name="later">Plus tard</string>
|
||||
<string name="home_next_kernelsu">🔥 Next build</string>
|
||||
<string name="home_next_kernelsu_repo">https://github.com/rifsxd/KernelSU-Next</string>
|
||||
<string name="home_next_kernelsu_repo">https://github.com/KernelSU-Next/KernelSU-Next</string>
|
||||
<string name="home_next_kernelsu_body">Branche expérimentale Next. Allez la voir sur Github!</string>
|
||||
<string name="home_experimental_kernelsu">⚠️ Attention, développement expérimental!</string>
|
||||
<string name="home_experimental_kernelsu_repo">127.0.0.1</string>
|
||||
@@ -91,7 +98,7 @@
|
||||
<string name="home_experimental_kernelsu_body_point_1"> • Soyez donc en connaissance des risques : vous pourriez faire face à des crashs, des réactions inattendues ou encore des erreurs système.</string>
|
||||
<string name="home_experimental_kernelsu_body_point_2"> • Aucune garantie : Les développeurs ne seront tenus responsables d\'aucune perte de données, de dégâts au système ou d\'autres conséquences résultant de l\'utilisation de KernelSU Next.</string>
|
||||
<string name="home_experimental_kernelsu_body_point_3"> • Uniquement à but de tests : KernelSU Next est prévue pour des utilisateurs avertis, qui comprennent les risques et qui sont à l\'aise avec le fait de localiser un problème.</string>
|
||||
<string name="about_source_code"><![CDATA[View source code at %1$s]]></string>
|
||||
<string name="about_source_code"><![CDATA[Voir le code source sur %1$s]]></string>
|
||||
<string name="profile" translatable="false">Profil d\'application</string>
|
||||
<string name="profile_default">Par défaut</string>
|
||||
<string name="profile_template">Modèle</string>
|
||||
@@ -106,12 +113,12 @@
|
||||
<string name="profile_selinux_context">Contexte SELinux</string>
|
||||
<string name="profile_umount_modules">Démonter les modules</string>
|
||||
<string name="failed_to_update_app_profile">Échec de la mise à jour du profil d\'application pour %s</string>
|
||||
<string name="require_kernel_version">La version actuelle de KernelSU next %1$d Est trop ancienne pour que le gestionnaire fonctionne correctement. Merci de mettre à jour vers la version %2$d ou supérieur!</string>
|
||||
<string name="require_kernel_version">La version actuelle de KernelSU Next %1$d Est trop ancienne pour que le gestionnaire fonctionne correctement. Merci de mettre à jour vers la version %2$d ou supérieur!</string>
|
||||
<string name="settings_umount_modules_default">Démonter les modules par défaut</string>
|
||||
<string name="settings_umount_modules_default_summary">Valeur globale par défaut pour \"Démonter les modules\" Dans le profil d\'application. Si activé, toutes les modifications systèmes effectuées par des modules seront annulées pour les applications n\'ayant pas de profil sélectionné.</string>
|
||||
<string name="settings_susfs_toggle">Cacher les hooks kprobe</string>
|
||||
<string name="settings_susfs_toggle_summary">Désactive les hooks kprobe créés par KSU et, à la place, active les hooks non-kprobe intégrés, implémentant les mêmes fonctionnalités qui seraient appliquées à un kernel non-GKI, qui ne supportent krpobe.</string>
|
||||
<string name="profile_umount_modules_summary">Activer cette option permettra à KernelSU Next de restaurer n\'importe quel fichier modifié par les modules pour cette app.</string>
|
||||
<string name="settings_susfs_toggle_summary">Désactive les hooks kprobe créés par KSU et, à la place, active les hooks non-kprobe intégrés, implémentant les mêmes fonctionnalités qui seraient appliquées à un kernel non-GKI, qui ne supportent pas les kprobes.</string>
|
||||
<string name="profile_umount_modules_summary">Activer cette option permettra à KernelSU Next de restaurer n\'importe quel fichier modifié par les modules pour cette application.</string>
|
||||
<string name="profile_selinux_domain">Domaine</string>
|
||||
<string name="profile_selinux_rules">Règles</string>
|
||||
<string name="module_update">Mise à jour</string>
|
||||
@@ -151,8 +158,8 @@
|
||||
<string name="grant_root_failed">Échec de l\'accord du statut root!</string>
|
||||
<string name="action">Action</string>
|
||||
<string name="open">Ouvrir</string>
|
||||
<string name="enable_web_debugging">Activer le débuggage WebView</string>
|
||||
<string name="enable_web_debugging_summary">Peut être utilisé pour débug la WebUI. Merci de ne l\'activer qu\'en cas de nécéssité.</string>
|
||||
<string name="enable_web_debugging">Activer le débogage de WebView</string>
|
||||
<string name="enable_web_debugging_summary">Peut être utilisé pour déboguer la WebUI. Merci de ne l\'activer qu\'en cas de nécéssité.</string>
|
||||
<string name="direct_install">Installation directe (recommandé)</string>
|
||||
<string name="select_file">Sélectionner un fichier</string>
|
||||
<string name="install_inactive_slot">Installer sur un slot inactif (après OTA)</string>
|
||||
@@ -161,12 +168,12 @@
|
||||
<string name="select_file_tip">%1$s Partitionner l\'image est recommandé.</string>
|
||||
<string name="select_kmi">Sélectionner le KMI</string>
|
||||
<string name="shrink_sparse_image">Minimiser l\'espace disponible réservé</string>
|
||||
<string name="shrink_sparse_image_message">Redimensionne l\'image où se trouve le module à sa taille réelle. Notez que cela peut entraîner un fonctionnement anormal du module. Veuillez donc l\'utiliser uniquement en cas de nécessité (par exemple, pour une sauvegarde).</string>
|
||||
<string name="shrink_sparse_image_message">Redimensionne l\'image où se trouve le module à sa taille réelle. Notez que cela peut entraîner un fonctionnement anormal des modules. Veuillez donc l\'utiliser uniquement en cas de nécessité (par exemple, pour une sauvegarde).</string>
|
||||
<string name="settings_uninstall">Désinstaller</string>
|
||||
<string name="settings_uninstall_temporary">Désinstaller temporairement</string>
|
||||
<string name="settings_uninstall_permanent">Désinstaller de façon permanente</string>
|
||||
<string name="settings_restore_stock_image">Restaurer l\'image stock</string>
|
||||
<string name="settings_uninstall_temporary_message">Déinsnstalle temporairement KernelSU Next, qui sera restauré au prochain redémarrage.</string>
|
||||
<string name="settings_uninstall_temporary_message">Désinstalle temporairement KernelSU Next, qui sera restauré au prochain redémarrage.</string>
|
||||
<string name="settings_uninstall_permanent_message">Désinstallation de KernelSU Next (Root et les modules) complètement et de façon définitive.</string>
|
||||
<string name="settings_restore_stock_image_message">Restaure l\'image stock (si une sauvegarde existe), habituellement utilisée avant une mise à jour OTA; Si vous avez besoin de désinstaller KernelSU Next, Merci de sélectionner \"Désinstaller de façon permanente\".</string>
|
||||
<string name="flashing">En cours de flash...</string>
|
||||
@@ -176,4 +183,6 @@
|
||||
<string name="save_log">Enregistrer les logs</string>
|
||||
<string name="log_saved">Logs Enregistrés</string>
|
||||
<string name="send_log">Partager les logs</string>
|
||||
<string name="settings_disable_su">Désactiver la compatibilité su</string>
|
||||
<string name="settings_disable_su_summary">Désactive temporairement la possibilité d\'obtenir les permissions root en utilisant la commande su pour toutes les application (les processus root actifs ne seront pas affectés).</string>
|
||||
</resources>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user