You've already forked KernelSU
mirror of
https://github.com/tiann/KernelSU.git
synced 2025-08-27 23:46:34 +00:00
Compare commits
320 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b52bf53d01 | ||
|
|
7bdb885816 | ||
|
|
cde3e95180 | ||
|
|
7ecb4b03ee | ||
|
|
e8a90aadb2 | ||
|
|
beaa048be3 | ||
|
|
936b650f12 | ||
|
|
747c91d5c8 | ||
|
|
5023d0ab1d | ||
|
|
5b638c876e | ||
|
|
d6a7231fae | ||
|
|
06681a2490 | ||
|
|
278cbef3ec | ||
|
|
a83390b0ec | ||
|
|
907bcad1a7 | ||
|
|
4c0a36785b | ||
|
|
983ad2c1fd | ||
|
|
a3590b767e | ||
|
|
12e00dc717 | ||
|
|
e00a355fa8 | ||
|
|
e95c5a9675 | ||
|
|
d0b8144b96 | ||
|
|
d16d3f87a6 | ||
|
|
76decba8d9 | ||
|
|
d75678fca0 | ||
|
|
abe1fa471d | ||
|
|
07a430aa5b | ||
|
|
df9cf61575 | ||
|
|
bfe8c2eecd | ||
|
|
b732765811 | ||
|
|
a966252fa5 | ||
|
|
3664003260 | ||
|
|
978178afc0 | ||
|
|
16f6f30eae | ||
|
|
63851f8c88 | ||
|
|
34c6765752 | ||
|
|
c23d1bcf58 | ||
|
|
6ae7e1624f | ||
|
|
ba4b014a99 | ||
|
|
f192638943 | ||
|
|
0323ee7958 | ||
|
|
021ef521cb | ||
|
|
34086cd445 | ||
|
|
c306eddee8 | ||
|
|
c72f7d750e | ||
|
|
f9a91848ad | ||
|
|
cd8013a616 | ||
|
|
dc536d652a | ||
|
|
3114f6d7f6 | ||
|
|
e2f5015107 | ||
|
|
884dd606eb | ||
|
|
8d246a6b9d | ||
|
|
2ee3d55c88 | ||
|
|
55b540bf4c | ||
|
|
c0d147dcad | ||
|
|
be413fd147 | ||
|
|
786f3d6441 | ||
|
|
8f50b5f6e5 | ||
|
|
90639fad6c | ||
|
|
2979434e2a | ||
|
|
56d145666c | ||
|
|
db5f77aa96 | ||
|
|
5fbce8ef07 | ||
|
|
4cc3644416 | ||
|
|
01a7678a26 | ||
|
|
95d22d2bb4 | ||
|
|
3efb2be456 | ||
|
|
3b7ca2d7e8 | ||
|
|
7b45bc5aad | ||
|
|
ed1e892600 | ||
|
|
f188802044 | ||
|
|
965c23867f | ||
|
|
b271b2f587 | ||
|
|
0953f50e0c | ||
|
|
77ac974ce8 | ||
|
|
d714ab0c5d | ||
|
|
38eb93d5ca | ||
|
|
080d5bd9e8 | ||
|
|
d05ec41379 | ||
|
|
d07956ead6 | ||
|
|
4e3af6dab3 | ||
|
|
45d96b98c5 | ||
|
|
b554c66b46 | ||
|
|
fb87d0f0f5 | ||
|
|
37abe48702 | ||
|
|
ec9babea76 | ||
|
|
8a464ac7b2 | ||
|
|
eb5a99e4b6 | ||
|
|
047312e0e5 | ||
|
|
45a25eda50 | ||
|
|
120c2f43de | ||
|
|
9deb820923 | ||
|
|
ae21d4c9fd | ||
|
|
5381ceabae | ||
|
|
5d07e1d392 | ||
|
|
d78f24098a | ||
|
|
cd952f5e45 | ||
|
|
156b17f69d | ||
|
|
f4d2b0feab | ||
|
|
2c0a9cd64c | ||
|
|
134507b928 | ||
|
|
2b42d14ef8 | ||
|
|
e6071b5247 | ||
|
|
f288cfccae | ||
|
|
1cc9da5efe | ||
|
|
0aee64f339 | ||
|
|
cbbdc665c8 | ||
|
|
1b2635784f | ||
|
|
ee5d2f8c84 | ||
|
|
dfc2a86e70 | ||
|
|
c0066b68f5 | ||
|
|
6ef5e4ef76 | ||
|
|
e3e77fde78 | ||
|
|
8ea55c7f2f | ||
|
|
d2a976b3cc | ||
|
|
d675662862 | ||
|
|
839b318785 | ||
|
|
971f59c11e | ||
|
|
ab58808b64 | ||
|
|
9cbb7cb10e | ||
|
|
70f2df11d1 | ||
|
|
827a2f2901 | ||
|
|
a9c33f6940 | ||
|
|
2bb73a2a92 | ||
|
|
90407986be | ||
|
|
b85ece440b | ||
|
|
a10d2651c1 | ||
|
|
b308a368d3 | ||
|
|
e6fea652de | ||
|
|
0856b718de | ||
|
|
6f1ccc5b3c | ||
|
|
da959b4e17 | ||
|
|
0bfd6d9e30 | ||
|
|
980f1d09bc | ||
|
|
b644c124e3 | ||
|
|
65005131bd | ||
|
|
18aa7f2a17 | ||
|
|
cd5bc2efa9 | ||
|
|
477361f119 | ||
|
|
d3632e4b3b | ||
|
|
0c2f90123b | ||
|
|
09d90e1a0a | ||
|
|
4fe167c361 | ||
|
|
58ffaeb4e2 | ||
|
|
76499ee629 | ||
|
|
fedfa3e3fd | ||
|
|
2902e42f91 | ||
|
|
37f4045499 | ||
|
|
12761ee167 | ||
|
|
0d254237a4 | ||
|
|
f5bb24676e | ||
|
|
303a3a8482 | ||
|
|
07273b6971 | ||
|
|
c7c9e9c3ed | ||
|
|
c3c990c50a | ||
|
|
6942fe12b5 | ||
|
|
f5cfb32882 | ||
|
|
e17f3eab96 | ||
|
|
08884da423 | ||
|
|
5f1d70dabb | ||
|
|
79bb9813ef | ||
|
|
1cda4ba245 | ||
|
|
1cc678d20b | ||
|
|
cd3e292489 | ||
|
|
40ea27a59c | ||
|
|
e95ca9316c | ||
|
|
9b2f907a48 | ||
|
|
90299ad0bb | ||
|
|
22d084f89b | ||
|
|
99770a7362 | ||
|
|
bbc7ebe5b1 | ||
|
|
d131b7561d | ||
|
|
ff8c614737 | ||
|
|
c12ad9d1c7 | ||
|
|
1703c16948 | ||
|
|
a48d7b112f | ||
|
|
f2d5e57f01 | ||
|
|
4111bbf720 | ||
|
|
cd32ad8751 | ||
|
|
fefb826177 | ||
|
|
e27fc04b5b | ||
|
|
168f412324 | ||
|
|
bd8434f4f4 | ||
|
|
c697398893 | ||
|
|
1e0176242a | ||
|
|
0e5e12b331 | ||
|
|
a2b240121a | ||
|
|
246dadea05 | ||
|
|
b5291077a9 | ||
|
|
ee548d513a | ||
|
|
bdd269ea22 | ||
|
|
e1d7bb91e8 | ||
|
|
593bc06302 | ||
|
|
8b6204a534 | ||
|
|
01ea9c9dce | ||
|
|
12e664b0a4 | ||
|
|
0ca92b9de9 | ||
|
|
2fc9e03d86 | ||
|
|
4218aa488f | ||
|
|
d95ede1f76 | ||
|
|
56f72e8702 | ||
|
|
964bf34325 | ||
|
|
3191165ba2 | ||
|
|
3ad27c5999 | ||
|
|
5b27f6c010 | ||
|
|
be44fad288 | ||
|
|
2a5fb76f95 | ||
|
|
5db7075432 | ||
|
|
1408175a35 | ||
|
|
ab1dc894e1 | ||
|
|
c5bc1c8b15 | ||
|
|
6d1ee60d67 | ||
|
|
a58e929205 | ||
|
|
53be8612c8 | ||
|
|
c7f6a7d11b | ||
|
|
e9011041c5 | ||
|
|
9803371fdb | ||
|
|
710edb72fa | ||
|
|
a4ddf59562 | ||
|
|
37dc9a27a7 | ||
|
|
27ccfa6395 | ||
|
|
116bc56cfe | ||
|
|
9ae0126be5 | ||
|
|
e8755f8ae0 | ||
|
|
6ba2bd3af9 | ||
|
|
4c5e485e76 | ||
|
|
05c6892f43 | ||
|
|
670a20c37f | ||
|
|
75509aba72 | ||
|
|
887f02e742 | ||
|
|
ef8da39d34 | ||
|
|
b7c759ece1 | ||
|
|
019da4a634 | ||
|
|
33aa6de50b | ||
|
|
0b3688c3b1 | ||
|
|
076e5d3655 | ||
|
|
c9608af0c8 | ||
|
|
908fbadaf5 | ||
|
|
ee97fdfc56 | ||
|
|
4ac137313f | ||
|
|
11d0029a4b | ||
|
|
8af5a9038b | ||
|
|
5d449988fb | ||
|
|
e969563df0 | ||
|
|
990626cf7d | ||
|
|
fc77ca989f | ||
|
|
2ce3976023 | ||
|
|
63ec531814 | ||
|
|
a4b55b30ca | ||
|
|
807556f361 | ||
|
|
2d854f2f37 | ||
|
|
5980c113fe | ||
|
|
1491465b55 | ||
|
|
728380222a | ||
|
|
005404f552 | ||
|
|
b55c229038 | ||
|
|
32538c9833 | ||
|
|
b31fc47197 | ||
|
|
5003824fa8 | ||
|
|
eea75b72a4 | ||
|
|
82d5ec3cc1 | ||
|
|
75721be8c0 | ||
|
|
de72eedb46 | ||
|
|
66827ab7de | ||
|
|
2a33433272 | ||
|
|
581dff8a5f | ||
|
|
fc425cbba2 | ||
|
|
87f55c1acb | ||
|
|
68d639e325 | ||
|
|
89f6cd044e | ||
|
|
bd3a1291da | ||
|
|
3abb7e4ca2 | ||
|
|
41265b0203 | ||
|
|
f2cb841b8a | ||
|
|
c69da29081 | ||
|
|
e304ef8cfb | ||
|
|
2ce76351da | ||
|
|
66cbd931a7 | ||
|
|
07bc28e386 | ||
|
|
2ef4ffe5eb | ||
|
|
128e7e394e | ||
|
|
998dc9b94b | ||
|
|
c6dafb1333 | ||
|
|
8ae7299d59 | ||
|
|
8f1b9c579b | ||
|
|
4b6573b521 | ||
|
|
325e843569 | ||
|
|
d014947a54 | ||
|
|
8858cc899e | ||
|
|
8f3e59803f | ||
|
|
95044bb551 | ||
|
|
b56448a929 | ||
|
|
c2b981dbdb | ||
|
|
28fb482720 | ||
|
|
d744a705a8 | ||
|
|
c62b89f02b | ||
|
|
f876b0114e | ||
|
|
9965988f26 | ||
|
|
828bb6fbb4 | ||
|
|
10d8d9efcd | ||
|
|
e59f3333cb | ||
|
|
31a9189d80 | ||
|
|
d162221fac | ||
|
|
0cdca0d053 | ||
|
|
4c934d460b | ||
|
|
76612b9cf7 | ||
|
|
9cf8ac9c51 | ||
|
|
b80e06256d | ||
|
|
08d9e5d6bc | ||
|
|
ed0cfd231e | ||
|
|
538d3f06f4 | ||
|
|
f5d3fb6217 | ||
|
|
ffa3579e6f | ||
|
|
c7adb8e3b1 | ||
|
|
c1427f658a | ||
|
|
eccce7b31f | ||
|
|
f81caf75a9 | ||
|
|
d4680c6de7 | ||
|
|
2716ec58a0 | ||
|
|
f25dbd8fed |
5
.github/FUNDING.yml
vendored
Normal file
5
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: tiann
|
||||
patreon: weishu
|
||||
custom: https://vxposed.com/donate.html
|
||||
10
.github/ISSUE_TEMPLATE/custom.md
vendored
10
.github/ISSUE_TEMPLATE/custom.md
vendored
@@ -1,10 +0,0 @@
|
||||
---
|
||||
name: Custom issue template
|
||||
about: Describe this issue template's purpose here.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
11
.github/ISSUE_TEMPLATE/custom.yml
vendored
Normal file
11
.github/ISSUE_TEMPLATE/custom.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
name: Custom issue template
|
||||
description: Ask questions about other aspects of the project
|
||||
title: '[Custom]'
|
||||
body:
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: "Describe your problem."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
40
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
40
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
name: Feature Request
|
||||
description: "Suggest an idea for this project"
|
||||
title: "[Feature]"
|
||||
labels: "feature"
|
||||
assignees: tiann
|
||||
body:
|
||||
- type: markdown
|
||||
id: feature-info
|
||||
attributes:
|
||||
value: "## Feature Infomation"
|
||||
- type: textarea
|
||||
id: feature-main
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "Is your feature request related to a problem? Please describe."
|
||||
description: "A clear and concise description of what the problem is."
|
||||
placeholder: "I'm always frustrated when [...]"
|
||||
- type: textarea
|
||||
id: feature-solution
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "Describe the solution you'd like."
|
||||
description: "A clear and concise description of what you want to happen."
|
||||
- type: textarea
|
||||
id: feature-describe
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "Describe alternatives you've considered."
|
||||
description: "A clear and concise description of any alternative solutions or features you've considered."
|
||||
- type: textarea
|
||||
id: feature-extra
|
||||
validations:
|
||||
required: false
|
||||
attributes:
|
||||
label: "Additional context"
|
||||
description: "Add any other context or screenshots about the feature request here."
|
||||
|
||||
10
.github/scripts/build_a12.sh
vendored
10
.github/scripts/build_a12.sh
vendored
@@ -11,7 +11,15 @@ build_from_image() {
|
||||
echo "[+] patch level: $PATCH_LEVEL"
|
||||
|
||||
echo '[+] Download prebuilt ramdisk'
|
||||
curl -Lo gki-kernel.zip https://dl.google.com/android/gki/gki-certified-boot-android12-5.10-"${PATCH_LEVEL}"_r1.zip
|
||||
GKI_URL=https://dl.google.com/android/gki/gki-certified-boot-android12-5.10-"${PATCH_LEVEL}"_r1.zip
|
||||
FALLBACK_URL=https://dl.google.com/android/gki/gki-certified-boot-android12-5.10-2023-01_r1.zip
|
||||
status=$(curl -sL -w "%{http_code}" "$GKI_URL" -o /dev/null)
|
||||
if [ "$status" = "200" ]; then
|
||||
curl -Lo gki-kernel.zip "$GKI_URL"
|
||||
else
|
||||
echo "[+] $GKI_URL not found, using $FALLBACK_URL"
|
||||
curl -Lo gki-kernel.zip "$FALLBACK_URL"
|
||||
fi
|
||||
unzip gki-kernel.zip && rm gki-kernel.zip
|
||||
|
||||
echo '[+] Unpack prebuilt boot.img'
|
||||
|
||||
6
.github/workflows/add-device.yml
vendored
6
.github/workflows/add-device.yml
vendored
@@ -42,12 +42,12 @@ jobs:
|
||||
run: |
|
||||
echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
|
||||
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"
|
||||
- uses: ben-z/actions-comment-on-issue@1.0.2
|
||||
- uses: Kernel-SU/actions-comment-on-issue@master
|
||||
if: ${{ steps.cpr.outputs.pull-request-number }}
|
||||
with:
|
||||
message: "Automatically created pull request: ${{ steps.cpr.outputs.pull-request-url }}"
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: ben-z/actions-comment-on-issue@1.0.2
|
||||
- uses: Kernel-SU/actions-comment-on-issue@master
|
||||
if: steps.handle-add-device.outputs.success != 'true'
|
||||
with:
|
||||
message: "Cannot create pull request. Please check the issue content. Or you can create a pull request manually."
|
||||
@@ -56,4 +56,4 @@ jobs:
|
||||
uses: peter-evans/close-issue@v1
|
||||
with:
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
31
.github/workflows/build-debug-kernel.yml
vendored
Normal file
31
.github/workflows/build-debug-kernel.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Build debug kernel
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-debug-kernel-a12:
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android12-5.10
|
||||
version_name: android12-5.10.177
|
||||
tag: android12-5.10-2023-06
|
||||
os_patch_level: 2023-06
|
||||
patch_path: "5.10"
|
||||
debug: true
|
||||
build-debug-kernel-a13:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.10"
|
||||
sub_level: 177
|
||||
os_patch_level: 2023-06
|
||||
- version: "5.15"
|
||||
sub_level: 94
|
||||
os_patch_level: 2023-06
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android13-${{ matrix.version }}
|
||||
version_name: android13-${{ matrix.version }}.${{ matrix.sub_level }}
|
||||
tag: android13-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||
patch_path: ${{ matrix.version }}
|
||||
debug: true
|
||||
20
.github/workflows/build-kernel-a12.yml
vendored
20
.github/workflows/build-kernel-a12.yml
vendored
@@ -21,6 +21,16 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- sub_level: 66
|
||||
os_patch_level: 2022-01
|
||||
- sub_level: 81
|
||||
os_patch_level: 2022-03
|
||||
- sub_level: 101
|
||||
os_patch_level: 2022-05
|
||||
- sub_level: 110
|
||||
os_patch_level: 2022-07
|
||||
- sub_level: 117
|
||||
os_patch_level: 2022-09
|
||||
- sub_level: 136
|
||||
os_patch_level: 2022-11
|
||||
- sub_level: 149
|
||||
@@ -28,7 +38,9 @@ jobs:
|
||||
- sub_level: 160
|
||||
os_patch_level: 2023-03
|
||||
- sub_level: 168
|
||||
os_patch_level: 2023-04
|
||||
os_patch_level: 2023-05
|
||||
- sub_level: 177
|
||||
os_patch_level: 2023-06
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
@@ -109,7 +121,7 @@ jobs:
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android12-5.10
|
||||
version_name: android12-5.10.160
|
||||
tag: android12-5.10-2023-02
|
||||
os_patch_level: 2023-02
|
||||
version_name: android12-5.10.177
|
||||
tag: android12-5.10-2023-06
|
||||
os_patch_level: 2023-06
|
||||
patch_path: "5.10"
|
||||
|
||||
27
.github/workflows/build-kernel-a13.yml
vendored
27
.github/workflows/build-kernel-a13.yml
vendored
@@ -29,7 +29,13 @@ jobs:
|
||||
os_patch_level: 2023-01
|
||||
- version: "5.10"
|
||||
sub_level: 157
|
||||
os_patch_level: 2023-02
|
||||
os_patch_level: 2023-03
|
||||
- version: "5.10"
|
||||
sub_level: 168
|
||||
os_patch_level: 2023-05
|
||||
- version: "5.10"
|
||||
sub_level: 177
|
||||
os_patch_level: 2023-06
|
||||
- version: "5.15"
|
||||
sub_level: 41
|
||||
os_patch_level: 2022-11
|
||||
@@ -38,13 +44,20 @@ jobs:
|
||||
os_patch_level: 2023-01
|
||||
- version: "5.15"
|
||||
sub_level: 78
|
||||
os_patch_level: 2023-02
|
||||
os_patch_level: 2023-03
|
||||
- version: "5.15"
|
||||
sub_level: 94
|
||||
os_patch_level: 2023-05
|
||||
- version: "5.15"
|
||||
sub_level: 104
|
||||
os_patch_level: 2023-06
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
version: android13-${{ matrix.version }}
|
||||
version_name: android13-${{ matrix.version }}.${{ matrix.sub_level }}
|
||||
tag: android13-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||
os_patch_level: ${{ matrix.os_patch_level }}
|
||||
patch_path: ${{ matrix.version }}
|
||||
upload-artifacts:
|
||||
needs: build-kernel
|
||||
@@ -119,14 +132,14 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- version: "5.10"
|
||||
sub_level: 149
|
||||
os_patch_level: 2022-11
|
||||
sub_level: 177
|
||||
os_patch_level: 2023-06
|
||||
- version: "5.15"
|
||||
sub_level: 74
|
||||
os_patch_level: 2023-01
|
||||
sub_level: 104
|
||||
os_patch_level: 2023-06
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
version: android13-${{ matrix.version }}
|
||||
version_name: android13-${{ matrix.version }}.${{ matrix.sub_level }}
|
||||
tag: android13-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||
patch_path: ${{ matrix.version }}
|
||||
patch_path: ${{ matrix.version }}
|
||||
|
||||
140
.github/workflows/build-kernel-arcvm.yml
vendored
Normal file
140
.github/workflows/build-kernel-arcvm.yml
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
name: Build Kernel - ChromeOS ARCVM
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-arcvm.yml"
|
||||
- "kernel/**"
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- ".github/workflows/build-kernel-arcvm.yml"
|
||||
- "kernel/**"
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86_64]
|
||||
version: ["5.10.178"]
|
||||
include:
|
||||
- arch: x86_64
|
||||
git_tag: chromeos-5.10-arcvm
|
||||
file_name: "bzImage"
|
||||
|
||||
name: Build ChromeOS ARCVM kernel
|
||||
runs-on: ubuntu-20.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=12
|
||||
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
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: KernelSU
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup kernel source
|
||||
run: git clone https://chromium.googlesource.com/chromiumos/third_party/kernel.git -b ${{ matrix.git_tag }} --depth=1
|
||||
|
||||
- name: Setup KernelSU
|
||||
working-directory: kernel
|
||||
run: |
|
||||
echo "[+] KernelSU setup"
|
||||
KERNEL_ROOT=$GITHUB_WORKSPACE/kernel
|
||||
echo "[+] KERNEL_ROOT: $KERNEL_ROOT"
|
||||
echo "[+] Copy KernelSU driver to $KERNEL_ROOT/drivers"
|
||||
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $KERNEL_ROOT/drivers/kernelsu
|
||||
|
||||
echo "[+] Add KernelSU driver to Makefile"
|
||||
DRIVER_MAKEFILE=$KERNEL_ROOT/drivers/Makefile
|
||||
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
|
||||
|
||||
echo "[+] Apply KernelSU patches"
|
||||
cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/5.10/*.patch
|
||||
|
||||
echo "[+] Patch script/setlocalversion"
|
||||
sed -i 's/-dirty//g' $KERNEL_ROOT/scripts/setlocalversion
|
||||
|
||||
echo "[+] KernelSU setup done."
|
||||
cd $GITHUB_WORKSPACE/KernelSU
|
||||
VERSION=$(($(git rev-list --count HEAD) + 10200))
|
||||
echo "VERSION: $VERSION"
|
||||
echo "kernelsu_version=$VERSION" >> $GITHUB_ENV
|
||||
|
||||
- name: Build Kernel
|
||||
working-directory: kernel
|
||||
run: |
|
||||
set -a && . build.config.gki.x86_64; set +a
|
||||
export DEFCONFIG=x86_64_arcvm_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) bzImage modules prepare-objtool
|
||||
|
||||
echo "file_path=${PWD}/arch/x86/boot/bzImage" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload kernel-ARCVM-${{ matrix.arch }}-${{ matrix.version }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: kernel-ARCVM-${{ matrix.arch }}-${{ matrix.version }}
|
||||
path: "${{ env.file_path }}"
|
||||
|
||||
- name: Post to Telegram
|
||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
||||
env:
|
||||
CHAT_ID: ${{ secrets.CHAT_ID }}
|
||||
CACHE_CHAT_ID: ${{ secrets.CACHE_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 }}
|
||||
run: |
|
||||
TITLE=kernel-ARCVM-${{ matrix.arch }}-${{ matrix.version }}
|
||||
echo "[+] title: $TITLE"
|
||||
export TITLE
|
||||
export VERSION="${{ env.kernelsu_version }}"
|
||||
echo "[+] Compress images"
|
||||
gzip -n -f -9 "${{ env.file_path }}"
|
||||
echo "[+] Image to upload"
|
||||
ls -l "${{ env.file_path }}.gz"
|
||||
if [ -n "${{ secrets.BOT_TOKEN }}" ]; then
|
||||
pip3 install python-telegram-bot
|
||||
python3 "$GITHUB_WORKSPACE/KernelSU/scripts/ksubot.py" "${{ env.file_path }}.gz"
|
||||
fi
|
||||
32
.github/workflows/build-kernel-wsa.yml
vendored
32
.github/workflows/build-kernel-wsa.yml
vendored
@@ -18,42 +18,32 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86_64, arm64]
|
||||
version: ["5.10.117.2", "5.15.78.1", "5.15.94.1"]
|
||||
version: ["5.15.94.4", "5.15.104.1"]
|
||||
include:
|
||||
- arch: x86_64
|
||||
file_name: "bzImage"
|
||||
- arch: arm64
|
||||
file_name: "Image"
|
||||
cross_compile: "aarch64-linux-gnu"
|
||||
- version: "5.10.117.2"
|
||||
arch: x86_64
|
||||
make_config: config-wsa-5.10
|
||||
- version: "5.10.117.2"
|
||||
arch: arm64
|
||||
make_config: config-wsa-arm64-5.10
|
||||
- version: "5.15.78.1"
|
||||
- version: "5.15.94.4"
|
||||
arch: x86_64
|
||||
make_config: config-wsa-x64
|
||||
- version: "5.15.78.1"
|
||||
- version: "5.15.94.4"
|
||||
arch: arm64
|
||||
make_config: config-wsa-arm64
|
||||
- version: "5.15.94.1"
|
||||
- version: "5.15.104.1"
|
||||
arch: x86_64
|
||||
make_config: config-wsa-x64
|
||||
- version: "5.15.94.1"
|
||||
- version: "5.15.104.1"
|
||||
arch: arm64
|
||||
make_config: config-wsa-arm64
|
||||
- version: "5.10.117.2"
|
||||
device_code: latte
|
||||
kernel_version: "5.10"
|
||||
- version: "5.15.78.1"
|
||||
- version: "5.15.94.4"
|
||||
device_code: latte-2
|
||||
kernel_version: "5.15"
|
||||
- version: "5.15.94.1"
|
||||
- version: "5.15.104.1"
|
||||
device_code: latte-2
|
||||
kernel_version: "5.15"
|
||||
|
||||
|
||||
name: Build WSA-Kernel-${{ matrix.version }}-${{ matrix.arch }}
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
@@ -85,7 +75,6 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: KernelSU
|
||||
ref: main
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup kernel source
|
||||
@@ -126,8 +115,13 @@ jobs:
|
||||
run: |
|
||||
cp configs/wsa/${{ matrix.make_config }} .config
|
||||
make olddefconfig
|
||||
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 -j`nproc` LLVM=1 ARCH=${{ matrix.arch }} CROSS_COMPILE=${{ matrix.cross_compile }} ${{ matrix.file_name }} CCACHE="/usr/bin/ccache"
|
||||
echo "file_path=WSA-Linux-Kernel/arch/${{ matrix.arch }}/boot/${{ matrix.file_name }}" >> $GITHUB_ENV
|
||||
declare -A ARCH_MAP=(["x86_64"]="x86" ["arm64"]="arm64")
|
||||
echo "file_path=WSA-Linux-Kernel/arch/${ARCH_MAP[${{ matrix.arch }}]}/boot/${{ matrix.file_name }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload kernel-${{ matrix.arch }}-${{ matrix.version }}
|
||||
uses: actions/upload-artifact@v3
|
||||
|
||||
3
.github/workflows/clippy.yml
vendored
3
.github/workflows/clippy.yml
vendored
@@ -22,7 +22,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
# cross build failed after Rust 1.68, see https://github.com/cross-rs/cross/issues/1222
|
||||
- run: rustup default 1.67.0
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: userspace/ksud
|
||||
|
||||
40
.github/workflows/gki-kernel.yml
vendored
40
.github/workflows/gki-kernel.yml
vendored
@@ -45,6 +45,10 @@ on:
|
||||
description: >
|
||||
Artifact name of prebuilt ksud to be embedded
|
||||
for example: ksud-aarch64-linux-android
|
||||
debug:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
secrets:
|
||||
BOOT_SIGN_KEY:
|
||||
required: false
|
||||
@@ -82,15 +86,31 @@ jobs:
|
||||
|
||||
- name: Setup kernel source
|
||||
run: |
|
||||
echo "Free space:"
|
||||
df -h
|
||||
cd $GITHUB_WORKSPACE
|
||||
git clone https://gerrit.googlesource.com/git-repo
|
||||
sudo apt-get install repo -y
|
||||
mkdir android-kernel && cd android-kernel
|
||||
../git-repo/repo init --depth=1 --u https://android.googlesource.com/kernel/manifest -b common-${{ inputs.tag }}
|
||||
../git-repo/repo sync -j$(nproc --all)
|
||||
repo init --depth=1 --u https://android.googlesource.com/kernel/manifest -b common-${{ inputs.tag }} --repo-rev=v2.16
|
||||
REMOTE_BRANCH=$(git ls-remote https://android.googlesource.com/kernel/common ${{ inputs.tag }})
|
||||
DEFAULT_MANIFEST_PATH=.repo/manifests/default.xml
|
||||
if grep -q deprecated <<< $REMOTE_BRANCH; then
|
||||
echo "Found deprecated branch: ${{ inputs.tag }}"
|
||||
sed -i 's/"${{ inputs.tag }}"/"deprecated\/${{ inputs.tag }}"/g' $DEFAULT_MANIFEST_PATH
|
||||
cat $DEFAULT_MANIFEST_PATH
|
||||
fi
|
||||
repo --version
|
||||
repo --trace sync -q -c -j$(nproc --all) --no-tags
|
||||
if [ -d prebuilts/clang/host/linux-x86 ] && [ -f common/build.config.constants ]; then
|
||||
ls -d prebuilts/clang/host/linux-x86/clang-r* | grep -v $(sed -n 's/^CLANG_VERSION=//p' common/build.config.constants)
|
||||
fi
|
||||
rm -rf .repo
|
||||
df -h
|
||||
|
||||
- name: Setup KernelSU
|
||||
env:
|
||||
PATCH_PATH: ${{ inputs.patch_path }}
|
||||
IS_DEBUG_KERNEL: ${{ inputs.debug }}
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/android-kernel
|
||||
echo "[+] KernelSU setup"
|
||||
@@ -103,8 +123,13 @@ jobs:
|
||||
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
|
||||
echo "[+] Apply KernelSU patches"
|
||||
cd $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/$PATCH_PATH/*.patch
|
||||
echo "Patch script/setlocalversion"
|
||||
echo "[+] Patch script/setlocalversion"
|
||||
sed -i 's/-dirty//g' $GKI_ROOT/common/scripts/setlocalversion
|
||||
|
||||
if [ "$IS_DEBUG_KERNEL" = "true" ]; then
|
||||
echo "[+] Enable debug features for kernel"
|
||||
echo "ccflags-y += -DCONFIG_KSU_DEBUG" >> $GITHUB_WORKSPACE/KernelSU/kernel/Makefile
|
||||
fi
|
||||
echo "[+] KernelSU setup done."
|
||||
|
||||
- name: Symbol magic
|
||||
@@ -128,7 +153,12 @@ jobs:
|
||||
|
||||
- name: Build boot.img
|
||||
working-directory: android-kernel
|
||||
run: CCACHE="/usr/bin/ccache" LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
|
||||
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
|
||||
CCACHE="/usr/bin/ccache" LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
|
||||
|
||||
- name: Prepare artifacts
|
||||
id: prepareArtifacts
|
||||
|
||||
13
.github/workflows/release.yml
vendored
13
.github/workflows/release.yml
vendored
@@ -15,12 +15,15 @@ jobs:
|
||||
uses: ./.github/workflows/build-kernel-a13.yml
|
||||
build-wsa-kernel:
|
||||
uses: ./.github/workflows/build-kernel-wsa.yml
|
||||
build-arcvm-kernel:
|
||||
uses: ./.github/workflows/build-kernel-arcvm.yml
|
||||
release:
|
||||
needs:
|
||||
- build-manager
|
||||
- build-a12-kernel
|
||||
- build-a13-kernel
|
||||
- build-wsa-kernel
|
||||
- build-arcvm-kernel
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
@@ -43,6 +46,15 @@ jobs:
|
||||
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
|
||||
|
||||
@@ -54,3 +66,4 @@ jobs:
|
||||
AnyKernel3-*.zip
|
||||
boot-images-*/Image-*/*.img.gz
|
||||
kernel-WSA*.zip
|
||||
kernel-ARCVM*.zip
|
||||
4
.github/workflows/rustfmt.yml
vendored
4
.github/workflows/rustfmt.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
with:
|
||||
components: rustfmt
|
||||
|
||||
- uses: LoliGothick/rustfmt-check@v0.3.1
|
||||
- uses: LoliGothick/rustfmt-check@master
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
options: --manifest-path userspace/ksud/Cargo.toml
|
||||
working-directory: userspace/ksud
|
||||
18
README.md
18
README.md
@@ -1,4 +1,5 @@
|
||||
**English** | [简体中文](README_CN.md) | [繁體中文](README_TW.md)
|
||||
|
||||
**English** | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Portuguese-Brazil](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
@@ -8,22 +9,25 @@ A Kernel based root solution for Android devices.
|
||||
|
||||
1. Kernel-based `su` and root access management.
|
||||
2. Module system based on overlayfs.
|
||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html): Lock up the root power in a cage.
|
||||
|
||||
## Compatibility State
|
||||
|
||||
KernelSU officially supports Android GKI 2.0 devices(with kernel 5.10+), old kernels(4.14+) is also compatiable, but you need to build kernel yourself.
|
||||
KernelSU officially supports Android GKI 2.0 devices(with kernel 5.10+), old kernels(4.14+) is also compatible, but you need to build kernel yourself.
|
||||
|
||||
WSA and containter-based Android should also work with KernelSU integrated.
|
||||
WSA, ChromeOS and containter-based Android can also work with KernelSU integrated.
|
||||
|
||||
And the current supported ABIs are : `arm64-v8a` and `x86_64`
|
||||
|
||||
## Usage
|
||||
|
||||
[Installation](https://kernelsu.org/guide/installation.html)
|
||||
- [Installation Instruction](https://kernelsu.org/guide/installation.html)
|
||||
- [How to build?](https://kernelsu.org/guide/how-to-build.html)
|
||||
- [Official Website](https://kernelsu.org/)
|
||||
|
||||
## Build
|
||||
## Translation
|
||||
|
||||
[How to build?](https://kernelsu.org/guide/how-to-build.html)
|
||||
To translate KernelSU into your language, or to improve an existing translation, use [Weblate](https://hosted.weblate.org/engage/kernelsu/) please.
|
||||
|
||||
### Discussion
|
||||
|
||||
@@ -37,6 +41,6 @@ And the current supported ABIs are : `arm64-v8a` and `x86_64`
|
||||
## 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.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): the sepolicy implementation.
|
||||
|
||||
16
README_CN.md
16
README_CN.md
@@ -1,4 +1,4 @@
|
||||
[English](README.md) | **简体中文** | [繁體中文](README_TW.md)
|
||||
[English](README.md) | [Español](README_ES.md) | **简体中文** | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Portuguese-Brazil](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
@@ -8,24 +8,26 @@
|
||||
|
||||
- 基于内核的 su 和权限管理。
|
||||
- 基于 overlayfs 的模块系统。
|
||||
- [App Profile](https://kernelsu.org/guide/app-profile.html): 把 Root 权限关进笼子里。
|
||||
|
||||
## 兼容状态
|
||||
|
||||
KernelSU 官方支持 GKI 2.0 的设备(内核版本5.10以上);旧内核也是兼容的(最低4.14+),不过需要自己编译内核。
|
||||
|
||||
WSA 和运行在容器上的 Android 也可以与 KernelSU 一起工作。
|
||||
WSA, ChromeOS 和运行在容器上的 Android 也可以与 KernelSU 一起工作。
|
||||
|
||||
目前支持架构 : `arm64-v8a` 和 `x86_64`
|
||||
|
||||
## 使用方法
|
||||
|
||||
[安装教程](https://kernelsu.org/zh_CN/guide/installation.html)
|
||||
- [安装教程](https://kernelsu.org/zh_CN/guide/installation.html)
|
||||
- [如何构建?](https://kernelsu.org/zh_CN/guide/how-to-build.html)
|
||||
|
||||
## 构建
|
||||
## 参与翻译
|
||||
|
||||
[如何构建?](https://kernelsu.org/zh_CN/guide/how-to-build.html)
|
||||
要将 KernelSU 翻译成您的语言,或完善现有的翻译,请使用 [Weblate](https://hosted.weblate.org/engage/kernelsu/)。
|
||||
|
||||
### 讨论
|
||||
## 讨论
|
||||
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
@@ -37,6 +39,6 @@ WSA 和运行在容器上的 Android 也可以与 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 技巧。
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk):sepolicy 的实现。
|
||||
|
||||
47
README_ES.md
Normal file
47
README_ES.md
Normal file
@@ -0,0 +1,47 @@
|
||||
[ 🇬🇧 English](README.md) | 🇪🇸 **Español** | [🇨🇳 简体中文](README_CN.md) | [🇹🇼 繁體中文](README_TW.md) | [ 🇯🇵 日本語](README_JP.md) | [🇵🇱 Polski](README_PL.md) | [🇧🇷 Portuguese-Brazil](README_PT-BR.md) | [🇹🇷 Türkçe](README_TR.md) | [Русский](README_RU.md)
|
||||
|
||||
<div style="display: flex; align-items: center;">
|
||||
<img src="https://kernelsu.org/logo.png" style="width: 96px;" alt="">
|
||||
<div style="margin-left: 20px;">
|
||||
<span style="font-size: large; "><b>KernelSU</b></span>
|
||||
<br>
|
||||
<span style="font-size: medium; "><i>Una solución root basada en el kernel para dispositivos Android.</i></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## 🚀 Características
|
||||
|
||||
**1.** Binario `su` basado en el kernel y gestión de acceso root.<br/>
|
||||
**2.** Sistema de módulos basado en **OverlayFS**.
|
||||
|
||||
## ✅ Estado de compatibilidad
|
||||
|
||||
**KernelSU** soporta de forma oficial dispositivos Android con **GKI 2.0** (a partir de la versión **5.10** del kernel). Los kernels antiguos (a partir de la versión **4.14**) también son compatibles, pero necesitas compilarlos por tu cuenta.
|
||||
|
||||
El **Subsistema de Windows para Android (WSA)** e implementaciones de Android basadas en contenedores, como **Waydroid**, también deberían funcionar con **KernelSU** integrado.
|
||||
|
||||
Actualmente se soportan las siguientes **ABIs**: `arm64-v8a`; `x86_64`.
|
||||
|
||||
## 📖 Uso
|
||||
|
||||
[¿Cómo instalarlo?](https://kernelsu.org/guide/installation.html)
|
||||
|
||||
## 🔨 Compilación
|
||||
|
||||
[¿Cómo compilarlo?](https://kernelsu.org/guide/how-to-build.html)
|
||||
|
||||
## 💬 Discusión
|
||||
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## ⚖️ Licencia
|
||||
|
||||
- Los archivos bajo el directorio `kernel` están licenciados bajo [GPL-2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
- Todas las demás partes, a excepción del directorio `kernel`, están licenciados bajo [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html).
|
||||
|
||||
## 👥 Créditos
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): la idea de **KernelSU**.
|
||||
- [genuine](https://github.com/brevent/genuine/): la validación del **esquema de firmas APK v2**.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): algunas habilidades de rootkit.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): la implementación de la **política de SELinux (SEPolicy)**.
|
||||
47
README_JP.md
Normal file
47
README_JP.md
Normal file
@@ -0,0 +1,47 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | **日本語** | [Polski](README_PL.md) | [Portuguese-Brazil](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
Android におけるカーネルベースの root ソリューションです。
|
||||
|
||||
## 特徴
|
||||
|
||||
1. カーネルベースの `su` と権限管理
|
||||
2. OverlayFS に基づくモジュールシステム
|
||||
3. [アプリのプロファイル](https://kernelsu.org/guide/app-profile.html): root の権限をケージ内に閉じ込めます。
|
||||
|
||||
|
||||
## 対応状況
|
||||
|
||||
KernelSU は GKI 2.0 デバイス(カーネルバージョン 5.10 以上)を公式にサポートしています。古いカーネル(4.14以上)とも互換性がありますが、自分でカーネルをビルドする必要があります。
|
||||
|
||||
WSA 、ChromeOS とコンテナ上で動作する Android でも KernelSU を統合して動かせます。
|
||||
|
||||
現在サポートしているアーキテクチャは `arm64-v8a` および `x86_64` です。
|
||||
|
||||
## 使用方法
|
||||
|
||||
- [インストール方法はこちら](https://kernelsu.org/ja_JP/guide/installation.html)
|
||||
- [ビルド方法はこちら](https://kernelsu.org/guide/how-to-build.html)
|
||||
- [公式サイト](https://kernelsu.org)
|
||||
|
||||
## 翻訳
|
||||
|
||||
KernelSU をあなたの言語に翻訳するか、既存の翻訳を改善するには、[Weblate](https://hosted.weblate.org/engage/kernelsu/) を使用してください。
|
||||
|
||||
## ディスカッション
|
||||
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## ライセンス
|
||||
|
||||
- `kernel` ディレクトリの下にあるすべてのファイル: [GPL-2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
- `kernel` ディレクトリ以外のすべてのファイル: [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
## クレジット
|
||||
|
||||
- [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 のスキル
|
||||
|
||||
42
README_PL.md
Normal file
42
README_PL.md
Normal file
@@ -0,0 +1,42 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | **Polski** | [Portuguese-Brazil](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
Rozwiązanie root oparte na jądrze dla urządzeń z systemem Android.
|
||||
|
||||
## Cechy
|
||||
|
||||
1. Oparte na jądrze `su` i zarządzanie dostępem roota.
|
||||
2. System modułów oparty na overlayfs.
|
||||
|
||||
## Kompatybilność
|
||||
|
||||
KernelSU oficjalnie obsługuje urządzenia z Androidem GKI 2.0 (z jądrem 5.10+), starsze jądra (4.14+) są również kompatybilne, ale musisz sam skompilować jądro.
|
||||
|
||||
WSA i Android oparty na kontenerach również powinny działać ze zintegrowanym KernelSU.
|
||||
|
||||
Aktualnie obsługiwane ABI to : `arm64-v8a` i `x86_64`.
|
||||
|
||||
## Użycie
|
||||
|
||||
[Instalacja](https://kernelsu.org/guide/installation.html)
|
||||
|
||||
## Kompilacja
|
||||
|
||||
[Jak skompilować?](https://kernelsu.org/guide/how-to-build.html)
|
||||
|
||||
### Dyskusja
|
||||
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## Licencja
|
||||
|
||||
- Pliki w katalogu `kernel` są na licencji [GPL-2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
- Wszystkie inne części poza katalogiem `kernel` są na licencji [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
## Podziękowania
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): pomysłodawca KernelSU.
|
||||
- [genuine](https://github.com/brevent/genuine/): walidacja podpisu apk v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): cenna znajomość rootkitów.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): implementacja sepolicy.
|
||||
46
README_PT-BR.md
Normal file
46
README_PT-BR.md
Normal file
@@ -0,0 +1,46 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | **Portuguese-Brazil** | [Türkçe](README_TR.md) | [Русский](README_RU.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
Uma solução raiz baseada em Kernel para dispositivos Android.
|
||||
|
||||
## Características
|
||||
|
||||
1. `su` baseado em kernel e gerenciamento de acesso root.
|
||||
|
||||
2. Sistema modular baseado em overlayfs.
|
||||
|
||||
3. [App Perfil](https://kernelsu.org/guide/app-profile.html): Tranque o poder raiz em uma gaiola.
|
||||
|
||||
## Estado de compatibilidade
|
||||
|
||||
O KernelSU suporta oficialmente dispositivos Android GKI 2.0 (com kernel 5.10+), kernels antigos (4.14+) também são compatíveis, mas você mesmo precisa construir o kernel.
|
||||
|
||||
WSA, ChromeOS e Android baseado em contêiner também deve funcionar com o KernelSU integrado.
|
||||
|
||||
E os ABIs atualmente suportados são: `arm64-v8a` e `x86_64`
|
||||
|
||||
## Uso
|
||||
- [Instalação](https://kernelsu.org/guide/installation.html)
|
||||
- [Como construir?](https://kernelsu.org/guide/how-to-build.html)
|
||||
- [Site Oficial](https://kernelsu.org/)
|
||||
|
||||
## Tradução
|
||||
Para traduzir o KernelSU para o seu idioma, ou para melhorar uma tradução existente, use o [Weblate](https://hosted.weblate.org/engage/kernelsu/), por favor.
|
||||
|
||||
### Discussão
|
||||
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## Licença
|
||||
|
||||
- Os arquivos no diretório `kernel` são [GPL-2](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](https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
## Créditos
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): a ideia do KernelSU.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): a implementação da sepolicy.
|
||||
- [genuine](https://github.com/brevent/genuine/): validação de assinatura apk v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): algumas habilidades de rootkit.
|
||||
42
README_RU.md
Normal file
42
README_RU.md
Normal file
@@ -0,0 +1,42 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Portuguese-Brazil](README_PT-BR.md) | [Türkçe](README_TR.md) | **Русский**
|
||||
|
||||
# KernelSU
|
||||
|
||||
Решение на основе ядра root для Android-устройств.
|
||||
|
||||
## Особенности
|
||||
|
||||
1. Управление `su` и root-доступом на основе ядра.
|
||||
2. Система модулей на основе overlayfs.
|
||||
|
||||
## Совместимость
|
||||
|
||||
KernelSU официально поддерживает устройства на базе Android GKI 2.0 (с ядром 5.10+), старые ядра (4.14+) также совместимы, но для этого необходимо собрать ядро самостоятельно.
|
||||
|
||||
WSA и Android на основе контейнеров также должны работать с интегрированным KernelSU.
|
||||
|
||||
В настоящее время поддерживаются следующие ABI: `arm64-v8a` и `x86_64`.
|
||||
|
||||
## Использование
|
||||
|
||||
[Установка](https://kernelsu.org/ru_RU/guide/installation.html)
|
||||
|
||||
## Сборка
|
||||
|
||||
[Как собрать?](https://kernelsu.org/ru_RU/guide/how-to-build.html)
|
||||
|
||||
## Обсуждение
|
||||
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## Лицензия
|
||||
|
||||
- Файлы в директории `kernel` - [GPL-2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
- Все остальные части, кроме директории `kernel` - [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
## Благодарности
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): идея KernelSU.
|
||||
- [genuine](https://github.com/brevent/genuine/): проверка подписи apk v2.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): некоторые навыки руткита.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): реализация sepolicy.
|
||||
45
README_TR.md
Normal file
45
README_TR.md
Normal file
@@ -0,0 +1,45 @@
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Portuguese-Brazil](README_PT-BR.md) | **Türkçe** | [Русский](README_RU.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
Android cihazlar için kernel tabanlı bir root çözümü.
|
||||
|
||||
## Özellikler
|
||||
|
||||
1. Kernel-tabanlı `su` ve root erişimi yönetimi.
|
||||
2. Overlayfs'ye dayalı modül sistemi.
|
||||
3. [Uygulama profili](https://kernelsu.org/guide/app-profile.html): Root gücünü bir kafese kapatın.
|
||||
|
||||
## Uyumluluk Durumu
|
||||
|
||||
KernelSU resmi olarak Android GKI 2.0 cihazlarını ( 5.10+ kernelli) destekler, eski kernellerle de (4.14+) uyumludur, ancak kerneli kendinizin inşaa etmesi gerekir.
|
||||
|
||||
WSA ve konteyner tabanlı Android'in de, KernelSU ile entegre olarak da çalışması gerekmektedir.
|
||||
|
||||
Ve desteklenen mevcut ABI'ler : `arm64-v8a` ve `x86_64`
|
||||
|
||||
## Kullanım
|
||||
|
||||
- [Yükleme](https://kernelsu.org/guide/installation.html)
|
||||
- [Nasıl inşa edilir?](https://kernelsu.org/guide/how-to-build.html)
|
||||
- [Resmi WEB sitesi](https://kernelsu.org/)
|
||||
|
||||
## Çeviri
|
||||
|
||||
KernelSU'yu kendi dilinize çevirmek veya varolan bir çeviriyi geliştirmek istiyorsanız, lütfen [Weblate](https://hosted.weblate.org/engage/kernelsu/)'i kullanın.
|
||||
|
||||
### Tartışma
|
||||
|
||||
- Telegram: [@KernelSU](https://t.me/KernelSU)
|
||||
|
||||
## Lisans
|
||||
|
||||
- `kernel` klasöründeki dosyalar [GPL-2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) lisansı altındadır.
|
||||
- `kernel` klasörü dışındaki bütün diğer bölümler [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html) lisansı altındadır.
|
||||
|
||||
## Krediler
|
||||
|
||||
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU fikri.
|
||||
- [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 becerileri.
|
||||
@@ -1,4 +1,4 @@
|
||||
[English](README.md) | [简体中文](README_CN.md) | **繁體中文**
|
||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | **繁體中文** | [日本語](README_JP.md) | [Polski](README_PL.md) | [Portuguese-Brazil](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md)
|
||||
|
||||
# KernelSU
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Diagnostics:
|
||||
UnusedIncludes: Strict
|
||||
ClangTidy:
|
||||
Remove: bugprone-sizeof-expression
|
||||
Remove: bugprone-sizeof-expression
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
menu "KernelSU"
|
||||
|
||||
config KSU
|
||||
tristate "KernelSU module"
|
||||
tristate "KernelSU function support"
|
||||
select OVERLAY_FS
|
||||
default y
|
||||
depends on OVERLAY_FS
|
||||
help
|
||||
This is the KSU privilege driver for android system.
|
||||
Enable kernel-level root privileges on Android System.
|
||||
|
||||
config KSU_DEBUG
|
||||
tristate "KernelSU module debug mode"
|
||||
default n
|
||||
bool "KernelSU debug mode"
|
||||
depends on KSU
|
||||
default n
|
||||
help
|
||||
This enables debug mode for KSU
|
||||
Enable KernelSU debug mode
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -15,18 +15,27 @@ obj-y += selinux/
|
||||
# .git is a text file while the module is imported by 'git submodule add'.
|
||||
ifeq ($(shell test -e $(srctree)/$(src)/../.git; echo $$?),0)
|
||||
KSU_GIT_VERSION := $(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git rev-list --count HEAD)
|
||||
ccflags-y += -DKSU_GIT_VERSION=$(KSU_GIT_VERSION)
|
||||
# ksu_version: major * 10000 + git version + 200 for historical reasons
|
||||
$(eval KSU_VERSION=$(shell expr 10000 + $(KSU_GIT_VERSION) + 200))
|
||||
$(info -- KernelSU 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 a git submodule!")
|
||||
ccflags-y += -DKSU_VERSION=16
|
||||
endif
|
||||
|
||||
ifndef EXPECTED_SIZE
|
||||
EXPECTED_SIZE := 0x033b
|
||||
ifndef KSU_EXPECTED_SIZE
|
||||
KSU_EXPECTED_SIZE := 0x033b
|
||||
endif
|
||||
|
||||
ifndef EXPECTED_HASH
|
||||
EXPECTED_HASH := 0xb0b91415
|
||||
ifndef KSU_EXPECTED_HASH
|
||||
KSU_EXPECTED_HASH := 0xb0b91415
|
||||
endif
|
||||
|
||||
ccflags-y += -DEXPECTED_SIZE=$(EXPECTED_SIZE)
|
||||
ccflags-y += -DEXPECTED_HASH=$(EXPECTED_HASH)
|
||||
$(info -- KernelSU Manager signature size: $(KSU_EXPECTED_SIZE))
|
||||
$(info -- KernelSU Manager signature hash: $(KSU_EXPECTED_HASH))
|
||||
|
||||
ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE)
|
||||
ccflags-y += -DEXPECTED_HASH=$(KSU_EXPECTED_HASH)
|
||||
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
||||
ccflags-y += -Wno-macro-redefined -Wno-declaration-after-statement
|
||||
ccflags-y += -Wno-declaration-after-statement
|
||||
|
||||
@@ -1,27 +1,91 @@
|
||||
#include "linux/delay.h"
|
||||
#include "ksu.h"
|
||||
#include "linux/compiler.h"
|
||||
#include "linux/fs.h"
|
||||
#include "linux/gfp.h"
|
||||
#include "linux/kernel.h"
|
||||
#include "linux/list.h"
|
||||
#include "linux/printk.h"
|
||||
#include "linux/slab.h"
|
||||
#include "linux/types.h"
|
||||
#include "linux/version.h"
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
||||
#include "linux/compiler_types.h"
|
||||
#endif
|
||||
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "selinux/selinux.h"
|
||||
#include "kernel_compat.h"
|
||||
#include "allowlist.h"
|
||||
|
||||
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
|
||||
#define FILE_FORMAT_VERSION 1 // u32
|
||||
#define FILE_FORMAT_VERSION 3 // u32
|
||||
|
||||
#define KSU_APP_PROFILE_PRESERVE_UID 9999 // NOBODY_UID
|
||||
#define KSU_DEFAULT_SELINUX_DOMAIN "u:r:su:s0"
|
||||
|
||||
static DEFINE_MUTEX(allowlist_mutex);
|
||||
|
||||
// default profiles, these may be used frequently, so we cache it
|
||||
static struct root_profile default_root_profile;
|
||||
static struct non_root_profile default_non_root_profile;
|
||||
|
||||
static int allow_list_arr[PAGE_SIZE / sizeof(int)] __read_mostly __aligned(PAGE_SIZE);
|
||||
static int allow_list_pointer __read_mostly = 0;
|
||||
|
||||
static void remove_uid_from_arr(uid_t uid)
|
||||
{
|
||||
int *temp_arr;
|
||||
int i, j;
|
||||
|
||||
if (allow_list_pointer == 0)
|
||||
return;
|
||||
|
||||
temp_arr = kmalloc(sizeof(allow_list_arr), GFP_KERNEL);
|
||||
if (temp_arr == NULL) {
|
||||
pr_err("%s: unable to allocate memory\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = j = 0; i < allow_list_pointer; i++) {
|
||||
if (allow_list_arr[i] == uid)
|
||||
continue;
|
||||
temp_arr[j++] = allow_list_arr[i];
|
||||
}
|
||||
|
||||
allow_list_pointer = j;
|
||||
|
||||
for (; j < ARRAY_SIZE(allow_list_arr); j++)
|
||||
temp_arr[j] = -1;
|
||||
|
||||
memcpy(&allow_list_arr, temp_arr, PAGE_SIZE);
|
||||
kfree(temp_arr);
|
||||
}
|
||||
|
||||
static void init_default_profiles()
|
||||
{
|
||||
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));
|
||||
default_root_profile.namespaces = 0;
|
||||
strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
|
||||
|
||||
// This means that we will umount modules by default!
|
||||
default_non_root_profile.umount_modules = true;
|
||||
}
|
||||
|
||||
struct perm_data {
|
||||
struct list_head list;
|
||||
uid_t uid;
|
||||
bool allow;
|
||||
struct app_profile profile;
|
||||
};
|
||||
|
||||
static struct list_head allow_list;
|
||||
|
||||
static uint8_t allow_list_bitmap[PAGE_SIZE] __read_mostly __aligned(PAGE_SIZE);
|
||||
#define BITMAP_UID_MAX ((sizeof(allow_list_bitmap) * BITS_PER_BYTE) - 1)
|
||||
|
||||
#define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist"
|
||||
|
||||
static struct work_struct ksu_save_work;
|
||||
@@ -36,67 +100,231 @@ void ksu_show_allow_list(void)
|
||||
pr_info("ksu_show_allow_list");
|
||||
list_for_each (pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
pr_info("uid :%d, allow: %d\n", p->uid, p->allow);
|
||||
pr_info("uid :%d, allow: %d\n", p->profile.current_uid,
|
||||
p->profile.allow_su);
|
||||
}
|
||||
}
|
||||
|
||||
bool ksu_allow_uid(uid_t uid, bool allow, bool persist)
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
static void ksu_grant_root_to_shell()
|
||||
{
|
||||
struct app_profile profile = {
|
||||
.allow_su = true,
|
||||
.current_uid = 2000,
|
||||
};
|
||||
strcpy(profile.key, "com.android.shell");
|
||||
strcpy(profile.rp_config.profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
|
||||
ksu_set_app_profile(&profile, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool ksu_get_app_profile(struct app_profile *profile)
|
||||
{
|
||||
struct perm_data *p = NULL;
|
||||
struct list_head *pos = NULL;
|
||||
bool found = false;
|
||||
|
||||
list_for_each (pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
bool uid_match = profile->current_uid == p->profile.current_uid;
|
||||
if (uid_match) {
|
||||
// found it, override it with ours
|
||||
memcpy(profile, &p->profile, sizeof(*profile));
|
||||
found = true;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return found;
|
||||
}
|
||||
|
||||
static inline bool forbid_system_uid(uid_t uid) {
|
||||
#define SHELL_UID 2000
|
||||
#define SYSTEM_UID 1000
|
||||
return uid < SHELL_UID && uid != SYSTEM_UID;
|
||||
}
|
||||
|
||||
static bool profile_valid(struct app_profile *profile)
|
||||
{
|
||||
if (!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;
|
||||
}
|
||||
|
||||
if (profile->allow_su) {
|
||||
if (profile->rp_config.profile.groups_count > KSU_MAX_GROUPS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strlen(profile->rp_config.profile.selinux_domain) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ksu_set_app_profile(struct app_profile *profile, bool persist)
|
||||
{
|
||||
// find the node first!
|
||||
struct perm_data *p = NULL;
|
||||
struct list_head *pos = NULL;
|
||||
bool result = false;
|
||||
|
||||
if (!profile_valid(profile)) {
|
||||
pr_err("Failed to set app profile: invalid profile!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
list_for_each (pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
if (uid == p->uid) {
|
||||
p->allow = allow;
|
||||
// both uid and package must match, otherwise it will break multiple package with different user id
|
||||
if (profile->current_uid == p->profile.current_uid &&
|
||||
!strcmp(profile->key, p->profile.key)) {
|
||||
// found it, just override it all!
|
||||
memcpy(&p->profile, profile, sizeof(*profile));
|
||||
result = true;
|
||||
goto exit;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
// not found, alloc a new node!
|
||||
p = (struct perm_data *)kmalloc(sizeof(struct perm_data), GFP_KERNEL);
|
||||
if (!p) {
|
||||
pr_err("alloc allow node failed.\n");
|
||||
pr_err("ksu_set_app_profile alloc failed\n");
|
||||
return false;
|
||||
}
|
||||
p->uid = uid;
|
||||
p->allow = allow;
|
||||
|
||||
pr_info("allow_uid: %d, allow: %d", uid, allow);
|
||||
|
||||
memcpy(&p->profile, profile, sizeof(*profile));
|
||||
if (profile->allow_su) {
|
||||
pr_info("set root profile, key: %s, uid: %d, gid: %d, context: %s\n",
|
||||
profile->key, profile->current_uid,
|
||||
profile->rp_config.profile.gid,
|
||||
profile->rp_config.profile.selinux_domain);
|
||||
} else {
|
||||
pr_info("set app profile, key: %s, uid: %d, umount modules: %d\n",
|
||||
profile->key, profile->current_uid,
|
||||
profile->nrp_config.profile.umount_modules);
|
||||
}
|
||||
list_add_tail(&p->list, &allow_list);
|
||||
|
||||
out:
|
||||
if (profile->current_uid <= BITMAP_UID_MAX) {
|
||||
if (profile->allow_su)
|
||||
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] |= 1 << (profile->current_uid % BITS_PER_BYTE);
|
||||
else
|
||||
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] &= ~(1 << (profile->current_uid % BITS_PER_BYTE));
|
||||
} else {
|
||||
if (profile->allow_su) {
|
||||
/*
|
||||
* 1024 apps with uid higher than BITMAP_UID_MAX
|
||||
* registered to request superuser?
|
||||
*/
|
||||
if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) {
|
||||
pr_err("too many apps registered\n");
|
||||
WARN_ON(1);
|
||||
return false;
|
||||
}
|
||||
allow_list_arr[allow_list_pointer++] = profile->current_uid;
|
||||
} else {
|
||||
remove_uid_from_arr(profile->current_uid);
|
||||
}
|
||||
}
|
||||
result = true;
|
||||
|
||||
exit:
|
||||
// check if the default profiles is changed, cache it to a single struct to accelerate access.
|
||||
if (unlikely(!strcmp(profile->key, "$"))) {
|
||||
// set default non root profile
|
||||
memcpy(&default_non_root_profile, &profile->nrp_config.profile,
|
||||
sizeof(default_non_root_profile));
|
||||
}
|
||||
|
||||
if (unlikely(!strcmp(profile->key, "#"))) {
|
||||
// set default root profile
|
||||
memcpy(&default_root_profile, &profile->rp_config.profile,
|
||||
sizeof(default_root_profile));
|
||||
}
|
||||
|
||||
if (persist)
|
||||
persistent_allow_list();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ksu_is_allow_uid(uid_t uid)
|
||||
bool __ksu_is_allow_uid(uid_t uid)
|
||||
{
|
||||
struct perm_data *p = NULL;
|
||||
struct list_head *pos = NULL;
|
||||
int i;
|
||||
|
||||
if (uid == 0) {
|
||||
if (unlikely(uid == 0)) {
|
||||
// already root, but only allow our domain.
|
||||
return is_ksu_domain();
|
||||
}
|
||||
|
||||
list_for_each (pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
// pr_info("is_allow_uid uid :%d, allow: %d\n", p->uid, p->allow);
|
||||
if (uid == p->uid) {
|
||||
return p->allow;
|
||||
if (forbid_system_uid(uid)) {
|
||||
// do not bother going through the list if it's system
|
||||
return false;
|
||||
}
|
||||
|
||||
if (likely(uid <= BITMAP_UID_MAX)) {
|
||||
return !!(allow_list_bitmap[uid / BITS_PER_BYTE] & (1 << (uid % BITS_PER_BYTE)));
|
||||
} else {
|
||||
for (i = 0; i < allow_list_pointer; i++) {
|
||||
if (allow_list_arr[i] == uid)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ksu_uid_should_umount(uid_t uid)
|
||||
{
|
||||
struct app_profile profile = { .current_uid = uid };
|
||||
bool found = ksu_get_app_profile(&profile);
|
||||
if (!found) {
|
||||
// no app profile found, it must be non root app
|
||||
return default_non_root_profile.umount_modules;
|
||||
}
|
||||
if (profile.allow_su) {
|
||||
// if found and it is granted to su, we shouldn't umount for it
|
||||
return false;
|
||||
} else {
|
||||
// found an app profile
|
||||
if (profile.nrp_config.use_default) {
|
||||
return default_non_root_profile.umount_modules;
|
||||
} else {
|
||||
return profile.nrp_config.profile.umount_modules;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct root_profile *ksu_get_root_profile(uid_t uid)
|
||||
{
|
||||
struct perm_data *p = NULL;
|
||||
struct list_head *pos = NULL;
|
||||
|
||||
list_for_each (pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
if (uid == p->profile.current_uid && p->profile.allow_su) {
|
||||
if (!p->profile.rp_config.use_default) {
|
||||
return &p->profile.rp_config.profile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use default profile
|
||||
return &default_root_profile;
|
||||
}
|
||||
|
||||
bool ksu_get_allow_list(int *array, int *length, bool allow)
|
||||
{
|
||||
struct perm_data *p = NULL;
|
||||
@@ -105,8 +333,8 @@ bool ksu_get_allow_list(int *array, int *length, bool allow)
|
||||
list_for_each (pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
// pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow);
|
||||
if (p->allow == allow) {
|
||||
array[i++] = p->uid;
|
||||
if (p->profile.allow_su == allow) {
|
||||
array[i++] = p->profile.current_uid;
|
||||
}
|
||||
}
|
||||
*length = i;
|
||||
@@ -114,24 +342,24 @@ bool ksu_get_allow_list(int *array, int *length, bool allow)
|
||||
return true;
|
||||
}
|
||||
|
||||
void do_persistent_allow_list(struct work_struct *work)
|
||||
void do_save_allow_list(struct work_struct *work)
|
||||
{
|
||||
u32 magic = FILE_MAGIC;
|
||||
u32 version = FILE_FORMAT_VERSION;
|
||||
struct perm_data *p = NULL;
|
||||
struct list_head *pos = NULL;
|
||||
loff_t off = 0;
|
||||
KWORKER_INSTALL_KEYRING();
|
||||
struct file *fp =
|
||||
filp_open(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT, 0644);
|
||||
|
||||
struct file *fp =
|
||||
ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT, 0644);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("save_allow_list creat file failed: %d\n", PTR_ERR(fp));
|
||||
pr_err("save_allow_list create file failed: %ld\n", PTR_ERR(fp));
|
||||
return;
|
||||
}
|
||||
|
||||
// store magic and version
|
||||
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
|
||||
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) !=
|
||||
sizeof(magic)) {
|
||||
pr_err("save_allow_list write magic failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
@@ -144,10 +372,12 @@ void do_persistent_allow_list(struct work_struct *work)
|
||||
|
||||
list_for_each (pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
pr_info("save allow list uid :%d, allow: %d\n", p->uid,
|
||||
p->allow);
|
||||
ksu_kernel_write_compat(fp, &p->uid, sizeof(p->uid), &off);
|
||||
ksu_kernel_write_compat(fp, &p->allow, sizeof(p->allow), &off);
|
||||
pr_info("save allow list, name: %s uid :%d, allow: %d\n",
|
||||
p->profile.key, p->profile.current_uid,
|
||||
p->profile.allow_su);
|
||||
|
||||
ksu_kernel_write_compat(fp, &p->profile, sizeof(p->profile),
|
||||
&off);
|
||||
}
|
||||
|
||||
exit:
|
||||
@@ -161,29 +391,22 @@ void do_load_allow_list(struct work_struct *work)
|
||||
struct file *fp = NULL;
|
||||
u32 magic;
|
||||
u32 version;
|
||||
KWORKER_INSTALL_KEYRING();
|
||||
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
// always allow adb shell by default
|
||||
ksu_grant_root_to_shell();
|
||||
#endif
|
||||
|
||||
// load allowlist now!
|
||||
fp = filp_open(KERNEL_SU_ALLOWLIST, O_RDONLY, 0);
|
||||
|
||||
fp = ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_RDONLY, 0);
|
||||
if (IS_ERR(fp)) {
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
int errno = PTR_ERR(fp);
|
||||
if (errno == -ENOENT) {
|
||||
ksu_allow_uid(2000, true,
|
||||
true); // allow adb shell by default
|
||||
} else {
|
||||
pr_err("load_allow_list open file failed: %d\n",
|
||||
PTR_ERR(fp));
|
||||
}
|
||||
#else
|
||||
pr_err("load_allow_list open file failed: %d\n", PTR_ERR(fp));
|
||||
#endif
|
||||
pr_err("load_allow_list open file failed: %ld\n", PTR_ERR(fp));
|
||||
return;
|
||||
}
|
||||
|
||||
// verify magic
|
||||
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic) ||
|
||||
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) !=
|
||||
sizeof(magic) ||
|
||||
magic != FILE_MAGIC) {
|
||||
pr_err("allowlist file invalid: %d!\n", magic);
|
||||
goto exit;
|
||||
@@ -198,18 +421,19 @@ void do_load_allow_list(struct work_struct *work)
|
||||
pr_info("allowlist version: %d\n", version);
|
||||
|
||||
while (true) {
|
||||
u32 uid;
|
||||
bool allow = false;
|
||||
ret = ksu_kernel_read_compat(fp, &uid, sizeof(uid), &off);
|
||||
struct app_profile profile;
|
||||
|
||||
ret = ksu_kernel_read_compat(fp, &profile, sizeof(profile),
|
||||
&off);
|
||||
|
||||
if (ret <= 0) {
|
||||
pr_info("load_allow_list read err: %d\n", ret);
|
||||
pr_info("load_allow_list read err: %zd\n", ret);
|
||||
break;
|
||||
}
|
||||
ret = ksu_kernel_read_compat(fp, &allow, sizeof(allow), &off);
|
||||
|
||||
pr_info("load_allow_uid: %d, allow: %d\n", uid, allow);
|
||||
|
||||
ksu_allow_uid(uid, allow, false);
|
||||
pr_info("load_allow_uid, name: %s, uid: %d, allow: %d\n",
|
||||
profile.key, profile.current_uid, profile.allow_su);
|
||||
ksu_set_app_profile(&profile, false);
|
||||
}
|
||||
|
||||
exit:
|
||||
@@ -226,11 +450,16 @@ void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, void *), void *data)
|
||||
// TODO: use RCU!
|
||||
mutex_lock(&allowlist_mutex);
|
||||
list_for_each_entry_safe (np, n, &allow_list, list) {
|
||||
uid_t uid = np->uid;
|
||||
if (!is_uid_exist(uid, data)) {
|
||||
uid_t uid = np->profile.current_uid;
|
||||
// we use this uid for special cases, don't prune it!
|
||||
bool is_preserved_uid = uid == KSU_APP_PROFILE_PRESERVE_UID;
|
||||
if (!is_preserved_uid && !is_uid_exist(uid, data)) {
|
||||
modified = true;
|
||||
pr_info("prune uid: %d\n", uid);
|
||||
list_del(&np->list);
|
||||
allow_list_bitmap[uid / BITS_PER_BYTE] &= ~(1 << (uid % BITS_PER_BYTE));
|
||||
remove_uid_from_arr(uid);
|
||||
smp_mb();
|
||||
kfree(np);
|
||||
}
|
||||
}
|
||||
@@ -254,10 +483,20 @@ bool ksu_load_allow_list(void)
|
||||
|
||||
void ksu_allowlist_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
BUILD_BUG_ON(sizeof(allow_list_bitmap) != PAGE_SIZE);
|
||||
BUILD_BUG_ON(sizeof(allow_list_arr) != PAGE_SIZE);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(allow_list_arr); i++)
|
||||
allow_list_arr[i] = -1;
|
||||
|
||||
INIT_LIST_HEAD(&allow_list);
|
||||
|
||||
INIT_WORK(&ksu_save_work, do_persistent_allow_list);
|
||||
INIT_WORK(&ksu_save_work, do_save_allow_list);
|
||||
INIT_WORK(&ksu_load_work, do_load_allow_list);
|
||||
|
||||
init_default_profiles();
|
||||
}
|
||||
|
||||
void ksu_allowlist_exit(void)
|
||||
@@ -265,7 +504,7 @@ void ksu_allowlist_exit(void)
|
||||
struct perm_data *np = NULL;
|
||||
struct perm_data *n = NULL;
|
||||
|
||||
do_persistent_allow_list(NULL);
|
||||
do_save_allow_list(NULL);
|
||||
|
||||
// free allowlist
|
||||
mutex_lock(&allowlist_mutex);
|
||||
@@ -274,4 +513,4 @@ void ksu_allowlist_exit(void)
|
||||
kfree(np);
|
||||
}
|
||||
mutex_unlock(&allowlist_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define __KSU_H_ALLOWLIST
|
||||
|
||||
#include "linux/types.h"
|
||||
#include "ksu.h"
|
||||
|
||||
void ksu_allowlist_init(void);
|
||||
|
||||
@@ -11,12 +12,16 @@ bool ksu_load_allow_list(void);
|
||||
|
||||
void ksu_show_allow_list(void);
|
||||
|
||||
bool ksu_is_allow_uid(uid_t uid);
|
||||
|
||||
bool ksu_allow_uid(uid_t uid, bool allow, bool persist);
|
||||
bool __ksu_is_allow_uid(uid_t uid);
|
||||
#define ksu_is_allow_uid(uid) unlikely(__ksu_is_allow_uid(uid))
|
||||
|
||||
bool ksu_get_allow_list(int *array, int *length, bool allow);
|
||||
|
||||
void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, void *), void *data);
|
||||
|
||||
#endif
|
||||
bool ksu_get_app_profile(struct app_profile *);
|
||||
bool ksu_set_app_profile(struct app_profile *, bool persist);
|
||||
|
||||
bool ksu_uid_should_umount(uid_t uid);
|
||||
struct root_profile *ksu_get_root_profile(uid_t uid);
|
||||
#endif
|
||||
|
||||
@@ -16,7 +16,7 @@ check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash)
|
||||
|
||||
int sign = -1;
|
||||
int i;
|
||||
struct file *fp = filp_open(path, O_RDONLY, 0);
|
||||
struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("open %s error.", path);
|
||||
return PTR_ERR(fp);
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
// return 0 if signature match
|
||||
int is_manager_apk(char *path);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
#define __PT_PARM1_REG regs[0]
|
||||
#define __PT_PARM2_REG regs[1]
|
||||
#define __PT_PARM3_REG regs[2]
|
||||
#define __PT_PARM4_REG regs[3]
|
||||
#define __PT_SYSCALL_PARM4_REG regs[3]
|
||||
#define __PT_CCALL_PARM4_REG regs[3]
|
||||
#define __PT_PARM5_REG regs[4]
|
||||
#define __PT_PARM6_REG regs[5]
|
||||
#define __PT_RET_REG regs[30]
|
||||
@@ -29,8 +30,8 @@
|
||||
#define __PT_PARM2_REG si
|
||||
#define __PT_PARM3_REG dx
|
||||
/* syscall uses r10 for PARM4 */
|
||||
#define __PT_PARM4_REG r10
|
||||
// #define __PT_PARM4_REG cx
|
||||
#define __PT_SYSCALL_PARM4_REG r10
|
||||
#define __PT_CCALL_PARM4_REG cx
|
||||
#define __PT_PARM5_REG r8
|
||||
#define __PT_PARM6_REG r9
|
||||
#define __PT_RET_REG sp
|
||||
@@ -56,7 +57,8 @@
|
||||
#define PT_REGS_PARM1(x) (__PT_REGS_CAST(x)->__PT_PARM1_REG)
|
||||
#define PT_REGS_PARM2(x) (__PT_REGS_CAST(x)->__PT_PARM2_REG)
|
||||
#define PT_REGS_PARM3(x) (__PT_REGS_CAST(x)->__PT_PARM3_REG)
|
||||
#define PT_REGS_PARM4(x) (__PT_REGS_CAST(x)->__PT_PARM4_REG)
|
||||
#define PT_REGS_SYSCALL_PARM4(x) (__PT_REGS_CAST(x)->__PT_SYSCALL_PARM4_REG)
|
||||
#define PT_REGS_CCALL_PARM4(x) (__PT_REGS_CAST(x)->__PT_CCALL_PARM4_REG)
|
||||
#define PT_REGS_PARM5(x) (__PT_REGS_CAST(x)->__PT_PARM5_REG)
|
||||
#define PT_REGS_PARM6(x) (__PT_REGS_CAST(x)->__PT_PARM6_REG)
|
||||
#define PT_REGS_RET(x) (__PT_REGS_CAST(x)->__PT_RET_REG)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "linux/capability.h"
|
||||
#include "linux/cred.h"
|
||||
#include "linux/dcache.h"
|
||||
#include "linux/err.h"
|
||||
@@ -53,24 +54,85 @@ static inline bool is_isolated_uid(uid_t uid)
|
||||
|
||||
static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
|
||||
|
||||
static void setup_groups(struct root_profile *profile, struct cred *cred)
|
||||
{
|
||||
if (profile->groups_count > KSU_MAX_GROUPS) {
|
||||
pr_warn("Failed to setgroups, too large group: %d!\n",
|
||||
profile->uid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (profile->groups_count == 1 && profile->groups[0] == 0) {
|
||||
// setgroup to root and return early.
|
||||
if (cred->group_info)
|
||||
put_group_info(cred->group_info);
|
||||
cred->group_info = get_group_info(&root_groups);
|
||||
return;
|
||||
}
|
||||
|
||||
u32 ngroups = profile->groups_count;
|
||||
struct group_info *group_info = groups_alloc(ngroups);
|
||||
if (!group_info) {
|
||||
pr_warn("Failed to setgroups, ENOMEM for: %d\n", profile->uid);
|
||||
return;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < ngroups; i++) {
|
||||
gid_t gid = profile->groups[i];
|
||||
kgid_t kgid = make_kgid(current_user_ns(), gid);
|
||||
if (!gid_valid(kgid)) {
|
||||
pr_warn("Failed to setgroups, invalid gid: %d\n", gid);
|
||||
put_group_info(group_info);
|
||||
return;
|
||||
}
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
|
||||
group_info->gid[i] = kgid;
|
||||
#else
|
||||
GROUP_AT(group_info, i) = kgid;
|
||||
#endif
|
||||
}
|
||||
|
||||
groups_sort(group_info);
|
||||
set_groups(cred, group_info);
|
||||
}
|
||||
|
||||
void escape_to_root(void)
|
||||
{
|
||||
struct cred *cred;
|
||||
|
||||
cred = (struct cred *)__task_cred(current);
|
||||
|
||||
memset(&cred->uid, 0, sizeof(cred->uid));
|
||||
memset(&cred->gid, 0, sizeof(cred->gid));
|
||||
memset(&cred->suid, 0, sizeof(cred->suid));
|
||||
memset(&cred->euid, 0, sizeof(cred->euid));
|
||||
memset(&cred->egid, 0, sizeof(cred->egid));
|
||||
memset(&cred->fsuid, 0, sizeof(cred->fsuid));
|
||||
memset(&cred->fsgid, 0, sizeof(cred->fsgid));
|
||||
memset(&cred->cap_inheritable, 0xff, sizeof(cred->cap_inheritable));
|
||||
memset(&cred->cap_permitted, 0xff, sizeof(cred->cap_permitted));
|
||||
memset(&cred->cap_effective, 0xff, sizeof(cred->cap_effective));
|
||||
memset(&cred->cap_bset, 0xff, sizeof(cred->cap_bset));
|
||||
memset(&cred->cap_ambient, 0xff, sizeof(cred->cap_ambient));
|
||||
if (cred->euid.val == 0) {
|
||||
pr_warn("Already root, don't escape!\n");
|
||||
return;
|
||||
}
|
||||
struct root_profile *profile = ksu_get_root_profile(cred->uid.val);
|
||||
|
||||
cred->uid.val = profile->uid;
|
||||
cred->suid.val = profile->uid;
|
||||
cred->euid.val = profile->uid;
|
||||
cred->fsuid.val = profile->uid;
|
||||
|
||||
cred->gid.val = profile->gid;
|
||||
cred->fsgid.val = profile->gid;
|
||||
cred->sgid.val = profile->gid;
|
||||
cred->egid.val = profile->gid;
|
||||
|
||||
BUILD_BUG_ON(sizeof(profile->capabilities.effective) !=
|
||||
sizeof(kernel_cap_t));
|
||||
|
||||
// capabilities
|
||||
memcpy(&cred->cap_effective, &profile->capabilities.effective,
|
||||
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));
|
||||
|
||||
// disable seccomp
|
||||
#if defined(CONFIG_GENERIC_ENTRY) && \
|
||||
@@ -86,12 +148,9 @@ void escape_to_root(void)
|
||||
#else
|
||||
#endif
|
||||
|
||||
// setgroup to root
|
||||
if (cred->group_info)
|
||||
put_group_info(cred->group_info);
|
||||
cred->group_info = get_group_info(&root_groups);
|
||||
setup_groups(profile, cred);
|
||||
|
||||
setup_selinux();
|
||||
setup_selinux(profile->selinux_domain);
|
||||
}
|
||||
|
||||
int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry)
|
||||
@@ -173,8 +232,10 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
// someone wants to be root manager, just check it!
|
||||
// arg3 should be `/data/user/<userId>/<manager_package_name>`
|
||||
char param[128];
|
||||
if (copy_from_user(param, arg3, sizeof(param))) {
|
||||
if (ksu_strncpy_from_user_nofault(param, arg3, sizeof(param)) == -EFAULT) {
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_err("become_manager: copy param err\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -186,7 +247,8 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
if (userId == 0) {
|
||||
prefix = "/data/data";
|
||||
} else {
|
||||
snprintf(prefixTmp, sizeof(prefixTmp), "/data/user/%d", userId);
|
||||
snprintf(prefixTmp, sizeof(prefixTmp), "/data/user/%d",
|
||||
userId);
|
||||
prefix = prefixTmp;
|
||||
}
|
||||
|
||||
@@ -227,10 +289,6 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("grant_root: prctl reply error\n");
|
||||
}
|
||||
} else {
|
||||
pr_info("deny root for: %d\n", current_uid());
|
||||
// add it to deny list!
|
||||
ksu_allow_uid(current_uid().val, false, true);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -325,6 +383,30 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_UID_GRANTED_ROOT || arg2 == CMD_UID_SHOULD_UMOUNT) {
|
||||
if (is_manager() || 0 == current_uid().val) {
|
||||
uid_t target_uid = (uid_t)arg3;
|
||||
bool allow = false;
|
||||
if (arg2 == CMD_UID_GRANTED_ROOT) {
|
||||
allow = ksu_is_allow_uid(target_uid);
|
||||
} else if (arg2 == CMD_UID_SHOULD_UMOUNT) {
|
||||
allow = ksu_uid_should_umount(target_uid);
|
||||
} else {
|
||||
pr_err("unknown cmd: %d\n", arg2);
|
||||
}
|
||||
if (!copy_to_user(arg4, &allow, sizeof(allow))) {
|
||||
if (copy_to_user(result, &reply_ok,
|
||||
sizeof(reply_ok))) {
|
||||
pr_err("prctl reply error, cmd: %d\n",
|
||||
arg2);
|
||||
}
|
||||
} else {
|
||||
pr_err("prctl copy err, cmd: %d\n", arg2);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// all other cmds are for 'root manager'
|
||||
if (!is_manager()) {
|
||||
last_failed_uid = current_uid().val;
|
||||
@@ -332,17 +414,39 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
}
|
||||
|
||||
// we are already manager
|
||||
if (arg2 == CMD_ALLOW_SU || arg2 == CMD_DENY_SU) {
|
||||
bool allow = arg2 == CMD_ALLOW_SU;
|
||||
bool success = false;
|
||||
uid_t uid = (uid_t)arg3;
|
||||
success = ksu_allow_uid(uid, allow, true);
|
||||
if (arg2 == CMD_GET_APP_PROFILE) {
|
||||
struct app_profile profile;
|
||||
if (copy_from_user(&profile, arg3, sizeof(profile))) {
|
||||
pr_err("copy profile failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool success = ksu_get_app_profile(&profile);
|
||||
if (success) {
|
||||
if (copy_to_user(arg3, &profile, sizeof(profile))) {
|
||||
pr_err("copy profile failed\n");
|
||||
return 0;
|
||||
}
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("prctl reply error, cmd: %d\n", arg2);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_SET_APP_PROFILE) {
|
||||
struct app_profile profile;
|
||||
if (copy_from_user(&profile, arg3, sizeof(profile))) {
|
||||
pr_err("copy profile failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// todo: validate the params
|
||||
if (ksu_set_app_profile(&profile, true)) {
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("prctl reply error, cmd: %d\n", arg2);
|
||||
}
|
||||
}
|
||||
ksu_show_allow_list();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -366,7 +470,8 @@ static bool should_umount(struct path *path)
|
||||
}
|
||||
|
||||
if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) {
|
||||
pr_info("ignore global mnt namespace process: %d\n", current_uid().val);
|
||||
pr_info("ignore global mnt namespace process: %d\n",
|
||||
current_uid().val);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -391,7 +496,7 @@ static void try_umount(const char *mnt)
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
||||
err = path_umount(&path, MNT_DETACH);
|
||||
err = path_umount(&path, 0);
|
||||
if (err) {
|
||||
pr_info("umount %s failed: %d\n", mnt, err);
|
||||
}
|
||||
@@ -424,6 +529,14 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ksu_uid_should_umount(new_uid.val)) {
|
||||
return 0;
|
||||
} else {
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_info("uid: %d should not umount!\n", current_uid().val);
|
||||
#endif
|
||||
}
|
||||
|
||||
// umount the target mnt
|
||||
pr_info("handle umount for uid: %d\n", new_uid.val);
|
||||
|
||||
@@ -448,7 +561,14 @@ static int handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
int option = (int)PT_REGS_PARM1(real_regs);
|
||||
unsigned long arg2 = (unsigned long)PT_REGS_PARM2(real_regs);
|
||||
unsigned long arg3 = (unsigned long)PT_REGS_PARM3(real_regs);
|
||||
unsigned long arg4 = (unsigned long)PT_REGS_PARM4(real_regs);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||
// PRCTL_SYMBOL is the arch-specificed one, which receive raw pt_regs from syscall
|
||||
unsigned long arg4 = (unsigned long)PT_REGS_SYSCALL_PARM4(real_regs);
|
||||
#else
|
||||
// PRCTL_SYMBOL is the common one, called by C convention in do_syscall_64
|
||||
// https://elixir.bootlin.com/linux/v4.15.18/source/arch/x86/entry/common.c#L287
|
||||
unsigned long arg4 = (unsigned long)PT_REGS_CCALL_PARM4(real_regs);
|
||||
#endif
|
||||
unsigned long arg5 = (unsigned long)PT_REGS_PARM5(real_regs);
|
||||
|
||||
return ksu_handle_prctl(option, arg2, arg3, arg4, arg5);
|
||||
@@ -468,7 +588,7 @@ static int renameat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
struct dentry *new_entry = rd->new_dentry;
|
||||
#else
|
||||
struct dentry *old_entry = (struct dentry *)PT_REGS_PARM2(regs);
|
||||
struct dentry *new_entry = (struct dentry *)PT_REGS_PARM4(regs);
|
||||
struct dentry *new_entry = (struct dentry *)PT_REGS_CCALL_PARM4(regs);
|
||||
#endif
|
||||
|
||||
return ksu_handle_rename(old_entry, new_entry);
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
// This file will be regenerated by CI
|
||||
|
||||
unsigned int ksud_size = 0;
|
||||
const char ksud[0] = {};
|
||||
const char ksud[0] = {};
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
register_kprobe
|
||||
unregister_kprobe
|
||||
unregister_kprobe
|
||||
|
||||
@@ -1,34 +1,174 @@
|
||||
#include "linux/version.h"
|
||||
#include "linux/fs.h"
|
||||
#include "linux/nsproxy.h"
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#include "linux/sched/task.h"
|
||||
#include "linux/uaccess.h"
|
||||
#else
|
||||
#include "linux/sched.h"
|
||||
#endif
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
|
||||
#include "linux/key.h"
|
||||
#include "linux/errno.h"
|
||||
struct key *init_session_keyring = NULL;
|
||||
|
||||
static inline int install_session_keyring(struct key *keyring)
|
||||
{
|
||||
struct cred *new;
|
||||
int ret;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = install_session_keyring_to_cred(new, keyring);
|
||||
if (ret < 0) {
|
||||
abort_creds(new);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return commit_creds(new);
|
||||
}
|
||||
#endif
|
||||
ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count, loff_t *pos){
|
||||
|
||||
extern struct task_struct init_task;
|
||||
|
||||
// mnt_ns context switch for environment that android_init->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns, such as WSA
|
||||
struct ksu_ns_fs_saved {
|
||||
struct nsproxy *ns;
|
||||
struct fs_struct *fs;
|
||||
};
|
||||
|
||||
static void ksu_save_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved)
|
||||
{
|
||||
ns_fs_saved->ns = current->nsproxy;
|
||||
ns_fs_saved->fs = current->fs;
|
||||
}
|
||||
|
||||
static void ksu_load_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved)
|
||||
{
|
||||
current->nsproxy = ns_fs_saved->ns;
|
||||
current->fs = ns_fs_saved->fs;
|
||||
}
|
||||
|
||||
static bool android_context_saved_checked = false;
|
||||
static bool android_context_saved_enabled = false;
|
||||
static struct ksu_ns_fs_saved android_context_saved;
|
||||
|
||||
void ksu_android_ns_fs_check()
|
||||
{
|
||||
if (android_context_saved_checked)
|
||||
return;
|
||||
android_context_saved_checked = true;
|
||||
task_lock(current);
|
||||
if (current->nsproxy && current->fs &&
|
||||
current->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns) {
|
||||
android_context_saved_enabled = true;
|
||||
pr_info("android context saved enabled due to init mnt_ns(%p) != android mnt_ns(%p)\n",
|
||||
current->nsproxy->mnt_ns, init_task.nsproxy->mnt_ns);
|
||||
ksu_save_ns_fs(&android_context_saved);
|
||||
} else {
|
||||
pr_info("android context saved disabled\n");
|
||||
}
|
||||
task_unlock(current);
|
||||
}
|
||||
|
||||
struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
|
||||
{
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
|
||||
if (init_session_keyring != NULL && !current_cred()->session_keyring &&
|
||||
(current->flags & PF_WQ_WORKER)) {
|
||||
pr_info("installing init session keyring for older kernel\n");
|
||||
install_session_keyring(init_session_keyring);
|
||||
}
|
||||
#endif
|
||||
// switch mnt_ns even if current is not wq_worker, to ensure what we open is the correct file in android mnt_ns, rather than user created mnt_ns
|
||||
struct ksu_ns_fs_saved saved;
|
||||
if (android_context_saved_enabled) {
|
||||
pr_info("start switch current nsproxy and fs to android context\n");
|
||||
task_lock(current);
|
||||
ksu_save_ns_fs(&saved);
|
||||
ksu_load_ns_fs(&android_context_saved);
|
||||
task_unlock(current);
|
||||
}
|
||||
struct file *fp = filp_open(filename, flags, mode);
|
||||
if (android_context_saved_enabled) {
|
||||
task_lock(current);
|
||||
ksu_load_ns_fs(&saved);
|
||||
task_unlock(current);
|
||||
pr_info("switch current nsproxy and fs back to saved successfully\n");
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
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)
|
||||
return kernel_read(p, buf, count, pos);
|
||||
return kernel_read(p, buf, count, pos);
|
||||
#else
|
||||
loff_t offset = pos ? *pos : 0;
|
||||
ssize_t result = kernel_read(p, offset, (char *)buf, count);
|
||||
if (pos && result > 0)
|
||||
{
|
||||
*pos = offset + result;
|
||||
}
|
||||
return result;
|
||||
loff_t offset = pos ? *pos : 0;
|
||||
ssize_t result = kernel_read(p, offset, (char *)buf, count);
|
||||
if (pos && result > 0) {
|
||||
*pos = offset + result;
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count, loff_t *pos){
|
||||
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)
|
||||
return kernel_write(p, buf, count, pos);
|
||||
return kernel_write(p, buf, count, pos);
|
||||
#else
|
||||
loff_t offset = pos ? *pos : 0;
|
||||
ssize_t result = kernel_write(p, buf, count, offset);
|
||||
if (pos && result > 0)
|
||||
{
|
||||
*pos = offset + result;
|
||||
}
|
||||
return result;
|
||||
loff_t offset = pos ? *pos : 0;
|
||||
ssize_t result = kernel_write(p, buf, count, offset);
|
||||
if (pos && result > 0) {
|
||||
*pos = offset + result;
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
|
||||
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
||||
long count)
|
||||
{
|
||||
return strncpy_from_user_nofault(dst, unsafe_addr, count);
|
||||
}
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
|
||||
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
||||
long count)
|
||||
{
|
||||
return strncpy_from_unsafe_user(dst, unsafe_addr, count);
|
||||
}
|
||||
#else
|
||||
// Copied from: https://elixir.bootlin.com/linux/v4.9.337/source/mm/maccess.c#L201
|
||||
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
||||
long count)
|
||||
{
|
||||
mm_segment_t old_fs = get_fs();
|
||||
long ret;
|
||||
|
||||
if (unlikely(count <= 0))
|
||||
return 0;
|
||||
|
||||
set_fs(USER_DS);
|
||||
pagefault_disable();
|
||||
ret = strncpy_from_user(dst, unsafe_addr, count);
|
||||
pagefault_enable();
|
||||
set_fs(old_fs);
|
||||
|
||||
if (ret >= count) {
|
||||
ret = count;
|
||||
dst[ret - 1] = '\0';
|
||||
} else if (ret > 0) {
|
||||
ret++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
@@ -5,38 +5,20 @@
|
||||
#include "linux/key.h"
|
||||
#include "linux/version.h"
|
||||
|
||||
extern struct key *init_session_keyring;
|
||||
|
||||
extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count, loff_t *pos);
|
||||
extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count, loff_t *pos);
|
||||
extern long ksu_strncpy_from_user_nofault(char *dst,
|
||||
const void __user *unsafe_addr,
|
||||
long count);
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
|
||||
static inline int install_session_keyring(struct key *keyring)
|
||||
{
|
||||
struct cred *new;
|
||||
int ret;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = install_session_keyring_to_cred(new, keyring);
|
||||
if (ret < 0) {
|
||||
abort_creds(new);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return commit_creds(new);
|
||||
}
|
||||
#define KWORKER_INSTALL_KEYRING() \
|
||||
static bool keyring_installed = false; \
|
||||
if (init_session_keyring != NULL && !keyring_installed) \
|
||||
{ \
|
||||
install_session_keyring(init_session_keyring); \
|
||||
keyring_installed = true; \
|
||||
}
|
||||
#else
|
||||
#define KWORKER_INSTALL_KEYRING()
|
||||
extern struct key *init_session_keyring;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
extern void ksu_android_ns_fs_check();
|
||||
extern struct file *ksu_filp_open_compat(const char *filename, int flags,
|
||||
umode_t mode);
|
||||
extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
||||
loff_t *pos);
|
||||
extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf,
|
||||
size_t count, loff_t *pos);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -8,4 +8,4 @@
|
||||
#define pr_fmt(fmt) "KernelSU: " fmt
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -39,7 +39,7 @@ int __init kernelsu_init(void)
|
||||
pr_alert("*************************************************************");
|
||||
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
|
||||
pr_alert("** **");
|
||||
pr_alert("** You are running DEBUG version of KernelSU **");
|
||||
pr_alert("** You are running KernelSU in DEBUG mode **");
|
||||
pr_alert("** **");
|
||||
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
|
||||
pr_alert("*************************************************************");
|
||||
@@ -47,7 +47,7 @@ int __init kernelsu_init(void)
|
||||
|
||||
ksu_core_init();
|
||||
|
||||
ksu_workqueue = alloc_workqueue("kernelsu_work_queue", 0, 0);
|
||||
ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
|
||||
|
||||
ksu_allowlist_init();
|
||||
|
||||
@@ -57,7 +57,7 @@ int __init kernelsu_init(void)
|
||||
ksu_enable_sucompat();
|
||||
ksu_enable_ksud();
|
||||
#else
|
||||
#warning("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html")
|
||||
pr_alert("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
67
kernel/ksu.h
67
kernel/ksu.h
@@ -1,15 +1,10 @@
|
||||
#ifndef __KSU_H_KSU
|
||||
#define __KSU_H_KSU
|
||||
|
||||
#include "linux/types.h"
|
||||
#include "linux/workqueue.h"
|
||||
|
||||
#ifndef KSU_GIT_VERSION
|
||||
#warning "KSU_GIT_VERSION not defined! It is better to make KernelSU a git submodule!"
|
||||
#define KERNEL_SU_VERSION (16)
|
||||
#else
|
||||
#define KERNEL_SU_VERSION (10000 + KSU_GIT_VERSION + 200) // major * 10000 + git version + 200 for historical reasons
|
||||
#endif
|
||||
|
||||
#define KERNEL_SU_VERSION KSU_VERSION
|
||||
#define KERNEL_SU_OPTION 0xDEADBEEF
|
||||
|
||||
#define CMD_GRANT_ROOT 0
|
||||
@@ -22,10 +17,68 @@
|
||||
#define CMD_REPORT_EVENT 7
|
||||
#define CMD_SET_SEPOLICY 8
|
||||
#define CMD_CHECK_SAFEMODE 9
|
||||
#define CMD_GET_APP_PROFILE 10
|
||||
#define CMD_SET_APP_PROFILE 11
|
||||
#define CMD_UID_GRANTED_ROOT 12
|
||||
#define CMD_UID_SHOULD_UMOUNT 13
|
||||
|
||||
#define EVENT_POST_FS_DATA 1
|
||||
#define EVENT_BOOT_COMPLETED 2
|
||||
|
||||
#define KSU_APP_PROFILE_VER 2
|
||||
#define KSU_MAX_PACKAGE_NAME 256
|
||||
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
|
||||
#define KSU_MAX_GROUPS 32
|
||||
#define KSU_SELINUX_DOMAIN 64
|
||||
|
||||
struct root_profile {
|
||||
int32_t uid;
|
||||
int32_t gid;
|
||||
|
||||
int32_t groups_count;
|
||||
int32_t groups[KSU_MAX_GROUPS];
|
||||
|
||||
// kernel_cap_t is u32[2] for capabilities v3
|
||||
struct {
|
||||
u64 effective;
|
||||
u64 permitted;
|
||||
u64 inheritable;
|
||||
} capabilities;
|
||||
|
||||
char selinux_domain[KSU_SELINUX_DOMAIN];
|
||||
|
||||
int32_t namespaces;
|
||||
};
|
||||
|
||||
struct non_root_profile {
|
||||
bool umount_modules;
|
||||
};
|
||||
|
||||
struct app_profile {
|
||||
// It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this.
|
||||
u32 version;
|
||||
|
||||
// this is usually the package of the app, but can be other value for special apps
|
||||
char key[KSU_MAX_PACKAGE_NAME];
|
||||
int32_t current_uid;
|
||||
bool allow_su;
|
||||
|
||||
union {
|
||||
struct {
|
||||
bool use_default;
|
||||
char template_name[KSU_MAX_PACKAGE_NAME];
|
||||
|
||||
struct root_profile profile;
|
||||
} rp_config;
|
||||
|
||||
struct {
|
||||
bool use_default;
|
||||
|
||||
struct non_root_profile profile;
|
||||
} nrp_config;
|
||||
};
|
||||
};
|
||||
|
||||
bool ksu_queue_work(struct work_struct *work);
|
||||
|
||||
static inline int startswith(char *s, char *prefix)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#include "asm/current.h"
|
||||
#include "linux/string.h"
|
||||
#include "linux/compat.h"
|
||||
#include "linux/cred.h"
|
||||
#include "linux/dcache.h"
|
||||
#include "linux/err.h"
|
||||
#include "linux/fs.h"
|
||||
@@ -12,12 +10,12 @@
|
||||
#include "linux/uaccess.h"
|
||||
#include "linux/version.h"
|
||||
#include "linux/workqueue.h"
|
||||
#include "linux/input.h"
|
||||
|
||||
#include "allowlist.h"
|
||||
#include "arch.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "ksud.h"
|
||||
#include "kernel_compat.h"
|
||||
#include "selinux/selinux.h"
|
||||
|
||||
static const char KERNEL_SU_RC[] =
|
||||
@@ -52,9 +50,9 @@ 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
|
||||
static bool vfs_read_hook = true;
|
||||
static bool execveat_hook = true;
|
||||
static bool input_hook = true;
|
||||
bool ksu_vfs_read_hook __read_mostly = true;
|
||||
bool ksu_execveat_hook __read_mostly = true;
|
||||
bool ksu_input_hook __read_mostly = true;
|
||||
#endif
|
||||
|
||||
void on_post_fs_data(void)
|
||||
@@ -108,7 +106,13 @@ static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr)
|
||||
/*
|
||||
* count() counts the number of strings in array ARGV.
|
||||
*/
|
||||
static int count(struct user_arg_ptr argv, int max)
|
||||
|
||||
/*
|
||||
* Make sure old GCC compiler can use __maybe_unused,
|
||||
* Test passed in 4.4.x ~ 4.9.x when use GCC.
|
||||
*/
|
||||
|
||||
static int __maybe_unused count(struct user_arg_ptr argv, int max)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
@@ -134,11 +138,12 @@ static int count(struct user_arg_ptr argv, int max)
|
||||
return i;
|
||||
}
|
||||
|
||||
// the call from execve_handler_pre won't provided correct value for __never_use_argument, use them after fix execve_handler_pre, keeping them for consistence for manually patched code
|
||||
int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||
void *argv, void *envp, int *flags)
|
||||
struct user_arg_ptr *argv, void *__never_use_envp, int *__never_use_flags)
|
||||
{
|
||||
#ifndef CONFIG_KPROBES
|
||||
if (!execveat_hook) {
|
||||
if (!ksu_execveat_hook) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -157,51 +162,31 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!memcmp(filename->name, system_bin_init,
|
||||
sizeof(system_bin_init) - 1)) {
|
||||
#ifdef __aarch64__
|
||||
if (unlikely(!memcmp(filename->name, system_bin_init,
|
||||
sizeof(system_bin_init) - 1))) {
|
||||
// /system/bin/init executed
|
||||
struct user_arg_ptr *ptr = (struct user_arg_ptr*) argv;
|
||||
int argc = count(*ptr, MAX_ARG_STRINGS);
|
||||
int argc = count(*argv, MAX_ARG_STRINGS);
|
||||
pr_info("/system/bin/init argc: %d\n", argc);
|
||||
if (argc > 1 && !init_second_stage_executed) {
|
||||
const char __user *p = get_user_arg_ptr(*ptr, 1);
|
||||
const char __user *p = get_user_arg_ptr(*argv, 1);
|
||||
if (p && !IS_ERR(p)) {
|
||||
char first_arg[16];
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
|
||||
strncpy_from_user_nofault(first_arg, p, sizeof(first_arg));
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
|
||||
strncpy_from_unsafe_user(first_arg, p, sizeof(first_arg));
|
||||
#else
|
||||
strncpy_from_user(first_arg, p, sizeof(first_arg));
|
||||
#endif
|
||||
ksu_strncpy_from_user_nofault(first_arg, p, sizeof(first_arg));
|
||||
pr_info("first arg: %s\n", first_arg);
|
||||
if (!strcmp(first_arg, "second_stage")) {
|
||||
pr_info("/system/bin/init second_stage executed\n");
|
||||
apply_kernelsu_rules();
|
||||
init_second_stage_executed = true;
|
||||
ksu_android_ns_fs_check();
|
||||
}
|
||||
} else {
|
||||
pr_err("/system/bin/init parse args err!\n");
|
||||
}
|
||||
}
|
||||
#else
|
||||
// The argument parse is incorrect becuase of the struct user_arg_ptr has 16bytes
|
||||
// and it is passed by value(not pointer), in arm64, it is correct becuase the register
|
||||
// is just arranged correct accidentally, but is not correct in x86_64
|
||||
// i have no device to test, so revert it for x86_64
|
||||
static int init_count = 0;
|
||||
if (++init_count == 2) {
|
||||
// 1: /system/bin/init selinux_setup
|
||||
// 2: /system/bin/init second_stage
|
||||
pr_info("/system/bin/init second_stage executed\n");
|
||||
apply_kernelsu_rules();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (first_app_process &&
|
||||
!memcmp(filename->name, app_process, sizeof(app_process) - 1)) {
|
||||
if (unlikely(first_app_process &&
|
||||
!memcmp(filename->name, app_process, sizeof(app_process) - 1))) {
|
||||
first_app_process = false;
|
||||
pr_info("exec app_process, /data prepared, second_stage: %d\n", init_second_stage_executed);
|
||||
on_post_fs_data(); // we keep this for old ksud
|
||||
@@ -244,7 +229,7 @@ int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
||||
size_t *count_ptr, loff_t **pos)
|
||||
{
|
||||
#ifndef CONFIG_KPROBES
|
||||
if (!vfs_read_hook) {
|
||||
if (!ksu_vfs_read_hook) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -345,7 +330,7 @@ int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
||||
int *value)
|
||||
{
|
||||
#ifndef CONFIG_KPROBES
|
||||
if (!input_hook) {
|
||||
if (!ksu_input_hook) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -394,11 +379,19 @@ 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);
|
||||
void *argv = (void *)&PT_REGS_PARM3(regs);
|
||||
void *envp = (void *)&PT_REGS_PARM4(regs);
|
||||
int *flags = (int *)&PT_REGS_PARM5(regs);
|
||||
struct user_arg_ptr argv;
|
||||
#ifdef CONFIG_COMPAT
|
||||
argv.is_compat = PT_REGS_PARM3(regs);
|
||||
if (unlikely(argv.is_compat)) {
|
||||
argv.ptr.compat = PT_REGS_CCALL_PARM4(regs);
|
||||
} else {
|
||||
argv.ptr.native = PT_REGS_CCALL_PARM4(regs);
|
||||
}
|
||||
#else
|
||||
argv.ptr.native = PT_REGS_PARM3(regs);
|
||||
#endif
|
||||
|
||||
return ksu_handle_execveat_ksud(fd, filename_ptr, argv, envp, flags);
|
||||
return ksu_handle_execveat_ksud(fd, filename_ptr, &argv, NULL, NULL);
|
||||
}
|
||||
|
||||
static int read_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
@@ -406,7 +399,7 @@ static int read_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
struct file **file_ptr = (struct file **)&PT_REGS_PARM1(regs);
|
||||
char __user **buf_ptr = (char **)&PT_REGS_PARM2(regs);
|
||||
size_t *count_ptr = (size_t *)&PT_REGS_PARM3(regs);
|
||||
loff_t **pos_ptr = (loff_t **)&PT_REGS_PARM4(regs);
|
||||
loff_t **pos_ptr = (loff_t **)&PT_REGS_CCALL_PARM4(regs);
|
||||
|
||||
return ksu_handle_vfs_read(file_ptr, buf_ptr, count_ptr, pos_ptr);
|
||||
}
|
||||
@@ -416,7 +409,7 @@ static int input_handle_event_handler_pre(struct kprobe *p,
|
||||
{
|
||||
unsigned int *type = (unsigned int *)&PT_REGS_PARM2(regs);
|
||||
unsigned int *code = (unsigned int *)&PT_REGS_PARM3(regs);
|
||||
int *value = (int *)&PT_REGS_PARM4(regs);
|
||||
int *value = (int *)&PT_REGS_CCALL_PARM4(regs);
|
||||
return ksu_handle_input_handle_event(type, code, value);
|
||||
}
|
||||
|
||||
@@ -463,7 +456,7 @@ static void stop_vfs_read_hook()
|
||||
bool ret = schedule_work(&stop_vfs_read_work);
|
||||
pr_info("unregister vfs_read kprobe: %d!\n", ret);
|
||||
#else
|
||||
vfs_read_hook = false;
|
||||
ksu_vfs_read_hook = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -473,7 +466,7 @@ static void stop_execve_hook()
|
||||
bool ret = schedule_work(&stop_execve_hook_work);
|
||||
pr_info("unregister execve kprobe: %d!\n", ret);
|
||||
#else
|
||||
execveat_hook = false;
|
||||
ksu_execveat_hook = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -488,7 +481,7 @@ static void stop_input_hook()
|
||||
bool ret = schedule_work(&stop_input_hook_work);
|
||||
pr_info("unregister input kprobe: %d!\n", ret);
|
||||
#else
|
||||
input_hook = false;
|
||||
ksu_input_hook = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -7,4 +7,4 @@ void on_post_fs_data(void);
|
||||
|
||||
bool ksu_is_safe_mode(void);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "ksu.h"
|
||||
#include "manager.h"
|
||||
|
||||
uid_t ksu_manager_uid = INVALID_UID;
|
||||
uid_t ksu_manager_uid = KSU_INVALID_UID;
|
||||
|
||||
bool become_manager(char *pkg)
|
||||
{
|
||||
@@ -39,39 +39,51 @@ bool become_manager(char *pkg)
|
||||
|
||||
files_table = files_fdtable(current->files);
|
||||
|
||||
int pkg_len = strlen(pkg);
|
||||
// todo: use iterate_fd
|
||||
while (files_table->fd[i] != NULL) {
|
||||
for (i = 0; files_table->fd[i] != NULL; i++) {
|
||||
files_path = files_table->fd[i]->f_path;
|
||||
if (!d_is_reg(files_path.dentry)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
cwd = d_path(&files_path, buf, PATH_MAX);
|
||||
if (startswith(cwd, "/data/app/") == 0 &&
|
||||
endswith(cwd, "/base.apk") == 0) {
|
||||
// we have found the apk!
|
||||
pr_info("found apk: %s", cwd);
|
||||
if (!strstr(cwd, pkg)) {
|
||||
pr_info("apk path not match package name!\n");
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (is_manager_apk(cwd) == 0) {
|
||||
// check passed
|
||||
uid_t uid = current_uid().val;
|
||||
pr_info("manager uid: %d\n", uid);
|
||||
|
||||
ksu_set_manager_uid(uid);
|
||||
|
||||
result = true;
|
||||
goto clean;
|
||||
} else {
|
||||
pr_info("manager signature invalid!");
|
||||
}
|
||||
|
||||
break;
|
||||
if (startswith(cwd, "/data/app/") != 0 ||
|
||||
endswith(cwd, "/base.apk") != 0) {
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
// we have found the apk!
|
||||
pr_info("found apk: %s", cwd);
|
||||
char *pkg_index = strstr(cwd, pkg);
|
||||
if (!pkg_index) {
|
||||
pr_info("apk path not match package name!\n");
|
||||
continue;
|
||||
}
|
||||
char *next_char = pkg_index + pkg_len;
|
||||
// because we ensure the cwd must startswith `/data/app` and endswith `base.apk`
|
||||
// we don't need to check if the pointer is out of bounds
|
||||
if (*next_char != '-') {
|
||||
// from android 8.1: http://aospxref.com/android-8.1.0_r81/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#17612
|
||||
// to android 13: http://aospxref.com/android-13.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java#1208
|
||||
// /data/app/~~[randomStringA]/[packageName]-[randomStringB]
|
||||
// the previous char must be `/` and the next char must be `-`
|
||||
// because we use strstr instead of equals, this is a strong verfication.
|
||||
pr_info("invalid pkg: %s\n", pkg);
|
||||
continue;
|
||||
}
|
||||
if (is_manager_apk(cwd) == 0) {
|
||||
// check passed
|
||||
uid_t uid = current_uid().val;
|
||||
pr_info("manager uid: %d\n", uid);
|
||||
|
||||
ksu_set_manager_uid(uid);
|
||||
|
||||
result = true;
|
||||
goto clean;
|
||||
} else {
|
||||
pr_info("manager signature invalid!");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
clean:
|
||||
|
||||
@@ -4,18 +4,18 @@
|
||||
#include "linux/cred.h"
|
||||
#include "linux/types.h"
|
||||
|
||||
#define INVALID_UID -1
|
||||
#define KSU_INVALID_UID -1
|
||||
|
||||
extern uid_t ksu_manager_uid; // DO NOT DIRECT USE
|
||||
|
||||
static inline bool ksu_is_manager_uid_valid()
|
||||
{
|
||||
return ksu_manager_uid != INVALID_UID;
|
||||
return ksu_manager_uid != KSU_INVALID_UID;
|
||||
}
|
||||
|
||||
static inline bool is_manager()
|
||||
{
|
||||
return ksu_manager_uid == current_uid().val;
|
||||
return unlikely(ksu_manager_uid == current_uid().val);
|
||||
}
|
||||
|
||||
static inline uid_t ksu_get_manager_uid()
|
||||
@@ -30,7 +30,7 @@ static inline void ksu_set_manager_uid(uid_t uid)
|
||||
|
||||
static inline void ksu_invalidate_manager_uid()
|
||||
{
|
||||
ksu_manager_uid = INVALID_UID;
|
||||
ksu_manager_uid = KSU_INVALID_UID;
|
||||
}
|
||||
|
||||
bool become_manager(char *pkg);
|
||||
|
||||
@@ -30,4 +30,4 @@ RE_EXPORT_SYMBOL1(unsigned long, kallsyms_lookup_name, const char *, name)
|
||||
// int ksu_register_kretprobe(struct kretprobe *rp);
|
||||
// void unregister_kretprobe(struct kretprobe *rp);
|
||||
// int register_kretprobes(struct kretprobe **rps, int num);
|
||||
// void unregister_kretprobes(struct kretprobe **rps, int num);
|
||||
// void unregister_kretprobes(struct kretprobe **rps, int num);
|
||||
|
||||
@@ -11,6 +11,6 @@ ccflags-y += -DKSU_COMPAT_HAS_SELINUX_STATE
|
||||
endif
|
||||
|
||||
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion
|
||||
ccflags-y += -Wno-macro-redefined -Wno-declaration-after-statement -Wno-unused-function
|
||||
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
|
||||
ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
|
||||
ccflags-y += -I$(objtree)/security/selinux
|
||||
ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h
|
||||
|
||||
@@ -114,6 +114,10 @@ void apply_kernelsu_rules()
|
||||
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "process",
|
||||
"getattr");
|
||||
|
||||
// For mounting loop devices, mirrors, tmpfs
|
||||
ksu_allow(db, "kernel", ALL, "file", "read");
|
||||
ksu_allow(db, "kernel", ALL, "file", "write");
|
||||
|
||||
// Allow all binder transactions
|
||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "binder", ALL);
|
||||
|
||||
|
||||
@@ -26,7 +26,9 @@ static int transive_to_domain(const char *domain)
|
||||
}
|
||||
|
||||
error = security_secctx_to_secid(domain, strlen(domain), &sid);
|
||||
pr_info("error: %d, sid: %d\n", error, sid);
|
||||
if (error) {
|
||||
pr_info("security_secctx_to_secid %s -> sid: %d, error: %d\n", domain, sid, error);
|
||||
}
|
||||
if (!error) {
|
||||
if (!ksu_sid)
|
||||
ksu_sid = sid;
|
||||
@@ -39,9 +41,9 @@ static int transive_to_domain(const char *domain)
|
||||
return error;
|
||||
}
|
||||
|
||||
void setup_selinux()
|
||||
void setup_selinux(const char *domain)
|
||||
{
|
||||
if (transive_to_domain(KERNEL_SU_DOMAIN)) {
|
||||
if (transive_to_domain(domain)) {
|
||||
pr_err("transive domain failed.");
|
||||
return;
|
||||
}
|
||||
@@ -88,7 +90,8 @@ bool getenforce()
|
||||
#endif
|
||||
}
|
||||
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && !defined(KSU_COMPAT_HAS_CURRENT_SID)
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && \
|
||||
!defined(KSU_COMPAT_HAS_CURRENT_SID)
|
||||
/*
|
||||
* get the subjective security ID of the current task
|
||||
*/
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#define KSU_COMPAT_USE_SELINUX_STATE
|
||||
#endif
|
||||
|
||||
void setup_selinux();
|
||||
void setup_selinux(const char *);
|
||||
|
||||
void setenforce(bool);
|
||||
|
||||
@@ -18,4 +18,4 @@ bool is_ksu_domain();
|
||||
|
||||
void apply_kernelsu_rules();
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -73,7 +73,7 @@ static bool add_typeattribute(struct policydb *db, const char *type,
|
||||
// rules
|
||||
#define strip_av(effect, invert) ((effect == AVTAB_AUDITDENY) == !invert)
|
||||
|
||||
#define hash_for_each(node_ptr, n_slot, cur) \
|
||||
#define ksu_hash_for_each(node_ptr, n_slot, cur) \
|
||||
int i; \
|
||||
for (i = 0; i < n_slot; ++i) \
|
||||
for (cur = node_ptr[i]; cur; cur = cur->next)
|
||||
@@ -81,10 +81,11 @@ static bool add_typeattribute(struct policydb *db, const char *type,
|
||||
// htable is a struct instead of pointer above 5.8.0:
|
||||
// https://elixir.bootlin.com/linux/v5.8-rc1/source/security/selinux/ss/symtab.h
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
|
||||
#define hashtab_for_each(htab, cur) hash_for_each (htab.htable, htab.size, cur)
|
||||
#define ksu_hashtab_for_each(htab, cur) \
|
||||
ksu_hash_for_each(htab.htable, htab.size, cur)
|
||||
#else
|
||||
#define hashtab_for_each(htab, cur) \
|
||||
hash_for_each (htab->htable, htab->size, cur)
|
||||
#define ksu_hashtab_for_each(htab, cur) \
|
||||
ksu_hash_for_each(htab->htable, htab->size, cur)
|
||||
#endif
|
||||
|
||||
// symtab_search is introduced on 5.9.0:
|
||||
@@ -95,8 +96,7 @@ static bool add_typeattribute(struct policydb *db, const char *type,
|
||||
#endif
|
||||
|
||||
#define avtab_for_each(avtab, cur) \
|
||||
hash_for_each (avtab.htable, avtab.nslot, cur) \
|
||||
;
|
||||
ksu_hash_for_each(avtab.htable, avtab.nslot, cur);
|
||||
|
||||
static struct avtab_node *get_avtab_node(struct policydb *db,
|
||||
struct avtab_key *key,
|
||||
@@ -210,14 +210,14 @@ static void add_rule_raw(struct policydb *db, struct type_datum *src,
|
||||
if (src == NULL) {
|
||||
struct hashtab_node *node;
|
||||
if (strip_av(effect, invert)) {
|
||||
hashtab_for_each(db->p_types.table, node)
|
||||
ksu_hashtab_for_each(db->p_types.table, node)
|
||||
{
|
||||
add_rule_raw(db,
|
||||
(struct type_datum *)node->datum,
|
||||
tgt, cls, perm, effect, invert);
|
||||
};
|
||||
} else {
|
||||
hashtab_for_each(db->p_types.table, node)
|
||||
ksu_hashtab_for_each(db->p_types.table, node)
|
||||
{
|
||||
struct type_datum *type =
|
||||
(struct type_datum *)(node->datum);
|
||||
@@ -230,14 +230,14 @@ static void add_rule_raw(struct policydb *db, struct type_datum *src,
|
||||
} else if (tgt == NULL) {
|
||||
struct hashtab_node *node;
|
||||
if (strip_av(effect, invert)) {
|
||||
hashtab_for_each(db->p_types.table, node)
|
||||
ksu_hashtab_for_each(db->p_types.table, node)
|
||||
{
|
||||
add_rule_raw(db, src,
|
||||
(struct type_datum *)node->datum,
|
||||
cls, perm, effect, invert);
|
||||
};
|
||||
} else {
|
||||
hashtab_for_each(db->p_types.table, node)
|
||||
ksu_hashtab_for_each(db->p_types.table, node)
|
||||
{
|
||||
struct type_datum *type =
|
||||
(struct type_datum *)(node->datum);
|
||||
@@ -249,7 +249,7 @@ static void add_rule_raw(struct policydb *db, struct type_datum *src,
|
||||
}
|
||||
} else if (cls == NULL) {
|
||||
struct hashtab_node *node;
|
||||
hashtab_for_each(db->p_classes.table, node)
|
||||
ksu_hashtab_for_each(db->p_classes.table, node)
|
||||
{
|
||||
add_rule_raw(db, src, tgt,
|
||||
(struct class_datum *)node->datum, perm,
|
||||
@@ -292,7 +292,7 @@ static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
|
||||
{
|
||||
if (src == NULL) {
|
||||
struct hashtab_node *node;
|
||||
hashtab_for_each(db->p_types.table, node)
|
||||
ksu_hashtab_for_each(db->p_types.table, node)
|
||||
{
|
||||
struct type_datum *type =
|
||||
(struct type_datum *)(node->datum);
|
||||
@@ -303,7 +303,7 @@ static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
|
||||
};
|
||||
} else if (tgt == NULL) {
|
||||
struct hashtab_node *node;
|
||||
hashtab_for_each(db->p_types.table, node)
|
||||
ksu_hashtab_for_each(db->p_types.table, node)
|
||||
{
|
||||
struct type_datum *type =
|
||||
(struct type_datum *)(node->datum);
|
||||
@@ -314,7 +314,7 @@ static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
|
||||
};
|
||||
} else if (cls == NULL) {
|
||||
struct hashtab_node *node;
|
||||
hashtab_for_each(db->p_classes.table, node)
|
||||
ksu_hashtab_for_each(db->p_classes.table, node)
|
||||
{
|
||||
add_xperm_rule_raw(db, src, tgt,
|
||||
(struct class_datum *)(node->datum),
|
||||
@@ -693,7 +693,7 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||
int i;
|
||||
for (i = 0; i < db->p_roles.nprim; ++i) {
|
||||
ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1,
|
||||
0);
|
||||
1);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -743,7 +743,7 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||
int i;
|
||||
for (i = 0; i < db->p_roles.nprim; ++i) {
|
||||
ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1,
|
||||
0);
|
||||
1);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -854,7 +854,7 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||
int i;
|
||||
for (i = 0; i < db->p_roles.nprim; ++i) {
|
||||
ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1,
|
||||
0);
|
||||
1);
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
@@ -870,7 +870,7 @@ static bool set_type_state(struct policydb *db, const char *type_name,
|
||||
struct type_datum *type;
|
||||
if (type_name == NULL) {
|
||||
struct hashtab_node *node;
|
||||
hashtab_for_each(db->p_types.table, node)
|
||||
ksu_hashtab_for_each(db->p_types.table, node)
|
||||
{
|
||||
type = (struct type_datum *)(node->datum);
|
||||
if (ebitmap_set_bit(&db->permissive_map, type->value,
|
||||
@@ -913,7 +913,7 @@ static void add_typeattribute_raw(struct policydb *db, struct type_datum *type,
|
||||
struct hashtab_node *node;
|
||||
struct constraint_node *n;
|
||||
struct constraint_expr *e;
|
||||
hashtab_for_each(db->p_classes.table, node)
|
||||
ksu_hashtab_for_each(db->p_classes.table, node)
|
||||
{
|
||||
struct class_datum *cls = (struct class_datum *)(node->datum);
|
||||
for (n = cls->constraints; n; n = n->next) {
|
||||
|
||||
@@ -11,7 +11,7 @@ elif test -d "$GKI_ROOT/drivers"; then
|
||||
DRIVER_DIR="$GKI_ROOT/drivers"
|
||||
else
|
||||
echo '[ERROR] "drivers/" directory is not found.'
|
||||
echo '[+] You should modify this scrpit by yourself.'
|
||||
echo '[+] You should modify this script by yourself.'
|
||||
exit 127
|
||||
fi
|
||||
|
||||
@@ -43,6 +43,8 @@ cd "$GKI_ROOT"
|
||||
echo '[+] Add kernel su driver to Makefile'
|
||||
|
||||
DRIVER_MAKEFILE=$DRIVER_DIR/Makefile
|
||||
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-y += kernelsu/\n" >> "$DRIVER_MAKEFILE"
|
||||
DRIVER_KCONFIG=$DRIVER_DIR/Kconfig
|
||||
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "obj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE"
|
||||
grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG"
|
||||
|
||||
echo '[+] Done.'
|
||||
echo '[+] Done.'
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "arch.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "ksud.h"
|
||||
#include "kernel_compat.h"
|
||||
|
||||
#define SU_PATH "/system/bin/su"
|
||||
#define SH_PATH "/system/bin/sh"
|
||||
@@ -41,65 +42,58 @@ static char __user *sh_user_path(void)
|
||||
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
||||
int *flags)
|
||||
{
|
||||
struct filename *filename;
|
||||
const char su[] = SU_PATH;
|
||||
|
||||
if (!ksu_is_allow_uid(current_uid().val)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
filename = getname(*filename_user);
|
||||
char path[sizeof(su)];
|
||||
memset(path, 0, sizeof(path));
|
||||
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
||||
|
||||
if (IS_ERR(filename)) {
|
||||
return 0;
|
||||
}
|
||||
if (!memcmp(filename->name, su, sizeof(su))) {
|
||||
if (unlikely(!memcmp(path, su, sizeof(su)))) {
|
||||
pr_info("faccessat su->sh!\n");
|
||||
*filename_user = sh_user_path();
|
||||
}
|
||||
|
||||
putname(filename);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
|
||||
{
|
||||
// const char sh[] = SH_PATH;
|
||||
struct filename *filename;
|
||||
const char su[] = SU_PATH;
|
||||
|
||||
if (!ksu_is_allow_uid(current_uid().val)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!filename_user) {
|
||||
if (unlikely(!filename_user)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
filename = getname(*filename_user);
|
||||
char path[sizeof(su)];
|
||||
memset(path, 0, sizeof(path));
|
||||
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
||||
|
||||
if (IS_ERR(filename)) {
|
||||
return 0;
|
||||
}
|
||||
if (!memcmp(filename->name, su, sizeof(su))) {
|
||||
if (unlikely(!memcmp(path, su, sizeof(su)))) {
|
||||
pr_info("newfstatat su->sh!\n");
|
||||
*filename_user = sh_user_path();
|
||||
}
|
||||
|
||||
putname(filename);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// the call from execve_handler_pre won't provided correct value for __never_use_argument, use them after fix execve_handler_pre, keeping them for consistence for manually patched code
|
||||
int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
||||
void *argv, void *envp, int *flags)
|
||||
void *__never_use_argv, void *__never_use_envp, int *__never_use_flags)
|
||||
{
|
||||
struct filename *filename;
|
||||
const char sh[] = KSUD_PATH;
|
||||
const char su[] = SU_PATH;
|
||||
|
||||
if (!filename_ptr)
|
||||
if (unlikely(!filename_ptr))
|
||||
return 0;
|
||||
|
||||
filename = *filename_ptr;
|
||||
@@ -107,16 +101,16 @@ int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ksu_is_allow_uid(current_uid().val)) {
|
||||
if (likely(memcmp(filename->name, su, sizeof(su))))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!memcmp(filename->name, su, sizeof(su))) {
|
||||
pr_info("do_execveat_common su found\n");
|
||||
memcpy((void *)filename->name, sh, sizeof(sh));
|
||||
if (!ksu_is_allow_uid(current_uid().val))
|
||||
return 0;
|
||||
|
||||
escape_to_root();
|
||||
}
|
||||
pr_info("do_execveat_common su found\n");
|
||||
memcpy((void *)filename->name, sh, sizeof(sh));
|
||||
|
||||
escape_to_root();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -128,7 +122,8 @@ 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);
|
||||
int *flags = (int *)&PT_REGS_PARM4(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);
|
||||
}
|
||||
@@ -142,7 +137,7 @@ static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
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_PARM4(regs);
|
||||
int *flags = (int *)&PT_REGS_CCALL_PARM4(regs);
|
||||
#endif
|
||||
|
||||
return ksu_handle_stat(dfd, filename_user, flags);
|
||||
@@ -154,12 +149,8 @@ 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);
|
||||
void *argv = (void *)&PT_REGS_PARM3(regs);
|
||||
void *envp = (void *)&PT_REGS_PARM4(regs);
|
||||
int *flags = (int *)&PT_REGS_PARM5(regs);
|
||||
|
||||
return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp,
|
||||
flags);
|
||||
return ksu_handle_execveat_sucompat(fd, filename_ptr, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static struct kprobe faccessat_kp = {
|
||||
|
||||
@@ -29,7 +29,7 @@ static bool is_uid_exist(uid_t uid, void *data)
|
||||
|
||||
bool exist = false;
|
||||
list_for_each_entry (np, list, list) {
|
||||
if (np->uid == uid) {
|
||||
if (np->uid == uid % 100000) {
|
||||
exist = true;
|
||||
break;
|
||||
}
|
||||
@@ -39,12 +39,11 @@ static bool is_uid_exist(uid_t uid, void *data)
|
||||
|
||||
static void do_update_uid(struct work_struct *work)
|
||||
{
|
||||
KWORKER_INSTALL_KEYRING();
|
||||
struct file *fp = filp_open(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0);
|
||||
struct file *fp = ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("do_update_uid, open " SYSTEM_PACKAGES_LIST_PATH
|
||||
" failed: %d\n",
|
||||
ERR_PTR(fp));
|
||||
PTR_ERR(fp));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -56,13 +55,15 @@ static void do_update_uid(struct work_struct *work)
|
||||
loff_t line_start = 0;
|
||||
char buf[128];
|
||||
for (;;) {
|
||||
ssize_t count = ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos);
|
||||
ssize_t count =
|
||||
ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos);
|
||||
if (count != sizeof(chr))
|
||||
break;
|
||||
if (chr != '\n')
|
||||
continue;
|
||||
|
||||
count = ksu_kernel_read_compat(fp, buf, sizeof(buf), &line_start);
|
||||
count = ksu_kernel_read_compat(fp, buf, sizeof(buf),
|
||||
&line_start);
|
||||
|
||||
struct uid_data *data =
|
||||
kmalloc(sizeof(struct uid_data), GFP_ATOMIC);
|
||||
@@ -136,4 +137,4 @@ int ksu_uid_observer_init()
|
||||
int ksu_uid_observer_exit()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,4 +7,4 @@ int ksu_uid_observer_exit();
|
||||
|
||||
void update_uid();
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@ plugins {
|
||||
alias(libs.plugins.kotlin)
|
||||
alias(libs.plugins.ksp)
|
||||
alias(libs.plugins.lsplugin.apksign)
|
||||
id("kotlin-parcelize")
|
||||
}
|
||||
|
||||
val managerVersionCode: Int by rootProject.extra
|
||||
@@ -96,8 +97,6 @@ dependencies {
|
||||
implementation(libs.compose.destinations.animations.core)
|
||||
ksp(libs.compose.destinations.ksp)
|
||||
|
||||
implementation(libs.com.github.alorma.compose.settings.ui.m3)
|
||||
|
||||
implementation(libs.com.github.topjohnwu.libsu.core)
|
||||
implementation(libs.com.github.topjohnwu.libsu.service)
|
||||
|
||||
@@ -108,4 +107,8 @@ dependencies {
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
|
||||
implementation(libs.me.zhanghai.android.appiconloader.coil)
|
||||
|
||||
implementation(libs.sheet.compose.dialogs.core)
|
||||
implementation(libs.sheet.compose.dialogs.list)
|
||||
implementation(libs.sheet.compose.dialogs.input)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:name=".KernelSUApplication"
|
||||
android:allowBackup="true"
|
||||
|
||||
@@ -3,15 +3,16 @@
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#include <android/log.h>
|
||||
#include <cstring>
|
||||
|
||||
#include "ksu.h"
|
||||
|
||||
#define LOG_TAG "KernelSu"
|
||||
#define LOG_TAG "KernelSU"
|
||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_me_weishu_kernelsu_Natives_becomeManager(JNIEnv *env, jclass clazz, jstring pkg) {
|
||||
Java_me_weishu_kernelsu_Natives_becomeManager(JNIEnv *env, jobject, jstring pkg) {
|
||||
auto cpkg = env->GetStringUTFChars(pkg, nullptr);
|
||||
auto result = become_manager(cpkg);
|
||||
env->ReleaseStringUTFChars(pkg, cpkg);
|
||||
@@ -20,13 +21,13 @@ Java_me_weishu_kernelsu_Natives_becomeManager(JNIEnv *env, jclass clazz, jstring
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_me_weishu_kernelsu_Natives_getVersion(JNIEnv *env, jclass clazz) {
|
||||
Java_me_weishu_kernelsu_Natives_getVersion(JNIEnv *env, jobject) {
|
||||
return get_version();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jintArray JNICALL
|
||||
Java_me_weishu_kernelsu_Natives_getAllowList(JNIEnv *env, jclass clazz) {
|
||||
Java_me_weishu_kernelsu_Natives_getAllowList(JNIEnv *env, jobject) {
|
||||
int uids[1024];
|
||||
int size = 0;
|
||||
bool result = get_allow_list(uids, &size);
|
||||
@@ -39,31 +40,260 @@ Java_me_weishu_kernelsu_Natives_getAllowList(JNIEnv *env, jclass clazz) {
|
||||
return env->NewIntArray(0);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jintArray JNICALL
|
||||
Java_me_weishu_kernelsu_Natives_getDenyList(JNIEnv *env, jclass clazz) {
|
||||
int uids[1024];
|
||||
int size = 0;
|
||||
bool result = get_deny_list(uids, &size);
|
||||
if (result) {
|
||||
// success!
|
||||
auto array = env->NewIntArray(size);
|
||||
env->SetIntArrayRegion(array, 0, size, uids);
|
||||
return array;
|
||||
}
|
||||
return env->NewIntArray(0);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_me_weishu_kernelsu_Natives_allowRoot(JNIEnv *env, jclass clazz, jint uid, jboolean allow) {
|
||||
return allow_su(uid, allow);
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_me_weishu_kernelsu_Natives_isSafeMode(JNIEnv *env, jclass clazz) {
|
||||
return is_safe_mode();
|
||||
}
|
||||
|
||||
static void fillIntArray(JNIEnv *env, jobject list, int *data, int count) {
|
||||
auto cls = env->GetObjectClass(list);
|
||||
auto add = env->GetMethodID(cls, "add", "(Ljava/lang/Object;)Z");
|
||||
auto integerCls = env->FindClass("java/lang/Integer");
|
||||
auto constructor = env->GetMethodID(integerCls, "<init>", "(I)V");
|
||||
for (int i = 0; i < count; ++i) {
|
||||
auto integer = env->NewObject(integerCls, constructor, data[i]);
|
||||
env->CallBooleanMethod(list, add, integer);
|
||||
}
|
||||
}
|
||||
|
||||
static void addIntToList(JNIEnv *env, jobject list, int ele) {
|
||||
auto cls = env->GetObjectClass(list);
|
||||
auto add = env->GetMethodID(cls, "add", "(Ljava/lang/Object;)Z");
|
||||
auto integerCls = env->FindClass("java/lang/Integer");
|
||||
auto constructor = env->GetMethodID(integerCls, "<init>", "(I)V");
|
||||
auto integer = env->NewObject(integerCls, constructor, ele);
|
||||
env->CallBooleanMethod(list, add, integer);
|
||||
}
|
||||
|
||||
static uint64_t capListToBits(JNIEnv *env, jobject list) {
|
||||
auto cls = env->GetObjectClass(list);
|
||||
auto get = env->GetMethodID(cls, "get", "(I)Ljava/lang/Object;");
|
||||
auto size = env->GetMethodID(cls, "size", "()I");
|
||||
auto listSize = env->CallIntMethod(list, size);
|
||||
auto integerCls = env->FindClass("java/lang/Integer");
|
||||
auto intValue = env->GetMethodID(integerCls, "intValue", "()I");
|
||||
uint64_t result = 0;
|
||||
for (int i = 0; i < listSize; ++i) {
|
||||
auto integer = env->CallObjectMethod(list, get, i);
|
||||
int data = env->CallIntMethod(integer, intValue);
|
||||
|
||||
if (cap_valid(data)) {
|
||||
result |= (1ULL << data);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int getListSize(JNIEnv *env, jobject list) {
|
||||
auto cls = env->GetObjectClass(list);
|
||||
auto size = env->GetMethodID(cls, "size", "()I");
|
||||
return env->CallIntMethod(list, size);
|
||||
}
|
||||
|
||||
static void fillArrayWithList(JNIEnv *env, jobject list, int *data, int count) {
|
||||
auto cls = env->GetObjectClass(list);
|
||||
auto get = env->GetMethodID(cls, "get", "(I)Ljava/lang/Object;");
|
||||
auto integerCls = env->FindClass("java/lang/Integer");
|
||||
auto intValue = env->GetMethodID(integerCls, "intValue", "()I");
|
||||
for (int i = 0; i < count; ++i) {
|
||||
auto integer = env->CallObjectMethod(list, get, i);
|
||||
data[i] = env->CallIntMethod(integer, intValue);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_me_weishu_kernelsu_Natives_getAppProfile(JNIEnv *env, jobject, jstring pkg, jint uid) {
|
||||
if (env->GetStringLength(pkg) > KSU_MAX_PACKAGE_NAME) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
p_key_t key = {};
|
||||
auto cpkg = env->GetStringUTFChars(pkg, nullptr);
|
||||
strcpy(key, cpkg);
|
||||
env->ReleaseStringUTFChars(pkg, cpkg);
|
||||
|
||||
app_profile profile = {};
|
||||
profile.version = KSU_APP_PROFILE_VER;
|
||||
|
||||
strcpy(profile.key, key);
|
||||
profile.current_uid = uid;
|
||||
|
||||
bool useDefaultProfile = !get_app_profile(key, &profile);
|
||||
|
||||
auto cls = env->FindClass("me/weishu/kernelsu/Natives$Profile");
|
||||
auto constructor = env->GetMethodID(cls, "<init>", "()V");
|
||||
auto obj = env->NewObject(cls, constructor);
|
||||
auto keyField = env->GetFieldID(cls, "name", "Ljava/lang/String;");
|
||||
auto currentUidField = env->GetFieldID(cls, "currentUid", "I");
|
||||
auto allowSuField = env->GetFieldID(cls, "allowSu", "Z");
|
||||
|
||||
auto rootUseDefaultField = env->GetFieldID(cls, "rootUseDefault", "Z");
|
||||
auto rootTemplateField = env->GetFieldID(cls, "rootTemplate", "Ljava/lang/String;");
|
||||
|
||||
auto uidField = env->GetFieldID(cls, "uid", "I");
|
||||
auto gidField = env->GetFieldID(cls, "gid", "I");
|
||||
auto groupsField = env->GetFieldID(cls, "groups", "Ljava/util/List;");
|
||||
auto capabilitiesField = env->GetFieldID(cls, "capabilities", "Ljava/util/List;");
|
||||
auto domainField = env->GetFieldID(cls, "context", "Ljava/lang/String;");
|
||||
auto namespacesField = env->GetFieldID(cls, "namespace", "I");
|
||||
|
||||
auto nonRootUseDefaultField = env->GetFieldID(cls, "nonRootUseDefault", "Z");
|
||||
auto umountModulesField = env->GetFieldID(cls, "umountModules", "Z");
|
||||
|
||||
env->SetObjectField(obj, keyField, env->NewStringUTF(profile.key));
|
||||
env->SetIntField(obj, currentUidField, profile.current_uid);
|
||||
|
||||
if (useDefaultProfile) {
|
||||
// no profile found, so just use default profile:
|
||||
// don't allow root and use default profile!
|
||||
LOGD("use default profile for: %s, %d", key, uid);
|
||||
|
||||
// allow_su = false
|
||||
// non root use default = true
|
||||
env->SetBooleanField(obj, allowSuField, false);
|
||||
env->SetBooleanField(obj, nonRootUseDefaultField, true);
|
||||
|
||||
jobject capList = env->GetObjectField(obj, capabilitiesField);
|
||||
int DEFAULT_CAPS[] = {CAP_DAC_READ_SEARCH};
|
||||
|
||||
for (auto i: DEFAULT_CAPS) {
|
||||
addIntToList(env, capList, i);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
auto allowSu = profile.allow_su;
|
||||
|
||||
if (allowSu) {
|
||||
env->SetBooleanField(obj, rootUseDefaultField, (jboolean) profile.rp_config.use_default);
|
||||
if (strlen(profile.rp_config.template_name) > 0) {
|
||||
env->SetObjectField(obj, rootTemplateField,
|
||||
env->NewStringUTF(profile.rp_config.template_name));
|
||||
}
|
||||
|
||||
env->SetIntField(obj, uidField, profile.rp_config.profile.uid);
|
||||
env->SetIntField(obj, gidField, profile.rp_config.profile.gid);
|
||||
|
||||
jobject groupList = env->GetObjectField(obj, groupsField);
|
||||
int groupCount = profile.rp_config.profile.groups_count;
|
||||
if (groupCount > KSU_MAX_GROUPS) {
|
||||
LOGD("kernel group count too large: %d???", groupCount);
|
||||
groupCount = KSU_MAX_GROUPS;
|
||||
}
|
||||
fillIntArray(env, groupList, profile.rp_config.profile.groups, groupCount);
|
||||
|
||||
jobject capList = env->GetObjectField(obj, capabilitiesField);
|
||||
for (int i = 0; i <= CAP_LAST_CAP; i++) {
|
||||
if (profile.rp_config.profile.capabilities.effective & (1ULL << i)) {
|
||||
addIntToList(env, capList, i);
|
||||
}
|
||||
}
|
||||
|
||||
env->SetObjectField(obj, domainField,
|
||||
env->NewStringUTF(profile.rp_config.profile.selinux_domain));
|
||||
env->SetIntField(obj, namespacesField, profile.rp_config.profile.namespaces);
|
||||
env->SetBooleanField(obj, allowSuField, profile.allow_su);
|
||||
} else {
|
||||
env->SetBooleanField(obj, nonRootUseDefaultField,
|
||||
(jboolean) profile.nrp_config.use_default);
|
||||
env->SetBooleanField(obj, umountModulesField, profile.nrp_config.profile.umount_modules);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_me_weishu_kernelsu_Natives_setAppProfile(JNIEnv *env, jobject clazz, jobject profile) {
|
||||
auto cls = env->FindClass("me/weishu/kernelsu/Natives$Profile");
|
||||
|
||||
auto keyField = env->GetFieldID(cls, "name", "Ljava/lang/String;");
|
||||
auto currentUidField = env->GetFieldID(cls, "currentUid", "I");
|
||||
auto allowSuField = env->GetFieldID(cls, "allowSu", "Z");
|
||||
|
||||
auto rootUseDefaultField = env->GetFieldID(cls, "rootUseDefault", "Z");
|
||||
auto rootTemplateField = env->GetFieldID(cls, "rootTemplate", "Ljava/lang/String;");
|
||||
|
||||
auto uidField = env->GetFieldID(cls, "uid", "I");
|
||||
auto gidField = env->GetFieldID(cls, "gid", "I");
|
||||
auto groupsField = env->GetFieldID(cls, "groups", "Ljava/util/List;");
|
||||
auto capabilitiesField = env->GetFieldID(cls, "capabilities", "Ljava/util/List;");
|
||||
auto domainField = env->GetFieldID(cls, "context", "Ljava/lang/String;");
|
||||
auto namespacesField = env->GetFieldID(cls, "namespace", "I");
|
||||
|
||||
auto nonRootUseDefaultField = env->GetFieldID(cls, "nonRootUseDefault", "Z");
|
||||
auto umountModulesField = env->GetFieldID(cls, "umountModules", "Z");
|
||||
|
||||
auto key = env->GetObjectField(profile, keyField);
|
||||
if (!key) {
|
||||
return false;
|
||||
}
|
||||
if (env->GetStringLength((jstring) key) > KSU_MAX_PACKAGE_NAME) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto cpkg = env->GetStringUTFChars((jstring) key, nullptr);
|
||||
p_key_t p_key = {};
|
||||
strcpy(p_key, cpkg);
|
||||
env->ReleaseStringUTFChars((jstring) key, cpkg);
|
||||
|
||||
auto currentUid = env->GetIntField(profile, currentUidField);
|
||||
|
||||
auto uid = env->GetIntField(profile, uidField);
|
||||
auto gid = env->GetIntField(profile, gidField);
|
||||
auto groups = env->GetObjectField(profile, groupsField);
|
||||
auto capabilities = env->GetObjectField(profile, capabilitiesField);
|
||||
auto domain = env->GetObjectField(profile, domainField);
|
||||
auto allowSu = env->GetBooleanField(profile, allowSuField);
|
||||
auto umountModules = env->GetBooleanField(profile, umountModulesField);
|
||||
|
||||
app_profile p = {};
|
||||
p.version = KSU_APP_PROFILE_VER;
|
||||
|
||||
strcpy(p.key, p_key);
|
||||
p.allow_su = allowSu;
|
||||
p.current_uid = currentUid;
|
||||
|
||||
if (allowSu) {
|
||||
p.rp_config.use_default = env->GetBooleanField(profile, rootUseDefaultField);
|
||||
auto templateName = env->GetObjectField(profile, rootTemplateField);
|
||||
if (templateName) {
|
||||
auto ctemplateName = env->GetStringUTFChars((jstring) templateName, nullptr);
|
||||
strcpy(p.rp_config.template_name, ctemplateName);
|
||||
env->ReleaseStringUTFChars((jstring) templateName, ctemplateName);
|
||||
}
|
||||
|
||||
p.rp_config.profile.uid = uid;
|
||||
p.rp_config.profile.gid = gid;
|
||||
|
||||
int groups_count = getListSize(env, groups);
|
||||
if (groups_count > KSU_MAX_GROUPS) {
|
||||
LOGD("groups count too large: %d", groups_count);
|
||||
return false;
|
||||
}
|
||||
p.rp_config.profile.groups_count = groups_count;
|
||||
fillArrayWithList(env, groups, p.rp_config.profile.groups, groups_count);
|
||||
|
||||
p.rp_config.profile.capabilities.effective = capListToBits(env, capabilities);
|
||||
|
||||
auto cdomain = env->GetStringUTFChars((jstring) domain, nullptr);
|
||||
strcpy(p.rp_config.profile.selinux_domain, cdomain);
|
||||
env->ReleaseStringUTFChars((jstring) domain, cdomain);
|
||||
|
||||
p.rp_config.profile.namespaces = env->GetIntField(profile, namespacesField);
|
||||
} else {
|
||||
p.nrp_config.use_default = env->GetBooleanField(profile, nonRootUseDefaultField);
|
||||
p.nrp_config.profile.umount_modules = umountModules;
|
||||
}
|
||||
|
||||
return set_app_profile(&p);
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_me_weishu_kernelsu_Natives_uidShouldUmount(JNIEnv *env, jobject thiz, jint uid) {
|
||||
return uid_should_umount(uid);
|
||||
}
|
||||
@@ -18,10 +18,16 @@
|
||||
#define CMD_GET_VERSION 2
|
||||
#define CMD_ALLOW_SU 3
|
||||
#define CMD_DENY_SU 4
|
||||
#define CMD_GET_ALLOW_LIST 5
|
||||
#define CMD_GET_SU_LIST 5
|
||||
#define CMD_GET_DENY_LIST 6
|
||||
#define CMD_CHECK_SAFEMODE 9
|
||||
|
||||
#define CMD_GET_APP_PROFILE 10
|
||||
#define CMD_SET_APP_PROFILE 11
|
||||
|
||||
#define CMD_IS_UID_GRANTED_ROOT 12
|
||||
#define CMD_IS_UID_SHOULD_UMOUNT 13
|
||||
|
||||
static bool ksuctl(int cmd, void* arg1, void* arg2) {
|
||||
int32_t result = 0;
|
||||
prctl(KERNEL_SU_OPTION, cmd, arg1, arg2, &result);
|
||||
@@ -49,19 +55,23 @@ int get_version() {
|
||||
return version;
|
||||
}
|
||||
|
||||
bool allow_su(int uid, bool allow) {
|
||||
int cmd = allow ? CMD_ALLOW_SU : CMD_DENY_SU;
|
||||
return ksuctl(cmd, (void*) uid, nullptr);
|
||||
}
|
||||
|
||||
bool get_allow_list(int *uids, int *size) {
|
||||
return ksuctl(CMD_GET_ALLOW_LIST, uids, size);
|
||||
}
|
||||
|
||||
bool get_deny_list(int *uids, int *size) {
|
||||
return ksuctl(CMD_GET_DENY_LIST, uids, size);
|
||||
return ksuctl(CMD_GET_SU_LIST, uids, size);
|
||||
}
|
||||
|
||||
bool is_safe_mode() {
|
||||
return ksuctl(CMD_CHECK_SAFEMODE, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
bool uid_should_umount(int uid) {
|
||||
bool should;
|
||||
return ksuctl(CMD_IS_UID_SHOULD_UMOUNT, reinterpret_cast<void*>(uid), &should) && should;
|
||||
}
|
||||
|
||||
bool set_app_profile(const app_profile *profile) {
|
||||
return ksuctl(CMD_SET_APP_PROFILE, (void*) profile, nullptr);
|
||||
}
|
||||
|
||||
bool get_app_profile(p_key_t key, app_profile *profile) {
|
||||
return ksuctl(CMD_GET_APP_PROFILE, (void*) profile, nullptr);
|
||||
}
|
||||
|
||||
@@ -5,16 +5,76 @@
|
||||
#ifndef KERNELSU_KSU_H
|
||||
#define KERNELSU_KSU_H
|
||||
|
||||
bool become_manager(const char*);
|
||||
#include <linux/capability.h>
|
||||
|
||||
bool become_manager(const char *);
|
||||
|
||||
int get_version();
|
||||
|
||||
bool allow_su(int uid, bool allow);
|
||||
|
||||
bool get_allow_list(int *uids, int *size);
|
||||
|
||||
bool get_deny_list(int *uids, int *size);
|
||||
bool uid_should_umount(int uid);
|
||||
|
||||
bool is_safe_mode();
|
||||
|
||||
#define KSU_APP_PROFILE_VER 2
|
||||
#define KSU_MAX_PACKAGE_NAME 256
|
||||
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
|
||||
#define KSU_MAX_GROUPS 32
|
||||
#define KSU_SELINUX_DOMAIN 64
|
||||
|
||||
using p_key_t = char[KSU_MAX_PACKAGE_NAME];
|
||||
|
||||
struct root_profile {
|
||||
int32_t uid;
|
||||
int32_t gid;
|
||||
|
||||
int32_t groups_count;
|
||||
int32_t groups[KSU_MAX_GROUPS];
|
||||
|
||||
// kernel_cap_t is u32[2] for capabilities v3
|
||||
struct {
|
||||
uint64_t effective;
|
||||
uint64_t permitted;
|
||||
uint64_t inheritable;
|
||||
} capabilities;
|
||||
|
||||
char selinux_domain[KSU_SELINUX_DOMAIN];
|
||||
|
||||
int32_t namespaces;
|
||||
};
|
||||
|
||||
struct non_root_profile {
|
||||
bool umount_modules;
|
||||
};
|
||||
|
||||
struct app_profile {
|
||||
// It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this.
|
||||
uint32_t version;
|
||||
|
||||
// this is usually the package of the app, but can be other value for special apps
|
||||
char key[KSU_MAX_PACKAGE_NAME];
|
||||
int32_t current_uid;
|
||||
bool allow_su;
|
||||
|
||||
union {
|
||||
struct {
|
||||
bool use_default;
|
||||
char template_name[KSU_MAX_PACKAGE_NAME];
|
||||
|
||||
struct root_profile profile;
|
||||
} rp_config;
|
||||
|
||||
struct {
|
||||
bool use_default;
|
||||
|
||||
struct non_root_profile profile;
|
||||
} nrp_config;
|
||||
};
|
||||
};
|
||||
|
||||
bool set_app_profile(const app_profile *profile);
|
||||
|
||||
bool get_app_profile(p_key_t key, app_profile *profile);
|
||||
|
||||
#endif //KERNELSU_KSU_H
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
package me.weishu.kernelsu;
|
||||
|
||||
/**
|
||||
* @author weishu
|
||||
* @date 2022/12/8.
|
||||
*/
|
||||
public final class Natives {
|
||||
|
||||
static {
|
||||
System.loadLibrary("kernelsu");
|
||||
}
|
||||
|
||||
// become root manager, return true if success.
|
||||
public static native boolean becomeManager(String pkg);
|
||||
|
||||
public static native int getVersion();
|
||||
|
||||
// get the uid list of allowed su processes.
|
||||
public static native int[] getAllowList();
|
||||
|
||||
public static native int[] getDenyList();
|
||||
|
||||
public static native boolean allowRoot(int uid, boolean allow);
|
||||
|
||||
public static native boolean isSafeMode();
|
||||
}
|
||||
107
manager/app/src/main/java/me/weishu/kernelsu/Natives.kt
Normal file
107
manager/app/src/main/java/me/weishu/kernelsu/Natives.kt
Normal file
@@ -0,0 +1,107 @@
|
||||
package me.weishu.kernelsu
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.annotation.Keep
|
||||
import androidx.compose.runtime.Immutable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
/**
|
||||
* @author weishu
|
||||
* @date 2022/12/8.
|
||||
*/
|
||||
object Natives {
|
||||
// minimal supported kernel version
|
||||
// 10915: allowlist breaking change, add app profile
|
||||
// 10931: app profile struct add 'version' field
|
||||
// 10946: add capabilities
|
||||
// 10977: change groups_count and groups to avoid overflow write
|
||||
// 11071: Fix the issue of failing to set a custom SELinux type.
|
||||
const val MINIMAL_SUPPORTED_KERNEL = 11071
|
||||
|
||||
init {
|
||||
System.loadLibrary("kernelsu")
|
||||
}
|
||||
|
||||
// become root manager, return true if success.
|
||||
external fun becomeManager(pkg: String?): Boolean
|
||||
val version: Int
|
||||
external get
|
||||
|
||||
// get the uid list of allowed su processes.
|
||||
val allowList: IntArray
|
||||
external get
|
||||
|
||||
val isSafeMode: Boolean
|
||||
external get
|
||||
|
||||
external fun uidShouldUmount(uid: Int): Boolean
|
||||
|
||||
/**
|
||||
* Get the profile of the given package.
|
||||
* @param key usually the package name
|
||||
* @return return null if failed.
|
||||
*/
|
||||
external fun getAppProfile(key: String?, uid: Int): Profile
|
||||
external fun setAppProfile(profile: Profile?): Boolean
|
||||
|
||||
private const val NON_ROOT_DEFAULT_PROFILE_KEY = "$"
|
||||
private const val ROOT_DEFAULT_PROFILE_KEY = "#"
|
||||
private const val NOBODY_UID = 9999
|
||||
|
||||
fun setDefaultUmountModules(umountModules: Boolean): Boolean {
|
||||
Profile(
|
||||
NON_ROOT_DEFAULT_PROFILE_KEY,
|
||||
NOBODY_UID,
|
||||
false,
|
||||
umountModules = umountModules
|
||||
).let {
|
||||
return setAppProfile(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun isDefaultUmountModules(): Boolean {
|
||||
getAppProfile(NON_ROOT_DEFAULT_PROFILE_KEY, NOBODY_UID).let {
|
||||
return it.umountModules
|
||||
}
|
||||
}
|
||||
|
||||
fun requireNewKernel(): Boolean {
|
||||
return version < MINIMAL_SUPPORTED_KERNEL
|
||||
}
|
||||
|
||||
@Immutable
|
||||
@Parcelize
|
||||
@Keep
|
||||
data class Profile(
|
||||
// and there is a default profile for root and non-root
|
||||
val name: String,
|
||||
// current uid for the package, this is convivent for kernel to check
|
||||
// if the package name doesn't match uid, then it should be invalidated.
|
||||
val currentUid: Int = 0,
|
||||
|
||||
// if this is true, kernel will grant root permission to this package
|
||||
val allowSu: Boolean = false,
|
||||
|
||||
// these are used for root profile
|
||||
val rootUseDefault: Boolean = true,
|
||||
val rootTemplate: String? = null,
|
||||
val uid: Int = 0,
|
||||
val gid: Int = 0,
|
||||
val groups: List<Int> = mutableListOf(),
|
||||
val capabilities: List<Int> = mutableListOf(),
|
||||
val context: String = "u:r:su:s0",
|
||||
val namespace: Int = Namespace.Inherited.ordinal,
|
||||
|
||||
val nonRootUseDefault: Boolean = true,
|
||||
val umountModules: Boolean = true,
|
||||
var rules: String = "", // this field is save in ksud!!
|
||||
) : Parcelable {
|
||||
enum class Namespace {
|
||||
Inherited,
|
||||
Global,
|
||||
Individual,
|
||||
}
|
||||
|
||||
constructor() : this("")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package me.weishu.kernelsu.profile
|
||||
|
||||
/**
|
||||
* @author weishu
|
||||
* @date 2023/6/3.
|
||||
*/
|
||||
enum class Capabilities(val cap: Int, val display: String, val desc: String) {
|
||||
CAP_CHOWN(0, "CHOWN", "Make arbitrary changes to file UIDs and GIDs (see chown(2))"),
|
||||
CAP_DAC_OVERRIDE(1, "DAC_OVERRIDE", "Bypass file read, write, and execute permission checks"),
|
||||
CAP_DAC_READ_SEARCH(2, "DAC_READ_SEARCH", "Bypass file read permission checks and directory read and execute permission checks"),
|
||||
CAP_FOWNER(3, "FOWNER", "Bypass permission checks on operations that normally require the filesystem UID of the process to match the UID of the file (e.g., chmod(2), utime(2)), excluding those operations covered by CAP_DAC_OVERRIDE and CAP_DAC_READ_SEARCH"),
|
||||
CAP_FSETID(4, "FSETID", "Don’t clear set-user-ID and set-group-ID permission bits when a file is modified; set the set-group-ID bit for a file whose GID does not match the filesystem or any of the supplementary GIDs of the calling process"),
|
||||
CAP_KILL(5, "KILL", "Bypass permission checks for sending signals (see kill(2))."),
|
||||
CAP_SETGID(6, "SETGID", "Make arbitrary manipulations of process GIDs and supplementary GID list; allow setgid(2) manipulation of the caller’s effective and real group IDs"),
|
||||
CAP_SETUID(7, "SETUID", "Make arbitrary manipulations of process UIDs (setuid(2), setreuid(2), setresuid(2), setfsuid(2)); allow changing the current process user IDs; allow changing of the current process group ID to any value in the system’s range of legal group IDs"),
|
||||
CAP_SETPCAP(8, "SETPCAP", "If file capabilities are supported: grant or remove any capability in the caller’s permitted capability set to or from any other process. (This property supersedes the obsolete notion of giving a process all capabilities by granting all capabilities in its permitted set, and of removing all capabilities from a process by granting no capabilities in its permitted set. It does not permit any actions that were not permitted before.)"),
|
||||
CAP_LINUX_IMMUTABLE(9, "LINUX_IMMUTABLE", "Set the FS_APPEND_FL and FS_IMMUTABLE_FL inode flags (see chattr(1))."),
|
||||
CAP_NET_BIND_SERVICE(10, "NET_BIND_SERVICE", "Bind a socket to Internet domain"),
|
||||
CAP_NET_BROADCAST(11, "NET_BROADCAST", "Make socket broadcasts, and listen to multicasts"),
|
||||
CAP_NET_ADMIN(12, "NET_ADMIN", "Perform various network-related operations: interface configuration, administration of IP firewall, masquerading, and accounting, modify routing tables, bind to any address for transparent proxying, set type-of-service (TOS), clear driver statistics, set promiscuous mode, enabling multicasting, use setsockopt(2) to set the following socket options: SO_DEBUG, SO_MARK, SO_PRIORITY (for a priority outside the range 0 to 6), SO_RCVBUFFORCE, and SO_SNDBUFFORCE"),
|
||||
CAP_NET_RAW(13, "NET_RAW", "Use RAW and PACKET sockets"),
|
||||
CAP_IPC_LOCK(14, "IPC_LOCK", "Lock memory (mlock(2), mlockall(2), mmap(2), shmctl(2))"),
|
||||
CAP_IPC_OWNER(15, "IPC_OWNER", "Bypass permission checks for operations on System V IPC objects"),
|
||||
CAP_SYS_MODULE(16, "SYS_MODULE", "Load and unload kernel modules (see init_module(2) and delete_module(2)); in kernels before 2.6.25, this also granted rights for various other operations related to kernel modules"),
|
||||
CAP_SYS_RAWIO(17, "SYS_RAWIO", "Perform I/O port operations (iopl(2) and ioperm(2)); access /proc/kcore"),
|
||||
CAP_SYS_CHROOT(18, "SYS_CHROOT", "Use chroot(2)"),
|
||||
CAP_SYS_PTRACE(19, "SYS_PTRACE", "Trace arbitrary processes using ptrace(2)"),
|
||||
CAP_SYS_PACCT(20, "SYS_PACCT", "Use acct(2)"),
|
||||
CAP_SYS_ADMIN(21, "SYS_ADMIN", "Perform a range of system administration operations including: quotactl(2), mount(2), umount(2), swapon(2), swapoff(2), sethostname(2), and setdomainname(2); set and modify process resource limits (setrlimit(2)); perform various network-related operations (e.g., setting privileged socket options, enabling multicasting, interface configuration); perform various IPC operations (e.g., SysV semaphores, POSIX message queues, System V shared memory); allow reboot and kexec_load(2); override /proc/sys kernel tunables; perform ptrace(2) PTRACE_SECCOMP_GET_FILTER operation; perform some tracing and debugging operations (see ptrace(2)); administer the lifetime of kernel tracepoints (tracefs(5)); perform the KEYCTL_CHOWN and KEYCTL_SETPERM keyctl(2) operations; perform the following keyctl(2) operations: KEYCTL_CAPABILITIES, KEYCTL_CAPSQUASH, and KEYCTL_PKEY_ OPERATIONS; set state for the Extensible Authentication Protocol (EAP) kernel module; and override the RLIMIT_NPROC resource limit; allow ioperm/iopl access to I/O ports"),
|
||||
CAP_SYS_BOOT(22, "SYS_BOOT", "Use reboot(2) and kexec_load(2), reboot and load a new kernel for later execution"),
|
||||
CAP_SYS_NICE(23, "SYS_NICE", "Raise process nice value (nice(2), setpriority(2)) and change the nice value for arbitrary processes; set real-time scheduling policies for calling process, and set scheduling policies and priorities for arbitrary processes (sched_setscheduler(2), sched_setparam(2)"),
|
||||
CAP_SYS_RESOURCE(24, "SYS_RESOURCE", "Override resource Limits. Set resource limits (setrlimit(2), prlimit(2)), override quota limits (quota(2), quotactl(2)), override reserved space on ext2 filesystem (ext2_ioctl(2)), override size restrictions on IPC message queues (msg(2)) and system V shared memory segments (shmget(2)), and override the /proc/sys/fs/pipe-size-max limit"),
|
||||
CAP_SYS_TIME(25, "SYS_TIME", "Set system clock (settimeofday(2), stime(2), adjtimex(2)); set real-time (hardware) clock"),
|
||||
CAP_SYS_TTY_CONFIG(26, "SYS_TTY_CONFIG", "Use vhangup(2); employ various privileged ioctl(2) operations on virtual terminals"),
|
||||
CAP_MKNOD(27, "MKNOD", "Create special files using mknod(2)"),
|
||||
CAP_LEASE(28, "LEASE", "Establish leases on arbitrary files (see fcntl(2))"),
|
||||
CAP_AUDIT_WRITE(29, "AUDIT_WRITE", "Write records to kernel auditing log"),
|
||||
CAP_AUDIT_CONTROL(30, "AUDIT_CONTROL", "Enable and disable kernel auditing; change auditing filter rules; retrieve auditing status and filtering rules"),
|
||||
CAP_SETFCAP(31, "SETFCAP", "If file capabilities are supported: grant or remove any capability in any capability set to any file"),
|
||||
CAP_MAC_OVERRIDE(32, "MAC_OVERRIDE", "Override Mandatory Access Control (MAC). Implemented for the Smack Linux Security Module (LSM)"),
|
||||
CAP_MAC_ADMIN(33, "MAC_ADMIN", "Allow MAC configuration or state changes. Implemented for the Smack LSM"),
|
||||
CAP_SYSLOG(34, "SYSLOG", "Perform privileged syslog(2) operations. See syslog(2) for information on which operations require privilege"),
|
||||
CAP_WAKE_ALARM(35, "WAKE_ALARM", "Trigger something that will wake up the system"),
|
||||
CAP_BLOCK_SUSPEND(36, "BLOCK_SUSPEND", "Employ features that can block system suspend"),
|
||||
CAP_AUDIT_READ(37, "AUDIT_READ", "Allow reading the audit log via a multicast netlink socket"),
|
||||
CAP_PERFMON(38, "PERFMON", "Allow performance monitoring via perf_event_open(2)"),
|
||||
CAP_BPF(39, "BPF", "Allow BPF operations via bpf(2)"),
|
||||
CAP_CHECKPOINT_RESTORE(40, "CHECKPOINT_RESTORE", "Allow processes to be checkpointed via checkpoint/restore in user namespace(2)"),
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package me.weishu.kernelsu.profile
|
||||
|
||||
/**
|
||||
* @author weishu
|
||||
* @date 2023/6/3.
|
||||
*/
|
||||
enum class Groups(val gid: Int, val display: String, val desc: String) {
|
||||
ROOT(0, "root", "traditional unix root user"),
|
||||
DAEMON(1, "daemon", "Traditional unix daemon owner."),
|
||||
BIN(2, "bin", "Traditional unix binaries owner."),
|
||||
SYS(3, "sys", "A group with the same gid on Linux/macOS/Android."),
|
||||
SYSTEM(1000, "system", "system server"),
|
||||
RADIO(1001, "radio", "telephony subsystem, RIL"),
|
||||
BLUETOOTH(1002, "bluetooth", "bluetooth subsystem"),
|
||||
GRAPHICS(1003, "graphics", "graphics devices"),
|
||||
INPUT(1004, "input", "input devices"),
|
||||
AUDIO(1005, "audio", "audio devices"),
|
||||
CAMERA(1006, "camera", "camera devices"),
|
||||
LOG(1007, "log", "log devices"),
|
||||
COMPASS(1008, "compass", "compass device"),
|
||||
MOUNT(1009, "mount", "mountd socket"),
|
||||
WIFI(1010, "wifi", "wifi subsystem"),
|
||||
ADB(1011, "adb", "android debug bridge (adbd)"),
|
||||
INSTALL(1012, "install", "group for installing packages"),
|
||||
MEDIA(1013, "media", "mediaserver process"),
|
||||
DHCP(1014, "dhcp", "dhcp client"),
|
||||
SDCARD_RW(1015, "sdcard_rw", "external storage write access"),
|
||||
VPN(1016, "vpn", "vpn system"),
|
||||
KEYSTORE(1017, "keystore", "keystore subsystem"),
|
||||
USB(1018, "usb", "USB devices"),
|
||||
DRM(1019, "drm", "DRM server"),
|
||||
MDNSR(1020, "mdnsr", "MulticastDNSResponder (service discovery)"),
|
||||
GPS(1021, "gps", "GPS daemon"),
|
||||
UNUSED1(1022, "unused1", "deprecated, DO NOT USE"),
|
||||
MEDIA_RW(1023, "media_rw", "internal media storage write access"),
|
||||
MTP(1024, "mtp", "MTP USB driver access"),
|
||||
UNUSED2(1025, "unused2", "deprecated, DO NOT USE"),
|
||||
DRMRPC(1026, "drmrpc", "group for drm rpc"),
|
||||
NFC(1027, "nfc", "nfc subsystem"),
|
||||
SDCARD_R(1028, "sdcard_r", "external storage read access"),
|
||||
CLAT(1029, "clat", "clat part of nat464"),
|
||||
LOOP_RADIO(1030, "loop_radio", "loop radio devices"),
|
||||
MEDIA_DRM(1031, "media_drm", "MediaDrm plugins"),
|
||||
PACKAGE_INFO(1032, "package_info", "access to installed package details"),
|
||||
SDCARD_PICS(1033, "sdcard_pics", "external storage photos access"),
|
||||
SDCARD_AV(1034, "sdcard_av", "external storage audio/video access"),
|
||||
SDCARD_ALL(1035, "sdcard_all", "access all users external storage"),
|
||||
LOGD(1036, "logd", "log daemon"),
|
||||
SHARED_RELRO(1037, "shared_relro", "creator of shared GNU RELRO files"),
|
||||
DBUS(1038, "dbus", "dbus-daemon IPC broker process"),
|
||||
TLSDATE(1039, "tlsdate", "tlsdate unprivileged user"),
|
||||
MEDIA_EX(1040, "media_ex", "mediaextractor process"),
|
||||
AUDIOSERVER(1041, "audioserver", "audioserver process"),
|
||||
METRICS_COLL(1042, "metrics_coll", "metrics_collector process"),
|
||||
METRICSD(1043, "metricsd", "metricsd process"),
|
||||
WEBSERV(1044, "webserv", "webservd process"),
|
||||
DEBUGGERD(1045, "debuggerd", "debuggerd unprivileged user"),
|
||||
MEDIA_CODEC(1046, "media_codec", "media_codec process"),
|
||||
CAMERASERVER(1047, "cameraserver", "cameraserver process"),
|
||||
FIREWALL(1048, "firewall", "firewall process"),
|
||||
TRUNKS(1049, "trunks", "trunksd process"),
|
||||
NVRAM(1050, "nvram", "nvram daemon"),
|
||||
DNS_TETHER(1051, "dns_tether", "dns_tether device"),
|
||||
DNS_TETHER_RESERVED(1052, "dns_tether_reserved", "Reserved range for dns_tether"),
|
||||
WEBVIEW_ZYGOTE(1053, "webview_zygote", "zygote process"),
|
||||
WEBVIEW_USER(1054, "webview_user", "webview chromium user"),
|
||||
ETHERNET(1055, "ethernet", "Ethernet"),
|
||||
TOMBSTONED(1056, "tombstoned", "tombstoned process"),
|
||||
GRAPHICS_RW(1057, "graphics_rw", "graphics devices"),
|
||||
|
||||
SHELL(2000, "shell", "adb and debug shell user"),
|
||||
CACHE(2001, "cache", "cache access"),
|
||||
DIAG(2002, "diag", "diagnostics"),
|
||||
NET_BT_ADMIN(3001, "net_bt_admin", "bluetooth: create any socket"),
|
||||
NET_BT(3002, "net_bt", "bluetooth: create sco, rfcomm or l2cap sockets"),
|
||||
INET(3003, "inet", "can create AF_INET and AF_INET6 sockets"),
|
||||
NET_RAW(3004, "net_raw", "can create raw INET sockets"),
|
||||
NET_ADMIN(3005, "net_admin", "can configure interfaces and routing tables."),
|
||||
NET_BW_STATS(3006, "net_bw_stats", "read bandwidth statistics"),
|
||||
NET_BW_ACCT(3007, "net_bw_acct", "change bandwidth statistics accounting"),
|
||||
NET_BT_STACK(3008, "net_bt_stack", "access to various bluetooth management functions"),
|
||||
QCOM_DIAG(3009, "qcom_diag", "allow msm specific diag commands"),
|
||||
EVERYBODY(9997, "everybody", "Shared external storage read/write"),
|
||||
MISC(9998, "misc", "Access to misc storage"),
|
||||
NOBODY(9999, "nobody", "Reserved"),
|
||||
APP(10000, "app", "Access to app data"),
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.NavigationBar
|
||||
import androidx.compose.material3.NavigationBarItem
|
||||
@@ -25,6 +24,8 @@ import com.google.accompanist.navigation.animation.rememberAnimatedNavController
|
||||
import com.ramcosta.composedestinations.DestinationsNavHost
|
||||
import com.ramcosta.composedestinations.navigation.popBackStack
|
||||
import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.ksuApp
|
||||
import me.weishu.kernelsu.ui.component.rememberDialogHostState
|
||||
import me.weishu.kernelsu.ui.screen.BottomBarDestination
|
||||
import me.weishu.kernelsu.ui.screen.NavGraphs
|
||||
@@ -34,7 +35,7 @@ import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class, ExperimentalMaterial3Api::class)
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@@ -64,8 +65,11 @@ class MainActivity : ComponentActivity() {
|
||||
|
||||
@Composable
|
||||
private fun BottomBar(navController: NavHostController) {
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
val fullFeatured = isManager && !Natives.requireNewKernel()
|
||||
NavigationBar(tonalElevation = 8.dp) {
|
||||
BottomBarDestination.values().forEach { destination ->
|
||||
if (!fullFeatured && destination.rootRequired) return@forEach
|
||||
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
|
||||
NavigationBarItem(
|
||||
selected = isCurrentDestOnBackStack,
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package me.weishu.kernelsu.ui.component
|
||||
|
||||
import androidx.compose.foundation.focusable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.input.key.KeyEvent
|
||||
import androidx.compose.ui.input.key.onKeyEvent
|
||||
|
||||
@Composable
|
||||
fun KeyEventBlocker(predicate: (KeyEvent) -> Boolean) {
|
||||
val requester = remember { FocusRequester() }
|
||||
Box(
|
||||
Modifier
|
||||
.onKeyEvent {
|
||||
predicate(it)
|
||||
}
|
||||
.focusRequester(requester)
|
||||
.focusable()
|
||||
)
|
||||
LaunchedEffect(Unit) {
|
||||
requester.requestFocus()
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,22 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material.icons.outlined.ArrowBack
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
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
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -21,11 +33,9 @@ import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import me.weishu.kernelsu.R
|
||||
|
||||
private const val TAG = "SearchBar"
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package me.weishu.kernelsu.ui.component
|
||||
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
|
||||
@Composable
|
||||
fun SwitchItem(
|
||||
icon: ImageVector? = null,
|
||||
title: String,
|
||||
summary: String? = null,
|
||||
checked: Boolean,
|
||||
enabled: Boolean = true,
|
||||
onCheckedChange: (Boolean) -> Unit
|
||||
) {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(title)
|
||||
},
|
||||
leadingContent = icon?.let {
|
||||
{ Icon(icon, title) }
|
||||
},
|
||||
trailingContent = {
|
||||
Switch(checked = checked, enabled = enabled, onCheckedChange = onCheckedChange)
|
||||
},
|
||||
supportingContent = {
|
||||
if (summary != null) {
|
||||
Text(summary)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RadioItem(
|
||||
title: String,
|
||||
selected: Boolean,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(title)
|
||||
},
|
||||
leadingContent = {
|
||||
RadioButton(selected = selected, onClick = onClick)
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package me.weishu.kernelsu.ui.component.profile
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.SwitchItem
|
||||
|
||||
@Composable
|
||||
fun AppProfileConfig(
|
||||
modifier: Modifier = Modifier,
|
||||
fixedName: Boolean,
|
||||
enabled: Boolean,
|
||||
profile: Natives.Profile,
|
||||
onProfileChange: (Natives.Profile) -> Unit,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
if (!fixedName) {
|
||||
OutlinedTextField(
|
||||
label = { Text(stringResource(R.string.profile_name)) },
|
||||
value = profile.name,
|
||||
onValueChange = { onProfileChange(profile.copy(name = it)) }
|
||||
)
|
||||
}
|
||||
|
||||
SwitchItem(
|
||||
title = stringResource(R.string.profile_umount_modules),
|
||||
summary = stringResource(R.string.profile_umount_modules_summary),
|
||||
checked = if (enabled) {
|
||||
profile.umountModules
|
||||
} else {
|
||||
Natives.isDefaultUmountModules()
|
||||
},
|
||||
enabled = enabled,
|
||||
onCheckedChange = {
|
||||
onProfileChange(
|
||||
profile.copy(
|
||||
umountModules = it,
|
||||
nonRootUseDefault = false
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun AppProfileConfigPreview() {
|
||||
var profile by remember { mutableStateOf(Natives.Profile("")) }
|
||||
AppProfileConfig(fixedName = true, enabled = false, profile = profile) {
|
||||
profile = it
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,464 @@
|
||||
@file:OptIn(ExperimentalMaterial3Api::class)
|
||||
|
||||
package me.weishu.kernelsu.ui.component.profile
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowDropDown
|
||||
import androidx.compose.material.icons.filled.ArrowDropUp
|
||||
import androidx.compose.material3.AssistChip
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedCard
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
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.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.text.isDigitsOnly
|
||||
import com.maxkeppeker.sheets.core.models.base.Header
|
||||
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
|
||||
import com.maxkeppeler.sheets.input.InputDialog
|
||||
import com.maxkeppeler.sheets.input.models.InputHeader
|
||||
import com.maxkeppeler.sheets.input.models.InputSelection
|
||||
import com.maxkeppeler.sheets.input.models.InputTextField
|
||||
import com.maxkeppeler.sheets.input.models.InputTextFieldType
|
||||
import com.maxkeppeler.sheets.input.models.ValidationResult
|
||||
import com.maxkeppeler.sheets.list.ListDialog
|
||||
import com.maxkeppeler.sheets.list.models.ListOption
|
||||
import com.maxkeppeler.sheets.list.models.ListSelection
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.profile.Capabilities
|
||||
import me.weishu.kernelsu.profile.Groups
|
||||
import me.weishu.kernelsu.ui.util.isSepolicyValid
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun RootProfileConfig(
|
||||
modifier: Modifier = Modifier,
|
||||
fixedName: Boolean,
|
||||
profile: Natives.Profile,
|
||||
onProfileChange: (Natives.Profile) -> Unit,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
if (!fixedName) {
|
||||
OutlinedTextField(
|
||||
label = { Text(stringResource(R.string.profile_name)) },
|
||||
value = profile.name,
|
||||
onValueChange = { onProfileChange(profile.copy(name = it)) }
|
||||
)
|
||||
}
|
||||
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
val currentNamespace = when (profile.namespace) {
|
||||
Natives.Profile.Namespace.Inherited.ordinal -> stringResource(R.string.profile_namespace_inherited)
|
||||
Natives.Profile.Namespace.Global.ordinal -> stringResource(R.string.profile_namespace_global)
|
||||
Natives.Profile.Namespace.Individual.ordinal -> stringResource(R.string.profile_namespace_individual)
|
||||
else -> stringResource(R.string.profile_namespace_inherited)
|
||||
}
|
||||
ListItem(headlineContent = {
|
||||
ExposedDropdownMenuBox(
|
||||
expanded = expanded,
|
||||
onExpandedChange = { expanded = !expanded }
|
||||
) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier
|
||||
.menuAnchor()
|
||||
.fillMaxWidth(),
|
||||
readOnly = true,
|
||||
label = { Text(stringResource(R.string.profile_namespace)) },
|
||||
value = currentNamespace,
|
||||
onValueChange = {},
|
||||
trailingIcon = {
|
||||
if (expanded) Icon(Icons.Filled.ArrowDropUp, null)
|
||||
else Icon(Icons.Filled.ArrowDropDown, null)
|
||||
},
|
||||
)
|
||||
ExposedDropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.profile_namespace_inherited)) },
|
||||
onClick = {
|
||||
onProfileChange(profile.copy(namespace = Natives.Profile.Namespace.Inherited.ordinal))
|
||||
expanded = false
|
||||
},
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.profile_namespace_global)) },
|
||||
onClick = {
|
||||
onProfileChange(profile.copy(namespace = Natives.Profile.Namespace.Global.ordinal))
|
||||
expanded = false
|
||||
},
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.profile_namespace_individual)) },
|
||||
onClick = {
|
||||
onProfileChange(profile.copy(namespace = Natives.Profile.Namespace.Individual.ordinal))
|
||||
expanded = false
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
UidPanel(uid = profile.uid, label = "uid", onUidChange = {
|
||||
onProfileChange(
|
||||
profile.copy(
|
||||
uid = it,
|
||||
rootUseDefault = false
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
UidPanel(uid = profile.gid, label = "gid", onUidChange = {
|
||||
onProfileChange(
|
||||
profile.copy(
|
||||
gid = it,
|
||||
rootUseDefault = false
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
val selectedGroups = profile.groups.ifEmpty { listOf(0) }.let { e ->
|
||||
e.mapNotNull { g ->
|
||||
Groups.values().find { it.gid == g }
|
||||
}
|
||||
}
|
||||
GroupsPanel(selectedGroups) {
|
||||
onProfileChange(
|
||||
profile.copy(
|
||||
groups = it.map { group -> group.gid }.ifEmpty { listOf(0) },
|
||||
rootUseDefault = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val selectedCaps = profile.capabilities.mapNotNull { e ->
|
||||
Capabilities.values().find { it.cap == e }
|
||||
}
|
||||
|
||||
CapsPanel(selectedCaps) {
|
||||
onProfileChange(
|
||||
profile.copy(
|
||||
capabilities = it.map { cap -> cap.cap },
|
||||
rootUseDefault = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
SELinuxPanel(profile = profile, onSELinuxChange = { domain, rules ->
|
||||
onProfileChange(
|
||||
profile.copy(
|
||||
context = domain,
|
||||
rules = rules,
|
||||
rootUseDefault = false
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun GroupsPanel(selected: List<Groups>, closeSelection: (selection: Set<Groups>) -> Unit) {
|
||||
|
||||
var showDialog by remember { mutableStateOf(false) }
|
||||
|
||||
if (showDialog) {
|
||||
val groups = Groups.values()
|
||||
val options = groups.map { value ->
|
||||
ListOption(
|
||||
titleText = value.display,
|
||||
subtitleText = value.desc,
|
||||
selected = selected.contains(value),
|
||||
)
|
||||
}
|
||||
|
||||
val selection = HashSet(selected)
|
||||
ListDialog(
|
||||
state = rememberUseCaseState(visible = true, onFinishedRequest = {
|
||||
closeSelection(selection)
|
||||
}, onCloseRequest = {
|
||||
showDialog = false
|
||||
}),
|
||||
header = Header.Default(
|
||||
title = stringResource(R.string.profile_groups),
|
||||
),
|
||||
selection = ListSelection.Multiple(
|
||||
showCheckBoxes = true,
|
||||
options = options,
|
||||
maxChoices = 32, // Kernel only supports 32 groups at most
|
||||
) { indecies, _ ->
|
||||
// Handle selection
|
||||
selection.clear()
|
||||
indecies.forEach { index ->
|
||||
val group = groups[index]
|
||||
selection.add(group)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
OutlinedCard(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
.clickable {
|
||||
showDialog = true
|
||||
}) {
|
||||
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(stringResource(R.string.profile_groups))
|
||||
FlowRow {
|
||||
selected.forEach { group ->
|
||||
AssistChip(
|
||||
modifier = Modifier.padding(3.dp),
|
||||
onClick = { /*TODO*/ },
|
||||
label = { Text(group.display) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun CapsPanel(
|
||||
selected: Collection<Capabilities>,
|
||||
closeSelection: (selection: Set<Capabilities>) -> Unit
|
||||
) {
|
||||
|
||||
var showDialog by remember { mutableStateOf(false) }
|
||||
|
||||
if (showDialog) {
|
||||
val caps = Capabilities.values()
|
||||
val options = caps.map { value ->
|
||||
ListOption(
|
||||
titleText = value.display,
|
||||
subtitleText = value.desc,
|
||||
selected = selected.contains(value),
|
||||
)
|
||||
}
|
||||
|
||||
val selection = HashSet(selected)
|
||||
ListDialog(
|
||||
state = rememberUseCaseState(visible = true, onFinishedRequest = {
|
||||
closeSelection(selection)
|
||||
}, onCloseRequest = {
|
||||
showDialog = false
|
||||
}),
|
||||
header = Header.Default(
|
||||
title = stringResource(R.string.profile_capabilities),
|
||||
),
|
||||
selection = ListSelection.Multiple(
|
||||
showCheckBoxes = true,
|
||||
options = options
|
||||
) { indecies, _ ->
|
||||
// Handle selection
|
||||
selection.clear()
|
||||
indecies.forEach { index ->
|
||||
val group = caps[index]
|
||||
selection.add(group)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
OutlinedCard(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
.clickable {
|
||||
showDialog = true
|
||||
}) {
|
||||
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(stringResource(R.string.profile_capabilities))
|
||||
FlowRow {
|
||||
selected.forEach { group ->
|
||||
AssistChip(
|
||||
modifier = Modifier.padding(3.dp),
|
||||
onClick = { /*TODO*/ },
|
||||
label = { Text(group.display) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
private fun UidPanel(uid: Int, label: String, onUidChange: (Int) -> Unit) {
|
||||
|
||||
ListItem(headlineContent = {
|
||||
var isError by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
var lastValidUid by remember {
|
||||
mutableStateOf(uid)
|
||||
}
|
||||
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
label = { Text(label) },
|
||||
value = uid.toString(),
|
||||
isError = isError,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Number,
|
||||
imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardActions = KeyboardActions(onDone = {
|
||||
keyboardController?.hide()
|
||||
}),
|
||||
onValueChange = {
|
||||
if (it.isEmpty()) {
|
||||
onUidChange(0)
|
||||
return@OutlinedTextField
|
||||
}
|
||||
val valid = isTextValidUid(it)
|
||||
|
||||
val targetUid = if (valid) it.toInt() else lastValidUid
|
||||
if (valid) {
|
||||
lastValidUid = it.toInt()
|
||||
}
|
||||
|
||||
onUidChange(targetUid)
|
||||
|
||||
isError = !valid
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SELinuxPanel(
|
||||
profile: Natives.Profile,
|
||||
onSELinuxChange: (domain: String, rules: String) -> Unit
|
||||
) {
|
||||
var showDialog by remember { mutableStateOf(false) }
|
||||
if (showDialog) {
|
||||
var domain by remember { mutableStateOf(profile.context) }
|
||||
var rules by remember { mutableStateOf(profile.rules) }
|
||||
|
||||
val inputOptions = listOf(
|
||||
InputTextField(
|
||||
text = domain,
|
||||
header = InputHeader(
|
||||
title = stringResource(id = R.string.profile_selinux_domain),
|
||||
),
|
||||
type = InputTextFieldType.OUTLINED,
|
||||
required = true,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Ascii,
|
||||
imeAction = ImeAction.Next
|
||||
),
|
||||
resultListener = {
|
||||
domain = it ?: ""
|
||||
},
|
||||
validationListener = { value ->
|
||||
// value can be a-zA-Z0-9_
|
||||
val regex = Regex("^[a-z_]+:[a-z0-9_]+:[a-z0-9_]+(:[a-z0-9_]+)?$")
|
||||
if (value?.matches(regex) == true) ValidationResult.Valid
|
||||
else ValidationResult.Invalid("Domain must be in the format of \"user:role:type:level\"")
|
||||
}
|
||||
),
|
||||
InputTextField(
|
||||
text = rules,
|
||||
header = InputHeader(
|
||||
title = stringResource(id = R.string.profile_selinux_rules),
|
||||
),
|
||||
type = InputTextFieldType.OUTLINED,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Ascii,
|
||||
),
|
||||
singleLine = false,
|
||||
resultListener = {
|
||||
rules = it ?: ""
|
||||
},
|
||||
validationListener = { value ->
|
||||
if (isSepolicyValid(value)) ValidationResult.Valid
|
||||
else ValidationResult.Invalid("SELinux rules is invalid!")
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
InputDialog(
|
||||
state = rememberUseCaseState(visible = true,
|
||||
onFinishedRequest = {
|
||||
onSELinuxChange(domain, rules)
|
||||
},
|
||||
onCloseRequest = {
|
||||
showDialog = false
|
||||
}),
|
||||
header = Header.Default(
|
||||
title = stringResource(R.string.profile_selinux_context),
|
||||
),
|
||||
selection = InputSelection(
|
||||
input = inputOptions,
|
||||
onPositiveClick = { result ->
|
||||
// Handle selection
|
||||
},
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
ListItem(headlineContent = {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
showDialog = true
|
||||
},
|
||||
enabled = false,
|
||||
colors = TextFieldDefaults.outlinedTextFieldColors(
|
||||
disabledTextColor = MaterialTheme.colorScheme.onSurface,
|
||||
disabledBorderColor = MaterialTheme.colorScheme.outline,
|
||||
disabledPlaceholderColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
disabledLabelColor = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
),
|
||||
label = { Text(text = stringResource(R.string.profile_selinux_context)) },
|
||||
value = profile.context,
|
||||
onValueChange = { },
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun RootProfileConfigPreview() {
|
||||
var profile by remember { mutableStateOf(Natives.Profile("")) }
|
||||
RootProfileConfig(fixedName = true, profile = profile) {
|
||||
profile = it
|
||||
}
|
||||
}
|
||||
|
||||
private fun isTextValidUid(text: String): Boolean {
|
||||
return text.isNotEmpty() && text.isDigitsOnly() && text.toInt() >= 0 && text.toInt() <= Int.MAX_VALUE
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
package me.weishu.kernelsu.ui.screen
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AccountCircle
|
||||
import androidx.compose.material.icons.filled.Android
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.ArrowDropDown
|
||||
import androidx.compose.material.icons.filled.ArrowDropUp
|
||||
import androidx.compose.material.icons.filled.Security
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||
import androidx.compose.material3.FilterChip
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
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.geometry.Offset
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.stringResource
|
||||
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 coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.coroutines.launch
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.SwitchItem
|
||||
import me.weishu.kernelsu.ui.component.profile.AppProfileConfig
|
||||
import me.weishu.kernelsu.ui.component.profile.RootProfileConfig
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
import me.weishu.kernelsu.ui.util.forceStopApp
|
||||
import me.weishu.kernelsu.ui.util.getSepolicy
|
||||
import me.weishu.kernelsu.ui.util.launchApp
|
||||
import me.weishu.kernelsu.ui.util.restartApp
|
||||
import me.weishu.kernelsu.ui.util.setSepolicy
|
||||
import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel
|
||||
|
||||
/**
|
||||
* @author weishu
|
||||
* @date 2023/5/16.
|
||||
*/
|
||||
@Destination
|
||||
@Composable
|
||||
fun AppProfileScreen(
|
||||
navigator: DestinationsNavigator,
|
||||
appInfo: SuperUserViewModel.AppInfo,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val snackbarHost = LocalSnackbarHost.current
|
||||
val scope = rememberCoroutineScope()
|
||||
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 packageName = appInfo.packageName
|
||||
val initialProfile = Natives.getAppProfile(packageName, appInfo.uid)
|
||||
if (initialProfile.allowSu) {
|
||||
initialProfile.rules = getSepolicy(packageName)
|
||||
}
|
||||
var profile by rememberSaveable {
|
||||
mutableStateOf(initialProfile)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = { TopBar { navigator.popBackStack() } },
|
||||
) { paddingValues ->
|
||||
AppProfileInner(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
packageName = appInfo.packageName,
|
||||
appLabel = appInfo.label,
|
||||
appIcon = {
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(context)
|
||||
.data(appInfo.packageInfo)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
contentDescription = appInfo.label,
|
||||
modifier = Modifier
|
||||
.padding(4.dp)
|
||||
.width(48.dp)
|
||||
.height(48.dp)
|
||||
)
|
||||
},
|
||||
profile = profile,
|
||||
onProfileChange = {
|
||||
scope.launch {
|
||||
if (it.allowSu && !it.rootUseDefault && it.rules.isNotEmpty()) {
|
||||
if (!setSepolicy(profile.name, it.rules)) {
|
||||
snackbarHost.showSnackbar(failToUpdateSepolicy)
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
if (!Natives.setAppProfile(it)) {
|
||||
snackbarHost.showSnackbar(failToUpdateAppProfile.format(appInfo.uid))
|
||||
} else {
|
||||
profile = it
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun AppProfileInner(
|
||||
modifier: Modifier = Modifier,
|
||||
packageName: String,
|
||||
appLabel: String,
|
||||
appIcon: @Composable () -> Unit,
|
||||
profile: Natives.Profile,
|
||||
onProfileChange: (Natives.Profile) -> Unit,
|
||||
) {
|
||||
val isRootGranted = profile.allowSu
|
||||
|
||||
Column(modifier = modifier) {
|
||||
AppMenuBox(packageName) {
|
||||
ListItem(
|
||||
headlineContent = { Text(appLabel) },
|
||||
supportingContent = { Text(packageName) },
|
||||
leadingContent = appIcon,
|
||||
)
|
||||
}
|
||||
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.Security,
|
||||
title = stringResource(id = R.string.superuser),
|
||||
checked = isRootGranted,
|
||||
onCheckedChange = { onProfileChange(profile.copy(allowSu = it)) },
|
||||
)
|
||||
|
||||
Crossfade(targetState = isRootGranted, label = "") { current ->
|
||||
Column {
|
||||
if (current) {
|
||||
val initialMode = if (profile.rootUseDefault) {
|
||||
Mode.Default
|
||||
} else if (profile.rootTemplate != null) {
|
||||
Mode.Template
|
||||
} else {
|
||||
Mode.Custom
|
||||
}
|
||||
var mode by remember {
|
||||
mutableStateOf(initialMode)
|
||||
}
|
||||
ProfileBox(mode, false) {
|
||||
// template mode shouldn't change profile here!
|
||||
if (it == Mode.Default || it == Mode.Custom) {
|
||||
onProfileChange(profile.copy(rootUseDefault = it == Mode.Default))
|
||||
}
|
||||
mode = it
|
||||
}
|
||||
Crossfade(targetState = mode, label = "") { currentMode ->
|
||||
if (currentMode == Mode.Template) {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
val templateNone = "None"
|
||||
var template by rememberSaveable {
|
||||
mutableStateOf(
|
||||
profile.rootTemplate
|
||||
?: templateNone
|
||||
)
|
||||
}
|
||||
ListItem(headlineContent = {
|
||||
ExposedDropdownMenuBox(
|
||||
expanded = expanded,
|
||||
onExpandedChange = { expanded = it },
|
||||
) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.menuAnchor(),
|
||||
readOnly = true,
|
||||
label = { Text(stringResource(R.string.profile_template)) },
|
||||
value = template,
|
||||
onValueChange = {
|
||||
if (template != templateNone) {
|
||||
onProfileChange(
|
||||
profile.copy(
|
||||
rootTemplate = it,
|
||||
rootUseDefault = false
|
||||
)
|
||||
)
|
||||
template = it
|
||||
}
|
||||
},
|
||||
trailingIcon = {
|
||||
if (expanded) Icon(Icons.Filled.ArrowDropUp, null)
|
||||
else Icon(Icons.Filled.ArrowDropDown, null)
|
||||
},
|
||||
)
|
||||
// TODO: Template
|
||||
}
|
||||
})
|
||||
} else if (mode == Mode.Custom) {
|
||||
RootProfileConfig(
|
||||
fixedName = true,
|
||||
profile = profile,
|
||||
onProfileChange = onProfileChange
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val mode = if (profile.nonRootUseDefault) Mode.Default else Mode.Custom
|
||||
ProfileBox(mode, false) {
|
||||
onProfileChange(profile.copy(nonRootUseDefault = (it == Mode.Default)))
|
||||
}
|
||||
Crossfade(targetState = mode, label = "") { currentMode ->
|
||||
val modifyEnabled = currentMode == Mode.Custom
|
||||
AppProfileConfig(
|
||||
fixedName = true,
|
||||
profile = profile,
|
||||
enabled = modifyEnabled,
|
||||
onProfileChange = onProfileChange
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum class Mode(@StringRes private val res: Int) {
|
||||
Default(R.string.profile_default),
|
||||
Template(R.string.profile_template),
|
||||
Custom(R.string.profile_custom);
|
||||
|
||||
val text: String
|
||||
@Composable get() = stringResource(res)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(onBack: () -> Unit) {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(stringResource(R.string.profile))
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(
|
||||
onClick = onBack
|
||||
) { Icon(Icons.Filled.ArrowBack, contentDescription = null) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun ProfileBox(
|
||||
mode: Mode,
|
||||
hasTemplate: Boolean,
|
||||
onModeChange: (Mode) -> Unit,
|
||||
) {
|
||||
ListItem(
|
||||
headlineContent = { Text(stringResource(R.string.profile)) },
|
||||
supportingContent = { Text(mode.text) },
|
||||
leadingContent = { Icon(Icons.Filled.AccountCircle, null) },
|
||||
)
|
||||
Divider(thickness = Dp.Hairline)
|
||||
ListItem(headlineContent = {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly
|
||||
) {
|
||||
FilterChip(
|
||||
selected = mode == Mode.Default,
|
||||
label = { Text(stringResource(R.string.profile_default)) },
|
||||
onClick = { onModeChange(Mode.Default) },
|
||||
)
|
||||
if (hasTemplate) {
|
||||
FilterChip(
|
||||
selected = mode == Mode.Template,
|
||||
label = { Text(stringResource(R.string.profile_template)) },
|
||||
onClick = { onModeChange(Mode.Template) },
|
||||
)
|
||||
}
|
||||
FilterChip(
|
||||
selected = mode == Mode.Custom,
|
||||
label = { Text(stringResource(R.string.profile_custom)) },
|
||||
onClick = { onModeChange(Mode.Custom) },
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AppMenuBox(packageName: String, content: @Composable () -> Unit) {
|
||||
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
var touchPoint: Offset by remember { mutableStateOf(Offset.Zero) }
|
||||
val density = LocalDensity.current
|
||||
|
||||
BoxWithConstraints(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures {
|
||||
touchPoint = it
|
||||
expanded = true
|
||||
}
|
||||
}
|
||||
) {
|
||||
|
||||
content()
|
||||
|
||||
val (offsetX, offsetY) = with(density) {
|
||||
(touchPoint.x.toDp()) to (touchPoint.y.toDp())
|
||||
}
|
||||
|
||||
DropdownMenu(
|
||||
expanded = expanded,
|
||||
offset = DpOffset(offsetX, -offsetY),
|
||||
onDismissRequest = {
|
||||
expanded = false
|
||||
},
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(id = R.string.launch_app)) },
|
||||
onClick = {
|
||||
expanded = false
|
||||
launchApp(packageName)
|
||||
},
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(id = R.string.force_stop_app)) },
|
||||
onClick = {
|
||||
expanded = false
|
||||
forceStopApp(packageName)
|
||||
},
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(id = R.string.restart_app)) },
|
||||
onClick = {
|
||||
expanded = false
|
||||
restartApp(packageName)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun AppProfilePreview() {
|
||||
var profile by remember { mutableStateOf(Natives.Profile("")) }
|
||||
AppProfileInner(
|
||||
packageName = "icu.nullptr.test",
|
||||
appLabel = "Test",
|
||||
appIcon = { Icon(Icons.Filled.Android, null) },
|
||||
profile = profile,
|
||||
onProfileChange = {
|
||||
profile = it
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -15,9 +15,10 @@ enum class BottomBarDestination(
|
||||
val direction: DirectionDestinationSpec,
|
||||
@StringRes val label: Int,
|
||||
val iconSelected: ImageVector,
|
||||
val iconNotSelected: ImageVector
|
||||
val iconNotSelected: ImageVector,
|
||||
val rootRequired: Boolean,
|
||||
) {
|
||||
Home(HomeScreenDestination, R.string.home, Icons.Filled.Home, Icons.Outlined.Home),
|
||||
SuperUser(SuperUserScreenDestination, R.string.superuser, Icons.Filled.Security, Icons.Outlined.Security),
|
||||
Module(ModuleScreenDestination, R.string.module, Icons.Filled.Apps, Icons.Outlined.Apps)
|
||||
Home(HomeScreenDestination, R.string.home, Icons.Filled.Home, Icons.Outlined.Home, false),
|
||||
SuperUser(SuperUserScreenDestination, R.string.superuser, Icons.Filled.Security, Icons.Outlined.Security, true),
|
||||
Module(ModuleScreenDestination, R.string.module, Icons.Filled.Apps, Icons.Outlined.Apps, true)
|
||||
}
|
||||
|
||||
@@ -19,32 +19,31 @@ import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
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 com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootNavGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.weishu.kernelsu.*
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.screen.destinations.SettingScreenDestination
|
||||
import me.weishu.kernelsu.ui.util.*
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@RootNavGraph(start = true)
|
||||
@Destination
|
||||
@Composable
|
||||
fun HomeScreen(navigator: DestinationsNavigator) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBar(onSettingsClick = {
|
||||
navigator.navigate(SettingScreenDestination)
|
||||
})
|
||||
}
|
||||
) { innerPadding ->
|
||||
Scaffold(topBar = {
|
||||
TopBar(onSettingsClick = {
|
||||
navigator.navigate(SettingScreenDestination)
|
||||
})
|
||||
}) { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
@@ -57,9 +56,17 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
||||
SideEffect {
|
||||
if (isManager) install()
|
||||
}
|
||||
val ksuVersion = if (isManager) Natives.getVersion() else null
|
||||
val ksuVersion = if (isManager) Natives.version else null
|
||||
|
||||
StatusCard(kernelVersion, ksuVersion)
|
||||
if (isManager && Natives.requireNewKernel()) {
|
||||
WarningCard(
|
||||
stringResource(id = R.string.require_kernel_version).format(
|
||||
ksuVersion, Natives.MINIMAL_SUPPORTED_KERNEL
|
||||
)
|
||||
)
|
||||
}
|
||||
UpdateCard()
|
||||
InfoCard()
|
||||
DonateCard()
|
||||
LearnMoreCard()
|
||||
@@ -68,6 +75,28 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UpdateCard() {
|
||||
val context = LocalContext.current
|
||||
val newVersion by produceState(initialValue = 0 to "") {
|
||||
value = withContext(Dispatchers.IO) { checkNewVersion() }
|
||||
}
|
||||
val currentVersionCode = getManagerVersion(context).second
|
||||
val newVersionCode = newVersion.first
|
||||
val newVersionUrl = newVersion.second
|
||||
if (newVersionCode <= currentVersionCode) {
|
||||
return
|
||||
}
|
||||
|
||||
val uriHandler = LocalUriHandler.current
|
||||
WarningCard(
|
||||
message = stringResource(id = R.string.new_version_available).format(newVersionCode),
|
||||
MaterialTheme.colorScheme.outlineVariant
|
||||
) {
|
||||
uriHandler.openUri(newVersionUrl)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RebootDropdownItem(@StringRes id: Int, reason: String = "") {
|
||||
DropdownMenuItem(text = {
|
||||
@@ -80,44 +109,41 @@ fun RebootDropdownItem(@StringRes id: Int, reason: String = "") {
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(onSettingsClick: () -> Unit) {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.app_name)) },
|
||||
actions = {
|
||||
var showDropdown by remember { mutableStateOf(false) }
|
||||
IconButton(onClick = {
|
||||
showDropdown = true
|
||||
TopAppBar(title = { Text(stringResource(R.string.app_name)) }, actions = {
|
||||
var showDropdown by remember { mutableStateOf(false) }
|
||||
IconButton(onClick = {
|
||||
showDropdown = true
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Refresh,
|
||||
contentDescription = stringResource(id = R.string.reboot)
|
||||
)
|
||||
|
||||
DropdownMenu(expanded = showDropdown, onDismissRequest = {
|
||||
showDropdown = false
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Refresh,
|
||||
contentDescription = stringResource(id = R.string.reboot)
|
||||
)
|
||||
|
||||
DropdownMenu(expanded = showDropdown, onDismissRequest = {
|
||||
showDropdown = false
|
||||
}) {
|
||||
RebootDropdownItem(id = R.string.reboot)
|
||||
|
||||
RebootDropdownItem(id = R.string.reboot)
|
||||
|
||||
val pm =
|
||||
LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && pm?.isRebootingUserspaceSupported == true) {
|
||||
RebootDropdownItem(id = R.string.reboot_userspace, reason = "userspace")
|
||||
}
|
||||
RebootDropdownItem(id = R.string.reboot_recovery, reason = "recovery")
|
||||
RebootDropdownItem(id = R.string.reboot_bootloader, reason = "bootloader")
|
||||
RebootDropdownItem(id = R.string.reboot_download, reason = "download")
|
||||
RebootDropdownItem(id = R.string.reboot_edl, reason = "edl")
|
||||
val pm =
|
||||
LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && pm?.isRebootingUserspaceSupported == true) {
|
||||
RebootDropdownItem(id = R.string.reboot_userspace, reason = "userspace")
|
||||
}
|
||||
}
|
||||
|
||||
IconButton(onClick = onSettingsClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Settings,
|
||||
contentDescription = stringResource(id = R.string.settings)
|
||||
)
|
||||
RebootDropdownItem(id = R.string.reboot_recovery, reason = "recovery")
|
||||
RebootDropdownItem(id = R.string.reboot_bootloader, reason = "bootloader")
|
||||
RebootDropdownItem(id = R.string.reboot_download, reason = "download")
|
||||
RebootDropdownItem(id = R.string.reboot_edl, reason = "edl")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
IconButton(onClick = onSettingsClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Settings,
|
||||
contentDescription = stringResource(id = R.string.settings)
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -129,20 +155,17 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
|
||||
})
|
||||
) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
if (kernelVersion.isGKI() && ksuVersion == null) {
|
||||
uriHandler.openUri("https://kernelsu.org/guide/installation.html")
|
||||
}
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
if (kernelVersion.isGKI() && ksuVersion == null) {
|
||||
uriHandler.openUri("https://kernelsu.org/guide/installation.html")
|
||||
}
|
||||
.padding(24.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
}
|
||||
.padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
when {
|
||||
ksuVersion != null -> {
|
||||
val appendText = if (Natives.isSafeMode()) {
|
||||
val appendText = if (Natives.isSafeMode) {
|
||||
" [${stringResource(id = R.string.safe_mode)}]"
|
||||
} else {
|
||||
""
|
||||
@@ -160,8 +183,9 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.home_superuser_count, getSuperuserCount()),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
text = stringResource(
|
||||
R.string.home_superuser_count, getSuperuserCount()
|
||||
), style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
@@ -170,6 +194,7 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
kernelVersion.isGKI() -> {
|
||||
Icon(Icons.Outlined.Warning, stringResource(R.string.home_not_installed))
|
||||
Column(Modifier.padding(start = 20.dp)) {
|
||||
@@ -184,12 +209,12 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
Icon(Icons.Outlined.Block, stringResource(R.string.home_unsupported))
|
||||
Column(Modifier.padding(start = 20.dp)) {
|
||||
Text(
|
||||
text = stringResource(R.string.home_unsupported),
|
||||
fontFamily = FontFamily.Serif,
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
@@ -204,6 +229,28 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun WarningCard(
|
||||
message: String, color: Color = MaterialTheme.colorScheme.error, onClick: (() -> Unit)? = null
|
||||
) {
|
||||
ElevatedCard(
|
||||
colors = CardDefaults.elevatedCardColors(
|
||||
containerColor = color
|
||||
)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.then(onClick?.let { Modifier.clickable { it() } } ?: Modifier)
|
||||
.padding(24.dp)
|
||||
) {
|
||||
Text(
|
||||
text = message, style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LearnMoreCard() {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
@@ -211,15 +258,12 @@ fun LearnMoreCard() {
|
||||
|
||||
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_learn_kernelsu),
|
||||
@@ -241,15 +285,12 @@ fun DonateCard() {
|
||||
|
||||
ElevatedCard {
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
uriHandler.openUri("https://patreon.com/weishu")
|
||||
}
|
||||
.padding(24.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
uriHandler.openUri("https://patreon.com/weishu")
|
||||
}
|
||||
.padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Column() {
|
||||
Text(
|
||||
text = stringResource(R.string.home_support_title),
|
||||
@@ -288,7 +329,11 @@ private fun InfoCard() {
|
||||
InfoCardItem(stringResource(R.string.home_kernel), uname.release)
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(stringResource(R.string.home_manager_version), getManagerVersion(context))
|
||||
val managerVersion = getManagerVersion(context)
|
||||
InfoCardItem(
|
||||
stringResource(R.string.home_manager_version),
|
||||
"${managerVersion.first} (${managerVersion.second})"
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(stringResource(R.string.home_fingerprint), Build.FINGERPRINT)
|
||||
@@ -299,9 +344,9 @@ private fun InfoCard() {
|
||||
}
|
||||
}
|
||||
|
||||
fun getManagerVersion(context: Context): String {
|
||||
fun getManagerVersion(context: Context): Pair<String, Int> {
|
||||
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
|
||||
return "${packageInfo.versionName} (${packageInfo.versionCode})"
|
||||
return Pair(packageInfo.versionName, packageInfo.versionCode)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@@ -313,3 +358,12 @@ private fun StatusCardPreview() {
|
||||
StatusCard(KernelVersion(4, 10, 101), null)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun WarningCardPreview() {
|
||||
Column {
|
||||
WarningCard(message = "Warning message")
|
||||
WarningCard(message = "Warning message ", MaterialTheme.colorScheme.outlineVariant, onClick = {})
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,10 @@ import androidx.compose.material.icons.filled.Save
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.key.Key
|
||||
import androidx.compose.ui.input.key.key
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -24,6 +27,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.KeyEventBlocker
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
import me.weishu.kernelsu.ui.util.installModule
|
||||
import me.weishu.kernelsu.ui.util.reboot
|
||||
@@ -35,16 +39,18 @@ import java.util.*
|
||||
* @author weishu
|
||||
* @date 2023/1/1.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
@Destination
|
||||
fun InstallScreen(navigator: DestinationsNavigator, uri: Uri) {
|
||||
|
||||
var text by rememberSaveable { mutableStateOf("") }
|
||||
val logContent = StringBuilder()
|
||||
var showFloatAction by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val scrollState = rememberScrollState()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (text.isNotEmpty()) {
|
||||
@@ -55,9 +61,15 @@ fun InstallScreen(navigator: DestinationsNavigator, uri: Uri) {
|
||||
if (success) {
|
||||
showFloatAction = true
|
||||
}
|
||||
}) {
|
||||
}, onStdout = {
|
||||
text += "$it\n"
|
||||
}
|
||||
scope.launch {
|
||||
scrollState.animateScrollTo(scrollState.maxValue)
|
||||
}
|
||||
logContent.append(it).append("\n")
|
||||
}, onStderr = {
|
||||
logContent.append(it).append("\n")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +87,7 @@ fun InstallScreen(navigator: DestinationsNavigator, uri: Uri) {
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
||||
"KernelSU_install_log_${date}.log"
|
||||
)
|
||||
file.writeText(text)
|
||||
file.writeText(logContent.toString())
|
||||
snackBarHost.showSnackbar("Log saved to ${file.absolutePath}")
|
||||
}
|
||||
}
|
||||
@@ -99,11 +111,14 @@ fun InstallScreen(navigator: DestinationsNavigator, uri: Uri) {
|
||||
|
||||
}
|
||||
) { innerPadding ->
|
||||
KeyEventBlocker {
|
||||
it.key == Key.VolumeDown || it.key == Key.VolumeUp
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize(1f)
|
||||
.padding(innerPadding)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
.verticalScroll(scrollState),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(8.dp),
|
||||
|
||||
@@ -2,12 +2,15 @@ package me.weishu.kernelsu.ui.screen
|
||||
|
||||
import android.app.Activity.RESULT_OK
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
@@ -19,6 +22,7 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
@@ -30,32 +34,33 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.ConfirmDialog
|
||||
import me.weishu.kernelsu.ui.component.ConfirmResult
|
||||
import me.weishu.kernelsu.ui.component.LoadingDialog
|
||||
import me.weishu.kernelsu.ui.screen.destinations.InstallScreenDestination
|
||||
import me.weishu.kernelsu.ui.util.*
|
||||
import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Destination
|
||||
@Composable
|
||||
fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
val viewModel = viewModel<ModuleViewModel>()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (viewModel.moduleList.isEmpty()) {
|
||||
if (viewModel.moduleList.isEmpty() || viewModel.isNeedRefresh) {
|
||||
viewModel.fetchModuleList()
|
||||
}
|
||||
}
|
||||
|
||||
val isSafeMode = Natives.isSafeMode()
|
||||
val isKSUVersionInvalid = Natives.getVersion() < 0
|
||||
val isSafeMode = Natives.isSafeMode
|
||||
val hasMagisk = hasMagisk()
|
||||
|
||||
val hideInstallButton = isSafeMode || isKSUVersionInvalid || hasMagisk
|
||||
val hideInstallButton = isSafeMode || hasMagisk
|
||||
|
||||
Scaffold(topBar = {
|
||||
TopBar()
|
||||
@@ -75,6 +80,8 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
|
||||
navigator.navigate(InstallScreenDestination(uri))
|
||||
|
||||
viewModel.markNeedRefresh()
|
||||
|
||||
Log.i("ModuleScreen", "select zip result: ${it.data}")
|
||||
}
|
||||
|
||||
@@ -93,12 +100,9 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
|
||||
ConfirmDialog()
|
||||
|
||||
LoadingDialog()
|
||||
|
||||
when {
|
||||
isKSUVersionInvalid -> {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text(stringResource(R.string.require_kernel_version_8))
|
||||
}
|
||||
}
|
||||
hasMagisk -> {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@@ -112,13 +116,15 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
ModuleList(
|
||||
viewModel = viewModel,
|
||||
modifier = Modifier
|
||||
viewModel = viewModel, modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize()
|
||||
)
|
||||
) {
|
||||
navigator.navigate(InstallScreenDestination(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,7 +132,9 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
private fun ModuleList(viewModel: ModuleViewModel, modifier: Modifier = Modifier) {
|
||||
private fun ModuleList(
|
||||
viewModel: ModuleViewModel, modifier: Modifier = Modifier, onInstallModule: (Uri) -> Unit
|
||||
) {
|
||||
val failedEnable = stringResource(R.string.module_failed_to_enable)
|
||||
val failedDisable = stringResource(R.string.module_failed_to_disable)
|
||||
val failedUninstall = stringResource(R.string.module_uninstall_failed)
|
||||
@@ -136,8 +144,7 @@ private fun ModuleList(viewModel: ModuleViewModel, modifier: Modifier = Modifier
|
||||
val moduleStr = stringResource(id = R.string.module)
|
||||
val uninstall = stringResource(id = R.string.uninstall)
|
||||
val cancel = stringResource(id = android.R.string.cancel)
|
||||
val moduleUninstallConfirm =
|
||||
stringResource(id = R.string.module_uninstall_confirm)
|
||||
val moduleUninstallConfirm = stringResource(id = R.string.module_uninstall_confirm)
|
||||
|
||||
val dialogHost = LocalDialogHost.current
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
@@ -153,7 +160,12 @@ private fun ModuleList(viewModel: ModuleViewModel, modifier: Modifier = Modifier
|
||||
return
|
||||
}
|
||||
|
||||
val success = uninstallModule(module.id)
|
||||
val success = dialogHost.withLoading {
|
||||
withContext(Dispatchers.IO) {
|
||||
uninstallModule(module.id)
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
viewModel.fetchModuleList()
|
||||
}
|
||||
@@ -173,46 +185,66 @@ private fun ModuleList(viewModel: ModuleViewModel, modifier: Modifier = Modifier
|
||||
}
|
||||
}
|
||||
|
||||
val refreshState = rememberPullRefreshState(
|
||||
refreshing = viewModel.isRefreshing,
|
||||
onRefresh = { viewModel.fetchModuleList() }
|
||||
)
|
||||
val refreshState = rememberPullRefreshState(refreshing = viewModel.isRefreshing,
|
||||
onRefresh = { viewModel.fetchModuleList() })
|
||||
Box(modifier.pullRefresh(refreshState)) {
|
||||
if (viewModel.isOverlayAvailable) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
contentPadding = remember {
|
||||
PaddingValues(
|
||||
start = 16.dp,
|
||||
top = 16.dp,
|
||||
end = 16.dp,
|
||||
bottom = 16.dp
|
||||
+ 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */
|
||||
)
|
||||
},
|
||||
) {
|
||||
val isEmpty = viewModel.moduleList.isEmpty()
|
||||
if (isEmpty) {
|
||||
val context = LocalContext.current
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
contentPadding = remember {
|
||||
PaddingValues(
|
||||
start = 16.dp,
|
||||
top = 16.dp,
|
||||
end = 16.dp,
|
||||
bottom = 16.dp + 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */
|
||||
)
|
||||
},
|
||||
) {
|
||||
when {
|
||||
!viewModel.isOverlayAvailable -> {
|
||||
item {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
modifier = Modifier.fillParentMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(stringResource(R.string.module_empty))
|
||||
Text(stringResource(R.string.module_overlay_fs_not_available), textAlign = TextAlign.Center)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
viewModel.moduleList.isEmpty() -> {
|
||||
item {
|
||||
Box(
|
||||
modifier = Modifier.fillParentMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(stringResource(R.string.module_empty), textAlign = TextAlign.Center)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
items(viewModel.moduleList) { module ->
|
||||
var isChecked by rememberSaveable(module) { mutableStateOf(module.enabled) }
|
||||
val scope = rememberCoroutineScope()
|
||||
ModuleItem(module, isChecked, onUninstall = {
|
||||
val updateUrl by produceState(initialValue = "") {
|
||||
viewModel.checkUpdate(module) { value = it.orEmpty() }
|
||||
}
|
||||
|
||||
val downloadingText = stringResource(R.string.module_downloading)
|
||||
val startDownloadingText = stringResource(R.string.module_start_downloading)
|
||||
|
||||
ModuleItem(module, isChecked, updateUrl, onUninstall = {
|
||||
scope.launch { onModuleUninstall(module) }
|
||||
}, onCheckChanged = {
|
||||
val success = toggleModule(module.id, !isChecked)
|
||||
if (success) {
|
||||
isChecked = it
|
||||
scope.launch {
|
||||
scope.launch {
|
||||
val success = dialogHost.withLoading {
|
||||
withContext(Dispatchers.IO) {
|
||||
toggleModule(module.id, !isChecked)
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
isChecked = it
|
||||
viewModel.fetchModuleList()
|
||||
|
||||
val result = snackBarHost.showSnackbar(
|
||||
@@ -221,27 +253,45 @@ private fun ModuleList(viewModel: ModuleViewModel, modifier: Modifier = Modifier
|
||||
if (result == SnackbarResult.ActionPerformed) {
|
||||
reboot()
|
||||
}
|
||||
} else {
|
||||
val message = if (isChecked) failedDisable else failedEnable
|
||||
snackBarHost.showSnackbar(message.format(module.name))
|
||||
}
|
||||
} else scope.launch {
|
||||
val message = if (isChecked) failedDisable else failedEnable
|
||||
snackBarHost.showSnackbar(message.format(module.name))
|
||||
}
|
||||
}, onUpdate = {
|
||||
|
||||
scope.launch {
|
||||
Toast.makeText(
|
||||
context,
|
||||
startDownloadingText.format(module.name),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
|
||||
val downloading = downloadingText.format(module.name)
|
||||
download(
|
||||
context,
|
||||
updateUrl,
|
||||
"${module.name}-${module.version}.zip",
|
||||
downloading,
|
||||
onDownloaded = onInstallModule,
|
||||
onDownloading = {
|
||||
Toast.makeText(context, downloading, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
// fix last item shadow incomplete in LazyColumn
|
||||
Spacer(Modifier.height(1.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text(stringResource(R.string.module_overlay_fs_not_available))
|
||||
}
|
||||
}
|
||||
|
||||
DownloadListener(context, onInstallModule)
|
||||
|
||||
PullRefreshIndicator(
|
||||
refreshing = viewModel.isRefreshing,
|
||||
state = refreshState,
|
||||
modifier = Modifier.align(
|
||||
refreshing = viewModel.isRefreshing, state = refreshState, modifier = Modifier.align(
|
||||
Alignment.TopCenter
|
||||
)
|
||||
)
|
||||
@@ -258,8 +308,10 @@ private fun TopBar() {
|
||||
private fun ModuleItem(
|
||||
module: ModuleViewModel.ModuleInfo,
|
||||
isChecked: Boolean,
|
||||
updateUrl: String,
|
||||
onUninstall: (ModuleViewModel.ModuleInfo) -> Unit,
|
||||
onCheckChanged: (Boolean) -> Unit
|
||||
onCheckChanged: (Boolean) -> Unit,
|
||||
onUpdate: (ModuleViewModel.ModuleInfo) -> Unit,
|
||||
) {
|
||||
ElevatedCard(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -341,6 +393,23 @@ private fun ModuleItem(
|
||||
) {
|
||||
Spacer(modifier = Modifier.weight(1f, true))
|
||||
|
||||
if (updateUrl.isNotEmpty()) {
|
||||
Button(
|
||||
modifier = Modifier
|
||||
.padding(0.dp)
|
||||
.defaultMinSize(52.dp, 32.dp),
|
||||
onClick = { onUpdate(module) },
|
||||
shape = RoundedCornerShape(6.dp),
|
||||
contentPadding = PaddingValues(0.dp)
|
||||
) {
|
||||
Text(
|
||||
fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
|
||||
fontSize = MaterialTheme.typography.labelMedium.fontSize,
|
||||
text = stringResource(R.string.module_update),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
TextButton(
|
||||
enabled = !module.remove,
|
||||
onClick = { onUninstall(module) },
|
||||
@@ -369,6 +438,7 @@ fun ModuleItemPreview() {
|
||||
enabled = true,
|
||||
update = true,
|
||||
remove = true,
|
||||
updateJson = ""
|
||||
)
|
||||
ModuleItem(module, true, {}, {})
|
||||
ModuleItem(module, true, "", {}, {}, {})
|
||||
}
|
||||
@@ -2,25 +2,31 @@ package me.weishu.kernelsu.ui.screen
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.BugReport
|
||||
import androidx.compose.material.icons.filled.ContactPage
|
||||
import androidx.compose.material.icons.filled.RemoveModerator
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.core.content.FileProvider
|
||||
import com.alorma.compose.settings.ui.*
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.weishu.kernelsu.BuildConfig
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.AboutDialog
|
||||
import me.weishu.kernelsu.ui.component.LoadingDialog
|
||||
import me.weishu.kernelsu.ui.component.SwitchItem
|
||||
import me.weishu.kernelsu.ui.util.LocalDialogHost
|
||||
import me.weishu.kernelsu.ui.util.getBugreportFile
|
||||
|
||||
@@ -28,7 +34,6 @@ import me.weishu.kernelsu.ui.util.getBugreportFile
|
||||
* @author weishu
|
||||
* @date 2023/1/1.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Destination
|
||||
@Composable
|
||||
fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
@@ -50,11 +55,25 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val dialogHost = LocalDialogHost.current
|
||||
SettingsMenuLink(
|
||||
title = {
|
||||
Text(stringResource(id = R.string.send_log))
|
||||
},
|
||||
onClick = {
|
||||
|
||||
var umountChecked by rememberSaveable {
|
||||
mutableStateOf(Natives.isDefaultUmountModules())
|
||||
}
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.RemoveModerator,
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
ListItem(
|
||||
leadingContent = { Icon(Icons.Filled.BugReport, stringResource(id = R.string.send_log)) },
|
||||
headlineContent = { Text(stringResource(id = R.string.send_log)) },
|
||||
modifier = Modifier.clickable {
|
||||
scope.launch {
|
||||
val bugreport = dialogHost.withLoading {
|
||||
withContext(Dispatchers.IO) {
|
||||
@@ -85,11 +104,10 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
)
|
||||
|
||||
val about = stringResource(id = R.string.about)
|
||||
SettingsMenuLink(
|
||||
title = {
|
||||
Text(about)
|
||||
},
|
||||
onClick = {
|
||||
ListItem(
|
||||
leadingContent = { Icon(Icons.Filled.ContactPage, stringResource(id = R.string.about)) },
|
||||
headlineContent = { Text(about) },
|
||||
modifier = Modifier.clickable {
|
||||
showAboutDialog.value = true
|
||||
}
|
||||
)
|
||||
@@ -108,4 +126,4 @@ private fun TopBar(onBack: () -> Unit = {}) {
|
||||
) { Icon(Icons.Filled.ArrowBack, contentDescription = null) }
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package me.weishu.kernelsu.ui.screen
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
@@ -11,33 +14,32 @@ import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.coroutines.launch
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.ConfirmDialog
|
||||
import me.weishu.kernelsu.ui.component.ConfirmResult
|
||||
import me.weishu.kernelsu.ui.component.SearchAppBar
|
||||
import me.weishu.kernelsu.ui.util.LocalDialogHost
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
import me.weishu.kernelsu.ui.screen.destinations.AppProfileScreenDestination
|
||||
import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel
|
||||
import java.util.*
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Destination
|
||||
@Composable
|
||||
fun SuperUserScreen() {
|
||||
fun SuperUserScreen(navigator: DestinationsNavigator) {
|
||||
val viewModel = viewModel<SuperUserViewModel>()
|
||||
val snackbarHost = LocalSnackbarHost.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
@@ -105,39 +107,12 @@ fun SuperUserScreen() {
|
||||
.padding(innerPadding)
|
||||
.pullRefresh(refreshState)
|
||||
) {
|
||||
val failMessage = stringResource(R.string.superuser_failed_to_grant_root)
|
||||
|
||||
LazyColumn(Modifier.fillMaxSize()) {
|
||||
items(viewModel.appList, key = { it.packageName + it.uid }) { app ->
|
||||
var isChecked by rememberSaveable(app) { mutableStateOf(app.onAllowList) }
|
||||
val dialogHost = LocalDialogHost.current
|
||||
val content =
|
||||
stringResource(id = R.string.superuser_allow_root_confirm, app.label)
|
||||
val confirm = stringResource(id = android.R.string.ok)
|
||||
val cancel = stringResource(id = android.R.string.cancel)
|
||||
|
||||
AppItem(app, isChecked) { checked ->
|
||||
scope.launch {
|
||||
if (checked) {
|
||||
val confirmResult = dialogHost.showConfirm(
|
||||
app.label,
|
||||
content = content,
|
||||
confirm = confirm,
|
||||
dismiss = cancel
|
||||
)
|
||||
if (confirmResult != ConfirmResult.Confirmed) {
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
|
||||
val success = Natives.allowRoot(app.uid, checked)
|
||||
if (success) {
|
||||
isChecked = checked
|
||||
} else {
|
||||
snackbarHost.showSnackbar(failMessage.format(app.uid))
|
||||
}
|
||||
}
|
||||
AppItem(app) {
|
||||
navigator.navigate(AppProfileScreenDestination(app))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,20 +125,36 @@ fun SuperUserScreen() {
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
private fun AppItem(
|
||||
app: SuperUserViewModel.AppInfo,
|
||||
isChecked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit
|
||||
onClickListener: () -> Unit,
|
||||
) {
|
||||
ListItem(
|
||||
headlineText = { Text(app.label) },
|
||||
supportingText = { Text(app.packageName) },
|
||||
modifier = Modifier.clickable(onClick = onClickListener),
|
||||
headlineContent = { Text(app.label) },
|
||||
supportingContent = {
|
||||
Column {
|
||||
Text(app.packageName)
|
||||
FlowRow {
|
||||
if (app.allowSu) {
|
||||
LabelText(label = "ROOT")
|
||||
} else {
|
||||
if (Natives.uidShouldUmount(app.uid)) {
|
||||
LabelText(label = "UMOUNT")
|
||||
}
|
||||
}
|
||||
if (app.hasCustomProfile) {
|
||||
LabelText(label = "CUSTOM")
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
leadingContent = {
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
.data(app.icon)
|
||||
.data(app.packageInfo)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
contentDescription = app.label,
|
||||
@@ -173,12 +164,26 @@ private fun AppItem(
|
||||
.height(48.dp)
|
||||
)
|
||||
},
|
||||
trailingContent = {
|
||||
Switch(
|
||||
checked = isChecked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
modifier = Modifier.padding(4.dp)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LabelText(label: String) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(top = 4.dp, end = 4.dp)
|
||||
.background(
|
||||
Color.Black,
|
||||
shape = RoundedCornerShape(4.dp)
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
modifier = Modifier.padding(vertical = 2.dp, horizontal = 5.dp),
|
||||
style = TextStyle(
|
||||
fontSize = 8.sp,
|
||||
color = Color.White,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package me.weishu.kernelsu.ui.util
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.DownloadManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.net.Uri
|
||||
import android.os.Environment
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
|
||||
/**
|
||||
* @author weishu
|
||||
* @date 2023/6/22.
|
||||
*/
|
||||
@SuppressLint("Range")
|
||||
fun download(
|
||||
context: Context,
|
||||
url: String,
|
||||
fileName: String,
|
||||
description: String,
|
||||
onDownloaded: (Uri) -> Unit = {},
|
||||
onDownloading: () -> Unit = {}
|
||||
) {
|
||||
val downloadManager =
|
||||
context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
||||
|
||||
val query = DownloadManager.Query()
|
||||
query.setFilterByStatus(DownloadManager.STATUS_RUNNING or DownloadManager.STATUS_PAUSED or DownloadManager.STATUS_PENDING)
|
||||
downloadManager.query(query).use { cursor ->
|
||||
while (cursor.moveToNext()) {
|
||||
val uri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_URI))
|
||||
val localUri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI))
|
||||
val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
|
||||
val columnTitle = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TITLE))
|
||||
if (url == uri || fileName == columnTitle) {
|
||||
if (status == DownloadManager.STATUS_RUNNING || status == DownloadManager.STATUS_PENDING) {
|
||||
onDownloading()
|
||||
return
|
||||
} else if (status == DownloadManager.STATUS_SUCCESSFUL) {
|
||||
onDownloaded(Uri.parse(localUri))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val request = DownloadManager.Request(Uri.parse(url))
|
||||
.setDestinationInExternalPublicDir(
|
||||
Environment.DIRECTORY_DOWNLOADS,
|
||||
fileName
|
||||
)
|
||||
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
||||
.setMimeType("application/zip")
|
||||
.setTitle(fileName)
|
||||
.setDescription(description)
|
||||
|
||||
downloadManager.enqueue(request)
|
||||
}
|
||||
|
||||
fun checkNewVersion(): Pair<Int, String> {
|
||||
val url = "https://api.github.com/repos/tiann/KernelSU/releases/latest"
|
||||
val defaultValue = 0 to ""
|
||||
runCatching {
|
||||
okhttp3.OkHttpClient().newCall(okhttp3.Request.Builder().url(url).build()).execute()
|
||||
.use { response ->
|
||||
if (!response.isSuccessful) {
|
||||
return defaultValue
|
||||
}
|
||||
val body = response.body?.string() ?: return defaultValue
|
||||
val json = org.json.JSONObject(body)
|
||||
|
||||
val assets = json.getJSONArray("assets")
|
||||
for (i in 0 until assets.length()) {
|
||||
val asset = assets.getJSONObject(i)
|
||||
val name = asset.getString("name")
|
||||
if (!name.endsWith(".apk")) {
|
||||
continue
|
||||
}
|
||||
|
||||
val regex = Regex("v(.+?)_(\\d+)-")
|
||||
val matchResult = regex.find(name) ?: continue
|
||||
val versionName = matchResult.groupValues[1]
|
||||
val versionCode = matchResult.groupValues[2].toInt()
|
||||
val downloadUrl = asset.getString("browser_download_url")
|
||||
|
||||
return versionCode to downloadUrl
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DownloadListener(context: Context, onDownloaded: (Uri) -> Unit) {
|
||||
DisposableEffect(context) {
|
||||
val receiver = object : BroadcastReceiver() {
|
||||
@SuppressLint("Range")
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (intent?.action == DownloadManager.ACTION_DOWNLOAD_COMPLETE) {
|
||||
val id = intent.getLongExtra(
|
||||
DownloadManager.EXTRA_DOWNLOAD_ID, -1
|
||||
)
|
||||
val query = DownloadManager.Query().setFilterById(id)
|
||||
val downloadManager =
|
||||
context?.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
||||
val cursor = downloadManager.query(query)
|
||||
if (cursor.moveToFirst()) {
|
||||
val status = cursor.getInt(
|
||||
cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)
|
||||
)
|
||||
if (status == DownloadManager.STATUS_SUCCESSFUL) {
|
||||
val uri = cursor.getString(
|
||||
cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)
|
||||
)
|
||||
onDownloaded(Uri.parse(uri))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
context.registerReceiver(
|
||||
receiver,
|
||||
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
|
||||
)
|
||||
onDispose {
|
||||
context.unregisterReceiver(receiver)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import com.topjohnwu.superuser.ShellUtils
|
||||
import me.weishu.kernelsu.BuildConfig
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.ksuApp
|
||||
import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel
|
||||
import org.json.JSONArray
|
||||
import java.io.File
|
||||
|
||||
@@ -71,7 +70,7 @@ fun getModuleCount(): Int {
|
||||
}
|
||||
|
||||
fun getSuperuserCount(): Int {
|
||||
return Natives.getAllowList().size
|
||||
return Natives.allowList.size
|
||||
}
|
||||
|
||||
fun toggleModule(id: String, enable: Boolean): Boolean {
|
||||
@@ -92,7 +91,7 @@ fun uninstallModule(id: String): Boolean {
|
||||
return result
|
||||
}
|
||||
|
||||
fun installModule(uri: Uri, onFinish: (Boolean) -> Unit, onOutput: (String) -> Unit): Boolean {
|
||||
fun installModule(uri: Uri, onFinish: (Boolean) -> Unit, onStdout: (String) -> Unit, onStderr: (String) -> Unit): Boolean {
|
||||
val resolver = ksuApp.contentResolver
|
||||
with(resolver.openInputStream(uri)) {
|
||||
val file = File(ksuApp.cacheDir, "module.zip")
|
||||
@@ -103,14 +102,20 @@ fun installModule(uri: Uri, onFinish: (Boolean) -> Unit, onOutput: (String) -> U
|
||||
|
||||
val shell = getRootShell()
|
||||
|
||||
val callbackList: CallbackList<String?> = object : CallbackList<String?>() {
|
||||
val stdoutCallback: CallbackList<String?> = object : CallbackList<String?>() {
|
||||
override fun onAddElement(s: String?) {
|
||||
onOutput(s ?: "")
|
||||
onStdout(s ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
val stderrCallback: CallbackList<String?> = object : CallbackList<String?>() {
|
||||
override fun onAddElement(s: String?) {
|
||||
onStderr(s ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
val result =
|
||||
shell.newJob().add("${getKsuDaemonPath()} $cmd").to(callbackList, callbackList).exec()
|
||||
shell.newJob().add("${getKsuDaemonPath()} $cmd").to(stdoutCallback, stderrCallback).exec()
|
||||
Log.i("KernelSU", "install module $uri result: $result")
|
||||
|
||||
file.delete()
|
||||
@@ -140,4 +145,48 @@ fun hasMagisk(): Boolean {
|
||||
val result = shell.newJob().add("nsenter --mount=/proc/1/ns/mnt which magisk").exec()
|
||||
Log.i(TAG, "has magisk: ${result.isSuccess}")
|
||||
return result.isSuccess
|
||||
}
|
||||
|
||||
fun isSepolicyValid(rules: String?): Boolean {
|
||||
if (rules == null) {
|
||||
return true
|
||||
}
|
||||
val shell = getRootShell()
|
||||
val result =
|
||||
shell.newJob().add("${getKsuDaemonPath()} sepolicy check '$rules'").to(ArrayList(), null).exec()
|
||||
return result.isSuccess
|
||||
}
|
||||
|
||||
fun getSepolicy(pkg: String): String {
|
||||
val shell = getRootShell()
|
||||
val result =
|
||||
shell.newJob().add("${getKsuDaemonPath()} profile get-sepolicy $pkg").to(ArrayList(), null).exec()
|
||||
Log.i(TAG, "code: ${result.code}, out: ${result.out}, err: ${result.err}")
|
||||
return result.out.joinToString("\n")
|
||||
}
|
||||
|
||||
fun setSepolicy(pkg: String, rules: String): Boolean {
|
||||
val shell = getRootShell()
|
||||
val result =
|
||||
shell.newJob().add("${getKsuDaemonPath()} profile set-sepolicy $pkg '$rules'").to(ArrayList(), null).exec()
|
||||
Log.i(TAG, "set sepolicy result: ${result.code}")
|
||||
return result.isSuccess
|
||||
}
|
||||
|
||||
fun forceStopApp(packageName: String) {
|
||||
val shell = getRootShell()
|
||||
val result = shell.newJob().add("am force-stop $packageName").exec()
|
||||
Log.i(TAG, "force stop $packageName result: $result")
|
||||
}
|
||||
|
||||
fun launchApp(packageName: String) {
|
||||
|
||||
val shell = getRootShell()
|
||||
val result = shell.newJob().add("monkey -p $packageName -c android.intent.category.LAUNCHER 1").exec()
|
||||
Log.i(TAG, "launch $packageName result: $result")
|
||||
}
|
||||
|
||||
fun restartApp(packageName: String) {
|
||||
forceStopApp(packageName)
|
||||
launchApp(packageName)
|
||||
}
|
||||
@@ -72,9 +72,9 @@ fun getBugreportFile(context: Context): File {
|
||||
pw.println("Nodename: ${uname.nodename}")
|
||||
pw.println("Sysname: ${uname.sysname}")
|
||||
|
||||
val ksuKernel = Natives.getVersion()
|
||||
val ksuKernel = Natives.version
|
||||
pw.println("KernelSU: $ksuKernel")
|
||||
val safeMode = Natives.isSafeMode()
|
||||
val safeMode = Natives.isSafeMode
|
||||
pw.println("SafeMode: $safeMode")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.weishu.kernelsu.ui.viewmodel
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
@@ -13,6 +14,7 @@ import kotlinx.coroutines.launch
|
||||
import me.weishu.kernelsu.ui.util.listModules
|
||||
import me.weishu.kernelsu.ui.util.overlayFsAvailable
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.text.Collator
|
||||
import java.util.*
|
||||
|
||||
@@ -33,6 +35,14 @@ class ModuleViewModel : ViewModel() {
|
||||
val enabled: Boolean,
|
||||
val update: Boolean,
|
||||
val remove: Boolean,
|
||||
val updateJson: String,
|
||||
)
|
||||
|
||||
data class ModuleUpdateInfo(
|
||||
val version: String,
|
||||
val versionCode: Int,
|
||||
val zipUrl: String,
|
||||
val changelog: String,
|
||||
)
|
||||
|
||||
var isRefreshing by mutableStateOf(false)
|
||||
@@ -48,6 +58,13 @@ class ModuleViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
var isNeedRefresh by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
fun markNeedRefresh() {
|
||||
isNeedRefresh = true
|
||||
}
|
||||
|
||||
fun fetchModuleList() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
isRefreshing = true
|
||||
@@ -70,16 +87,19 @@ class ModuleViewModel : ViewModel() {
|
||||
.map { obj ->
|
||||
ModuleInfo(
|
||||
obj.getString("id"),
|
||||
obj.getString("name"),
|
||||
|
||||
obj.optString("name"),
|
||||
obj.optString("author", "Unknown"),
|
||||
obj.optString("version", "Unknown"),
|
||||
obj.optInt("versionCode", 0),
|
||||
obj.getString("description"),
|
||||
obj.optString("description"),
|
||||
obj.getBoolean("enabled"),
|
||||
obj.getBoolean("update"),
|
||||
obj.getBoolean("remove"),
|
||||
obj.optString("updateJson")
|
||||
)
|
||||
}.toList()
|
||||
isNeedRefresh = false
|
||||
}.onFailure { e ->
|
||||
Log.e(TAG, "fetchModuleList: ", e)
|
||||
isRefreshing = false
|
||||
@@ -94,4 +114,57 @@ class ModuleViewModel : ViewModel() {
|
||||
Log.i(TAG, "load cost: ${SystemClock.elapsedRealtime() - start}, modules: $modules")
|
||||
}
|
||||
}
|
||||
|
||||
fun checkUpdate(m: ModuleInfo, callback: (String?) -> Unit) {
|
||||
if (m.updateJson.isEmpty() || m.remove || m.update || !m.enabled) {
|
||||
callback(null)
|
||||
return
|
||||
}
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
// download updateJson
|
||||
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()
|
||||
).execute()
|
||||
Log.d(TAG, "checkUpdate code: ${response.code}")
|
||||
if (response.isSuccessful) {
|
||||
response.body?.string() ?: ""
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}.getOrDefault("")
|
||||
Log.i(TAG, "checkUpdate result: $result")
|
||||
|
||||
if (result.isEmpty()) {
|
||||
callback(null)
|
||||
return@launch
|
||||
}
|
||||
|
||||
val updateJson = kotlin.runCatching {
|
||||
JSONObject(result)
|
||||
}.getOrNull()
|
||||
|
||||
if (updateJson == null) {
|
||||
callback(null)
|
||||
return@launch
|
||||
}
|
||||
|
||||
val version = updateJson.optString("version", "")
|
||||
val versionCode = updateJson.optInt("versionCode", 0)
|
||||
val zipUrl = updateJson.optString("zipUrl", "")
|
||||
val changelog = updateJson.optString("changelog", "")
|
||||
if (versionCode <= m.versionCode || zipUrl.isEmpty()) {
|
||||
callback(null)
|
||||
return@launch
|
||||
}
|
||||
|
||||
callback(zipUrl)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.content.ServiceConnection
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageInfo
|
||||
import android.os.IBinder
|
||||
import android.os.Parcelable
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
@@ -16,6 +17,7 @@ import androidx.lifecycle.ViewModel
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import me.weishu.kernelsu.IKsuInterface
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.ksuApp
|
||||
@@ -34,14 +36,32 @@ class SuperUserViewModel : ViewModel() {
|
||||
private var apps by mutableStateOf<List<AppInfo>>(emptyList())
|
||||
}
|
||||
|
||||
class AppInfo(
|
||||
@Parcelize
|
||||
data class AppInfo(
|
||||
val label: String,
|
||||
val packageName: String,
|
||||
val icon: PackageInfo,
|
||||
val uid: Int,
|
||||
val onAllowList: Boolean,
|
||||
val onDenyList: Boolean
|
||||
)
|
||||
val packageInfo: PackageInfo,
|
||||
val profile: Natives.Profile?,
|
||||
) : Parcelable {
|
||||
val packageName: String
|
||||
get() = packageInfo.packageName
|
||||
val uid: Int
|
||||
get() = packageInfo.applicationInfo.uid
|
||||
|
||||
val allowSu: Boolean
|
||||
get() = profile != null && profile.allowSu
|
||||
val hasCustomProfile: Boolean
|
||||
get() {
|
||||
if (profile == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
return if (profile.allowSu) {
|
||||
!profile.rootUseDefault
|
||||
} else {
|
||||
!profile.nonRootUseDefault
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var search by mutableStateOf("")
|
||||
var showSystemApps by mutableStateOf(false)
|
||||
@@ -51,8 +71,8 @@ class SuperUserViewModel : ViewModel() {
|
||||
private val sortedList by derivedStateOf {
|
||||
val comparator = compareBy<AppInfo> {
|
||||
when {
|
||||
it.onAllowList -> 0
|
||||
it.onDenyList -> 1
|
||||
it.allowSu -> 0
|
||||
it.hasCustomProfile -> 1
|
||||
else -> 2
|
||||
}
|
||||
}.then(compareBy(Collator.getInstance(Locale.getDefault()), AppInfo::label))
|
||||
@@ -67,7 +87,7 @@ class SuperUserViewModel : ViewModel() {
|
||||
.toPinyinString(it.label).contains(search)
|
||||
}.filter {
|
||||
it.uid == 2000 // Always show shell
|
||||
|| showSystemApps || it.icon.applicationInfo.flags.and(ApplicationInfo.FLAG_SYSTEM) == 0
|
||||
|| showSystemApps || it.packageInfo.applicationInfo.flags.and(ApplicationInfo.FLAG_SYSTEM) == 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,13 +127,9 @@ class SuperUserViewModel : ViewModel() {
|
||||
val result = connectKsuService {
|
||||
Log.w(TAG, "KsuService disconnected")
|
||||
}
|
||||
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
val pm = ksuApp.packageManager
|
||||
val allowList = Natives.getAllowList().toSet()
|
||||
val denyList = Natives.getDenyList().toSet()
|
||||
Log.i(TAG, "allowList: $allowList")
|
||||
Log.i(TAG, "denyList: $denyList")
|
||||
val start = SystemClock.elapsedRealtime()
|
||||
|
||||
val binder = result.first
|
||||
@@ -128,13 +144,11 @@ class SuperUserViewModel : ViewModel() {
|
||||
apps = packages.map {
|
||||
val appInfo = it.applicationInfo
|
||||
val uid = appInfo.uid
|
||||
val profile = Natives.getAppProfile(it.packageName, uid)
|
||||
AppInfo(
|
||||
label = appInfo.loadLabel(pm).toString(),
|
||||
packageName = it.packageName,
|
||||
icon = it,
|
||||
uid = uid,
|
||||
onAllowList = uid in allowList,
|
||||
onDenyList = uid in denyList
|
||||
packageInfo = it,
|
||||
profile = profile,
|
||||
)
|
||||
}.filter { it.packageName != ksuApp.packageName }
|
||||
Log.i(TAG, "load cost: ${SystemClock.elapsedRealtime() - start}")
|
||||
|
||||
@@ -1,68 +1,82 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">KernelSU</string>
|
||||
|
||||
<string name="home">الرئيسية</string>
|
||||
<string name="home_not_installed">غير مثبت</string>
|
||||
<string name="home_click_to_install">اضغط لتثبيت</string>
|
||||
<string name="home_click_to_install">إضغط للتثبيت</string>
|
||||
<string name="home_working">يعمل</string>
|
||||
<string name="home_working_version">الاصدار: %d</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_unsupported">غير مدعوم</string>
|
||||
<string name="home_unsupported_reason">KernelSU يدعم GKI kernels الان</string>
|
||||
<string name="home_copied_to_clipboard">نسخ الي حافظة</string>
|
||||
<string name="home_support">مدعوم</string>
|
||||
|
||||
<string name="home_kernel">نواه</string>
|
||||
<string name="home_arch">معمارية</string>
|
||||
<string name="home_manager_version">اصدار المدير</string>
|
||||
<string name="home_api">API Level</string>
|
||||
<string name="home_abi">ABI</string>
|
||||
<string name="home_fingerprint">بصمة</string>
|
||||
<string name="home_securitypatch">تصحيح الأمان</string>
|
||||
|
||||
<string name="home_unsupported_reason">KernelSU يدعم GKI kernels فقط</string>
|
||||
<string name="home_kernel">إصدار النواة</string>
|
||||
<string name="home_manager_version">إصدار المدير</string>
|
||||
<string name="home_fingerprint">البصمة</string>
|
||||
<string name="home_selinux_status">وضع SELinux</string>
|
||||
<string name="selinux_status_disabled">غير مفعل</string>
|
||||
<string name="selinux_status_enforcing">فرض</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="superuser_failed_to_grant_root">فشل منح الجذر لـ %d</string>
|
||||
<string name="superuser_allow_root_confirm">هل أنت متأكد من منح حق الوصول إلى الجذر إلى %s?</string>
|
||||
<string name="module_failed_to_enable">فشل في تمكين الوحدة النمطية: %s</string>
|
||||
<string name="module_failed_to_disable">فشل تعطيل الوحدة النمطية: %s</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="uninstall">الغاء التثبيت</string>
|
||||
<string name="module_install">تثبيت</string>
|
||||
<string name="uninstall">إلغاء التثبيت</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="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="require_kernel_version_8">يتطلب KernelSU اصدار 8+</string>
|
||||
<string name="module_uninstall_confirm">Are you sure you want to uninstall module %s?</string>
|
||||
<string name="module_uninstall_success">%s uninstalled</string>
|
||||
<string name="module_uninstall_failed">Failed to uninstall: %s</string>
|
||||
<string name="module_version">الاصدار</string>
|
||||
<string name="module_author">مؤلف</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_version">الإصدار</string>
|
||||
<string name="module_author">المطور</string>
|
||||
<string name="module_overlay_fs_not_available">التراكبات غير متوفرة ، لا يمكن للوحدة أن تعمل!</string>
|
||||
<string name="refresh">رفريش</string>
|
||||
<string name="show_system_apps">عرض تطبيقات النظام</string>
|
||||
<string name="hide_system_apps">اخفاء تطبيقات النظام</string>
|
||||
<string name="send_log">ارسال اللوج</string>
|
||||
<string name="safe_mode">وضع الامن</string>
|
||||
<string name="reboot_to_apply">اعادة تشغيل لتطبيق التغيرات</string>
|
||||
<string name="module_magisk_conflict">تم تعطيل الوحدات النمطية لأنها تتعارض مع Magisk\'s!</string>
|
||||
<string name="refresh">إنعاش</string>
|
||||
<string name="show_system_apps">إظهار تطبيقات النظام</string>
|
||||
<string name="hide_system_apps">إخفاء تطبيقات النظام</string>
|
||||
<string name="send_log">إرسال السجلات</string>
|
||||
<string name="safe_mode">الوضع الآمن</string>
|
||||
<string name="reboot_to_apply">إعادة التشغيل لتطبيق التغييرات</string>
|
||||
<string name="module_magisk_conflict">تم تعطيل الوحدات النمطية لأنها تتعارض مع Magisk!</string>
|
||||
<string name="home_learn_kernelsu">تعلم KernelSU</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">تعرف على كيفية تثبيت KernelSU واستخدام الوحدات النمطية</string>
|
||||
<string name="home_support_title">ادعمنا</string>
|
||||
<string name="home_support_content">KernelSU هو وسيظل دائمًا مجانيًا ومفتوح المصدر. ومع ذلك ، يمكنك أن تظهر لنا أنك تهتم بالتبرع.</string>
|
||||
<string name="about_source_code"><![CDATA[View source code at %1$s<br/>Join our %2$s channel]]></string>
|
||||
</resources>
|
||||
<string name="home_click_to_learn_kernelsu">تعرف على كيفية تثبيت KernelSU واستخدام الوحدات</string>
|
||||
<string name="home_support_title">إدعمنا</string>
|
||||
<string name="home_support_content">KernelSU سيظل دائماً مجانياً ومفتوح المصدر. مع ذلك، يمكنك أن تظهر لنا أنك تهتم بالتبرع.</string>
|
||||
<string name="about_source_code"><![CDATA[أنظر إلى مصدر البرمجة في %1$s<br/>إنضم إلى قناتنا في %2$s ]]></string>
|
||||
<string name="profile_capabilities">القدرات</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="profile_default">الإفتراضي</string>
|
||||
<string name="profile_template">نموذج</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_custom">مُخصّص</string>
|
||||
<string name="profile_namespace">تركيب مساحة الاسم</string>
|
||||
<string name="profile_umount_modules">الغاء تحميل الوحدات</string>
|
||||
<string name="failed_to_update_app_profile">فشل تحديث ملف تعريف التطبيق لـ %s</string>
|
||||
<string name="profile_selinux_context">سياق SELinux</string>
|
||||
<string name="force_stop_app">ايقاف إجباري</string>
|
||||
<string name="settings_umount_modules_default">الغاء تحميل الوحدات بشكل افتراضي</string>
|
||||
<string name="settings_umount_modules_default_summary">القيمة الافتراضية العامة ل \"إلغاء تحميل الوحدات \" في ملفات تعريف التطبيقات. إذا تم تمكينه ، إزالة جميع تعديلات الوحدة النمطية على النظام للتطبيقات التي لا تحتوي على مجموعة ملف تعريف.</string>
|
||||
<string name="profile_umount_modules_summary">سيسمح تمكين هذا الخيار ل KernelSU باستعادة أي ملفات معدلة بواسطة الوحدات النمطية لهذا التطبيق.</string>
|
||||
<string name="profile_selinux_domain">المجال</string>
|
||||
<string name="profile_selinux_rules">القواعد</string>
|
||||
<string name="restart_app">إعادة تشغيل التطبيق</string>
|
||||
<string name="failed_to_update_sepolicy">فشل تحديث قواعد SELinux لما يلي: %s</string>
|
||||
<string name="profile_name">اسم الملف الشخصي</string>
|
||||
<string name="require_kernel_version">إصدار KernelSU الحالي %d منخفض جدًا بحيث لا يعمل المدير بشكل صحيح. الرجاء الترقية إلى الإصدار %d أو أعلى!</string>
|
||||
</resources>
|
||||
81
manager/app/src/main/res/values-az/strings.xml
Normal file
81
manager/app/src/main/res/values-az/strings.xml
Normal file
@@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="home">Ana səhifə</string>
|
||||
<string name="home_superuser_count">Super istifadəçilər: %d</string>
|
||||
<string name="home_kernel">Nüvə</string>
|
||||
<string name="home_not_installed">Yüklənmədi</string>
|
||||
<string name="home_click_to_install">Yükləmək üçün toxunun</string>
|
||||
<string name="home_working">İşləyir</string>
|
||||
<string name="home_working_version">Versiya: %d</string>
|
||||
<string name="home_module_count">Modullar: %d</string>
|
||||
<string name="home_unsupported_reason">Hal-hazırda KernelSU yalnız GKI nüvələrini dəstəkləyir</string>
|
||||
<string name="home_unsupported">Dəstəklənmir</string>
|
||||
<string name="module_install">Yüklə</string>
|
||||
<string name="install">Yüklə</string>
|
||||
<string name="selinux_status_unknown">Naməlum</string>
|
||||
<string name="home_fingerprint">Barmaq izi</string>
|
||||
<string name="home_manager_version">Menecer versiyası</string>
|
||||
<string name="selinux_status_disabled">Qeyri-aktiv</string>
|
||||
<string name="home_selinux_status">SELinux vəziyyəti</string>
|
||||
<string name="selinux_status_permissive">Sərbəst</string>
|
||||
<string name="selinux_status_enforcing">Məcburi</string>
|
||||
<string name="superuser">Super istifadəçi</string>
|
||||
<string name="uninstall">Sil</string>
|
||||
<string name="module_failed_to_enable">Modulu aktiv etmək mümkün olmadı: %s</string>
|
||||
<string name="module_failed_to_disable">Modulu deaktiv etmək mümkün olmadı: %s</string>
|
||||
<string name="module_empty">Heç bir modul quraşdırılmayıb</string>
|
||||
<string name="module">Modul</string>
|
||||
<string name="reboot">Yenidən başlat</string>
|
||||
<string name="settings">Parametrlər</string>
|
||||
<string name="reboot_recovery">Bərpa rejimində yenidən başlat</string>
|
||||
<string name="reboot_userspace">Yüngül vəziyyətdə yenodən başlat</string>
|
||||
<string name="reboot_bootloader">Bootloader rejimində yenidən başlat</string>
|
||||
<string name="reboot_download">Yükləmə rejimində yenidən başlat</string>
|
||||
<string name="module_version">Versiya</string>
|
||||
<string name="module_author">Sahib</string>
|
||||
<string name="module_uninstall_confirm">Modulu silmək istədiyinizdən əminsiniz %s\?</string>
|
||||
<string name="show_system_apps">Sistem proqramlarını göstər</string>
|
||||
<string name="about">Haqqında</string>
|
||||
<string name="reboot_edl">EDL rejimində yenidən başlat</string>
|
||||
<string name="module_uninstall_failed">Silmək mümkün olmadı: %s</string>
|
||||
<string name="module_uninstall_success">%s silindi</string>
|
||||
<string name="hide_system_apps">Sistem proqramlarını gizlət</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs mövcud deyil,modul işləyə bilməyəcək!</string>
|
||||
<string name="send_log">Log-u göndər</string>
|
||||
<string name="refresh">Yenilə</string>
|
||||
<string name="safe_mode">Təhlükəsiz rejimi</string>
|
||||
<string name="reboot_to_apply">Qüvvəyə minməsi üçün yenidən başlat</string>
|
||||
<string name="module_magisk_conflict">Modular deaktiv edilir,çünki o Magisk-in modulları ilə toqquşur!</string>
|
||||
<string name="home_learn_kernelsu">KernelSU-yu öyrən</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_support_title">Bizi dəstəkləyin</string>
|
||||
<string name="home_click_to_learn_kernelsu">KernelSU-yu necə quraşdırılacağını və modulların necə istifadə ediləcəyini öyrən</string>
|
||||
<string name="profile_template">Şablon</string>
|
||||
<string name="profile_default">Defolt</string>
|
||||
<string name="profile_custom">Özəl</string>
|
||||
<string name="home_support_content">KernelSU pulsuz və açıq mənbəlidir,həmişə belə olacaqdır. Bununla belə, ianə etməklə bizə qayğı göstərdiyinizi göstərə bilərsiniz.</string>
|
||||
<string name="about_source_code">Mənbə kodlarımıza baxın %1$s<br/>Kanalımıza %2$s qoşulun</string>
|
||||
<string name="profile_name">Profil adı</string>
|
||||
<string name="profile_capabilities">Bacarıqlar</string>
|
||||
<string name="profile_umount_modules">Modulları umount et</string>
|
||||
<string name="profile_namespace_inherited">Miras qalmış</string>
|
||||
<string name="profile_namespace_global">Qlobal</string>
|
||||
<string name="profile_namespace">Bölmənin ad sahəsi</string>
|
||||
<string name="profile_namespace_individual">Fərdi</string>
|
||||
<string name="profile_groups">Qruplar</string>
|
||||
<string name="settings_umount_modules_default">Defolt olaraq modulları umount et</string>
|
||||
<string name="profile_selinux_context">SELinux konteksi</string>
|
||||
<string name="failed_to_update_app_profile">%s görə tətbiq profillərini güncəlləmək mümkün olmadı</string>
|
||||
<string name="settings_umount_modules_default_summary">Tətbiq Profillərində \"Umount modulları\" üçün qlobal standart dəyər. Aktivləşdirilərsə, o, Profil dəsti olmayan proqramlar üçün sistemdəki bütün modul dəyişikliklərini siləcək.</string>
|
||||
<string name="profile_selinux_domain">Domen</string>
|
||||
<string name="profile_selinux_rules">Qaydalar</string>
|
||||
<string name="module_update">Güncəllə</string>
|
||||
<string name="module_start_downloading">Endirməni başlat: %s</string>
|
||||
<string name="new_version_available">Yeni versiya: %s əlçatandır, endirmək üçün toxunun</string>
|
||||
<string name="module_downloading">Modul yüklənir: %s</string>
|
||||
<string name="profile_umount_modules_summary">Bu seçimi aktivləşdirmək KernelSU-ya bu proqram üçün modullar tərəfindən hər hansı dəyişdirilmiş faylları bərpa etməyə imkan verəcək.</string>
|
||||
<string name="launch_app">Aç</string>
|
||||
<string name="force_stop_app">Məcburi dayandır</string>
|
||||
<string name="restart_app">Yenidən başlat</string>
|
||||
<string name="failed_to_update_sepolicy">%s görə SELinux qaydalarını güncəlləmək mümkün olmadı</string>
|
||||
</resources>
|
||||
51
manager/app/src/main/res/values-bn-rBD/strings.xml
Normal file
51
manager/app/src/main/res/values-bn-rBD/strings.xml
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="home_unsupported_reason">কর্নেল এস ইউ কেবল মাত্র জিকআই কর্নেল সাপোর্ট করে</string>
|
||||
<string name="home_selinux_status">এসইলিনাক্স স্টেটাস</string>
|
||||
<string name="selinux_status_unknown">আননোন</string>
|
||||
<string name="module_failed_to_enable">মোডিউল ইনেবল করা যায়নি: %s</string>
|
||||
<string name="home_click_to_install">ইন্সটল করটে চাপুন</string>
|
||||
<string name="home_working">কাজ করছে</string>
|
||||
<string name="home_module_count">মোডিউল: %d</string>
|
||||
<string name="home_unsupported">অমূলক</string>
|
||||
<string name="home_kernel">কর্নেল</string>
|
||||
<string name="home_manager_version">ম্যানেজার ভারসন</string>
|
||||
<string name="home_fingerprint">ফিঙ্গারপ্রিন্ট</string>
|
||||
<string name="selinux_status_disabled">ডিসেবল</string>
|
||||
<string name="selinux_status_enforcing">এনফোর্সিং</string>
|
||||
<string name="superuser">সুপার ইউজার</string>
|
||||
<string name="module">মোডিউল</string>
|
||||
<string name="uninstall">আনইন্সটল</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="profile_namespace_global">গ্লোবাল</string>
|
||||
<string name="profile_groups">গ্রুপস</string>
|
||||
<string name="profile_selinux_context">এসইলিনাক্স কন্টেক্সট</string>
|
||||
<string name="failed_to_update_app_profile">%s এর জন্য অ্যাপ প্রফাইল আপডেট করা যায়নি</string>
|
||||
<string name="settings_umount_modules_default">বাইডিফল্ট মোডিউল আনমাউন্ট</string>
|
||||
<string name="home">হোম</string>
|
||||
<string name="home_not_installed">ইন্সটল হয়নী</string>
|
||||
<string name="selinux_status_permissive">পারমিসিভ</string>
|
||||
<string name="module_failed_to_disable">মোডিউল ডিসেবল করা যায়নি: %s</string>
|
||||
<string name="module_empty">কোনো মোডিউল ইন্সটল করা নেই</string>
|
||||
<string name="home_working_version">ভারসন: %d</string>
|
||||
<string name="home_superuser_count">সুপার ইউজার: %d</string>
|
||||
<string name="profile_namespace">নেইম স্পেস মাউন্ট</string>
|
||||
<string name="profile_namespace_inherited">ইনহেরিটেড</string>
|
||||
<string name="profile_namespace_individual">ইন্ডিভিজুয়াল</string>
|
||||
<string name="profile_capabilities">ক্যাপাবিলিটিস</string>
|
||||
<string name="profile_umount_modules">আনমাউন্ট মোডিউলস</string>
|
||||
<string name="reboot_recovery">রিকভারিতে বুট</string>
|
||||
<string name="reboot_bootloader">বুটলোডারে বুট</string>
|
||||
<string name="reboot_download">ডাউনলোড মডে বুট</string>
|
||||
<string name="reboot_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_version">ভার্সন</string>
|
||||
<string name="module_author">অথার</string>
|
||||
</resources>
|
||||
@@ -1,7 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">কার্নেলএসইউ</string>
|
||||
|
||||
<string name="home">হোম</string>
|
||||
<string name="home_not_installed">ইনস্টল করা হয়নি</string>
|
||||
<string name="home_click_to_install">ইনস্টল করার জন্য ক্লিক করুন</string>
|
||||
@@ -11,16 +9,10 @@
|
||||
<string name="home_module_count">মডিউল: %d</string>
|
||||
<string name="home_unsupported">অসমর্থিত</string>
|
||||
<string name="home_unsupported_reason">কার্নেলএসইউ শুধুমাত্র জিকেআই কার্নেল সমর্থন করে</string>
|
||||
<string name="home_copied_to_clipboard">ক্লিপবোর্ডে কপি করা হয়েছে</string>
|
||||
<string name="home_support">সমর্থন</string>
|
||||
|
||||
<string name="home_kernel">কার্নেল</string>
|
||||
<string name="home_arch">খিলান</string>
|
||||
<string name="home_manager_version">ম্যানেজার সংস্করণ</string>
|
||||
<string name="home_api">এপিআই স্তর</string>
|
||||
<string name="home_abi">এবিআই</string>
|
||||
<string name="home_fingerprint">ফিঙ্গারপ্রিন্ট</string>
|
||||
<string name="home_securitypatch">সিকিউরিটি প্যাচ</string>
|
||||
|
||||
<string name="home_selinux_status">সেলিনাক্স স্ট্যাটাস</string>
|
||||
<string name="selinux_status_disabled">ডিজেবল</string>
|
||||
@@ -28,8 +20,6 @@
|
||||
<string name="selinux_status_permissive">অনুমতিমূলক</string>
|
||||
<string name="selinux_status_unknown">অপরিচিত</string>
|
||||
<string name="superuser">সুপার ইউজার</string>
|
||||
<string name="superuser_failed_to_grant_root">এর জন্য রুট প্রদান করতে ব্যর্থ হয়েছে %d</string>
|
||||
<string name="superuser_allow_root_confirm">নিশ্চিত আপনি রুট দিতে চান %s?</string>
|
||||
<string name="module_failed_to_enable">মডিউল সক্ষম করতে ব্যর্থ হয়েছে: %s</string>
|
||||
<string name="module_failed_to_disable">মডিউল নিষ্ক্রিয় করতে ব্যর্থ হয়েছে: %s</string>
|
||||
<string name="module_empty">কোন মডিউল ইনস্টল করা নেই</string>
|
||||
@@ -46,7 +36,6 @@
|
||||
<string name="reboot_download">রিবুট ডাউনলোড</string>
|
||||
<string name="reboot_edl">রিবুট ইডিএল</string>
|
||||
<string name="about">এবাউট</string>
|
||||
<string name="require_kernel_version_8">কার্নেলএসইউ সংস্করণ প্রয়োজন ৮+</string>
|
||||
<string name="module_uninstall_confirm">মডিউল আনইনস্টল নিশ্চিত করুন %s?</string>
|
||||
<string name="module_uninstall_success">%s আনইনস্টল সফল</string>
|
||||
<string name="module_uninstall_failed">আনইন্সটল ব্যর্থ: %s</string>
|
||||
|
||||
82
manager/app/src/main/res/values-da/strings.xml
Normal file
82
manager/app/src/main/res/values-da/strings.xml
Normal file
@@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="home_working">Arbejder</string>
|
||||
<string name="home_module_count">Moduler: %d</string>
|
||||
<string name="home_unsupported">Ikke understøttet</string>
|
||||
<string name="home_kernel">Kernel</string>
|
||||
<string name="home_unsupported_reason">KernelSU understøtter kun GKI kernels</string>
|
||||
<string name="home_manager_version">Manager Version</string>
|
||||
<string name="home_selinux_status">SELinux-status</string>
|
||||
<string name="selinux_status_disabled">Deaktiveret</string>
|
||||
<string name="selinux_status_permissive">Tilladende</string>
|
||||
<string name="superuser">Superbruger</string>
|
||||
<string name="selinux_status_enforcing">Håndhævende</string>
|
||||
<string name="module_failed_to_disable">Deaktivering af modul fejlede: %s</string>
|
||||
<string name="module_empty">Intet modul installeret</string>
|
||||
<string name="uninstall">Afinstaller</string>
|
||||
<string name="module_install">Installer</string>
|
||||
<string name="install">Installer</string>
|
||||
<string name="reboot">Genstart</string>
|
||||
<string name="settings">Indstillinger</string>
|
||||
<string name="reboot_userspace">Blød Genstart</string>
|
||||
<string name="reboot_download">Genstart til Download</string>
|
||||
<string name="reboot_edl">Genstart til EDL</string>
|
||||
<string name="about">Om</string>
|
||||
<string name="module_uninstall_confirm">Er du sikker på, at du vil afinstallere modulet %s\?</string>
|
||||
<string name="module_uninstall_success">%s afinstalleret</string>
|
||||
<string name="module_uninstall_failed">Afinstallation af: %s fejlede</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs er ikke tilgængeligt, modulet kan ikke fungere!</string>
|
||||
<string name="refresh">Opdater</string>
|
||||
<string name="send_log">Send Log</string>
|
||||
<string name="safe_mode">Sikker tilstand</string>
|
||||
<string name="reboot_to_apply">Genstart for at tage effekt</string>
|
||||
<string name="home_learn_kernelsu">Lær KernelSU</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">Lær hvordan man installerer KernelSU og moduler</string>
|
||||
<string name="about_source_code">Se source koden ved %1$s<br/>Deltage i vores %2$s kanal</string>
|
||||
<string name="profile_default">Standard</string>
|
||||
<string name="profile_template">Skabelon</string>
|
||||
<string name="profile_namespace">Monter navnerum</string>
|
||||
<string name="profile_namespace_inherited">Arvet</string>
|
||||
<string name="profile_namespace_global">Global</string>
|
||||
<string name="profile_groups">Grupper</string>
|
||||
<string name="profile_capabilities">Evner</string>
|
||||
<string name="profile_selinux_context">SELinux-kontext</string>
|
||||
<string name="profile_umount_modules">Afmonteret moduler</string>
|
||||
<string name="settings_umount_modules_default">Afmontere moduler som standard</string>
|
||||
<string name="profile_umount_modules_summary">Aktivering af denne indstilling vil tillade KernelSU at gendanne hvilken som helst modificeret filer af modulet for denne applikation.</string>
|
||||
<string name="module_update">Opdatering</string>
|
||||
<string name="module_downloading">Downloader modulet: %s</string>
|
||||
<string name="new_version_available">Ny version: %s er tilgængelig, kilk for at downloade</string>
|
||||
<string name="launch_app">Start</string>
|
||||
<string name="force_stop_app">Tving Stop</string>
|
||||
<string name="failed_to_update_sepolicy">Opdatering af SELinux-regler for: %s fejlede</string>
|
||||
<string name="module_start_downloading">Start download: %s</string>
|
||||
<string name="home_click_to_install">Klik for at installere</string>
|
||||
<string name="home_working_version">Version: %d</string>
|
||||
<string name="home">Hjem</string>
|
||||
<string name="home_not_installed">Ikke installeret</string>
|
||||
<string name="home_superuser_count">Superbrugere: %d</string>
|
||||
<string name="home_fingerprint">Fingeraftryk</string>
|
||||
<string name="selinux_status_unknown">Ukendt</string>
|
||||
<string name="module_failed_to_enable">Aktivering af modul fejlede: %s</string>
|
||||
<string name="reboot_recovery">Genstart til Recovery</string>
|
||||
<string name="module">Modul</string>
|
||||
<string name="module_author">Forfatter</string>
|
||||
<string name="reboot_bootloader">Genstart til Bootloader</string>
|
||||
<string name="module_version">Version</string>
|
||||
<string name="hide_system_apps">Gem system-apps</string>
|
||||
<string name="show_system_apps">Vis system-apps</string>
|
||||
<string name="module_magisk_conflict">Moduler er deaktiveret, fordi der er konflikt med Magiskes!</string>
|
||||
<string name="home_support_title">Støt Os</string>
|
||||
<string name="home_support_content">KernelSU er, og vil altid være gratis og open source. Du kan stadig vise os din støtte ved at donere.</string>
|
||||
<string name="profile_custom">Brugerdefineret</string>
|
||||
<string name="profile_name">Profilnavn</string>
|
||||
<string name="profile_namespace_individual">Individuel</string>
|
||||
<string name="failed_to_update_app_profile">Opdatering af App Profil for %s fejlede</string>
|
||||
<string name="settings_umount_modules_default_summary">Den globale standard værdi for \"Afmonter moduler\" i App Profiler. Hvis aktiveret vil den fjerne alle modulers modifikationer til system applikationerne der ikke har en sat Profil.</string>
|
||||
<string name="profile_selinux_domain">Domæne</string>
|
||||
<string name="profile_selinux_rules">Regler</string>
|
||||
<string name="restart_app">Genstart</string>
|
||||
<string name="require_kernel_version">Den nuværende KernelSU version %d er for lav til manageren for at fungere ordentligt. Opgrader til version %d eller højere!</string>
|
||||
</resources>
|
||||
81
manager/app/src/main/res/values-de/strings.xml
Normal file
81
manager/app/src/main/res/values-de/strings.xml
Normal file
@@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="home">Startseite</string>
|
||||
<string name="home_not_installed">Nicht installiert</string>
|
||||
<string name="selinux_status_permissive">Permissiv</string>
|
||||
<string name="home_working">Funktioniert</string>
|
||||
<string name="home_working_version">Version: %d</string>
|
||||
<string name="superuser">Superuser</string>
|
||||
<string name="home_click_to_install">Klicken Sie zum Installieren</string>
|
||||
<string name="home_superuser_count">Superusers: %d</string>
|
||||
<string name="selinux_status_unknown">Unbekannt</string>
|
||||
<string name="selinux_status_enforcing">erzwingen</string>
|
||||
<string name="reboot_bootloader">Zum Bootloader-Modus neustarten</string>
|
||||
<string name="reboot_download">Zum Download-Modus neustarten</string>
|
||||
<string name="reboot_edl">Zum EDL-Modus neustarten</string>
|
||||
<string name="module_author">Autor</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs ist nicht verfügbar, Modul kann nicht funktionieren!</string>
|
||||
<string name="about">Über</string>
|
||||
<string name="module_magisk_conflict">Module sind deaktiviert, weil es einen Konflikt mit Magisks gibt!</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">Erfahren Sie, wie Sie KernelSU installieren und Module verwenden</string>
|
||||
<string name="home_support_title">Unterstütze uns</string>
|
||||
<string name="home_support_content">"KernelSU ist und bleibt kostenlos und Open Source. Sie können uns jedoch zeigen, dass Sie sich für uns interessieren, indem Sie eine Spende tätigen."</string>
|
||||
<string name="profile_selinux_context">SELinux-Kontext</string>
|
||||
<string name="settings_umount_modules_default">Demontiere Module als Standard</string>
|
||||
<string name="settings_umount_modules_default_summary">Der globale Standard-Wert für „Demontiere Module“ in App-Profilen. Falls aktiviert, werden alle Modul-Modifikationen zu dem System deaktiviert; für Applikationen, welche kein Profil gesetzt haben.</string>
|
||||
<string name="profile_default">Standard</string>
|
||||
<string name="profile_template">Vorlage</string>
|
||||
<string name="profile_custom">Benutzerdefiniert</string>
|
||||
<string name="failed_to_update_app_profile">Aktualisierung des App-Profils fehlgeschlagen für %s</string>
|
||||
<string name="profile_namespace_inherited">Übernommen</string>
|
||||
<string name="profile_namespace_global">Global</string>
|
||||
<string name="profile_namespace_individual">Individuell</string>
|
||||
<string name="profile_selinux_domain">Domain</string>
|
||||
<string name="module_update">Aktualisierung</string>
|
||||
<string name="profile_umount_modules_summary">Aktivierung dieser Option erlaubt KernelSU, alle modifizierten Dateien von den Modulen dieser Applikation wiederherstellen.</string>
|
||||
<string name="profile_selinux_rules">Regeln</string>
|
||||
<string name="module_start_downloading">Starte herunterladen: %s</string>
|
||||
<string name="failed_to_update_sepolicy">Aktualisierung der SELinux-Regeln fehlgeschlagen für: %s</string>
|
||||
<string name="launch_app">Start</string>
|
||||
<string name="new_version_available">Neue Version: %s ist verfügbar, klicke zum herunterladen</string>
|
||||
<string name="force_stop_app">Erzwinge Stopp</string>
|
||||
<string name="restart_app">Neustart</string>
|
||||
<string name="home_module_count">Module: %d</string>
|
||||
<string name="home_manager_version">Verwalter-Version</string>
|
||||
<string name="home_selinux_status">SELinux-Status</string>
|
||||
<string name="selinux_status_disabled">Deaktiviert</string>
|
||||
<string name="module_failed_to_enable">Modulaktivierung fehlgeschlagen: %s</string>
|
||||
<string name="module_failed_to_disable">Moduldeaktivierung fehlgeschlagen: %s</string>
|
||||
<string name="module_empty">Kein Modul installiert</string>
|
||||
<string name="module">Modul</string>
|
||||
<string name="uninstall">Deinstallieren</string>
|
||||
<string name="install">Installieren</string>
|
||||
<string name="reboot">Neustart</string>
|
||||
<string name="settings">Einstellungen</string>
|
||||
<string name="reboot_recovery">Neustart zur Recovery</string>
|
||||
<string name="module_uninstall_success">%s deinstalliert</string>
|
||||
<string name="module_version">Version</string>
|
||||
<string name="refresh">Neu laden</string>
|
||||
<string name="show_system_apps">Zeige System-Apps</string>
|
||||
<string name="hide_system_apps">Verstecke System-Apps</string>
|
||||
<string name="send_log">Sende Verlauf</string>
|
||||
<string name="home_learn_kernelsu">Lerne KernelSU</string>
|
||||
<string name="safe_mode">Sicherer Modus</string>
|
||||
<string name="reboot_to_apply">Starte neu, damit die Effekte auftreten</string>
|
||||
<string name="about_source_code">Sehe den Quellcode bei %1$s<br/>Trete unserem %2$s Kanal bei</string>
|
||||
<string name="profile_name">Profilname</string>
|
||||
<string name="profile_namespace">Montiere Namensraum</string>
|
||||
<string name="profile_groups">Gruppen</string>
|
||||
<string name="profile_capabilities">Fähigkeiten</string>
|
||||
<string name="profile_umount_modules">Demontiere Module</string>
|
||||
<string name="module_downloading">Lädt Modul herunter: %s</string>
|
||||
<string name="home_unsupported">Nicht unterstützt</string>
|
||||
<string name="home_unsupported_reason">KernelSU unterstützt nun nur GKI kernels</string>
|
||||
<string name="home_kernel">Kernel</string>
|
||||
<string name="home_fingerprint">Fingerabdruck</string>
|
||||
<string name="module_install">Installieren</string>
|
||||
<string name="reboot_userspace">Leichter Neustart</string>
|
||||
<string name="module_uninstall_confirm">Bist du dir sicher, dass du das Modul %s deinstallieren möchtest\?</string>
|
||||
<string name="module_uninstall_failed">Deinstallation fehlgeschlagen: %s</string>
|
||||
</resources>
|
||||
82
manager/app/src/main/res/values-es/strings.xml
Normal file
82
manager/app/src/main/res/values-es/strings.xml
Normal file
@@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="home">Inicio</string>
|
||||
<string name="home_not_installed">No instalado</string>
|
||||
<string name="home_click_to_install">Instalar</string>
|
||||
<string name="home_working">Activo</string>
|
||||
<string name="home_working_version">Versión: %d</string>
|
||||
<string name="home_superuser_count">Superusuarios: %d</string>
|
||||
<string name="home_module_count">Módulos: %d</string>
|
||||
<string name="home_unsupported">No soportado</string>
|
||||
<string name="home_unsupported_reason">Por el momento, KernelSU solo es compatible con kernels genéricos (GKIs)</string>
|
||||
<string name="home_kernel">Versión del kernel</string>
|
||||
<string name="home_manager_version">Versión del manager</string>
|
||||
<string name="home_fingerprint">Huella del dispositivo</string>
|
||||
<string name="home_selinux_status">Estado de SELinux</string>
|
||||
<!-- It may be better to leave SELinux statuses untranslated -->
|
||||
<string name="selinux_status_disabled">Disabled</string>
|
||||
<string name="selinux_status_enforcing">Enforcing</string>
|
||||
<string name="selinux_status_permissive">Permissive</string>
|
||||
<string name="selinux_status_unknown">Unknown</string>
|
||||
<string name="superuser">Superusuario</string>
|
||||
<string name="module_failed_to_enable">No se pudo habilitar el módulo \"%s\"</string>
|
||||
<string name="module_failed_to_disable">No se pudo deshabilitar el módulo \"%s\"</string>
|
||||
<string name="module_empty">No hay ningún módulo instalado</string>
|
||||
<string name="module">Módulo</string>
|
||||
<string name="uninstall">Desinstalar</string>
|
||||
<string name="module_install">Instalar módulo</string>
|
||||
<string name="install">Instalar</string>
|
||||
<string name="reboot">Reiniciar</string>
|
||||
<string name="settings">Ajustes</string>
|
||||
<string name="reboot_userspace">Reinicio suave</string>
|
||||
<string name="reboot_recovery">Reiniciar en modo recovery</string>
|
||||
<string name="reboot_bootloader">Reiniciar en modo bootloader</string>
|
||||
<string name="reboot_download">Reiniciar en modo download</string>
|
||||
<string name="reboot_edl">Reiniciar en modo EDL</string>
|
||||
<string name="about">Acerca de</string>
|
||||
<string name="module_uninstall_confirm">¿Estás seguro de que quieres desinstalar el módulo \"%s\"?</string>
|
||||
<string name="module_uninstall_success">\"%s\" desinstalado.</string>
|
||||
<string name="module_uninstall_failed">No se pudo desinstalar \"%s\".</string>
|
||||
<string name="module_version">Versión</string>
|
||||
<string name="module_author">Autor</string>
|
||||
<string name="module_overlay_fs_not_available">El módulo no puede funcionar ya que OverlayFS no está disponible</string>
|
||||
<string name="refresh">Recargar</string>
|
||||
<string name="show_system_apps">Mostrar apps del sistema</string>
|
||||
<string name="hide_system_apps">Ocultar apps del sistema</string>
|
||||
<string name="send_log">Enviar registro</string>
|
||||
<string name="safe_mode">Modo seguro</string>
|
||||
<string name="reboot_to_apply">Reiniciar para aplicar cambios</string>
|
||||
<string name="module_magisk_conflict">Se deshabilitaron los módulos ya que entran en conflicto con Magisk.</string>
|
||||
<string name="home_learn_kernelsu">Descubre KernelSU</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">Descubre cómo instalar KernelSU y utilizar módulos</string>
|
||||
<string name="home_support_title">Apóyanos</string>
|
||||
<string name="home_support_content">KernelSU es y siempre será, libre y de código abierto. De todas formas, puedes mostrarnos tu apoyo mediante una donación.</string>
|
||||
<string name="about_source_code"><![CDATA[Ver código fuente en %1$s<br/>Únete a nuestro canal de %2$s]]></string>
|
||||
<string name="profile_default">Predeterminado</string>
|
||||
<string name="profile_template">Plantilla</string>
|
||||
<string name="profile_custom">Personalizado</string>
|
||||
<string name="profile_name">Nombre de perfil</string>
|
||||
<string name="profile_namespace">Modo de montaje del espacio de nombres</string>
|
||||
<string name="profile_namespace_inherited">Heredado</string>
|
||||
<string name="profile_namespace_global">Global</string>
|
||||
<string name="profile_namespace_individual">Individual</string>
|
||||
<string name="profile_groups">Grupos</string>
|
||||
<string name="profile_capabilities">Capacidades</string>
|
||||
<string name="profile_selinux_context">Contexto de SELinux</string>
|
||||
<string name="profile_umount_modules">Desmontar módulos</string>
|
||||
<string name="failed_to_update_app_profile">No se pudo actualizar el perfil de la app para %s</string>
|
||||
<string name="settings_umount_modules_default">Desmontar módulos por defecto</string>
|
||||
<string name="settings_umount_modules_default_summary">El valor global predeterminado para \"Desmontar módulos\" en los perfiles de las aplicaciones. Si la habilitas, se desharán todas las modificaciones al sistema hechas por el módulo para las apps que no tengan un perfil establecido.</string>
|
||||
<string name="profile_umount_modules_summary">Si habilitas esta opción, KernelSU podrá restaurar cualquier archivo modificado por los módulos para esta app.</string>
|
||||
<string name="profile_selinux_domain">Dominio</string>
|
||||
<string name="profile_selinux_rules">Reglas</string>
|
||||
<string name="module_update">Actualizar</string>
|
||||
<string name="module_downloading">Descargando módulo: \"%s\"</string>
|
||||
<string name="module_start_downloading">Descargar</string>
|
||||
<string name="new_version_available">Hay una nueva versión disponible (%s). Toca para descargar</string>
|
||||
<string name="launch_app">Lanzar Aplicacion</string>
|
||||
<string name="force_stop_app">Forzar cierre de la aplicacion</string>
|
||||
<string name="restart_app">Reiniciar aplicacion</string>
|
||||
<string name="failed_to_update_sepolicy">Falló al actualizar reglas de SEpolicy por: %s</string>
|
||||
</resources>
|
||||
66
manager/app/src/main/res/values-fa/strings.xml
Normal file
66
manager/app/src/main/res/values-fa/strings.xml
Normal file
@@ -0,0 +1,66 @@
|
||||
<resources>
|
||||
<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_unsupported">پشتیبانی نشده</string>
|
||||
<string name="home_unsupported_reason">کرنل اس یو فقط هسته های gki را پشتیبانی میکند</string>
|
||||
<string name="home_kernel">هسته</string>
|
||||
<string name="home_manager_version">نسخه برنامه</string>
|
||||
<string name="home_fingerprint">اثرانگشت</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="uninstall">لغو نصب</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_version">نسخه</string>
|
||||
<string name="module_author">سازنده</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="send_log">ارسال وقایع</string>
|
||||
<string name="safe_mode">حالت امن</string>
|
||||
<string name="reboot_to_apply">راهاندازی مجدد برای تاثیرگذاری</string>
|
||||
<string name="module_magisk_conflict">مازول به دلیل تعارض با مجیسک غیرفعال شده اند\'s!</string>
|
||||
<string name="home_learn_kernelsu">یادگیری کرنل اس یو</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">یاد بگیرید چگونه از کرنل اس یو و ماژول ها استفاده کنید</string>
|
||||
<string name="home_support_title">از ما حمایت کنید</string>
|
||||
<string name="home_support_content">KernelSU رایگان است و همیشه خواهد بود و منبع باز است. با این حال، می توانید با اهدای کمک مالی به ما نشان دهید که برایتان مهم است.</string>
|
||||
<string name="about_source_code">
|
||||
<![CDATA[ View source code at %1$s<br/>Join our %2$s channel ]]>
|
||||
</string>
|
||||
<string name="profile">پروفایل برنامه</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">Mount namespace</string>
|
||||
<string name="profile_namespace_inherited">اثر گرفته</string>
|
||||
<string name="profile_namespace_global">گلوبال</string>
|
||||
<string name="profile_namespace_individual">تکی</string>
|
||||
<string name="profile_umount_modules">جداکردن ماژول ها</string>
|
||||
</resources>
|
||||
81
manager/app/src/main/res/values-fr/strings.xml
Normal file
81
manager/app/src/main/res/values-fr/strings.xml
Normal file
@@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="home_not_installed">Non installé</string>
|
||||
<string name="home_working">Fonctionnel</string>
|
||||
<string name="home_working_version">Version : %d</string>
|
||||
<string name="home_superuser_count">Superutilisateurs : %d</string>
|
||||
<string name="home_module_count">Modules : %d</string>
|
||||
<string name="home_unsupported_reason">Actuellement, KernelSU ne supporte que les noyaux GKI.</string>
|
||||
<string name="home_kernel">Noyau</string>
|
||||
<string name="home_fingerprint">Fingerprint</string>
|
||||
<string name="home_selinux_status">Statut de SELinux</string>
|
||||
<string name="selinux_status_disabled">Désactivé</string>
|
||||
<string name="selinux_status_permissive">Permissive</string>
|
||||
<string name="selinux_status_unknown">Inconnu</string>
|
||||
<string name="superuser">Superutilisateur</string>
|
||||
<string name="module_empty">Aucun module installé</string>
|
||||
<string name="home">Accueil</string>
|
||||
<string name="home_click_to_install">Cliquez ici pour installer</string>
|
||||
<string name="home_unsupported">Non supporté</string>
|
||||
<string name="module_uninstall_failed">Échec de la désinstallation : %s</string>
|
||||
<string name="module_version">Version</string>
|
||||
<string name="home_manager_version">Version du gestionnaire</string>
|
||||
<string name="selinux_status_enforcing">Enforcing</string>
|
||||
<string name="module_failed_to_enable">Échec de l\'activation du module : %s</string>
|
||||
<string name="module">Module</string>
|
||||
<string name="uninstall">Désinstaller</string>
|
||||
<string name="module_install">Installer</string>
|
||||
<string name="module_failed_to_disable">Échec de la désactivation du module : %s</string>
|
||||
<string name="reboot">Redémarrer</string>
|
||||
<string name="install">Installer</string>
|
||||
<string name="settings">Paramètres</string>
|
||||
<string name="reboot_bootloader">Redémarrer vers le bootloader</string>
|
||||
<string name="reboot_userspace">Redémarrage logiciel</string>
|
||||
<string name="reboot_recovery">Redémarrer en mode récupération</string>
|
||||
<string name="reboot_edl">Redémarrer en mode EDL</string>
|
||||
<string name="about">À propos</string>
|
||||
<string name="module_uninstall_success">%s désinstallé</string>
|
||||
<string name="reboot_download">Redémarrer en mode téléchargement</string>
|
||||
<string name="module_author">Auteur</string>
|
||||
<string name="module_uninstall_confirm">Êtes-vous sûr(e) de vouloir désinstaller le module %s \?</string>
|
||||
<string name="home_learn_kernelsu">Découvrir KernelSU</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs n\'est pas disponible, impossible de faire fonctionner le module !</string>
|
||||
<string name="refresh">Rafraîchir</string>
|
||||
<string name="show_system_apps">Afficher les applications système</string>
|
||||
<string name="hide_system_apps">Masquer les applications système</string>
|
||||
<string name="safe_mode">Mode sécurisé</string>
|
||||
<string name="send_log">Envoyer les logs</string>
|
||||
<string name="reboot_to_apply">Redémarrez pour appliquer les modifications</string>
|
||||
<string name="module_magisk_conflict">Les modules sont désactivés car ils sont en conflit avec ceux de Magisk !</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_support_title">Soutenez-nous</string>
|
||||
<string name="home_click_to_learn_kernelsu">Découvrez comment installer KernelSU et utiliser les modules.</string>
|
||||
<string name="home_support_content">KernelSU est gratuit et open-source et le restera toujours. Vous pouvez néanmoins montrer votre soutien en faisant un don.</string>
|
||||
<string name="about_source_code">Voir le code source sur %1$s<br/>Rejoindre notre chaîne %2$s</string>
|
||||
<string name="profile_template">Modèle</string>
|
||||
<string name="profile_default">Par défaut</string>
|
||||
<string name="profile_custom">Personnalisé</string>
|
||||
<string name="profile_name">Nom du profil</string>
|
||||
<string name="profile_namespace">Espace de noms de montage</string>
|
||||
<string name="profile_namespace_inherited">Hérité</string>
|
||||
<string name="profile_namespace_individual">Individuel</string>
|
||||
<string name="profile_selinux_context">Contexte SELinux</string>
|
||||
<string name="profile_namespace_global">Global</string>
|
||||
<string name="profile_groups">Groupes</string>
|
||||
<string name="profile_capabilities">Capabilities</string>
|
||||
<string name="profile_umount_modules">Démonter les modules</string>
|
||||
<string name="failed_to_update_app_profile">Échec de la modification du profil d\'application de %s</string>
|
||||
<string name="profile_umount_modules_summary">L\'activation de cette option permettra à KernelSU de restaurer tous les fichiers modifiés par les modules pour cette application.</string>
|
||||
<string name="settings_umount_modules_default">Démonter par défaut les modules</string>
|
||||
<string name="settings_umount_modules_default_summary">Valeur globale par défaut de « Démonter les modules » dans les App Profiles. Si l\'option est activée, toutes les modifications des modules apportées au système seront enlevées pour les applications qui n\'ont pas de profil défini.</string>
|
||||
<string name="profile_selinux_domain">Domaine</string>
|
||||
<string name="profile_selinux_rules">Règles</string>
|
||||
<string name="module_update">Mettre à jour</string>
|
||||
<string name="module_downloading">Téléchargement du module : %s</string>
|
||||
<string name="launch_app">Lancer</string>
|
||||
<string name="new_version_available">Nouvelle version : la %s est disponible, cliquez ici pour la télécharger</string>
|
||||
<string name="module_start_downloading">Début du téléchargement de : %s</string>
|
||||
<string name="force_stop_app">Forcer l\'arrêt</string>
|
||||
<string name="restart_app">Relancer l\'application</string>
|
||||
<string name="failed_to_update_sepolicy">Échec de la mise à jour des règles SELinux pour : %s</string>
|
||||
</resources>
|
||||
@@ -1,64 +1,82 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="home">Beranda</string>
|
||||
<string name="home_not_installed">Tidak terinstall</string>
|
||||
<string name="home_click_to_install">Klik untuk menginstall</string>
|
||||
<string name="home_not_installed">Tidak terpasang</string>
|
||||
<string name="home_click_to_install">Klik untuk memasang</string>
|
||||
<string name="home_working">Bekerja</string>
|
||||
<string name="home_working_version">Versi: %d</string>
|
||||
<string name="home_superuser_count">Superuser: %d</string>
|
||||
<string name="home_module_count">Module: %d</string>
|
||||
<string name="home_superuser_count">Superusers: %d</string>
|
||||
<string name="home_module_count">Modul: %d</string>
|
||||
<string name="home_unsupported">Tidak didukung</string>
|
||||
<string name="home_unsupported_reason">Saat ini kernelSu hanya mendukung GKI kernel</string>
|
||||
<string name="home_copied_to_clipboard">Salin ke clipboard</string>
|
||||
<string name="home_support">Dukungan</string>
|
||||
<string name="home_unsupported_reason">KernelSU hanya mendukung kernel GKI saat ini</string>
|
||||
<string name="home_kernel">Kernel</string>
|
||||
<string name="home_arch">Arch</string>
|
||||
<string name="home_manager_version">Versi manager</string>
|
||||
<string name="home_api">API Level</string>
|
||||
<string name="home_abi">ABI</string>
|
||||
<string name="home_fingerprint">Fingerprint</string>
|
||||
<string name="home_securitypatch">Patch keamanan</string>
|
||||
<string name="home_selinux_status">Status SElinux</string>
|
||||
<string name="selinux_status_disabled">Cacat</string>
|
||||
<string name="home_manager_version">Versi Manager</string>
|
||||
<string name="home_fingerprint">Sidik jari</string>
|
||||
<string name="home_selinux_status">Status SELinux</string>
|
||||
<string name="selinux_status_disabled">Dinonaktifkan</string>
|
||||
<string name="selinux_status_enforcing">Enforcing</string>
|
||||
<string name="selinux_status_permissive">Permissive</string>
|
||||
<string name="selinux_status_unknown">Tidak tersedia</string>
|
||||
<string name="selinux_status_unknown">Tidak dikenal</string>
|
||||
<string name="superuser">Superuser</string>
|
||||
<string name="superuser_failed_to_grant_root">Gagal mengizinkan root untuk %d</string>
|
||||
<string name="superuser_allow_root_confirm">Ijinkan akses root untuk aplikasi %s?</string>
|
||||
<string name="module_failed_to_enable">Gagal mengaktifkan module: %s</string>
|
||||
<string name="module_failed_to_disable">Gagal menonaktifkan module: %s</string>
|
||||
<string name="module_empty">Tidak ada module terpasang</string>
|
||||
<string name="module">Module</string>
|
||||
<string name="uninstall">Hapus</string>
|
||||
<string name="module_install">Pasang module</string>
|
||||
<string name="install">Memasang module...</string>
|
||||
<string name="reboot">Reboot perangkat</string>
|
||||
<string name="module_failed_to_enable">Gagal mengaktifkan modul: %s</string>
|
||||
<string name="module_failed_to_disable">Gagal menonaktifkan modul: %s</string>
|
||||
<string name="module_empty">Tidak ada modul terpasang</string>
|
||||
<string name="module">Modul</string>
|
||||
<string name="uninstall">Copot</string>
|
||||
<string name="module_install">Pasang</string>
|
||||
<string name="install">Pasang</string>
|
||||
<string name="reboot">Mulai ulang</string>
|
||||
<string name="settings">Pengaturan</string>
|
||||
<string name="reboot_userspace">Soft Reboot</string>
|
||||
<string name="reboot_recovery">Reboot ke Recovery</string>
|
||||
<string name="reboot_bootloader">Reboot ke Bootloader</string>
|
||||
<string name="reboot_download">Reboot ke Download</string>
|
||||
<string name="reboot_edl">Reboot ke EDL</string>
|
||||
<string name="reboot_recovery">Mulai ulang ke Pemulihan</string>
|
||||
<string name="reboot_bootloader">Mulai ulang ke Bootloader</string>
|
||||
<string name="reboot_download">Mulai ulang ke Download</string>
|
||||
<string name="reboot_edl">Mulai ulang ke EDL</string>
|
||||
<string name="about">Tentang</string>
|
||||
<string name="require_kernel_version_8">Membutuhkan KernelSU Versi 8+</string>
|
||||
<string name="module_uninstall_confirm">Apakah anda yakin ingin menghapus module ini %s?</string>
|
||||
<string name="module_uninstall_success">%s terhapus</string>
|
||||
<string name="module_uninstall_failed">Gagal untuk menghapus: %s</string>
|
||||
<string name="module_uninstall_confirm">Apakah Anda yakin ingin mencopot modul %s?</string>
|
||||
<string name="module_uninstall_success">%s Tercopot</string>
|
||||
<string name="module_uninstall_failed">Gagal untuk mencopot: %s</string>
|
||||
<string name="module_version">Versi</string>
|
||||
<string name="module_author">Pembuat</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs tidak tersedia, module tidak bekerja!</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs tidak tersedia, modul tidak dapat bekerja!</string>
|
||||
<string name="refresh">Segarkan</string>
|
||||
<string name="show_system_apps">Tampilkan system apps</string>
|
||||
<string name="hide_system_apps">Sembunyikan system apps</string>
|
||||
<string name="send_log">Kirim logs</string>
|
||||
<string name="show_system_apps">Tampilkan apl sistem</string>
|
||||
<string name="hide_system_apps">Sembunyikan apl sistem</string>
|
||||
<string name="send_log">Kirim Log</string>
|
||||
<string name="safe_mode">Mode aman</string>
|
||||
<string name="reboot_to_apply">Restart untuk menerapkan</string>
|
||||
<string name="module_magisk_conflict">Module akan di nonaktifkan karna konflik dengan magisk\'s!</string>
|
||||
<string name="home_learn_kernelsu">Belajar KernelSU</string>
|
||||
<string name="reboot_to_apply">Mulai ulang untuk menerapkan</string>
|
||||
<string name="module_magisk_conflict">Modul dinonaktifkan karena bertentangan dengan Magisk!</string>
|
||||
<string name="home_learn_kernelsu">Pelajari KernelSU</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/id_ID/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">Cara menginstall KernelSu dan menggunakan Module</string>
|
||||
<string name="home_support_title">Dukung kami</string>
|
||||
<string name="home_support_content">KernelSU akan selalu gratis dan open source. Namun Anda dapat menunjukkan kepada kami bahwa Anda peduli dengan memberikan sedikit donasi.</string>
|
||||
<string name="about_source_code"><![CDATA[Lihat sumber kode di %1$s<br/>Gabung sekarang %2$s channel]]></string>
|
||||
</resources>
|
||||
<string name="home_click_to_learn_kernelsu">Pelajari cara memasang KernelSU dan menggunakan modul</string>
|
||||
<string name="home_support_title">Dukung Kami</string>
|
||||
<string name="home_support_content">KernelSU gratis dan bersumber terbuka, dan akan selalu seperti itu. Bagaimanapun juga Anda dapat menunjukan kepedulian Anda kepada kami dengan mengirimkan sedikit donasi.</string>
|
||||
<string name="about_source_code"><![CDATA[Lihat sumber code di %1$s<br/>Gabung kanal %2$s kami]]></string>
|
||||
<string name="profile">Profil Apl</string>
|
||||
<string name="profile_default">Bawaan</string>
|
||||
<string name="profile_template">Templat</string>
|
||||
<string name="profile_custom">Khusus</string>
|
||||
<string name="profile_name">Nama profil</string>
|
||||
<string name="profile_namespace">Ikat ruang-nama</string>
|
||||
<string name="profile_namespace_inherited">Diwariskan</string>
|
||||
<string name="profile_namespace_global">Universal</string>
|
||||
<string name="profile_namespace_individual">Personal</string>
|
||||
<string name="profile_groups">Kelompok</string>
|
||||
<string name="profile_capabilities">Kemampuan</string>
|
||||
<string name="profile_selinux_context">Konteks SELinux</string>
|
||||
<string name="profile_umount_modules">Lepas modul</string>
|
||||
<string name="failed_to_update_app_profile">Gagal memperbarui Profil Apl untuk %s</string>
|
||||
<string name="settings_umount_modules_default">Lepas modul secara bawaan</string>
|
||||
<string name="settings_umount_modules_default_summary">Nilai bawaan universal untuk \"Lepas modul\" di Profil-profil Apl. Jika diaktifkan, ini akan menghapus semua modifikasi modul pada sistem untuk aplikasi yang tidak memiliki set Profil.</string>
|
||||
<string name="profile_umount_modules_summary">Mengaktifkan opsi ini akan mengizinkan KernelSU memulihkan file-file yang dimodifikasi oleh modul untuk aplikasi ini.</string>
|
||||
<string name="profile_selinux_domain">Domain</string>
|
||||
<string name="profile_selinux_rules">Aturan</string>
|
||||
<string name="module_update">Perbarui</string>
|
||||
<string name="module_downloading">Mengunduh module: %s</string>
|
||||
<string name="module_start_downloading">Mulai mengunduh: %s</string>
|
||||
<string name="new_version_available">Versi baru: %s telah tersedia, tap untuk mengunduh</string>
|
||||
<string name="launch_app">Jalankan</string>
|
||||
<string name="force_stop_app">Paksa Berhenti</string>
|
||||
<string name="restart_app">Mulai ulang</string>
|
||||
<string name="failed_to_update_sepolicy">Gagal memperbarui aturan SELinux untuk: %s</string>
|
||||
</resources>
|
||||
@@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="home">Home</string>
|
||||
<string name="home_not_installed">Non installato</string>
|
||||
@@ -8,29 +9,18 @@
|
||||
<string name="home_module_count">Moduli: %d</string>
|
||||
<string name="home_unsupported">Non supportato</string>
|
||||
<string name="home_unsupported_reason">KernelSU ora supporta solo i kernel GKI</string>
|
||||
<string name="home_copied_to_clipboard">Copiato negli appunti</string>
|
||||
<string name="home_support">Supporto</string>
|
||||
|
||||
<string name="home_kernel">Kernel</string>
|
||||
<string name="home_arch">Architettura</string>
|
||||
<string name="home_manager_version">Versione del manager</string>
|
||||
<string name="home_api">Livello API</string>
|
||||
<string name="home_abi">ABI</string>
|
||||
<string name="home_fingerprint">Fingerprint</string>
|
||||
<string name="home_securitypatch">Patch di sicurezza</string>
|
||||
|
||||
<string name="home_selinux_status">Stato SELinux</string>
|
||||
<string name="selinux_status_disabled">Disabilitato</string>
|
||||
<string name="selinux_status_enforcing">Enforcing</string>
|
||||
<string name="selinux_status_permissive">Permissive</string>
|
||||
<string name="selinux_status_unknown">Sconosciuto</string>
|
||||
<string name="superuser">Accesso root</string>
|
||||
<string name="superuser_failed_to_grant_root">Impossibile concedere l\'accesso root per %d</string>
|
||||
<string name="superuser_allow_root_confirm">Sei sicuro di concedere l\'accesso root a %s?</string>
|
||||
<string name="module_failed_to_enable">Impossibile abilitare il modulo: %s</string>
|
||||
<string name="module_failed_to_disable">Impossibile disabilitare il modulo: %s</string>
|
||||
<string name="module_empty">Nessun modulo installato</string>
|
||||
|
||||
<string name="module">Moduli</string>
|
||||
<string name="uninstall">Disinstalla</string>
|
||||
<string name="module_install">Installa</string>
|
||||
@@ -43,7 +33,6 @@
|
||||
<string name="reboot_download">Riavvia in modalità Download</string>
|
||||
<string name="reboot_edl">Riavvia in modalità EDL</string>
|
||||
<string name="about">Informazioni</string>
|
||||
<string name="require_kernel_version_8">Richiede KernelSU versione 8+</string>
|
||||
<string name="module_uninstall_confirm">Sei sicuro di voler disinstallare il modulo %s?</string>
|
||||
<string name="module_uninstall_success">%s disinstallato</string>
|
||||
<string name="module_uninstall_failed">Impossibile disinstallare: %s</string>
|
||||
@@ -63,4 +52,30 @@
|
||||
<string name="home_support_title">Supportaci</string>
|
||||
<string name="home_support_content">KernelSU è, e sempre sarà, gratuito e open source. Puoi comunque mostrarci il tuo apprezzamento facendo una donazione.</string>
|
||||
<string name="about_source_code"><![CDATA[Visualizza il codice sorgente su %1$s<br/>Unisciti al nostro canale %2$s]]></string>
|
||||
</resources>
|
||||
<string name="profile_name">Nome profilo</string>
|
||||
<string name="profile_namespace">Namespace di mount</string>
|
||||
<string name="profile_namespace_global">Globale</string>
|
||||
<string name="profile_groups">Gruppi</string>
|
||||
<string name="profile_namespace_inherited">Ereditato</string>
|
||||
<string name="profile_namespace_individual">Individuale</string>
|
||||
<string name="profile_default">Predefinito</string>
|
||||
<string name="profile_custom">Personalizzato</string>
|
||||
<string name="profile_template">Template</string>
|
||||
<string name="profile_umount_modules">Scollega moduli</string>
|
||||
<string name="profile_selinux_context">Contesto SELinux</string>
|
||||
<string name="failed_to_update_app_profile">Aggiornamento Profilo per %s fallito</string>
|
||||
<string name="module_update">Aggiorna</string>
|
||||
<string name="launch_app">Apri</string>
|
||||
<string name="profile_capabilities">Capacità</string>
|
||||
<string name="settings_umount_modules_default">Scollega moduli da default</string>
|
||||
<string name="profile_selinux_rules">Regole</string>
|
||||
<string name="module_downloading">Sto scaricando il modulo: %s</string>
|
||||
<string name="module_start_downloading">Inizia a scaricare:%s</string>
|
||||
<string name="new_version_available">Nuova versione: %s disponibile, tocca per scaricare</string>
|
||||
<string name="force_stop_app">Arresto forzato</string>
|
||||
<string name="restart_app">Riavvia</string>
|
||||
<string name="failed_to_update_sepolicy">Aggiornamento regole SELinux per %s fallito</string>
|
||||
<string name="profile_umount_modules_summary">Attivando questa opzione permetterai a KernelSU di ripristinare ogni file modificato dai moduli per questa app.</string>
|
||||
<string name="profile_selinux_domain">Dominio</string>
|
||||
<string name="settings_umount_modules_default_summary">Il valore predefinito per \"Scollega moduli\" in Profili App. Se attivato, rimuoverà tutte le modifiche al sistema da parte dei moduli per le applicazioni che non hanno un profilo impostato.</string>
|
||||
</resources>
|
||||
@@ -1,55 +1,83 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<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_unsupported">非対応</string>
|
||||
<string name="home_unsupported_reason">KernelSUは現在、GKIカーネルのみサポートをしています</string>
|
||||
<string name="home_copied_to_clipboard">クリップボードにコピーしました</string>
|
||||
<string name="home_support">対応</string>
|
||||
<string name="home_kernel">カーネル</string>
|
||||
<string name="home_arch">アーキテクチャ</string>
|
||||
<string name="home_manager_version">バージョン</string>
|
||||
<string name="home_api">APIレベル</string>
|
||||
<string name="home_abi">ABI</string>
|
||||
<string name="home_fingerprint">Fingerprint</string>
|
||||
<string name="home_securitypatch">セキュリティパッチ</string>
|
||||
<string name="home_selinux_status">SELinuxの状態</string>
|
||||
<string name="selinux_status_disabled">無効</string>
|
||||
<string name="selinux_status_enforcing">Enforcing</string>
|
||||
<string name="selinux_status_permissive">Permissive</string>
|
||||
<string name="selinux_status_unknown">不明</string>
|
||||
<string name="superuser">スーパーユーザー</string>
|
||||
<string name="superuser_failed_to_grant_root">%dの権限の付与に失敗しました</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="uninstall">アンインストール</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="require_kernel_version_8">KernelSU バージョン8以降が必要です</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_version">バージョン</string>
|
||||
<string name="module_author">作者</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="send_log">ログを送信</string>
|
||||
<string name="safe_mode">セーフモード</string>
|
||||
<string name="reboot_to_apply">再起動をして有効化する</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
</resources>
|
||||
<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_unsupported">非対応</string>
|
||||
<string name="home_unsupported_reason">KernelSU は現在、GKI カーネルにのみ対応しています</string>
|
||||
<string name="home_kernel">カーネル</string>
|
||||
<string name="home_manager_version">バージョン</string>
|
||||
<string name="home_fingerprint">フィンガープリント</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="uninstall">アンインストール</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_version">バージョン</string>
|
||||
<string name="module_author">制作者</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="send_log">ログを送信</string>
|
||||
<string name="safe_mode">セーフモード</string>
|
||||
<string name="reboot_to_apply">再起動すると有効化されます</string>
|
||||
<string name="module_magisk_conflict">Magisk と競合しているためモジュールは無効になっています!</string>
|
||||
<string name="home_learn_kernelsu">KernelSU の詳細</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/ja_JP/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">KernelSU のインストール方法やモジュールの使い方はこちら</string>
|
||||
<string name="home_support_title">支援する</string>
|
||||
<string name="home_support_content">KernelSU は、現在も、今後も、無料のオープン ソースです。ただし、寄付をすることで私たちを気にかけていることを示すことができます。</string>
|
||||
<string name="about_source_code"><![CDATA[%1$s でソースコードを表示<br/>%2$s チャンネルに参加]]></string>
|
||||
<string name="profile">アプリのプロファイル</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_umount_modules">モジュールのアンマウント</string>
|
||||
<string name="profile_groups">グループ</string>
|
||||
<string name="profile_selinux_context">SELinux コンテキスト</string>
|
||||
<string name="failed_to_update_app_profile">%sのアプリのプロファイルの更新をできませでした</string>
|
||||
<string name="profile_selinux_domain">ドメイン</string>
|
||||
<string name="profile_selinux_rules">ルール</string>
|
||||
<string name="new_version_available">新しいバージョン: %s が利用可能です。クリックしてダウンロードしてください</string>
|
||||
<string name="module_update">アップデート</string>
|
||||
<string name="module_start_downloading">ダウンロードを開始:%s</string>
|
||||
<string name="launch_app">起動</string>
|
||||
<string name="force_stop_app">強制停止</string>
|
||||
<string name="restart_app">再起動</string>
|
||||
<string name="failed_to_update_sepolicy">SELinux ルールの更新に失敗しました: %s</string>
|
||||
<string name="profile_capabilities">ケイパビリティ</string>
|
||||
<string name="module_downloading">ダウンロードモジュール:%s</string>
|
||||
<string name="profile_umount_modules_summary">このオプションを有効にすると、KernelSU はこのアプリケーションのモジュールによって変更されたファイルを復元できるようになります。</string>
|
||||
<string name="settings_umount_modules_default">デフォルトでモジュールをアンインストールする</string>
|
||||
<string name="settings_umount_modules_default_summary">アプリプロファイルの「モジュールのマウント解除」のグローバルデフォルト値。 有効にすると、プロファイル セットを持たないアプリケーションのシステムに対するすべてのモジュール変更が削除されます。</string>
|
||||
<string name="require_kernel_version">現在の KernelSU バージョン %d はマネージャーが適切に機能するには低すぎます。 バージョン %d 以降にアップグレードしてください!</string>
|
||||
</resources>
|
||||
@@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="home">홈</string>
|
||||
<string name="home_not_installed">설치되지 않음</string>
|
||||
@@ -8,28 +9,18 @@
|
||||
<string name="home_module_count">설치된 모듈: %d개</string>
|
||||
<string name="home_unsupported">지원되지 않음</string>
|
||||
<string name="home_unsupported_reason">KernelSU는 현재 GKI 커널만 지원합니다</string>
|
||||
<string name="home_copied_to_clipboard">클립보드로 복사됨</string>
|
||||
<string name="home_support">지원</string>
|
||||
|
||||
<string name="home_kernel">커널</string>
|
||||
<string name="home_arch">아키텍처</string>
|
||||
<string name="home_manager_version">매니저 버전</string>
|
||||
<string name="home_api">API 레벨</string>
|
||||
<string name="home_fingerprint">빌드 정보</string>
|
||||
<string name="home_securitypatch">보안 패치 버전</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="superuser_failed_to_grant_root">%d에 루트 권한 부여 실패</string>
|
||||
<string name="superuser_allow_root_confirm">%s에 루트 권한을 부여할까요?</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="uninstall">삭제</string>
|
||||
<string name="module_install">설치</string>
|
||||
@@ -42,7 +33,6 @@
|
||||
<string name="reboot_download">다운로드 모드로 다시 시작</string>
|
||||
<string name="reboot_edl">EDL 모드로 다시 시작</string>
|
||||
<string name="about">정보</string>
|
||||
<string name="require_kernel_version_8">버전 8 이상의 KernelSU가 필요합니다</string>
|
||||
<string name="module_uninstall_confirm">%s 모듈을 삭제할까요?</string>
|
||||
<string name="module_uninstall_success">%s 모듈 삭제됨</string>
|
||||
<string name="module_uninstall_failed">모듈 삭제 실패: %s</string>
|
||||
@@ -57,8 +47,35 @@
|
||||
<string name="reboot_to_apply">다시 시작하여 변경 사항 적용</string>
|
||||
<string name="module_magisk_conflict">Magisk와의 충돌로 인해 모듈을 사용할 수 없습니다!</string>
|
||||
<string name="home_learn_kernelsu">KernelSU 알아보기</string>
|
||||
<string name="home_click_to_learn_kernelsu">KernelSU 설치 방법과 모듈 사용 방법을 확인합니다.</string>
|
||||
<string name="home_support_title">지원이 필요해요!</string>
|
||||
<string name="home_click_to_learn_kernelsu">KernelSU 설치 방법과 모듈 사용 방법을 확인합니다</string>
|
||||
<string name="home_support_title">지원이 필요합니다</string>
|
||||
<string name="home_support_content">KernelSU는 지금도, 앞으로도 항상 무료이며 오픈 소스로 유지됩니다. 기부를 통해 여러분의 관심을 보여주세요.</string>
|
||||
<string name="about_source_code"><![CDATA[%1$s에서 소스 코드 보기<br/>%2$s 채널 참가하기]]></string>
|
||||
</resources>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="settings_umount_modules_default_summary">앱 프로필 메뉴의 \"모듈 사용 해제\" 설정에 대한 전역 기본값을 설정합니다. 활성화 시, 개별 프로필이 설정되지 않은 앱은 시스템에 대한 모듈의 모든 수정사항이 적용되지 않습니다.</string>
|
||||
<string name="restart_app">다시 시작</string>
|
||||
<string name="profile_selinux_rules">규칙</string>
|
||||
<string name="new_version_available">새 버전: %s 사용 가능, 여기를 눌러서 받기</string>
|
||||
<string name="module_start_downloading">다운로드 시작: %s</string>
|
||||
<string name="force_stop_app">강제 중지</string>
|
||||
<string name="profile_default">기본값</string>
|
||||
<string name="profile_custom">사용자 지정</string>
|
||||
<string name="profile_template">템플릿</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_umount_modules">모듈 사용 해제</string>
|
||||
<string name="profile_selinux_context">SELinux 컨텍스트</string>
|
||||
<string name="profile_capabilities">권한</string>
|
||||
<string name="failed_to_update_app_profile">%s에 대한 앱 프로필 업데이트 실패</string>
|
||||
<string name="settings_umount_modules_default">기본값으로 모듈 사용 해제</string>
|
||||
<string name="profile_umount_modules_summary">이 옵션이 활성화되면, KernelSU는 이 애플리케이션에 대한 모듈의 모든 수정사항을 복구합니다.</string>
|
||||
<string name="module_update">업데이트</string>
|
||||
<string name="module_downloading">모듈 받는 중: %s</string>
|
||||
<string name="profile_selinux_domain">도메인</string>
|
||||
<string name="launch_app">실행</string>
|
||||
<string name="failed_to_update_sepolicy">다음 앱에 대한 SELinux 규칙 업데이트 실패: %s</string>
|
||||
</resources>
|
||||
81
manager/app/src/main/res/values-lt/strings.xml
Normal file
81
manager/app/src/main/res/values-lt/strings.xml
Normal file
@@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="home_fingerprint">Pirštų atspaudas</string>
|
||||
<string name="selinux_status_disabled">Išjungta</string>
|
||||
<string name="selinux_status_enforcing">Enforcing</string>
|
||||
<string name="selinux_status_unknown">Nežinomas</string>
|
||||
<string name="superuser">Supernaudotojai</string>
|
||||
<string name="module_failed_to_enable">Nepavyko įjungti modulio: %s</string>
|
||||
<string name="module_failed_to_disable">Nepavyko išjungti modulio: %s</string>
|
||||
<string name="selinux_status_permissive">Permissive</string>
|
||||
<string name="module_empty">Nėra įdiegtų modulių</string>
|
||||
<string name="module">Moduliai</string>
|
||||
<string name="reboot_userspace">Perkrovimas neišjungus</string>
|
||||
<string name="reboot_recovery">Perkrauti į atkūrimo rėžimą</string>
|
||||
<string name="reboot_bootloader">Perkrauti į įkrovos tvarkyklę</string>
|
||||
<string name="reboot_download">Perkrauti į atsisiuntimo rėžimą</string>
|
||||
<string name="about">Apie</string>
|
||||
<string name="module_uninstall_failed">Nepavyko išdiegti: %s</string>
|
||||
<string name="module_uninstall_success">%s išdiegtas</string>
|
||||
<string name="module_version">Versija</string>
|
||||
<string name="module_author">Autorius</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs nepasiekiamas, modulis negali veikti!</string>
|
||||
<string name="show_system_apps">Rodyti sistemos programas</string>
|
||||
<string name="hide_system_apps">Slėpti sistemos programas</string>
|
||||
<string name="send_log">Siųsti žurnalą</string>
|
||||
<string name="reboot">Paleisti iš naujo</string>
|
||||
<string name="refresh">Atšviežinti</string>
|
||||
<string name="safe_mode">Saugus rėžimas</string>
|
||||
<string name="reboot_to_apply">Paleiskite iš naujo, kad įsigaliotų</string>
|
||||
<string name="module_magisk_conflict">Moduliai yra išjungti, nes jie konfliktuoja su Magisk\'s!</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_learn_kernelsu">Sužinokite apie KernelSU</string>
|
||||
<string name="home_click_to_learn_kernelsu">Sužinokite, kaip įdiegti KernelSU ir naudoti modulius</string>
|
||||
<string name="about_source_code">Peržiūrėkite šaltinio kodą %1$s<br/>Prisijunkite prie mūsų %2$s kanalo</string>
|
||||
<string name="profile_default">Numatytas</string>
|
||||
<string name="profile_template">Šablonas</string>
|
||||
<string name="profile_custom">Pasirinktinis</string>
|
||||
<string name="profile_name">Profilio pavadinimas</string>
|
||||
<string name="profile_namespace">Prijungti vardų erdvę</string>
|
||||
<string name="profile_namespace_inherited">Paveldėtas</string>
|
||||
<string name="profile_namespace_global">Globalus</string>
|
||||
<string name="profile_namespace_individual">Individualus</string>
|
||||
<string name="profile_groups">Grupės</string>
|
||||
<string name="profile_capabilities">Galimybės</string>
|
||||
<string name="profile_selinux_context">SELinux kontekstas</string>
|
||||
<string name="profile_umount_modules">Atjungti modulius</string>
|
||||
<string name="settings_umount_modules_default">Atjungti modulius pagal numatytuosius parametrus</string>
|
||||
<string name="profile_umount_modules_summary">Įjungus šią parinktį, KernelSU galės atkurti visus modulių modifikuotus failus šiai programai.</string>
|
||||
<string name="profile_selinux_domain">Domenas</string>
|
||||
<string name="profile_selinux_rules">Taisyklės</string>
|
||||
<string name="module_update">Atnaujinti</string>
|
||||
<string name="module_downloading">Atsisiunčiamas modulis: %s</string>
|
||||
<string name="module_start_downloading">Pradedamas atsisiuntimas: %s</string>
|
||||
<string name="new_version_available">Nauja versija: %s pasiekiama, spustelėkite norėdami atsisiųsti</string>
|
||||
<string name="launch_app">Paleisti</string>
|
||||
<string name="force_stop_app">Priversti sustoti</string>
|
||||
<string name="restart_app">Perkrauti</string>
|
||||
<string name="failed_to_update_sepolicy">Nepavyko atnaujinti SELinux taisyklių: %s</string>
|
||||
<string name="home">Namai</string>
|
||||
<string name="home_not_installed">Neįdiegta</string>
|
||||
<string name="home_unsupported_reason">KernelSU dabar palaiko tik GKI branduolius</string>
|
||||
<string name="home_click_to_install">Spustelėkite norėdami įdiegti</string>
|
||||
<string name="home_working">Veikia</string>
|
||||
<string name="home_superuser_count">Supernaudotojai: %d</string>
|
||||
<string name="home_working_version">Versija: %d</string>
|
||||
<string name="home_unsupported">Nepalaikoma</string>
|
||||
<string name="home_module_count">Moduliai: %d</string>
|
||||
<string name="home_manager_version">Tvarkyklės versija</string>
|
||||
<string name="home_kernel">Branduolys</string>
|
||||
<string name="home_selinux_status">SELinux statusas</string>
|
||||
<string name="uninstall">Išdiegti</string>
|
||||
<string name="module_install">Įdiegti</string>
|
||||
<string name="install">Įdiegti</string>
|
||||
<string name="settings">Parametrai</string>
|
||||
<string name="reboot_edl">Perkrauti į EDL</string>
|
||||
<string name="module_uninstall_confirm">Ar tikrai norite išdiegti modulį %s\?</string>
|
||||
<string name="home_support_title">Paremkite mus</string>
|
||||
<string name="home_support_content">KernelSU yra ir visada bus nemokamas ir atvirojo kodo. Tačiau galite parodyti, kad jums rūpi, paaukodami mums.</string>
|
||||
<string name="failed_to_update_app_profile">Nepavyko atnaujinti programos profilio %s</string>
|
||||
<string name="settings_umount_modules_default_summary">Visuotinė numatytoji „Modulių atjungimo“ reikšmė programų profiliuose. Jei įjungta, ji pašalins visus sistemos modulio pakeitimus programoms, kurios neturi profilio.</string>
|
||||
</resources>
|
||||
81
manager/app/src/main/res/values-mr/strings.xml
Normal file
81
manager/app/src/main/res/values-mr/strings.xml
Normal file
@@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="home_not_installed">इंस्टॉल केले नाही</string>
|
||||
<string name="home">होम</string>
|
||||
<string name="home_click_to_install">इंस्टॉल साठी क्लिक करा</string>
|
||||
<string name="home_working">कार्यरत</string>
|
||||
<string name="home_working_version">आवृत्ती: %d</string>
|
||||
<string name="home_module_count">मॉड्यूल्स: %d</string>
|
||||
<string name="home_superuser_count">सुपरयूझर: %d</string>
|
||||
<string name="home_unsupported">असमर्थित</string>
|
||||
<string name="home_unsupported_reason">KernelSU आता फक्त GKI कर्नलचे समर्थन करते</string>
|
||||
<string name="home_kernel">कर्नल</string>
|
||||
<string name="home_fingerprint">फिंगरप्रिंट</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="install">स्थापित करा</string>
|
||||
<string name="module_empty">कोणतेही मॉड्यूल स्थापित केलेले नाही</string>
|
||||
<string name="reboot">रीबूट करा</string>
|
||||
<string name="superuser">सुपरयुझर</string>
|
||||
<string name="module_failed_to_enable">मॉड्यूल सक्षम करण्यात अयशस्वी: %s</string>
|
||||
<string name="uninstall">विस्थापित करा</string>
|
||||
<string name="module_failed_to_disable">मॉड्यूल अक्षम करण्यात अयशस्वी: %s</string>
|
||||
<string name="module">मॉड्यूल</string>
|
||||
<string name="module_install">स्थापित करा</string>
|
||||
<string name="settings">सेटिंग्ज</string>
|
||||
<string name="reboot_userspace">सॉफ्ट रीबूट</string>
|
||||
<string name="about">बद्दल</string>
|
||||
<string name="reboot_edl">EDL वर रीबूट करा</string>
|
||||
<string name="module_uninstall_confirm">तुमची खात्री आहे की तुम्ही मॉड्यूल %s विस्थापित करू इच्छिता\?</string>
|
||||
<string name="module_uninstall_failed">विस्थापित करण्यात अयशस्वी: %s</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs उपलब्ध नाही, मॉड्यूल काम करू शकत नाही!</string>
|
||||
<string name="show_system_apps">सिस्टम अॅप्स दाखवा</string>
|
||||
<string name="reboot_bootloader">बूटलोडरवर रीबूट करा</string>
|
||||
<string name="module_uninstall_success">%s विस्थापित</string>
|
||||
<string name="module_version">आवृत्ती</string>
|
||||
<string name="module_author">लेखक</string>
|
||||
<string name="refresh">रिफ्रेश करा</string>
|
||||
<string name="reboot_recovery">रिकवरी मध्ये रिबुट करा</string>
|
||||
<string name="reboot_download">डाउनलोड करण्यासाठी रीबूट करा</string>
|
||||
<string name="send_log">लॉग पाठवा</string>
|
||||
<string name="safe_mode">सुरक्षित मोड</string>
|
||||
<string name="hide_system_apps">सिस्टम अॅप्स लपवा</string>
|
||||
<string name="reboot_to_apply">प्रभावी होण्यासाठी रीबूट करा</string>
|
||||
<string name="home_learn_kernelsu">KernelSU शिका</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="module_magisk_conflict">मॉड्यूल अक्षम केले आहेत कारण ते Magisk च्या विरोधाभास आहे!</string>
|
||||
<string name="home_click_to_learn_kernelsu">KernelSU कसे स्थापित करायचे आणि मॉड्यूल कसे वापरायचे ते शिका</string>
|
||||
<string name="home_support_content">KernelSU विनामूल्य आणि मुक्त स्रोत आहे, आणि नेहमीच असेल. तथापि, देणगी देऊन तुम्ही आम्हाला दाखवू शकता की तुमची काळजी आहे.</string>
|
||||
<string name="home_support_title">आम्हाला पाठिंबा द्या</string>
|
||||
<string name="profile_custom">कस्टम</string>
|
||||
<string name="profile_namespace">माउंट नेमस्पेस</string>
|
||||
<string name="profile_default">डीफॉल्ट</string>
|
||||
<string name="profile_template">साचा</string>
|
||||
<string name="profile_namespace_individual">वैयक्तिक</string>
|
||||
<string name="profile_capabilities">क्षमता</string>
|
||||
<string name="about_source_code">%1$s वर स्रोत कोड पहा<br/>आमच्या %2$s चॅनेलमध्ये सामील व्हा</string>
|
||||
<string name="profile_name">प्रोफाइल नाव</string>
|
||||
<string name="profile_namespace_inherited">इनहेरीटेड</string>
|
||||
<string name="profile_namespace_global">जागतिक</string>
|
||||
<string name="profile_groups">गट</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="settings_umount_modules_default">डीफॉल्टनुसार मॉड्यूल्स उमाउंट करा</string>
|
||||
<string name="settings_umount_modules_default_summary">अॅप प्रोफाइलमधील \"उमाउंट मॉड्यूल्स\" साठी जागतिक डीफॉल्ट मूल्य. सक्षम असल्यास, ते प्रोफाइल सेट नसलेल्या ॲप्लिकेशनचे सिस्टममधील सर्व मॉड्यूल बदल काढून टाकेल.</string>
|
||||
<string name="profile_umount_modules_summary">हा पर्याय सक्षम केल्याने KernelSU ला या ऍप्लिकेशनसाठी मॉड्यूल्सद्वारे कोणत्याही सुधारित फाइल्स पुनर्संचयित करण्यास अनुमती मिळेल.</string>
|
||||
<string name="failed_to_update_sepolicy">यासाठी SELinux नियम अपडेट करण्यात अयशस्वी: %s</string>
|
||||
<string name="profile_selinux_rules">नियम</string>
|
||||
<string name="module_update">अपडेट करा</string>
|
||||
<string name="profile_selinux_domain">डोमेन</string>
|
||||
<string name="module_downloading">मॉड्यूल डाउनलोड करत आहे: %s</string>
|
||||
<string name="module_start_downloading">डाउनलोड करणे सुरू करा: %s</string>
|
||||
<string name="new_version_available">नवीन आवृत्ती: %s उपलब्ध आहे, डाउनलोड करण्यासाठी क्लिक करा</string>
|
||||
<string name="force_stop_app">सक्तीने थांबा</string>
|
||||
<string name="launch_app">लाँच करा</string>
|
||||
<string name="restart_app">पुन्हा सुरू करा</string>
|
||||
</resources>
|
||||
@@ -1,38 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">KernelSU</string>
|
||||
|
||||
<string name="home">Home</string>
|
||||
<string name="home_not_installed">Niet geïnstalleerd</string>
|
||||
<string name="home_click_to_install">Klik om te installeren</string>
|
||||
<string name="home_working">Werkend</string>
|
||||
<string name="home_working_version">Versie: %d</string>
|
||||
<string name="home_superuser_count">Superusers: %d</string>
|
||||
<string name="home_superuser_count">Supergebruikers: %d</string>
|
||||
<string name="home_module_count">Modules: %d</string>
|
||||
<string name="home_unsupported">Niet ondersteund</string>
|
||||
<string name="home_unsupported_reason">KernelSU ondersteunt alleen GKI kernels</string>
|
||||
<string name="home_copied_to_clipboard">Gekopiëerd naar clipboard</string>
|
||||
<string name="home_support">Ondersteuning</string>
|
||||
|
||||
<string name="home_kernel">Kernel</string>
|
||||
<string name="home_arch">Arch</string>
|
||||
<string name="home_manager_version">Manager Versie</string>
|
||||
<string name="home_api">API Niveau</string>
|
||||
<string name="home_abi">ABI</string>
|
||||
<string name="home_fingerprint">Fingerprint</string>
|
||||
<string name="home_securitypatch">Veiligheidspatch</string>
|
||||
|
||||
<string name="home_selinux_status">SELinux status</string>
|
||||
<string name="selinux_status_disabled">Uitgeschakeld</string>
|
||||
<string name="selinux_status_enforcing">Afgedwongen</string>
|
||||
<string name="selinux_status_permissive">Permissief</string>
|
||||
<string name="selinux_status_unknown">Niet gekend</string>
|
||||
<string name="superuser">Superuser</string>
|
||||
<string name="superuser_failed_to_grant_root">Mislukt om root te geven voor %d</string>
|
||||
<string name="superuser_allow_root_confirm">Zeker dat je root wil geven aan %s?</string>
|
||||
<string name="superuser">Supergebruiker</string>
|
||||
<string name="module_failed_to_enable">Mislukt om module in te schakelen: %s</string>
|
||||
<string name="module_failed_to_disable">Mislukt om module uit te schakelen: %s</string>
|
||||
<string name="module_empty">Geen module geïnstalleerd</string>
|
||||
|
||||
<string name="module">Module</string>
|
||||
<string name="uninstall">Verwijderen</string>
|
||||
<string name="module_install">Installeren</string>
|
||||
@@ -45,7 +33,6 @@
|
||||
<string name="reboot_download">Herstart om te downloaden</string>
|
||||
<string name="reboot_edl">Herstart naar EDL</string>
|
||||
<string name="about">Over</string>
|
||||
<string name="require_kernel_version_8">Vereist KernelSU versie 8+</string>
|
||||
<string name="module_uninstall_confirm">Zeker van het verwijderen van module %s?</string>
|
||||
<string name="module_uninstall_success">%s verwijderd</string>
|
||||
<string name="module_uninstall_failed">Mislukt om te verwijderen: %s</string>
|
||||
@@ -53,8 +40,8 @@
|
||||
<string name="module_author">Auteur</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs is niet beschikbaar, module kan niet werken!</string>
|
||||
<string name="refresh">Vernieuwen</string>
|
||||
<string name="show_system_apps">Toon system apps</string>
|
||||
<string name="hide_system_apps">Verberg system apps</string>
|
||||
<string name="show_system_apps">Toon systeem apps</string>
|
||||
<string name="hide_system_apps">Verberg systeem apps</string>
|
||||
<string name="send_log">Stuur Log</string>
|
||||
<string name="safe_mode">Safe mode</string>
|
||||
<string name="reboot_to_apply">Herstart om effect te hebben</string>
|
||||
@@ -65,4 +52,32 @@
|
||||
<string name="home_support_title">Ondersteun ons</string>
|
||||
<string name="home_support_content">KernelSU is, en zal altijd, vrij en open source zijn. Je kan altijd je appreciatie tonen met een donatie.</string>
|
||||
<string name="about_source_code"><![CDATA[Bekijk source code op %1$s<br/>Vervoeg ons %2$s kanaal]]></string>
|
||||
</resources>
|
||||
<string name="profile" translatable="false">App profiel</string>
|
||||
<string name="profile_default">Standaard</string>
|
||||
<string name="profile_template">Sjabloon</string>
|
||||
<string name="profile_custom">Aangepast</string>
|
||||
<string name="profile_name">Profiel naam</string>
|
||||
<string name="profile_namespace">Koppel naamruimte</string>
|
||||
<string name="profile_namespace_inherited">Overgenomen</string>
|
||||
<string name="profile_namespace_global">Globaal</string>
|
||||
<string name="profile_namespace_individual">Individuëel</string>
|
||||
<string name="profile_groups">Groepen</string>
|
||||
<string name="profile_capabilities">Mogelijkheden</string>
|
||||
<string name="profile_selinux_context">SELinux context</string>
|
||||
<string name="profile_umount_modules">Ontkoppel modules</string>
|
||||
<string name="failed_to_update_app_profile">Mislukt om App Profiel te updaten voor %s</string>
|
||||
<string name="settings_umount_modules_default">Ontkoppel standaard de modules</string>
|
||||
<string name="settings_umount_modules_default_summary">De globale standaard waarde voor \"Ontkoppel modules\" in App Profielen. Indien geactiveerd, zal het alle module wijzigingen tot het systeem verwijderen voor applicaties die geen Profiel ingesteld hebben.</string>
|
||||
<string name="profile_umount_modules_summary">Met deze optie ingeschakeld zal KernelSU toelaten om alle gewijzigde bestanden door de modules voor deze applicatie te herstellen.</string>
|
||||
<string name="profile_selinux_domain">Domein</string>
|
||||
<string name="profile_selinux_rules">Regels</string>
|
||||
<string name="module_update">Update</string>
|
||||
<string name="module_downloading">Downloaden van module: %s</string>
|
||||
<string name="new_version_available">Nieuwe versie: %s is beschikbaar, klik om te downloaden</string>
|
||||
<string name="launch_app">Start</string>
|
||||
<string name="force_stop_app">Forceer Stop</string>
|
||||
<string name="restart_app">Herstart</string>
|
||||
<string name="module_start_downloading">Begin met downloaden: %s</string>
|
||||
<string name="failed_to_update_sepolicy">Kan SELinux-regels niet bijwerken voor: %s</string>
|
||||
<string name="require_kernel_version">De huidige KernelSU-versie %d is te laag om de manager correct te laten functioneren. Upgrade naar versie %d of hoger!</string>
|
||||
</resources>
|
||||
86
manager/app/src/main/res/values-pl/strings.xml
Normal file
86
manager/app/src/main/res/values-pl/strings.xml
Normal file
@@ -0,0 +1,86 @@
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">KernelSU</string>
|
||||
|
||||
<string name="home">Menu</string>
|
||||
<string name="home_not_installed">Nie zainstalowano</string>
|
||||
<string name="home_click_to_install">Kliknij, aby zainstalować</string>
|
||||
<string name="home_working">Działa</string>
|
||||
<string name="home_working_version">Wersja: %d</string>
|
||||
<string name="home_superuser_count">Superużytkowników: %d</string>
|
||||
<string name="home_module_count">Modułów: %d</string>
|
||||
<string name="home_unsupported">Nieobsługiwany</string>
|
||||
<string name="home_unsupported_reason">KernelSU obsługuje obecnie tylko jądra GKI</string>
|
||||
|
||||
<string name="home_kernel">Jądro</string>
|
||||
<string name="home_manager_version">Wersja menedżera</string>
|
||||
<string name="home_fingerprint">Odcisk</string>
|
||||
|
||||
<string name="home_selinux_status">Status SELinux</string>
|
||||
<string name="selinux_status_disabled">Wyłączony</string>
|
||||
<string name="selinux_status_enforcing">Enforcing</string>
|
||||
<string name="selinux_status_permissive">Permissive</string>
|
||||
<string name="selinux_status_unknown">Nieznany</string>
|
||||
<string name="superuser">Superużytkownik</string>
|
||||
<string name="module_failed_to_enable">Nie udało się włączyć modułu: %s</string>
|
||||
<string name="module_failed_to_disable">Nie udało się wyłączyć modułu: %s</string>
|
||||
<string name="module_empty">Brak zainstalowanych modułów</string>
|
||||
|
||||
<string name="module">Moduł</string>
|
||||
<string name="uninstall">Odinstaluj</string>
|
||||
<string name="module_install">Instaluj</string>
|
||||
<string name="install">Instaluj</string>
|
||||
<string name="reboot">Restartuj</string>
|
||||
<string name="settings">Ustawienia</string>
|
||||
<string name="reboot_userspace">Miękki restart</string>
|
||||
<string name="reboot_recovery">Restart do trybu Recovery</string>
|
||||
<string name="reboot_bootloader">Restart do trybu Bootloader</string>
|
||||
<string name="reboot_download">Restart do trybu Download</string>
|
||||
<string name="reboot_edl">Restart do trybu EDL</string>
|
||||
<string name="about">Informacje</string>
|
||||
<string name="module_uninstall_confirm">Czy na pewno chcesz odinstalować moduł %s?</string>
|
||||
<string name="module_uninstall_success">Odinstalowano %s</string>
|
||||
<string name="module_uninstall_failed">Nie udało się odinstalować:: %s</string>
|
||||
<string name="module_version">Wersja</string>
|
||||
<string name="module_author">Autor</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs jest niedostępny, moduł nie zadziała!</string>
|
||||
<string name="refresh">Odśwież</string>
|
||||
<string name="show_system_apps">Pokaż aplikacje systemowe</string>
|
||||
<string name="hide_system_apps">Ukryj aplikacje systemowe</string>
|
||||
<string name="send_log">Wyślij log</string>
|
||||
<string name="safe_mode">Tryb bezpieczny</string>
|
||||
<string name="reboot_to_apply">Uruchom ponownie, aby zastosować zmiany</string>
|
||||
<string name="module_magisk_conflict">Moduły są wyłączone, ponieważ są w konflikcie z modułami Magiska!</string>
|
||||
<string name="home_learn_kernelsu">Poznaj KernelSU</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">Dowiedz się jak zainstalować KernelSU i jak korzystać z modułów.</string>
|
||||
<string name="home_support_title">Wesprzyj nas</string>
|
||||
<string name="home_support_content">KernelSU jest i zawsze będzie darmowy oraz otwarty. Niemniej jednak możesz nam pokazać, że Ci zależy, wysyłając darowiznę.</string>
|
||||
<string name="about_source_code"><![CDATA[Przejrzyj kod źródłowy na %1$s<br/>Dołącz do kanału %2$s]]></string>
|
||||
<string name="profile" translatable="false">Profil aplikacji</string>
|
||||
<string name="profile_default">Domyślny</string>
|
||||
<string name="profile_template">Szablon</string>
|
||||
<string name="profile_custom">Własny</string>
|
||||
<string name="profile_name">Nazwa profilu</string>
|
||||
<string name="profile_namespace">Przestrzeń nazw montowania</string>
|
||||
<string name="profile_namespace_inherited">Odziedziczona</string>
|
||||
<string name="profile_namespace_global">Globalna</string>
|
||||
<string name="profile_namespace_individual">Indywidualna</string>
|
||||
<string name="profile_groups">Grupy</string>
|
||||
<string name="profile_capabilities">Uprawnienia</string>
|
||||
<string name="profile_selinux_context">Kontekst SELinux</string>
|
||||
<string name="profile_umount_modules">Odmontuj moduły</string>
|
||||
<string name="failed_to_update_app_profile">Nie udało się zaktualizować profilu aplikacji dla %s</string>
|
||||
<string name="settings_umount_modules_default">Domyślnie odmontuj moduły</string>
|
||||
<string name="settings_umount_modules_default_summary">Globalna wartość domyślna opcji \"Odmontuj moduły\" w profilach aplikacji. Jeśli jest włączona, odwraca wszystkie modyfikacje dokonane przez moduły dla aplikacji, które nie mają ustawionego profilu.</string>
|
||||
<string name="profile_umount_modules_summary">Włączenie tej opcji umożliwi KernelSU przywrócenie wszelkich zmodyfikowanych plików przez moduły dla tej aplikacji.</string>
|
||||
<string name="profile_selinux_domain">Domena</string>
|
||||
<string name="profile_selinux_rules">Reguły</string>
|
||||
<string name="module_update">Zaktualizuj</string>
|
||||
<string name="module_downloading">Pobieranie modułu: %s</string>
|
||||
<string name="module_start_downloading">Rozpocznij pobieranie: %s</string>
|
||||
<string name="new_version_available">Nowa wersja: %s jest dostępna, kliknij, aby pobrać</string>
|
||||
<string name="launch_app">Uruchom</string>
|
||||
<string name="force_stop_app">Wymuś zatrzymanie</string>
|
||||
<string name="restart_app">Restartuj</string>
|
||||
<string name="failed_to_update_sepolicy">Nie udało się zaktualizować reguł SELinux dla: %s</string>
|
||||
</resources>
|
||||
82
manager/app/src/main/res/values-pt-rBR/strings.xml
Normal file
82
manager/app/src/main/res/values-pt-rBR/strings.xml
Normal file
@@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="home">Início</string>
|
||||
<string name="home_not_installed">Não instalado</string>
|
||||
<string name="home_click_to_install">Clique para instalar</string>
|
||||
<string name="home_working">Em Execução</string>
|
||||
<string name="home_working_version">Versão: %d</string>
|
||||
<string name="home_superuser_count">Superusuários: %d</string>
|
||||
<string name="home_module_count">Módulos: %d</string>
|
||||
<string name="home_unsupported">Sem Suporte</string>
|
||||
<string name="home_unsupported_reason">Por enquanto, KernelSU suporta apenas kernels GKI</string>
|
||||
<string name="home_kernel">Kernel</string>
|
||||
<string name="home_manager_version">Versão do gerenciador</string>
|
||||
<string name="home_fingerprint">Impressão digital</string>
|
||||
<string name="home_selinux_status">Status do SELinux</string>
|
||||
<string name="selinux_status_disabled">Desabilitado</string>
|
||||
<string name="selinux_status_enforcing">Enforcing</string>
|
||||
<string name="selinux_status_permissive">Permissivo</string>
|
||||
<string name="selinux_status_unknown">Desconhecido</string>
|
||||
<string name="superuser">Superusuário</string>
|
||||
<string name="module_failed_to_enable">Falha ao ativar o módulo: %s</string>
|
||||
<string name="module_failed_to_disable">Falha ao desativar o módulo: %s</string>
|
||||
<string name="module_empty">Nenhum módulo instalado</string>
|
||||
<string name="module">Módulos</string>
|
||||
<string name="uninstall">Desinstalar</string>
|
||||
<string name="module_install">Instalar</string>
|
||||
<string name="install">Instalar</string>
|
||||
<string name="reboot">Reiniciar</string>
|
||||
<string name="settings">Configurações</string>
|
||||
<string name="reboot_userspace">Reinicialização suave</string>
|
||||
<string name="reboot_recovery">Reiniciar para recuperação</string>
|
||||
<string name="reboot_bootloader">Reiniciar para bootloader</string>
|
||||
<string name="reboot_download">Reiniciar para download</string>
|
||||
<string name="reboot_edl">Reiniciar para EDL</string>
|
||||
<string name="about">Sobre</string>
|
||||
<string name="module_uninstall_confirm">Tem certeza de que deseja desinstalar o módulo %s?</string>
|
||||
<string name="module_uninstall_success">%s desinstalado</string>
|
||||
<string name="module_uninstall_failed">Falha ao desinstalar: %s</string>
|
||||
<string name="module_version">Versão</string>
|
||||
<string name="module_author">Autor</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs não está disponível, o módulo não funcionará!</string>
|
||||
<string name="refresh">Atualizar</string>
|
||||
<string name="show_system_apps">Mostrar aplicativos do sistema</string>
|
||||
<string name="hide_system_apps">Ocultar aplicativos do sistema</string>
|
||||
<string name="send_log">Enviar log</string>
|
||||
<string name="safe_mode">Modo de segurança</string>
|
||||
<string name="reboot_to_apply">Reinicie para entrar em vigor</string>
|
||||
<string name="module_magisk_conflict">Os módulos estão desativados porque entraram em conflito com o Magisk\'s!</string>
|
||||
<string name="home_learn_kernelsu">Leia mais sobre KernelSU</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">Saiba como instalar o KernelSU e usar os módulos</string>
|
||||
<string name="home_support_title">Apoie-nos</string>
|
||||
<string name="home_support_content">O KernelSU é, e sempre será, gratuito e de código aberto. No entanto, você pode mostrar seu apoio fazendo uma doação.</string>
|
||||
<string name="about_source_code"><![CDATA[Veja o código-fonte no %1$s<br/>Junte-se ao nosso canal do %2$s]]></string>
|
||||
<string name="profile" translatable="false">Perfil do Aplicativo</string>
|
||||
<string name="profile_default">Padrão</string>
|
||||
<string name="profile_template">Modelo</string>
|
||||
<string name="profile_custom">Personalizado</string>
|
||||
<string name="profile_name">Nome do perfil</string>
|
||||
<string name="profile_namespace">Montar namespace</string>
|
||||
<string name="profile_namespace_inherited">Herdada</string>
|
||||
<string name="profile_namespace_global">Global</string>
|
||||
<string name="profile_namespace_individual">Individual</string>
|
||||
<string name="profile_groups">Grupos</string>
|
||||
<string name="profile_capabilities">Permissões</string>
|
||||
<string name="profile_selinux_context">Contexto do SELinux</string>
|
||||
<string name="profile_umount_modules">Módulos não montados</string>
|
||||
<string name="failed_to_update_app_profile">Falha ao atualizar o perfil do aplicativo para %s</string>
|
||||
<string name="settings_umount_modules_default">Não montar módulos por padrão</string>
|
||||
<string name="settings_umount_modules_default_summary">O valor padrão global para \"Módulos não montados\" em perfis de aplicativos. Se ativado, removerá todas as modificações do módulo do sistema para aplicativos que não possuem um perfil definido.</string>
|
||||
<string name="profile_umount_modules_summary">Ativar esta opção permitirá que o KernelSU restaure quaisquer arquivos modificados pelos módulos para este aplicativo.</string>
|
||||
<string name="profile_selinux_domain">Domínio</string>
|
||||
<string name="profile_selinux_rules">Regras</string>
|
||||
<string name="module_update">Atualizar</string>
|
||||
<string name="module_downloading">Baixando módulo: %s</string>
|
||||
<string name="module_start_downloading">Comece a baixar: %s</string>
|
||||
<string name="new_version_available">Nova versão: %s está disponível, clique para baixar</string>
|
||||
<string name="launch_app">Iniciar</string>
|
||||
<string name="force_stop_app">Forçar parada</string>
|
||||
<string name="restart_app">Reiniciar</string>
|
||||
<string name="failed_to_update_sepolicy">Falha ao atualizar as regras do SELinux para: %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