You've already forked KernelSU
mirror of
https://github.com/tiann/KernelSU.git
synced 2025-08-27 23:46:34 +00:00
Compare commits
309 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b766b98513 | ||
|
|
f381e32434 | ||
|
|
916d6bcd06 | ||
|
|
9343376bb3 | ||
|
|
1071a5c135 | ||
|
|
bd5100d698 | ||
|
|
81f1a47cec | ||
|
|
6a3979842b | ||
|
|
038dae1d6f | ||
|
|
0674841b94 | ||
|
|
57a5f39f81 | ||
|
|
49157113c3 | ||
|
|
dc233700aa | ||
|
|
d6860160e8 | ||
|
|
4e3f03ae96 | ||
|
|
717c0fee61 | ||
|
|
82d965f44c | ||
|
|
9c2e48bb3e | ||
|
|
4871e11a55 | ||
|
|
b67c3122d3 | ||
|
|
fa7f5959b2 | ||
|
|
3969f79de3 | ||
|
|
31653b11f1 | ||
|
|
d36e365921 | ||
|
|
0576495b4b | ||
|
|
2e94e07d70 | ||
|
|
99e2707be4 | ||
|
|
64ddf47783 | ||
|
|
d3a5054f68 | ||
|
|
123b66e84b | ||
|
|
d76004c6f1 | ||
|
|
71cb86c2e9 | ||
|
|
fb6ce7ee62 | ||
|
|
7af4f338e5 | ||
|
|
109442f8c4 | ||
|
|
a943528d82 | ||
|
|
a3df721b84 | ||
|
|
98757bcdb3 | ||
|
|
e0267a22f8 | ||
|
|
21573bbd5b | ||
|
|
fe526cb029 | ||
|
|
57b96da9db | ||
|
|
0134b27ca7 | ||
|
|
239989719d | ||
|
|
2f8323e0a3 | ||
|
|
935dc18faa | ||
|
|
f2816653d9 | ||
|
|
8ee274f7d8 | ||
|
|
32e0b57b46 | ||
|
|
d623b9fe09 | ||
|
|
66ae50eb49 | ||
|
|
839fc0534e | ||
|
|
e9b6fcfa22 | ||
|
|
646b6f71f4 | ||
|
|
efbc07fde3 | ||
|
|
0be7846f9d | ||
|
|
51bc01afde | ||
|
|
045bc7f6fb | ||
|
|
f982683825 | ||
|
|
2e1bb8e303 | ||
|
|
fcc3d2d3d4 | ||
|
|
dedd54d890 | ||
|
|
2027ac325f | ||
|
|
c8dd0b070c | ||
|
|
063d5c8025 | ||
|
|
ac4c6f7895 | ||
|
|
472a94b6f2 | ||
|
|
8ba008fb9f | ||
|
|
825b14635f | ||
|
|
52c7f0e2bf | ||
|
|
05eb280d79 | ||
|
|
97dd3d9ec2 | ||
|
|
3aac979caa | ||
|
|
0697db618e | ||
|
|
4922d89823 | ||
|
|
3750e6e759 | ||
|
|
ba28d57e94 | ||
|
|
dff1452eaa | ||
|
|
311fad7462 | ||
|
|
54383dfc94 | ||
|
|
c2bf237fd2 | ||
|
|
1db9fc2029 | ||
|
|
99f7bfea6e | ||
|
|
1f67487c37 | ||
|
|
7aa36dc326 | ||
|
|
9b72b4542c | ||
|
|
b1a225799a | ||
|
|
2e2992a1c2 | ||
|
|
50387a9d3d | ||
|
|
488564fe76 | ||
|
|
37df6ffc52 | ||
|
|
ee05aed5e5 | ||
|
|
60dd52afd1 | ||
|
|
1be266b6f6 | ||
|
|
1e029eddee | ||
|
|
f0b18a1e18 | ||
|
|
a7f54af273 | ||
|
|
c2e71f1141 | ||
|
|
7c9246bc15 | ||
|
|
6ea2438425 | ||
|
|
705a9b7238 | ||
|
|
3010ed89c0 | ||
|
|
2d86c8bbfc | ||
|
|
6aa0e4cf28 | ||
|
|
582e81d5af | ||
|
|
605d33056b | ||
|
|
112321f9cc | ||
|
|
6c6fc44cae | ||
|
|
11a98e855a | ||
|
|
464847daee | ||
|
|
2449b494a3 | ||
|
|
637caa84ce | ||
|
|
57314c9963 | ||
|
|
15970b321f | ||
|
|
7e3c756803 | ||
|
|
11ff59a81c | ||
|
|
4f9bbf199b | ||
|
|
a34090bc57 | ||
|
|
be452c5a42 | ||
|
|
e7dfaefaac | ||
|
|
9cb28b6e92 | ||
|
|
6901371852 | ||
|
|
828ce6d29d | ||
|
|
2df8caaa85 | ||
|
|
31fbeef7e3 | ||
|
|
eba87b7223 | ||
|
|
24d77e0ad5 | ||
|
|
13ae650445 | ||
|
|
a43dddf6f1 | ||
|
|
a81201f368 | ||
|
|
4a73672190 | ||
|
|
942866d986 | ||
|
|
d533c27e4f | ||
|
|
97ee114aa5 | ||
|
|
869cd50b79 | ||
|
|
7fe07329fc | ||
|
|
fd17e852d0 | ||
|
|
f8c6c747b3 | ||
|
|
a5fa45baf2 | ||
|
|
e1c59f8e18 | ||
|
|
444574739c | ||
|
|
4f05fe226e | ||
|
|
58416be4a5 | ||
|
|
eb5b36f138 | ||
|
|
9ffdd171f6 | ||
|
|
4ac203a78d | ||
|
|
c9d8c62497 | ||
|
|
8fe19d3876 | ||
|
|
09402ccfa6 | ||
|
|
5f7d4b609f | ||
|
|
eea2287a39 | ||
|
|
ed08f4e9af | ||
|
|
511a671824 | ||
|
|
e51c78ada9 | ||
|
|
eb24884684 | ||
|
|
ac56a017ab | ||
|
|
801de95d1a | ||
|
|
e4c2f3eb20 | ||
|
|
006a1b7657 | ||
|
|
5068e2fcc4 | ||
|
|
c35f7e984d | ||
|
|
05366ca7d0 | ||
|
|
8b85e003f6 | ||
|
|
c4d28e6256 | ||
|
|
cb7f414e39 | ||
|
|
858ec910fd | ||
|
|
5167dc7352 | ||
|
|
96242df228 | ||
|
|
dc5f911e43 | ||
|
|
fc613d5b6a | ||
|
|
f3cdfab88f | ||
|
|
b5cc931d00 | ||
|
|
e1f9900b2f | ||
|
|
a5e3cab177 | ||
|
|
4adc9873a9 | ||
|
|
9a04211051 | ||
|
|
9403c8f606 | ||
|
|
998664402d | ||
|
|
f73885fc95 | ||
|
|
7451d0fb83 | ||
|
|
c924c655df | ||
|
|
98030ee1ae | ||
|
|
e124aab76a | ||
|
|
ef92c32729 | ||
|
|
b2d0de325f | ||
|
|
fd7234bf11 | ||
|
|
c189320a66 | ||
|
|
c46fefc58e | ||
|
|
24385691ea | ||
|
|
3ebe2c6a81 | ||
|
|
3291538446 | ||
|
|
e38a5e52d2 | ||
|
|
b3a15e2b6b | ||
|
|
c5d423c4eb | ||
|
|
4511d4b7bf | ||
|
|
64908583e9 | ||
|
|
c408710b11 | ||
|
|
bc1e03feb1 | ||
|
|
39b025b235 | ||
|
|
65a0f0070a | ||
|
|
8b71d3c9ba | ||
|
|
2bcb6a93c0 | ||
|
|
51880e3e90 | ||
|
|
4cd435b194 | ||
|
|
c986b7a53a | ||
|
|
808342bf04 | ||
|
|
0b9f675013 | ||
|
|
f19d157887 | ||
|
|
a160a7bf0d | ||
|
|
ef26aba4d8 | ||
|
|
71b56ba700 | ||
|
|
d958e6d7e7 | ||
|
|
815f4d0428 | ||
|
|
2a64784a33 | ||
|
|
8f33926aa0 | ||
|
|
2b0d19928a | ||
|
|
1fc1ffe2ab | ||
|
|
97faab6be4 | ||
|
|
247aa877e4 | ||
|
|
cbd47329e8 | ||
|
|
eb25644a0e | ||
|
|
65b5ce2a50 | ||
|
|
8c44e82db2 | ||
|
|
79a1410fd3 | ||
|
|
ce0c397a65 | ||
|
|
99847cb986 | ||
|
|
f41d73f7eb | ||
|
|
7f73827658 | ||
|
|
053fce61c0 | ||
|
|
8ae6eaa5e3 | ||
|
|
35553afd12 | ||
|
|
0c11d210a9 | ||
|
|
9759a779cd | ||
|
|
4bad691ec8 | ||
|
|
fefb02e578 | ||
|
|
d6770467fa | ||
|
|
95dc7fcbe1 | ||
|
|
b4cfc2f298 | ||
|
|
6016937d5a | ||
|
|
117b4dc051 | ||
|
|
f6d552c797 | ||
|
|
a5e76553e4 | ||
|
|
44c0b3a767 | ||
|
|
177ef6b634 | ||
|
|
9a4ea27e9d | ||
|
|
339e75be24 | ||
|
|
cf210d629f | ||
|
|
ca480a5ec3 | ||
|
|
23263a55de | ||
|
|
f65ea5a340 | ||
|
|
ad6e2390f5 | ||
|
|
383f164453 | ||
|
|
f675ce9aba | ||
|
|
7fd760f4f4 | ||
|
|
972d347a14 | ||
|
|
ca8a88f0cc | ||
|
|
e39be55db8 | ||
|
|
d00d3cbf82 | ||
|
|
7168a974be | ||
|
|
ddc086c4ef | ||
|
|
076511e275 | ||
|
|
9d5529fb09 | ||
|
|
ceb00dfdfd | ||
|
|
39504ec2f3 | ||
|
|
1642e92c6f | ||
|
|
0f220f4044 | ||
|
|
2c34ec1742 | ||
|
|
7568d55be1 | ||
|
|
e3998c0744 | ||
|
|
50914ce39b | ||
|
|
6af2480008 | ||
|
|
625e1aafd1 | ||
|
|
d77988c1ac | ||
|
|
fe7ec370d4 | ||
|
|
ce5aa990ed | ||
|
|
22a1276a22 | ||
|
|
f9a4186dc7 | ||
|
|
8b29137e83 | ||
|
|
ae17001033 | ||
|
|
0eea198c2f | ||
|
|
425713fad3 | ||
|
|
7611accc33 | ||
|
|
323eaa0242 | ||
|
|
217755bb5a | ||
|
|
170cd3f912 | ||
|
|
1d7d406745 | ||
|
|
c8fc6a0656 | ||
|
|
3829894d4d | ||
|
|
cd772fa250 | ||
|
|
dbe43b1540 | ||
|
|
8a59fe1969 | ||
|
|
2a4fa94af0 | ||
|
|
6de330b00a | ||
|
|
8c0d06bc68 | ||
|
|
ed254b7ab4 | ||
|
|
5355625ed6 | ||
|
|
7b89ec89c0 | ||
|
|
5aa025c3f0 | ||
|
|
e39a80f91e | ||
|
|
622a7d73dc | ||
|
|
922703d2ff | ||
|
|
f459dfad54 | ||
|
|
3e2de84a81 | ||
|
|
796f8a448a | ||
|
|
7775ce3938 | ||
|
|
2fb5334ac6 | ||
|
|
afe0e691aa | ||
|
|
532796de48 | ||
|
|
e691c62811 |
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.bat eol=crlf
|
||||||
1
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
1
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -2,7 +2,6 @@ name: Feature Request
|
|||||||
description: "Suggest an idea for this project"
|
description: "Suggest an idea for this project"
|
||||||
title: "[Feature]"
|
title: "[Feature]"
|
||||||
labels: "feature"
|
labels: "feature"
|
||||||
assignees: tiann
|
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
id: feature-info
|
id: feature-info
|
||||||
|
|||||||
21
.github/dependabot.yml
vendored
Normal file
21
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: github-actions
|
||||||
|
directory: /
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
|
||||||
|
- package-ecosystem: cargo
|
||||||
|
directory: userspace/ksud
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
|
||||||
|
- package-ecosystem: gradle
|
||||||
|
directory: manager
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
|
||||||
|
- package-ecosystem: npm
|
||||||
|
directory: website
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
71
.github/manifests/android-14-avd_x86_64.xml
vendored
Normal file
71
.github/manifests/android-14-avd_x86_64.xml
vendored
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<!--https://ci.android.com/builds/submitted/9964412/kernel_virt_x86_64/latest/manifest_9964412.xml-->
|
||||||
|
<manifest>
|
||||||
|
<remote name="aosp" fetch="https://android.googlesource.com/" review="https://android.googlesource.com/" />
|
||||||
|
|
||||||
|
<default revision="master" remote="aosp" sync-j="4" />
|
||||||
|
|
||||||
|
<superproject name="kernel/superproject" remote="aosp" revision="common-android14-6.1" />
|
||||||
|
|
||||||
|
<project path="build/kernel" name="kernel/build" revision="b0377a072bb3f78cdacfd6d809914a9d1b0c0148">
|
||||||
|
<linkfile dest="tools/bazel" src="kleaf/bazel.sh" />
|
||||||
|
|
||||||
|
<linkfile dest="WORKSPACE" src="kleaf/bazel.WORKSPACE" />
|
||||||
|
|
||||||
|
<linkfile dest="build/build.sh" src="build.sh" />
|
||||||
|
|
||||||
|
<linkfile dest="build/build_abi.sh" src="build_abi.sh" />
|
||||||
|
|
||||||
|
<linkfile dest="build/build_test.sh" src="build_test.sh" />
|
||||||
|
|
||||||
|
<linkfile dest="build/build_utils.sh" src="build_utils.sh" />
|
||||||
|
|
||||||
|
<linkfile dest="build/config.sh" src="config.sh" />
|
||||||
|
|
||||||
|
<linkfile dest="build/envsetup.sh" src="envsetup.sh" />
|
||||||
|
|
||||||
|
<linkfile dest="build/_setup_env.sh" src="_setup_env.sh" />
|
||||||
|
|
||||||
|
<linkfile dest="build/multi-switcher.sh" src="multi-switcher.sh" />
|
||||||
|
|
||||||
|
<linkfile dest="build/abi" src="abi" />
|
||||||
|
|
||||||
|
<linkfile dest="build/static_analysis" src="static_analysis" />
|
||||||
|
</project>
|
||||||
|
|
||||||
|
<project path="common" name="kernel/common" revision="7e35917775b8b3e3346a87f294e334e258bf15e6">
|
||||||
|
<linkfile dest=".source_date_epoch_dir" src="." />
|
||||||
|
</project>
|
||||||
|
|
||||||
|
<project path="kernel/tests" name="kernel/tests" revision="c90a1c1b226b975cc31e709fa96fc1c6ecdbe272" />
|
||||||
|
|
||||||
|
<project path="kernel/configs" name="kernel/configs" revision="52a7267d6a9f9efabf3cb43839bb5e7f7ff05be3" />
|
||||||
|
|
||||||
|
<project path="common-modules/virtual-device" name="kernel/common-modules/virtual-device" revision="0d03de3246301028775f05ea388c2c444344a268" />
|
||||||
|
|
||||||
|
<project path="prebuilts/clang/host/linux-x86" name="platform/prebuilts/clang/host/linux-x86" clone-depth="1" revision="4f7e5adc160ab726ac5bafb260de98e612904c50" />
|
||||||
|
|
||||||
|
<project path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.17-4.8" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.17-4.8" clone-depth="1" revision="f7b0d5b0ee369864d5ac3e96ae24ec9e2b6a52da" />
|
||||||
|
|
||||||
|
<project path="prebuilts/build-tools" name="platform/prebuilts/build-tools" clone-depth="1" revision="dc92e06585a7647bf739a2309a721b82fcfa01d4" />
|
||||||
|
|
||||||
|
<project path="prebuilts/clang-tools" name="platform/prebuilts/clang-tools" clone-depth="1" revision="5611871963f54c688d3ac49e527aecdef21e8567" />
|
||||||
|
|
||||||
|
<project path="prebuilts/kernel-build-tools" name="kernel/prebuilts/build-tools" clone-depth="1" revision="2597cb1b5525e419b7fa806373be673054a68d29" />
|
||||||
|
|
||||||
|
<project path="tools/mkbootimg" name="platform/system/tools/mkbootimg" revision="2680066d0844544b3e78d6022cd21321d31837c3" />
|
||||||
|
|
||||||
|
<project path="prebuilts/bazel/linux-x86_64" name="platform/prebuilts/bazel/linux-x86_64" clone-depth="1" revision="4fdb9395071ff22118311d434d697c2b6fd887b4" />
|
||||||
|
|
||||||
|
<project path="prebuilts/jdk/jdk11" name="platform/prebuilts/jdk/jdk11" clone-depth="1" revision="491e6aa056676f29c4541f71bd738e4e876e4ba2" />
|
||||||
|
|
||||||
|
<project path="prebuilts/ndk-r23" name="toolchain/prebuilts/ndk/r23" clone-depth="1" revision="19ac7e4eded12adb99d4f613490dde6dd0e72664" />
|
||||||
|
|
||||||
|
<project path="external/bazel-skylib" name="platform/external/bazel-skylib" revision="f998e5dc13c03f0eae9e373263d3afff0932c738" />
|
||||||
|
|
||||||
|
<project path="build/bazel_common_rules" name="platform/build/bazel_common_rules" revision="707b2c5fe3d0d7d934a93e00a8a4062e83557831" />
|
||||||
|
|
||||||
|
<project path="external/stardoc" name="platform/external/stardoc" revision="e83f522ee95419e55d2c5654aa6e0143beeef595" />
|
||||||
|
|
||||||
|
<project path="external/python/absl-py" name="platform/external/python/absl-py" revision="393d0b1e3f0fea3e95944a2fd3282cc9f76d4f14" />
|
||||||
|
</manifest>
|
||||||
89
.github/manifests/android-15-avd_aarch64.xml
vendored
Normal file
89
.github/manifests/android-15-avd_aarch64.xml
vendored
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<!-- https://ci.android.com/builds/submitted/11577653/kernel_virt_aarch64/latest/manifest_11577653.xml -->
|
||||||
|
<manifest>
|
||||||
|
<remote name="aosp" fetch="https://android.googlesource.com/" review="https://android.googlesource.com/" />
|
||||||
|
|
||||||
|
<default revision="main" remote="aosp" sync-j="4" />
|
||||||
|
|
||||||
|
<superproject name="kernel/superproject" remote="aosp" revision="common-android15-6.6" />
|
||||||
|
|
||||||
|
<project path="build/kernel" name="kernel/build" groups="ddk" revision="9a2196a1ec1048c2869750c9d3969c88ac18adcd">
|
||||||
|
<linkfile dest="tools/bazel" src="kleaf/bazel.sh" />
|
||||||
|
|
||||||
|
<linkfile dest="WORKSPACE" src="kleaf/bazel.WORKSPACE" />
|
||||||
|
|
||||||
|
<linkfile dest="MODULE.bazel" src="kleaf/bzlmod/bazel.MODULE.bazel" />
|
||||||
|
|
||||||
|
<linkfile dest="WORKSPACE.bzlmod" src="kleaf/bzlmod/bazel.WORKSPACE.bzlmod" />
|
||||||
|
</project>
|
||||||
|
|
||||||
|
<project path="common" name="kernel/common" revision="ac1a7c65ff1bc7ece5569d62f02b121b4f2364f8" />
|
||||||
|
|
||||||
|
<project path="kernel/common-patches" name="kernel/common-patches" revision="3807ce65081de12ef4baa2a04487306672685160">
|
||||||
|
<linkfile dest="common/patches" src="android-mainline" />
|
||||||
|
</project>
|
||||||
|
|
||||||
|
<project path="kernel/tests" name="kernel/tests" revision="ca9fd66f5b48abc92990c9c770f73380b428362b" />
|
||||||
|
|
||||||
|
<project path="kernel/configs" name="kernel/configs" revision="be625f2ccf377a75d0ea86c082c716c322b8d4c6" />
|
||||||
|
|
||||||
|
<project path="common-modules/virtual-device" name="kernel/common-modules/virtual-device" revision="60a24583ac921279e40a44f818040e40abb3ef46" />
|
||||||
|
|
||||||
|
<project path="prebuilts/clang/host/linux-x86" name="platform/prebuilts/clang/host/linux-x86" revision="93a1369ba33743a87bdf0183373f590a36ff7cb1" clone-depth="1" groups="ddk" />
|
||||||
|
|
||||||
|
<project path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.17-4.8" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.17-4.8" clone-depth="1" groups="ddk" revision="cef8f53bb61fbdb02dbf4d433004f6cb637c3bc6" />
|
||||||
|
|
||||||
|
<project path="prebuilts/build-tools" name="platform/prebuilts/build-tools" clone-depth="1" groups="ddk" revision="5aca9957ab19d2668c7f1da1954bbe89652d5fed" />
|
||||||
|
|
||||||
|
<project path="prebuilts/clang-tools" name="platform/prebuilts/clang-tools" clone-depth="1" revision="69f9fb9b8e75c6f1ff01f380d5251757785bb823" />
|
||||||
|
|
||||||
|
<project path="prebuilts/kernel-build-tools" name="kernel/prebuilts/build-tools" clone-depth="1" groups="ddk" revision="b09295493adc8d804b6d24286660f6e451e387fd" />
|
||||||
|
|
||||||
|
<project path="prebuilts/rust" name="platform/prebuilts/rust" revision="adc0e5499c3ddac831ca596d12cbef8d9747f737" clone-depth="1" />
|
||||||
|
|
||||||
|
<project path="prebuilts/tradefed" name="platform/tools/tradefederation/prebuilts" clone-depth="1" revision="a76ca09c5593e22e65b0d823d508882c6c64c13e" />
|
||||||
|
|
||||||
|
<project path="prebuilts/asuite" name="platform/prebuilts/asuite" clone-depth="1" revision="24510f175cb313a92241500efee917c2930d5d30" />
|
||||||
|
|
||||||
|
<project path="tools/mkbootimg" name="platform/system/tools/mkbootimg" revision="28b7934249c2885db8b561f1439d74663fcdce93" />
|
||||||
|
|
||||||
|
<project path="prebuilts/jdk/jdk11" name="platform/prebuilts/jdk/jdk11" revision="c6c90521b7c317f13d41bbd9336a8d45ee202cec" clone-depth="1" groups="ddk" />
|
||||||
|
|
||||||
|
<project path="prebuilts/ndk-r26" name="toolchain/prebuilts/ndk/r26" clone-depth="1" groups="ddk" revision="e535051ebc04204cec44bde38f62385d63180388" />
|
||||||
|
|
||||||
|
<project path="external/bazel-skylib" name="platform/external/bazel-skylib" groups="ddk" revision="6b103c40d8113f001475d5e13672922ef2aa0e5a" />
|
||||||
|
|
||||||
|
<project path="build/bazel_common_rules" name="platform/build/bazel_common_rules" groups="ddk" revision="2a10807a06153b5862da0369f4b6b368afc2dd08" />
|
||||||
|
|
||||||
|
<project path="external/libcap-ng" name="platform/external/libcap-ng" revision="2bcc92ae19481dd2b8d3ce3abdfbbee49261abe6" />
|
||||||
|
|
||||||
|
<project path="external/libcap" name="platform/external/libcap" revision="d7d1a0a38c5be06a7e7d6391d140b54878836f48" />
|
||||||
|
|
||||||
|
<project path="external/stardoc" name="platform/external/stardoc" groups="ddk" revision="f31250f9f5b03834d9964aaee7a3794c1d73d4a2" />
|
||||||
|
|
||||||
|
<project path="external/python/absl-py" name="platform/external/python/absl-py" groups="ddk" revision="9ae5a78fc57c3cd539398373ae39601a8b923e62" />
|
||||||
|
|
||||||
|
<project path="external/bazelbuild-bazel-central-registry" name="platform/external/bazelbuild-bazel-central-registry" revision="3422f064566c274ea66633442521704d4a22486d" groups="ddk" />
|
||||||
|
|
||||||
|
<project path="external/bazelbuild-platforms" name="platform/external/bazelbuild-platforms" groups="ddk" revision="e352aabd0131f3ac3f340282a43ba85ffc3fe8fa" />
|
||||||
|
|
||||||
|
<project path="external/bazelbuild-apple_support" name="platform/external/bazelbuild-apple_support" groups="ddk" revision="f6003e1e3763f8aad9fb9acae79cfa5fff9ae988" />
|
||||||
|
|
||||||
|
<project path="external/bazelbuild-rules_cc" name="platform/external/bazelbuild-rules_cc" groups="ddk" revision="f0df148dbeb9b9ed3816aad328ebe7c65efaaa24" />
|
||||||
|
|
||||||
|
<project path="external/bazelbuild-rules_java" name="platform/external/bazelbuild-rules_java" groups="ddk" revision="8e548c7053dffd1717d565f0409a88992f401da1" />
|
||||||
|
|
||||||
|
<project path="external/bazelbuild-rules_license" name="platform/external/bazelbuild-rules_license" groups="ddk" revision="f578df4fd057ffe2023728444759535685631548" />
|
||||||
|
|
||||||
|
<project path="external/bazelbuild-rules_pkg" name="platform/external/bazelbuild-rules_pkg" groups="ddk" revision="429887dfd8db834498ad95e99043f771a3882af0" />
|
||||||
|
|
||||||
|
<project path="external/bazelbuild-rules_python" name="platform/external/bazelbuild-rules_python" groups="ddk" revision="f71847ac898655b67634bb14e77a7408c4fb5e00" />
|
||||||
|
|
||||||
|
<project path="external/bazelbuild-rules_rust" name="platform/external/bazelbuild-rules_rust" groups="ddk" revision="1520b49835be9122c2424231357d4db80069cc38" />
|
||||||
|
|
||||||
|
<project path="external/pigz" name="platform/external/pigz" groups="ddk" revision="9bc9fa17d499ddde88b77820f6d063e16c0cdd42" />
|
||||||
|
|
||||||
|
<project path="external/zlib" name="platform/external/zlib" groups="ddk" revision="eff168fd731068a3faddd9aae056875e10014a51" />
|
||||||
|
|
||||||
|
<project path="external/zopfli" name="platform/external/zopfli" groups="ddk" revision="36c79f00e5229800d2aaa13fc42c301ec8ef1153" />
|
||||||
|
</manifest>
|
||||||
89
.github/manifests/android-15-avd_x86_64.xml
vendored
Normal file
89
.github/manifests/android-15-avd_x86_64.xml
vendored
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<!-- https://ci.android.com/builds/submitted/11577653/kernel_virt_x86_64/latest/manifest_11577653.xml -->
|
||||||
|
<manifest>
|
||||||
|
<remote name="aosp" fetch="https://android.googlesource.com/" review="https://android.googlesource.com/" />
|
||||||
|
|
||||||
|
<default revision="main" remote="aosp" sync-j="4" />
|
||||||
|
|
||||||
|
<superproject name="kernel/superproject" remote="aosp" revision="common-android15-6.6" />
|
||||||
|
|
||||||
|
<project path="build/kernel" name="kernel/build" groups="ddk" revision="9a2196a1ec1048c2869750c9d3969c88ac18adcd">
|
||||||
|
<linkfile dest="tools/bazel" src="kleaf/bazel.sh" />
|
||||||
|
|
||||||
|
<linkfile dest="WORKSPACE" src="kleaf/bazel.WORKSPACE" />
|
||||||
|
|
||||||
|
<linkfile dest="MODULE.bazel" src="kleaf/bzlmod/bazel.MODULE.bazel" />
|
||||||
|
|
||||||
|
<linkfile dest="WORKSPACE.bzlmod" src="kleaf/bzlmod/bazel.WORKSPACE.bzlmod" />
|
||||||
|
</project>
|
||||||
|
|
||||||
|
<project path="common" name="kernel/common" revision="ac1a7c65ff1bc7ece5569d62f02b121b4f2364f8" />
|
||||||
|
|
||||||
|
<project path="kernel/common-patches" name="kernel/common-patches" revision="3807ce65081de12ef4baa2a04487306672685160">
|
||||||
|
<linkfile dest="common/patches" src="android-mainline" />
|
||||||
|
</project>
|
||||||
|
|
||||||
|
<project path="kernel/tests" name="kernel/tests" revision="ca9fd66f5b48abc92990c9c770f73380b428362b" />
|
||||||
|
|
||||||
|
<project path="kernel/configs" name="kernel/configs" revision="be625f2ccf377a75d0ea86c082c716c322b8d4c6" />
|
||||||
|
|
||||||
|
<project path="common-modules/virtual-device" name="kernel/common-modules/virtual-device" revision="60a24583ac921279e40a44f818040e40abb3ef46" />
|
||||||
|
|
||||||
|
<project path="prebuilts/clang/host/linux-x86" name="platform/prebuilts/clang/host/linux-x86" revision="93a1369ba33743a87bdf0183373f590a36ff7cb1" clone-depth="1" groups="ddk" />
|
||||||
|
|
||||||
|
<project path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.17-4.8" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.17-4.8" clone-depth="1" groups="ddk" revision="cef8f53bb61fbdb02dbf4d433004f6cb637c3bc6" />
|
||||||
|
|
||||||
|
<project path="prebuilts/build-tools" name="platform/prebuilts/build-tools" clone-depth="1" groups="ddk" revision="5aca9957ab19d2668c7f1da1954bbe89652d5fed" />
|
||||||
|
|
||||||
|
<project path="prebuilts/clang-tools" name="platform/prebuilts/clang-tools" clone-depth="1" revision="69f9fb9b8e75c6f1ff01f380d5251757785bb823" />
|
||||||
|
|
||||||
|
<project path="prebuilts/kernel-build-tools" name="kernel/prebuilts/build-tools" clone-depth="1" groups="ddk" revision="b09295493adc8d804b6d24286660f6e451e387fd" />
|
||||||
|
|
||||||
|
<project path="prebuilts/rust" name="platform/prebuilts/rust" revision="adc0e5499c3ddac831ca596d12cbef8d9747f737" clone-depth="1" />
|
||||||
|
|
||||||
|
<project path="prebuilts/tradefed" name="platform/tools/tradefederation/prebuilts" clone-depth="1" revision="a76ca09c5593e22e65b0d823d508882c6c64c13e" />
|
||||||
|
|
||||||
|
<project path="prebuilts/asuite" name="platform/prebuilts/asuite" clone-depth="1" revision="24510f175cb313a92241500efee917c2930d5d30" />
|
||||||
|
|
||||||
|
<project path="tools/mkbootimg" name="platform/system/tools/mkbootimg" revision="28b7934249c2885db8b561f1439d74663fcdce93" />
|
||||||
|
|
||||||
|
<project path="prebuilts/jdk/jdk11" name="platform/prebuilts/jdk/jdk11" revision="c6c90521b7c317f13d41bbd9336a8d45ee202cec" clone-depth="1" groups="ddk" />
|
||||||
|
|
||||||
|
<project path="prebuilts/ndk-r26" name="toolchain/prebuilts/ndk/r26" clone-depth="1" groups="ddk" revision="e535051ebc04204cec44bde38f62385d63180388" />
|
||||||
|
|
||||||
|
<project path="external/bazel-skylib" name="platform/external/bazel-skylib" groups="ddk" revision="6b103c40d8113f001475d5e13672922ef2aa0e5a" />
|
||||||
|
|
||||||
|
<project path="build/bazel_common_rules" name="platform/build/bazel_common_rules" groups="ddk" revision="2a10807a06153b5862da0369f4b6b368afc2dd08" />
|
||||||
|
|
||||||
|
<project path="external/libcap-ng" name="platform/external/libcap-ng" revision="2bcc92ae19481dd2b8d3ce3abdfbbee49261abe6" />
|
||||||
|
|
||||||
|
<project path="external/libcap" name="platform/external/libcap" revision="d7d1a0a38c5be06a7e7d6391d140b54878836f48" />
|
||||||
|
|
||||||
|
<project path="external/stardoc" name="platform/external/stardoc" groups="ddk" revision="f31250f9f5b03834d9964aaee7a3794c1d73d4a2" />
|
||||||
|
|
||||||
|
<project path="external/python/absl-py" name="platform/external/python/absl-py" groups="ddk" revision="9ae5a78fc57c3cd539398373ae39601a8b923e62" />
|
||||||
|
|
||||||
|
<project path="external/bazelbuild-bazel-central-registry" name="platform/external/bazelbuild-bazel-central-registry" revision="3422f064566c274ea66633442521704d4a22486d" groups="ddk" />
|
||||||
|
|
||||||
|
<project path="external/bazelbuild-platforms" name="platform/external/bazelbuild-platforms" groups="ddk" revision="e352aabd0131f3ac3f340282a43ba85ffc3fe8fa" />
|
||||||
|
|
||||||
|
<project path="external/bazelbuild-apple_support" name="platform/external/bazelbuild-apple_support" groups="ddk" revision="f6003e1e3763f8aad9fb9acae79cfa5fff9ae988" />
|
||||||
|
|
||||||
|
<project path="external/bazelbuild-rules_cc" name="platform/external/bazelbuild-rules_cc" groups="ddk" revision="f0df148dbeb9b9ed3816aad328ebe7c65efaaa24" />
|
||||||
|
|
||||||
|
<project path="external/bazelbuild-rules_java" name="platform/external/bazelbuild-rules_java" groups="ddk" revision="8e548c7053dffd1717d565f0409a88992f401da1" />
|
||||||
|
|
||||||
|
<project path="external/bazelbuild-rules_license" name="platform/external/bazelbuild-rules_license" groups="ddk" revision="f578df4fd057ffe2023728444759535685631548" />
|
||||||
|
|
||||||
|
<project path="external/bazelbuild-rules_pkg" name="platform/external/bazelbuild-rules_pkg" groups="ddk" revision="429887dfd8db834498ad95e99043f771a3882af0" />
|
||||||
|
|
||||||
|
<project path="external/bazelbuild-rules_python" name="platform/external/bazelbuild-rules_python" groups="ddk" revision="f71847ac898655b67634bb14e77a7408c4fb5e00" />
|
||||||
|
|
||||||
|
<project path="external/bazelbuild-rules_rust" name="platform/external/bazelbuild-rules_rust" groups="ddk" revision="1520b49835be9122c2424231357d4db80069cc38" />
|
||||||
|
|
||||||
|
<project path="external/pigz" name="platform/external/pigz" groups="ddk" revision="9bc9fa17d499ddde88b77820f6d063e16c0cdd42" />
|
||||||
|
|
||||||
|
<project path="external/zlib" name="platform/external/zlib" groups="ddk" revision="eff168fd731068a3faddd9aae056875e10014a51" />
|
||||||
|
|
||||||
|
<project path="external/zopfli" name="platform/external/zopfli" groups="ddk" revision="36c79f00e5229800d2aaa13fc42c301ec8ef1153" />
|
||||||
|
</manifest>
|
||||||
2
.github/scripts/build_a12.sh
vendored
2
.github/scripts/build_a12.sh
vendored
@@ -51,7 +51,7 @@ build_from_image() {
|
|||||||
echo "[+] Images to upload"
|
echo "[+] Images to upload"
|
||||||
find . -type f -name "*.gz"
|
find . -type f -name "*.gz"
|
||||||
|
|
||||||
find . -type f -name "*.gz" -exec python3 "$GITHUB_WORKSPACE"/KernelSU/scripts/ksubot.py {} +
|
# find . -type f -name "*.gz" -exec python3 "$GITHUB_WORKSPACE"/KernelSU/scripts/ksubot.py {} +
|
||||||
}
|
}
|
||||||
|
|
||||||
for dir in Image*; do
|
for dir in Image*; do
|
||||||
|
|||||||
2
.github/scripts/build_a13.sh
vendored
2
.github/scripts/build_a13.sh
vendored
@@ -30,7 +30,7 @@ build_from_image() {
|
|||||||
echo '[+] Images to upload'
|
echo '[+] Images to upload'
|
||||||
find . -type f -name "*.gz"
|
find . -type f -name "*.gz"
|
||||||
|
|
||||||
find . -type f -name "*.gz" -exec python3 "$GITHUB_WORKSPACE"/KernelSU/scripts/ksubot.py {} +
|
# find . -type f -name "*.gz" -exec python3 "$GITHUB_WORKSPACE"/KernelSU/scripts/ksubot.py {} +
|
||||||
}
|
}
|
||||||
|
|
||||||
for dir in Image*; do
|
for dir in Image*; do
|
||||||
|
|||||||
4
.github/workflows/add-device.yml
vendored
4
.github/workflows/add-device.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
|||||||
- name: Make pull request
|
- name: Make pull request
|
||||||
if: steps.handle-add-device.outputs.success == 'true'
|
if: steps.handle-add-device.outputs.success == 'true'
|
||||||
id: cpr
|
id: cpr
|
||||||
uses: peter-evans/create-pull-request@v4
|
uses: peter-evans/create-pull-request@v6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
commit-message: "[add device]: ${{ steps.handle-add-device.outputs.device }}"
|
commit-message: "[add device]: ${{ steps.handle-add-device.outputs.device }}"
|
||||||
@@ -53,7 +53,7 @@ jobs:
|
|||||||
message: "Cannot create pull request. Please check the issue content. Or you can create a pull request manually."
|
message: "Cannot create pull request. Please check the issue content. Or you can create a pull request manually."
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: close issue
|
- name: close issue
|
||||||
uses: peter-evans/close-issue@v1
|
uses: peter-evans/close-issue@v3
|
||||||
with:
|
with:
|
||||||
issue-number: ${{ github.event.issue.number }}
|
issue-number: ${{ github.event.issue.number }}
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
137
.github/workflows/avd-kernel.yml
vendored
Normal file
137
.github/workflows/avd-kernel.yml
vendored
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
name: GKI Kernel Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
version_name:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
description: >
|
||||||
|
With SUBLEVEL of kernel,
|
||||||
|
for example: android12-5.10.66
|
||||||
|
arch:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
description: >
|
||||||
|
Build arch: aarch64/x86_64
|
||||||
|
debug:
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
manifest_name:
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
description: >
|
||||||
|
Local repo manifest xml path,
|
||||||
|
typically for AVD kernel build.
|
||||||
|
secrets:
|
||||||
|
BOOT_SIGN_KEY:
|
||||||
|
required: false
|
||||||
|
CHAT_ID:
|
||||||
|
required: false
|
||||||
|
BOT_TOKEN:
|
||||||
|
required: false
|
||||||
|
MESSAGE_THREAD_ID:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build ${{ inputs.version_name }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Maximize build space
|
||||||
|
uses: easimon/maximize-build-space@master
|
||||||
|
with:
|
||||||
|
root-reserve-mb: 8192
|
||||||
|
temp-reserve-mb: 2048
|
||||||
|
remove-dotnet: 'true'
|
||||||
|
remove-android: 'true'
|
||||||
|
remove-haskell: 'true'
|
||||||
|
remove-codeql: 'true'
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: KernelSU
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup need_upload
|
||||||
|
id: need_upload
|
||||||
|
run: |
|
||||||
|
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
||||||
|
echo "UPLOAD=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Setup kernel source
|
||||||
|
run: |
|
||||||
|
echo "Free space:"
|
||||||
|
df -h
|
||||||
|
cd $GITHUB_WORKSPACE
|
||||||
|
sudo apt-get install repo -y
|
||||||
|
mkdir android-kernel && cd android-kernel
|
||||||
|
repo init --depth=1 -u https://android.googlesource.com/kernel/manifest -m "$GITHUB_WORKSPACE/KernelSU/.github/manifests/${{ inputs.manifest_name }}" --repo-rev=v2.16
|
||||||
|
repo --version
|
||||||
|
repo --trace sync -c -j$(nproc --all) --no-tags
|
||||||
|
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"
|
||||||
|
GKI_ROOT=$(pwd)
|
||||||
|
echo "[+] GKI_ROOT: $GKI_ROOT"
|
||||||
|
echo "[+] Copy KernelSU driver to $GKI_ROOT/common/drivers"
|
||||||
|
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $GKI_ROOT/common/drivers/kernelsu
|
||||||
|
echo "[+] Add KernelSU driver to Makefile"
|
||||||
|
DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile
|
||||||
|
DRIVER_KCONFIG=$GKI_ROOT/common/drivers/Kconfig
|
||||||
|
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE"
|
||||||
|
grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG"
|
||||||
|
echo "[+] Apply KernelSU patches"
|
||||||
|
cd $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/$PATCH_PATH/*.patch || echo "[-] No patch found"
|
||||||
|
|
||||||
|
if [ "$IS_DEBUG_KERNEL" = "true" ]; then
|
||||||
|
echo "[+] Enable debug features for kernel"
|
||||||
|
printf "\nccflags-y += -DCONFIG_KSU_DEBUG\n" >> $GITHUB_WORKSPACE/KernelSU/kernel/Makefile
|
||||||
|
fi
|
||||||
|
repo status
|
||||||
|
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: Make working directory clean to avoid dirty
|
||||||
|
working-directory: android-kernel
|
||||||
|
run: |
|
||||||
|
rm common/android/abi_gki_protected_exports_* || echo "No protected exports!"
|
||||||
|
git config --global user.email "bot@kernelsu.org"
|
||||||
|
git config --global user.name "KernelSUBot"
|
||||||
|
cd common/ && git add -A && git commit -a -m "Add KernelSU"
|
||||||
|
repo status
|
||||||
|
|
||||||
|
- name: Build kernel
|
||||||
|
working-directory: android-kernel
|
||||||
|
run: |
|
||||||
|
if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then
|
||||||
|
export KSU_EXPECTED_SIZE=${{ vars.EXPECTED_SIZE }}
|
||||||
|
export KSU_EXPECTED_HASH=${{ vars.EXPECTED_HASH }}
|
||||||
|
fi
|
||||||
|
tools/bazel run --config=fast --config=stamp --lto=thin //common-modules/virtual-device:virtual_device_${{ inputs.arch }}_dist -- --dist_dir=dist
|
||||||
|
NAME=kernel-${{ inputs.arch }}-avd-${{ inputs.version_name }}-${{ env.kernelsu_version }}
|
||||||
|
TARGET_IMAGE=dist/bzImage
|
||||||
|
if [ ! -e $TARGET_IMAGE ]; then
|
||||||
|
TARGET_IMAGE=dist/Image
|
||||||
|
fi
|
||||||
|
mv $TARGET_IMAGE $NAME
|
||||||
|
echo "file_path=android-kernel/$NAME" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Upload Kernel
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: kernel-${{ inputs.arch }}-avd-${{ inputs.version_name }}-${{ env.kernelsu_version }}
|
||||||
|
path: "${{ env.file_path }}"
|
||||||
31
.github/workflows/build-debug-kernel.yml
vendored
31
.github/workflows/build-debug-kernel.yml
vendored
@@ -7,9 +7,9 @@ jobs:
|
|||||||
uses: ./.github/workflows/gki-kernel.yml
|
uses: ./.github/workflows/gki-kernel.yml
|
||||||
with:
|
with:
|
||||||
version: android12-5.10
|
version: android12-5.10
|
||||||
version_name: android12-5.10.185
|
version_name: android12-5.10.209
|
||||||
tag: android12-5.10-2023-09
|
tag: android12-5.10-2024-05
|
||||||
os_patch_level: 2023-09
|
os_patch_level: 2024-05
|
||||||
patch_path: "5.10"
|
patch_path: "5.10"
|
||||||
debug: true
|
debug: true
|
||||||
build-debug-kernel-a13:
|
build-debug-kernel-a13:
|
||||||
@@ -17,11 +17,11 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- version: "5.10"
|
- version: "5.10"
|
||||||
sub_level: 187
|
sub_level: 209
|
||||||
os_patch_level: 2023-08
|
os_patch_level: 2024-05
|
||||||
- version: "5.15"
|
- version: "5.15"
|
||||||
sub_level: 119
|
sub_level: 148
|
||||||
os_patch_level: 2023-09
|
os_patch_level: 2024-05
|
||||||
uses: ./.github/workflows/gki-kernel.yml
|
uses: ./.github/workflows/gki-kernel.yml
|
||||||
with:
|
with:
|
||||||
version: android13-${{ matrix.version }}
|
version: android13-${{ matrix.version }}
|
||||||
@@ -29,3 +29,20 @@ jobs:
|
|||||||
tag: android13-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
tag: android13-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||||
patch_path: ${{ matrix.version }}
|
patch_path: ${{ matrix.version }}
|
||||||
debug: true
|
debug: true
|
||||||
|
build-debug-kernel-a14:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 148
|
||||||
|
os_patch_level: 2024-05
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 75
|
||||||
|
os_patch_level: 2024-05
|
||||||
|
uses: ./.github/workflows/gki-kernel.yml
|
||||||
|
with:
|
||||||
|
version: android14-${{ matrix.version }}
|
||||||
|
version_name: android14-${{ matrix.version }}.${{ matrix.sub_level }}
|
||||||
|
tag: android14-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||||
|
patch_path: ${{ matrix.version }}
|
||||||
|
debug: true
|
||||||
41
.github/workflows/build-kernel-a12.yml
vendored
41
.github/workflows/build-kernel-a12.yml
vendored
@@ -21,30 +21,12 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
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
|
|
||||||
os_patch_level: 2023-01
|
|
||||||
- sub_level: 160
|
|
||||||
os_patch_level: 2023-03
|
|
||||||
- sub_level: 168
|
|
||||||
os_patch_level: 2023-05
|
|
||||||
- sub_level: 177
|
|
||||||
os_patch_level: 2023-07
|
|
||||||
- sub_level: 185
|
|
||||||
os_patch_level: 2023-09
|
|
||||||
- sub_level: 198
|
- sub_level: 198
|
||||||
os_patch_level: 2023-11
|
os_patch_level: 2024-01
|
||||||
|
- sub_level: 205
|
||||||
|
os_patch_level: 2024-03
|
||||||
|
- sub_level: 209
|
||||||
|
os_patch_level: 2024-05
|
||||||
uses: ./.github/workflows/gki-kernel.yml
|
uses: ./.github/workflows/gki-kernel.yml
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
with:
|
with:
|
||||||
@@ -85,7 +67,7 @@ jobs:
|
|||||||
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
|
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
|
||||||
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
|
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
|
||||||
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
|
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
|
||||||
pip3 install telethon==1.31.1
|
pip3 install telethon
|
||||||
|
|
||||||
- name: Set boot sign key
|
- name: Set boot sign key
|
||||||
env:
|
env:
|
||||||
@@ -97,7 +79,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Bot session cache
|
- name: Bot session cache
|
||||||
id: bot_session_cache
|
id: bot_session_cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
|
if: false
|
||||||
with:
|
with:
|
||||||
path: scripts/ksubot.session
|
path: scripts/ksubot.session
|
||||||
key: ${{ runner.os }}-bot-session
|
key: ${{ runner.os }}-bot-session
|
||||||
@@ -129,7 +112,7 @@ jobs:
|
|||||||
uses: ./.github/workflows/gki-kernel.yml
|
uses: ./.github/workflows/gki-kernel.yml
|
||||||
with:
|
with:
|
||||||
version: android12-5.10
|
version: android12-5.10
|
||||||
version_name: android12-5.10.177
|
version_name: android12-5.10.209
|
||||||
tag: android12-5.10-2023-06
|
tag: android12-5.10-2024-05
|
||||||
os_patch_level: 2023-06
|
os_patch_level: 2024-05
|
||||||
patch_path: "5.10"
|
patch_path: "5.10"
|
||||||
70
.github/workflows/build-kernel-a13.yml
vendored
70
.github/workflows/build-kernel-a13.yml
vendored
@@ -21,57 +21,30 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- version: "5.10"
|
|
||||||
sub_level: 107
|
|
||||||
os_patch_level: 2022-11
|
|
||||||
- version: "5.10"
|
|
||||||
sub_level: 149
|
|
||||||
os_patch_level: 2023-01
|
|
||||||
- version: "5.10"
|
|
||||||
sub_level: 157
|
|
||||||
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.10"
|
|
||||||
sub_level: 186
|
|
||||||
os_patch_level: 2023-08
|
|
||||||
- version: "5.10"
|
|
||||||
sub_level: 186
|
|
||||||
os_patch_level: 2023-09
|
|
||||||
- version: "5.10"
|
- version: "5.10"
|
||||||
sub_level: 189
|
sub_level: 189
|
||||||
os_patch_level: 2023-11
|
os_patch_level: 2023-11
|
||||||
- version: "5.10"
|
- version: "5.10"
|
||||||
sub_level: 198
|
sub_level: 198
|
||||||
os_patch_level: 2023-12
|
os_patch_level: 2024-01
|
||||||
- version: "5.15"
|
- version: "5.10"
|
||||||
sub_level: 41
|
sub_level: 205
|
||||||
os_patch_level: 2022-11
|
os_patch_level: 2024-03
|
||||||
- version: "5.15"
|
- version: "5.10"
|
||||||
sub_level: 74
|
sub_level: 209
|
||||||
os_patch_level: 2023-01
|
os_patch_level: 2024-05
|
||||||
- version: "5.15"
|
|
||||||
sub_level: 78
|
|
||||||
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-07
|
|
||||||
- version: "5.15"
|
|
||||||
sub_level: 119
|
|
||||||
os_patch_level: 2023-09
|
|
||||||
- version: "5.15"
|
- version: "5.15"
|
||||||
sub_level: 123
|
sub_level: 123
|
||||||
os_patch_level: 2023-11
|
os_patch_level: 2023-11
|
||||||
- version: "5.15"
|
- version: "5.15"
|
||||||
sub_level: 137
|
sub_level: 137
|
||||||
os_patch_level: 2023-12
|
os_patch_level: 2024-01
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 144
|
||||||
|
os_patch_level: 2024-03
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 148
|
||||||
|
os_patch_level: 2024-05
|
||||||
uses: ./.github/workflows/gki-kernel.yml
|
uses: ./.github/workflows/gki-kernel.yml
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
with:
|
with:
|
||||||
@@ -112,7 +85,7 @@ jobs:
|
|||||||
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
|
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
|
||||||
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
|
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
|
||||||
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
|
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
|
||||||
pip3 install telethon==1.31.1
|
pip3 install telethon
|
||||||
|
|
||||||
- name: Set boot sign key
|
- name: Set boot sign key
|
||||||
env:
|
env:
|
||||||
@@ -124,7 +97,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Bot session cache
|
- name: Bot session cache
|
||||||
id: bot_session_cache
|
id: bot_session_cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
|
if: false
|
||||||
with:
|
with:
|
||||||
path: scripts/ksubot.session
|
path: scripts/ksubot.session
|
||||||
key: ${{ runner.os }}-bot-session
|
key: ${{ runner.os }}-bot-session
|
||||||
@@ -157,15 +131,15 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- version: "5.10"
|
- version: "5.10"
|
||||||
sub_level: 189
|
sub_level: 209
|
||||||
os_patch_level: 2023-10
|
os_patch_level: 2024-05
|
||||||
- version: "5.15"
|
- version: "5.15"
|
||||||
sub_level: 123
|
sub_level: 148
|
||||||
os_patch_level: 2023-10
|
os_patch_level: 2024-05
|
||||||
uses: ./.github/workflows/gki-kernel.yml
|
uses: ./.github/workflows/gki-kernel.yml
|
||||||
with:
|
with:
|
||||||
version: android13-${{ matrix.version }}
|
version: android13-${{ matrix.version }}
|
||||||
version_name: android13-${{ matrix.version }}.${{ matrix.sub_level }}
|
version_name: android13-${{ matrix.version }}.${{ matrix.sub_level }}
|
||||||
tag: android13-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
tag: android13-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||||
os_patch_level: ${{ matrix.os_patch_level }}
|
os_patch_level: ${{ matrix.os_patch_level }}
|
||||||
patch_path: ${{ matrix.version }}
|
patch_path: ${{ matrix.version }}
|
||||||
34
.github/workflows/build-kernel-a14.yml
vendored
34
.github/workflows/build-kernel-a14.yml
vendored
@@ -21,12 +21,18 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- version: "5.15"
|
|
||||||
sub_level: 110
|
|
||||||
os_patch_level: 2023-09
|
|
||||||
- version: "5.15"
|
- version: "5.15"
|
||||||
sub_level: 131
|
sub_level: 131
|
||||||
os_patch_level: 2023-11
|
os_patch_level: 2023-11
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 137
|
||||||
|
os_patch_level: 2024-01
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 144
|
||||||
|
os_patch_level: 2024-03
|
||||||
|
- version: "5.15"
|
||||||
|
sub_level: 148
|
||||||
|
os_patch_level: 2024-05
|
||||||
- version: "6.1"
|
- version: "6.1"
|
||||||
sub_level: 25
|
sub_level: 25
|
||||||
os_patch_level: 2023-10
|
os_patch_level: 2023-10
|
||||||
@@ -35,7 +41,13 @@ jobs:
|
|||||||
os_patch_level: 2023-11
|
os_patch_level: 2023-11
|
||||||
- version: "6.1"
|
- version: "6.1"
|
||||||
sub_level: 57
|
sub_level: 57
|
||||||
os_patch_level: 2023-12
|
os_patch_level: 2024-01
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 68
|
||||||
|
os_patch_level: 2024-03
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 75
|
||||||
|
os_patch_level: 2024-05
|
||||||
uses: ./.github/workflows/gki-kernel.yml
|
uses: ./.github/workflows/gki-kernel.yml
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
with:
|
with:
|
||||||
@@ -76,7 +88,7 @@ jobs:
|
|||||||
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
|
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
|
||||||
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
|
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
|
||||||
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
|
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
|
||||||
pip3 install telethon==1.31.1
|
pip3 install telethon
|
||||||
|
|
||||||
- name: Set boot sign key
|
- name: Set boot sign key
|
||||||
env:
|
env:
|
||||||
@@ -88,7 +100,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Bot session cache
|
- name: Bot session cache
|
||||||
id: bot_session_cache
|
id: bot_session_cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
|
if: false
|
||||||
with:
|
with:
|
||||||
path: scripts/ksubot.session
|
path: scripts/ksubot.session
|
||||||
key: ${{ runner.os }}-bot-session
|
key: ${{ runner.os }}-bot-session
|
||||||
@@ -121,12 +134,15 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- version: "5.15"
|
- version: "5.15"
|
||||||
sub_level: 110
|
sub_level: 148
|
||||||
os_patch_level: 2023-09
|
os_patch_level: 2024-05
|
||||||
|
- version: "6.1"
|
||||||
|
sub_level: 75
|
||||||
|
os_patch_level: 2024-05
|
||||||
uses: ./.github/workflows/gki-kernel.yml
|
uses: ./.github/workflows/gki-kernel.yml
|
||||||
with:
|
with:
|
||||||
version: android14-${{ matrix.version }}
|
version: android14-${{ matrix.version }}
|
||||||
version_name: android14-${{ matrix.version }}.${{ matrix.sub_level }}
|
version_name: android14-${{ matrix.version }}.${{ matrix.sub_level }}
|
||||||
tag: android14-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
tag: android14-${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||||
os_patch_level: ${{ matrix.os_patch_level }}
|
os_patch_level: ${{ matrix.os_patch_level }}
|
||||||
patch_path: ${{ matrix.version }}
|
patch_path: ${{ matrix.version }}
|
||||||
79
.github/workflows/build-kernel-arcvm.yml
vendored
79
.github/workflows/build-kernel-arcvm.yml
vendored
@@ -13,17 +13,23 @@ on:
|
|||||||
workflow_call:
|
workflow_call:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
git_tag: chromeos-5.10-arcvm
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: github.event_name != 'pull_request' || (github.event_name == 'pull_request' && !github.event.pull_request.draft)
|
if: github.event_name != 'pull_request' || (github.event_name == 'pull_request' && !github.event.pull_request.draft)
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
arch: [x86_64]
|
|
||||||
version: ["5.10.178"]
|
|
||||||
include:
|
include:
|
||||||
- arch: x86_64
|
- arch: x86_64
|
||||||
git_tag: chromeos-5.10-arcvm
|
kernel_image_name: bzImage
|
||||||
file_name: "bzImage"
|
build_config: build.config.gki.x86_64
|
||||||
|
defconfig: x86_64_arcvm_defconfig
|
||||||
|
- arch: arm64
|
||||||
|
kernel_image_name: Image
|
||||||
|
build_config: build.config.gki.aarch64
|
||||||
|
defconfig: arm64_arcvm_defconfig
|
||||||
|
|
||||||
name: Build ChromeOS ARCVM kernel
|
name: Build ChromeOS ARCVM kernel
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
@@ -66,8 +72,17 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup kernel source
|
- name: Setup kernel source
|
||||||
run: git clone https://chromium.googlesource.com/chromiumos/third_party/kernel.git -b ${{ matrix.git_tag }} --depth=1
|
run: git clone https://chromium.googlesource.com/chromiumos/third_party/kernel.git -b ${{ env.git_tag }} --depth=1
|
||||||
|
|
||||||
|
- name: Extract version from Makefile
|
||||||
|
working-directory: kernel
|
||||||
|
run: |
|
||||||
|
VERSION=$(grep -E '^VERSION = ' Makefile | awk '{print $3}')
|
||||||
|
PATCHLEVEL=$(grep -E '^PATCHLEVEL = ' Makefile | awk '{print $3}')
|
||||||
|
SUBLEVEL=$(grep -E '^SUBLEVEL = ' Makefile | awk '{print $3}')
|
||||||
|
echo "ChromeOS ARCVM Linux kernel version: $VERSION.$PATCHLEVEL.$SUBLEVEL"
|
||||||
|
echo "version=$VERSION.$PATCHLEVEL.$SUBLEVEL" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Setup KernelSU
|
- name: Setup KernelSU
|
||||||
working-directory: kernel
|
working-directory: kernel
|
||||||
run: |
|
run: |
|
||||||
@@ -89,15 +104,18 @@ jobs:
|
|||||||
|
|
||||||
echo "[+] KernelSU setup done."
|
echo "[+] KernelSU setup done."
|
||||||
cd $GITHUB_WORKSPACE/KernelSU
|
cd $GITHUB_WORKSPACE/KernelSU
|
||||||
VERSION=$(($(git rev-list --count HEAD) + 10200))
|
KSU_VERSION=$(($(git rev-list --count HEAD) + 10200))
|
||||||
echo "VERSION: $VERSION"
|
echo "KernelSU version: $KSU_VERSION"
|
||||||
echo "kernelsu_version=$VERSION" >> $GITHUB_ENV
|
echo "kernelsu_version=$KSU_VERSION" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Build Kernel
|
- name: Build Kernel
|
||||||
working-directory: kernel
|
working-directory: kernel
|
||||||
|
env:
|
||||||
|
KERNEL_IMAGE_NAME: ${{ matrix.kernel_image_name }}
|
||||||
|
ARCH: ${{ matrix.arch }}
|
||||||
run: |
|
run: |
|
||||||
set -a && . build.config.gki.x86_64; set +a
|
set -a && . ${{ matrix.build_config }}; set +a
|
||||||
export DEFCONFIG=x86_64_arcvm_defconfig
|
export DEFCONFIG=${{ matrix.defconfig }}
|
||||||
if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then
|
if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then
|
||||||
export KSU_EXPECTED_SIZE=${{ vars.EXPECTED_SIZE }}
|
export KSU_EXPECTED_SIZE=${{ vars.EXPECTED_SIZE }}
|
||||||
export KSU_EXPECTED_HASH=${{ vars.EXPECTED_HASH }}
|
export KSU_EXPECTED_HASH=${{ vars.EXPECTED_HASH }}
|
||||||
@@ -106,43 +124,12 @@ jobs:
|
|||||||
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} mrproper
|
||||||
make LLVM=1 LLVM_IAS=1 DEPMOD=depmod DTC=dtc O=${PWD} ${DEFCONFIG} < /dev/null
|
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
|
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
|
make LLVM=1 LLVM_IAS=1 DEPMOD=depmod DTC=dtc O=${PWD} -j$(nproc) ${KERNEL_IMAGE_NAME} modules prepare-objtool
|
||||||
|
ls -l -h ${PWD}/arch/${ARCH}/boot
|
||||||
|
echo "file_path=${PWD}/arch/${ARCH}/boot/${KERNEL_IMAGE_NAME}" >> $GITHUB_ENV
|
||||||
|
|
||||||
echo "file_path=${PWD}/arch/x86/boot/bzImage" >> $GITHUB_ENV
|
- name: Upload kernel-ARCVM-${{ matrix.arch }}-${{ env.version }}
|
||||||
|
|
||||||
- name: Upload kernel-ARCVM-${{ matrix.arch }}-${{ matrix.version }}
|
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: kernel-ARCVM-${{ matrix.arch }}-${{ matrix.version }}
|
name: kernel-ARCVM-${{ matrix.arch }}-${{ env.version }}
|
||||||
path: "${{ env.file_path }}"
|
path: "${{ env.file_path }}"
|
||||||
|
|
||||||
- name: Bot session cache
|
|
||||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
|
||||||
id: bot_session_cache
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: scripts/ksubot.session
|
|
||||||
key: ${{ runner.os }}-bot-session
|
|
||||||
|
|
||||||
- name: Post to Telegram
|
|
||||||
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
|
||||||
env:
|
|
||||||
CHAT_ID: ${{ secrets.CHAT_ID }}
|
|
||||||
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
|
||||||
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
|
|
||||||
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
|
||||||
COMMIT_URL: ${{ github.event.head_commit.url }}
|
|
||||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
||||||
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 telethon==1.31.1
|
|
||||||
python3 "$GITHUB_WORKSPACE/KernelSU/scripts/ksubot.py" "${{ env.file_path }}.gz"
|
|
||||||
fi
|
|
||||||
|
|||||||
40
.github/workflows/build-kernel-avd.yml
vendored
Normal file
40
.github/workflows/build-kernel-avd.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
name: Build Kernel - AVD
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["main", "ci", "checkci"]
|
||||||
|
paths:
|
||||||
|
- ".github/workflows/build-kernel-avd.yml"
|
||||||
|
- ".github/workflows/avd-kernel.yml"
|
||||||
|
- ".github/workflows/manifests/*xml"
|
||||||
|
- "kernel/**"
|
||||||
|
pull_request:
|
||||||
|
branches: ["main"]
|
||||||
|
paths:
|
||||||
|
- ".github/workflows/build-kernel-avd.yml"
|
||||||
|
- ".github/workflows/avd-kernel.yml"
|
||||||
|
- ".github/workflows/manifests/*.xml"
|
||||||
|
- "kernel/**"
|
||||||
|
workflow_call:
|
||||||
|
workflow_dispatch:
|
||||||
|
jobs:
|
||||||
|
build-kernel:
|
||||||
|
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
|
||||||
|
uses: ./.github/workflows/avd-kernel.yml
|
||||||
|
secrets: inherit
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- version: "android-14-avd_x86_64"
|
||||||
|
manifest: "android-14-avd_x86_64.xml"
|
||||||
|
arch: "x86_64"
|
||||||
|
- version: "android-15-avd_aarch64"
|
||||||
|
manifest: "android-15-avd_aarch64.xml"
|
||||||
|
arch: "aarch64"
|
||||||
|
- version: "android-15-avd_x86_64"
|
||||||
|
manifest: "android-15-avd_x86_64.xml"
|
||||||
|
arch: "x86_64"
|
||||||
|
with:
|
||||||
|
version_name: ${{ matrix.version }}
|
||||||
|
manifest_name: ${{ matrix.manifest }}
|
||||||
|
arch: ${{ matrix.arch }}
|
||||||
|
debug: true
|
||||||
25
.github/workflows/build-ksud.yml
vendored
25
.github/workflows/build-ksud.yml
vendored
@@ -1,25 +0,0 @@
|
|||||||
name: Build KSUD
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "main", "ci" ]
|
|
||||||
paths:
|
|
||||||
- '.github/workflows/build-ksud.yml'
|
|
||||||
- '.github/workflows/ksud.yml'
|
|
||||||
- 'userspace/ksud/**'
|
|
||||||
pull_request:
|
|
||||||
branches: [ "main" ]
|
|
||||||
paths:
|
|
||||||
- '.github/workflows/build-ksud.yml'
|
|
||||||
- '.github/workflows/ksud.yml'
|
|
||||||
- 'userspace/ksud/**'
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- target: aarch64-linux-android
|
|
||||||
- target: x86_64-linux-android
|
|
||||||
- target: x86_64-pc-windows-gnu # only for build
|
|
||||||
uses: ./.github/workflows/ksud.yml
|
|
||||||
with:
|
|
||||||
target: ${{ matrix.target }}
|
|
||||||
38
.github/workflows/build-lkm.yml
vendored
Normal file
38
.github/workflows/build-lkm.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
name: Build LKM for KernelSU
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["main", "ci", "checkci"]
|
||||||
|
paths:
|
||||||
|
- ".github/workflows/build-lkm.yml"
|
||||||
|
pull_request:
|
||||||
|
branches: ["main"]
|
||||||
|
paths:
|
||||||
|
- ".github/workflows/build-lkm.yml"
|
||||||
|
workflow_call:
|
||||||
|
jobs:
|
||||||
|
build-lkm:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- version: "android12-5.10"
|
||||||
|
sub_level: 209
|
||||||
|
os_patch_level: 2024-05
|
||||||
|
- version: "android13-5.10"
|
||||||
|
sub_level: 209
|
||||||
|
os_patch_level: 2024-05
|
||||||
|
- version: "android13-5.15"
|
||||||
|
sub_level: 148
|
||||||
|
os_patch_level: 2024-05
|
||||||
|
- version: "android14-5.15"
|
||||||
|
sub_level: 148
|
||||||
|
os_patch_level: 2024-05
|
||||||
|
- version: "android14-6.1"
|
||||||
|
sub_level: 75
|
||||||
|
os_patch_level: 2024-05
|
||||||
|
uses: ./.github/workflows/gki-kernel.yml
|
||||||
|
with:
|
||||||
|
version: ${{ matrix.version }}
|
||||||
|
version_name: ${{ matrix.version }}.${{ matrix.sub_level }}
|
||||||
|
tag: ${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||||
|
os_patch_level: ${{ matrix.os_patch_level }}
|
||||||
|
build_lkm: true
|
||||||
35
.github/workflows/build-manager.yml
vendored
35
.github/workflows/build-manager.yml
vendored
@@ -2,10 +2,11 @@ name: Build Manager
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main" ]
|
branches: [ "main", "ci" ]
|
||||||
paths:
|
paths:
|
||||||
- '.github/workflows/build-manager.yml'
|
- '.github/workflows/build-manager.yml'
|
||||||
- 'manager/**'
|
- 'manager/**'
|
||||||
|
- 'kernel/**'
|
||||||
- 'userspace/ksud/**'
|
- 'userspace/ksud/**'
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "main" ]
|
branches: [ "main" ]
|
||||||
@@ -14,15 +15,33 @@ on:
|
|||||||
workflow_call:
|
workflow_call:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
build-lkm:
|
||||||
|
uses: ./.github/workflows/build-lkm.yml
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
build-ksud:
|
build-ksud:
|
||||||
|
needs: build-lkm
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- target: aarch64-linux-android
|
- target: aarch64-linux-android
|
||||||
|
os: ubuntu-latest
|
||||||
- target: x86_64-linux-android
|
- target: x86_64-linux-android
|
||||||
|
os: ubuntu-latest
|
||||||
|
- target: x86_64-pc-windows-gnu # windows pc
|
||||||
|
os: ubuntu-latest
|
||||||
|
- target: x86_64-apple-darwin # Intel mac
|
||||||
|
os: macos-latest
|
||||||
|
- target: aarch64-apple-darwin # M chip mac
|
||||||
|
os: macos-latest
|
||||||
|
- target: aarch64-unknown-linux-musl # arm64 Linux
|
||||||
|
os: ubuntu-latest
|
||||||
|
- target: x86_64-unknown-linux-musl # x86 Linux
|
||||||
|
os: ubuntu-latest
|
||||||
uses: ./.github/workflows/ksud.yml
|
uses: ./.github/workflows/ksud.yml
|
||||||
with:
|
with:
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
|
os: ${{ matrix.os }}
|
||||||
|
|
||||||
build-manager:
|
build-manager:
|
||||||
needs: build-ksud
|
needs: build-ksud
|
||||||
@@ -54,7 +73,7 @@ jobs:
|
|||||||
echo KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}'
|
echo KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}'
|
||||||
echo KEY_ALIAS='${{ secrets.KEY_ALIAS }}'
|
echo KEY_ALIAS='${{ secrets.KEY_ALIAS }}'
|
||||||
echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}'
|
echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}'
|
||||||
echo KEYSTORE_FILE='../key.jks'
|
echo KEYSTORE_FILE='key.jks'
|
||||||
} >> gradle.properties
|
} >> gradle.properties
|
||||||
echo ${{ secrets.KEYSTORE }} | base64 -d > key.jks
|
echo ${{ secrets.KEYSTORE }} | base64 -d > key.jks
|
||||||
fi
|
fi
|
||||||
@@ -62,11 +81,11 @@ jobs:
|
|||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: "temurin"
|
distribution: temurin
|
||||||
java-version: "17"
|
java-version: 21
|
||||||
|
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/actions/setup-gradle@v3
|
||||||
with:
|
with:
|
||||||
gradle-home-cache-cleanup: true
|
gradle-home-cache-cleanup: true
|
||||||
|
|
||||||
@@ -102,12 +121,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload build artifact
|
- name: Upload build artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
||||||
with:
|
with:
|
||||||
name: manager
|
name: manager
|
||||||
path: manager/app/build/outputs/apk/release/*.apk
|
path: manager/app/build/outputs/apk/release/*.apk
|
||||||
|
|
||||||
- name: Upload mappings
|
- name: Upload mappings
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
||||||
with:
|
with:
|
||||||
name: "mappings"
|
name: "mappings"
|
||||||
path: "manager/app/build/outputs/mapping/release/"
|
path: "manager/app/build/outputs/mapping/release/"
|
||||||
@@ -134,6 +155,6 @@ jobs:
|
|||||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
||||||
export VERSION=$(git rev-list --count HEAD)
|
export VERSION=$(git rev-list --count HEAD)
|
||||||
APK=$(find ./app/build/outputs/apk/release -name "*.apk")
|
APK=$(find ./app/build/outputs/apk/release -name "*.apk")
|
||||||
pip3 install telethon==1.31.1
|
pip3 install telethon
|
||||||
python3 $GITHUB_WORKSPACE/scripts/ksubot.py $APK
|
python3 $GITHUB_WORKSPACE/scripts/ksubot.py $APK
|
||||||
fi
|
fi
|
||||||
29
.github/workflows/build-su.yml
vendored
29
.github/workflows/build-su.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
- uses: nttld/setup-ndk@v1
|
- uses: nttld/setup-ndk@v1
|
||||||
with:
|
with:
|
||||||
ndk-version: r25c
|
ndk-version: r26d
|
||||||
- name: Build su
|
- name: Build su
|
||||||
working-directory: ./userspace/su
|
working-directory: ./userspace/su
|
||||||
run: ndk-build
|
run: ndk-build
|
||||||
@@ -36,29 +36,4 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: su
|
name: su
|
||||||
path: ./userspace/su/libs
|
path: ./userspace/su/libs
|
||||||
- name: Bot session cache
|
|
||||||
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
|
|
||||||
id: bot_session_cache
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: scripts/ksubot.session
|
|
||||||
key: ${{ runner.os }}-bot-session
|
|
||||||
- name: Upload to telegram
|
|
||||||
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
|
|
||||||
env:
|
|
||||||
CHAT_ID: ${{ secrets.CHAT_ID }}
|
|
||||||
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
|
||||||
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
|
|
||||||
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
|
||||||
COMMIT_URL: ${{ github.event.head_commit.url }}
|
|
||||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
||||||
TITLE: SU
|
|
||||||
run: |
|
|
||||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
|
||||||
export VERSION=$(git rev-list --count HEAD)
|
|
||||||
pip3 install telethon==1.31.1
|
|
||||||
mv ./userspace/su/libs/arm64-v8a/su su-arm64
|
|
||||||
mv ./userspace/su/libs/x86_64/su su-x86_64
|
|
||||||
python3 scripts/ksubot.py su-arm64 su-x86_64
|
|
||||||
fi
|
|
||||||
6
.github/workflows/clippy.yml
vendored
6
.github/workflows/clippy.yml
vendored
@@ -22,14 +22,14 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
# cross build failed after Rust 1.68, see https://github.com/cross-rs/cross/issues/1222
|
- run: rustup update --force-non-host stable-x86_64-unknown-linux-gnu
|
||||||
- run: rustup default 1.67.0
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
workspaces: userspace/ksud
|
workspaces: userspace/ksud
|
||||||
|
|
||||||
- name: Install cross
|
- name: Install cross
|
||||||
run: cargo install cross --locked
|
run: |
|
||||||
|
cargo install cross --git https://github.com/cross-rs/cross --rev 66845c1
|
||||||
|
|
||||||
- name: Run clippy
|
- name: Run clippy
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
4
.github/workflows/deploy-website.yml
vendored
4
.github/workflows/deploy-website.yml
vendored
@@ -37,11 +37,11 @@ jobs:
|
|||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 20
|
||||||
cache: yarn # or pnpm / yarn
|
cache: yarn # or pnpm / yarn
|
||||||
cache-dependency-path: website/yarn.lock
|
cache-dependency-path: website/yarn.lock
|
||||||
- name: Setup Pages
|
- name: Setup Pages
|
||||||
uses: actions/configure-pages@v4
|
uses: actions/configure-pages@v5
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install --frozen-lockfile
|
run: yarn install --frozen-lockfile
|
||||||
- name: Build with VitePress
|
- name: Build with VitePress
|
||||||
|
|||||||
68
.github/workflows/gki-kernel.yml
vendored
68
.github/workflows/gki-kernel.yml
vendored
@@ -29,7 +29,7 @@ on:
|
|||||||
for example: 2021-11
|
for example: 2021-11
|
||||||
default: 2022-05
|
default: 2022-05
|
||||||
patch_path:
|
patch_path:
|
||||||
required: true
|
required: false
|
||||||
type: string
|
type: string
|
||||||
description: >
|
description: >
|
||||||
Directory name of .github/patches/<patch_path>
|
Directory name of .github/patches/<patch_path>
|
||||||
@@ -49,6 +49,10 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
|
build_lkm:
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
secrets:
|
secrets:
|
||||||
BOOT_SIGN_KEY:
|
BOOT_SIGN_KEY:
|
||||||
required: false
|
required: false
|
||||||
@@ -124,13 +128,15 @@ jobs:
|
|||||||
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $GKI_ROOT/common/drivers/kernelsu
|
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $GKI_ROOT/common/drivers/kernelsu
|
||||||
echo "[+] Add KernelSU driver to Makefile"
|
echo "[+] Add KernelSU driver to Makefile"
|
||||||
DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile
|
DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile
|
||||||
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
|
DRIVER_KCONFIG=$GKI_ROOT/common/drivers/Kconfig
|
||||||
|
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE"
|
||||||
|
grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG"
|
||||||
echo "[+] Apply KernelSU patches"
|
echo "[+] Apply KernelSU patches"
|
||||||
cd $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/$PATCH_PATH/*.patch || echo "[-] No patch found"
|
cd $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/$PATCH_PATH/*.patch || echo "[-] No patch found"
|
||||||
|
|
||||||
if [ "$IS_DEBUG_KERNEL" = "true" ]; then
|
if [ "$IS_DEBUG_KERNEL" = "true" ]; then
|
||||||
echo "[+] Enable debug features for kernel"
|
echo "[+] Enable debug features for kernel"
|
||||||
echo "ccflags-y += -DCONFIG_KSU_DEBUG" >> $GITHUB_WORKSPACE/KernelSU/kernel/Makefile
|
printf "\nccflags-y += -DCONFIG_KSU_DEBUG\n" >> $GITHUB_WORKSPACE/KernelSU/kernel/Makefile
|
||||||
fi
|
fi
|
||||||
repo status
|
repo status
|
||||||
echo "[+] KernelSU setup done."
|
echo "[+] KernelSU setup done."
|
||||||
@@ -148,12 +154,40 @@ jobs:
|
|||||||
|
|
||||||
- name: Setup ccache
|
- name: Setup ccache
|
||||||
if: inputs.use_cache == true
|
if: inputs.use_cache == true
|
||||||
uses: hendrikmuhs/ccache-action@v1.2
|
uses: hendrikmuhs/ccache-action@v1
|
||||||
with:
|
with:
|
||||||
key: gki-kernel-aarch64-${{ inputs.version_name }}
|
key: gki-kernel-aarch64-${{ inputs.version_name }}
|
||||||
max-size: 2G
|
max-size: 2G
|
||||||
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
||||||
|
|
||||||
|
- name: Setup for LKM
|
||||||
|
if: ${{ inputs.build_lkm == true }}
|
||||||
|
working-directory: android-kernel
|
||||||
|
run: |
|
||||||
|
pip install ast-grep-cli
|
||||||
|
sudo apt-get install llvm-15 -y
|
||||||
|
ast-grep -U -p '$$$ check_exports($$$) {$$$}' -r '' common/scripts/mod/modpost.c
|
||||||
|
ast-grep -U -p 'check_exports($$$);' -r '' common/scripts/mod/modpost.c
|
||||||
|
sed -i '/config KSU/,/help/{s/default y/default m/}' common/drivers/kernelsu/Kconfig
|
||||||
|
echo "drivers/kernelsu/kernelsu.ko" >> common/android/gki_aarch64_modules
|
||||||
|
|
||||||
|
# bazel build, android14-5.15, android14-6.1 use bazel
|
||||||
|
if [ ! -e build/build.sh ]; then
|
||||||
|
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"
|
||||||
|
if [ -e common/modules.bzl ]; then
|
||||||
|
sed -i 's/_COMMON_GKI_MODULES_LIST = \[/_COMMON_GKI_MODULES_LIST = \[ "drivers\/kernelsu\/kernelsu.ko",/g' common/modules.bzl
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
TARGET_FILE="build/kernel/build.sh"
|
||||||
|
if [ ! -e "$TARGET_FILE" ]; then
|
||||||
|
TARGET_FILE="build/build.sh"
|
||||||
|
fi
|
||||||
|
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' $TARGET_FILE || echo "No unknown symbol in $TARGET_FILE"
|
||||||
|
sed -i 's/if ! diff -u "\${KERNEL_DIR}\/\${MODULES_ORDER}" "\${OUT_DIR}\/modules\.order"; then/if false; then/g' $TARGET_FILE
|
||||||
|
sed -i 's@${ROOT_DIR}/build/abi/compare_to_symbol_list@echo@g' $TARGET_FILE
|
||||||
|
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Make working directory clean to avoid dirty
|
- name: Make working directory clean to avoid dirty
|
||||||
working-directory: android-kernel
|
working-directory: android-kernel
|
||||||
run: |
|
run: |
|
||||||
@@ -163,7 +197,7 @@ jobs:
|
|||||||
cd common/ && git add -A && git commit -a -m "Add KernelSU"
|
cd common/ && git add -A && git commit -a -m "Add KernelSU"
|
||||||
repo status
|
repo status
|
||||||
|
|
||||||
- name: Build boot.img
|
- name: Build Kernel/LKM
|
||||||
working-directory: android-kernel
|
working-directory: android-kernel
|
||||||
run: |
|
run: |
|
||||||
if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then
|
if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then
|
||||||
@@ -184,20 +218,34 @@ jobs:
|
|||||||
OUTDIR=android-kernel/dist
|
OUTDIR=android-kernel/dist
|
||||||
fi
|
fi
|
||||||
mkdir output
|
mkdir output
|
||||||
cp $OUTDIR/Image ./output/
|
if [ "${{ inputs.build_lkm}}" = "true" ]; then
|
||||||
cp $OUTDIR/Image.lz4 ./output/
|
llvm-strip-15 -d $OUTDIR/kernelsu.ko
|
||||||
git clone https://github.com/Kernel-SU/AnyKernel3
|
mv $OUTDIR/kernelsu.ko ./output/${{ inputs.version }}_kernelsu.ko
|
||||||
rm -rf ./AnyKernel3/.git
|
else
|
||||||
cp $OUTDIR/Image ./AnyKernel3/
|
cp $OUTDIR/Image ./output/
|
||||||
|
cp $OUTDIR/Image.lz4 ./output/
|
||||||
|
git clone https://github.com/Kernel-SU/AnyKernel3
|
||||||
|
rm -rf ./AnyKernel3/.git
|
||||||
|
cp $OUTDIR/Image ./AnyKernel3/
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Upload Image and Image.gz
|
- name: Upload Image and Image.gz
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
if: ${{ inputs.build_lkm == false }}
|
||||||
with:
|
with:
|
||||||
name: Image-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
|
name: Image-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
|
||||||
path: ./output/*
|
path: ./output/*
|
||||||
|
|
||||||
- name: Upload AnyKernel3
|
- name: Upload AnyKernel3
|
||||||
|
if: ${{ inputs.build_lkm == false }}
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: AnyKernel3-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
|
name: AnyKernel3-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
|
||||||
path: ./AnyKernel3/*
|
path: ./AnyKernel3/*
|
||||||
|
|
||||||
|
- name: Upload LKM
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
if: ${{ inputs.build_lkm == true }}
|
||||||
|
with:
|
||||||
|
name: ${{ inputs.version }}-lkm
|
||||||
|
path: ./output/*_kernelsu.ko
|
||||||
33
.github/workflows/ksud.yml
vendored
33
.github/workflows/ksud.yml
vendored
@@ -5,32 +5,53 @@ on:
|
|||||||
target:
|
target:
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
os:
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: ubuntu-latest
|
||||||
|
pack_lkm:
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
use_cache:
|
use_cache:
|
||||||
required: false
|
required: false
|
||||||
type: boolean
|
type: boolean
|
||||||
default: true
|
default: true
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ${{ inputs.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
# cross build failed after Rust 1.68, see https://github.com/cross-rs/cross/issues/1222
|
|
||||||
- run: rustup default 1.67.0
|
- name: Download artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
|
||||||
|
- name: Prepare LKM fies
|
||||||
|
if: ${{ inputs.pack_lkm }}
|
||||||
|
run: |
|
||||||
|
cp android*-lkm/*_kernelsu.ko ./userspace/ksud/bin/aarch64/
|
||||||
|
|
||||||
|
- name: Setup rustup
|
||||||
|
run: |
|
||||||
|
rustup update stable
|
||||||
|
rustup target add x86_64-apple-darwin
|
||||||
|
rustup target add aarch64-apple-darwin
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
workspaces: userspace/ksud
|
workspaces: userspace/ksud
|
||||||
cache-targets: false
|
cache-targets: false
|
||||||
|
|
||||||
- name: Install cross
|
- name: Install cross
|
||||||
run: cargo install cross --locked
|
run: |
|
||||||
|
cargo install cross --git https://github.com/cross-rs/cross --rev 66845c1
|
||||||
|
|
||||||
- name: Build ksud
|
- name: Build ksud
|
||||||
run: cross build --target ${{ inputs.target }} --release --manifest-path ./userspace/ksud/Cargo.toml
|
run: CROSS_NO_WARNINGS=0 cross build --target ${{ inputs.target }} --release --manifest-path ./userspace/ksud/Cargo.toml
|
||||||
|
|
||||||
- name: Upload ksud artifact
|
- name: Upload ksud artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ksud-${{ inputs.target }}
|
name: ksud-${{ inputs.target }}
|
||||||
path: userspace/ksud/target/**/release/ksud
|
path: userspace/ksud/target/**/release/ksud*
|
||||||
|
|||||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -67,11 +67,13 @@ jobs:
|
|||||||
run: ls -R
|
run: ls -R
|
||||||
|
|
||||||
- name: release
|
- name: release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
manager/*.apk
|
manager/*.apk
|
||||||
|
android*-lkm/*_kernelsu.ko
|
||||||
AnyKernel3-*.zip
|
AnyKernel3-*.zip
|
||||||
boot-images-*/Image-*/*.img.gz
|
boot-images-*/Image-*/*.img.gz
|
||||||
kernel-WSA*.zip
|
kernel-WSA*.zip
|
||||||
kernel-ARCVM*.zip
|
kernel-ARCVM*.zip
|
||||||
|
ksud-*
|
||||||
|
|||||||
43
.github/workflows/wsa-kernel.yml
vendored
43
.github/workflows/wsa-kernel.yml
vendored
@@ -15,7 +15,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build WSA-Kernel-${{ inputs.version }}-${{ inputs.arch }}
|
name: Build WSA-Kernel-${{ inputs.version }}-${{ inputs.arch }}
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-22.04
|
||||||
env:
|
env:
|
||||||
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
|
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
|
||||||
CCACHE_NOHASHDIR: "true"
|
CCACHE_NOHASHDIR: "true"
|
||||||
@@ -30,7 +30,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Cache LLVM
|
- name: Cache LLVM
|
||||||
id: cache-llvm
|
id: cache-llvm
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ./llvm
|
path: ./llvm
|
||||||
key: llvm-12.0.1
|
key: llvm-12.0.1
|
||||||
@@ -57,7 +57,7 @@ jobs:
|
|||||||
path: WSA-Linux-Kernel
|
path: WSA-Linux-Kernel
|
||||||
|
|
||||||
- name: Setup Ccache
|
- name: Setup Ccache
|
||||||
uses: hendrikmuhs/ccache-action@v1.2
|
uses: hendrikmuhs/ccache-action@v1
|
||||||
with:
|
with:
|
||||||
key: WSA-Kernel-${{ inputs.version }}-${{ inputs.arch }}
|
key: WSA-Kernel-${{ inputs.version }}-${{ inputs.arch }}
|
||||||
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
||||||
@@ -73,7 +73,9 @@ jobs:
|
|||||||
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $KERNEL_ROOT/drivers/kernelsu
|
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $KERNEL_ROOT/drivers/kernelsu
|
||||||
echo "[+] Add KernelSU driver to Makefile"
|
echo "[+] Add KernelSU driver to Makefile"
|
||||||
DRIVER_MAKEFILE=$KERNEL_ROOT/drivers/Makefile
|
DRIVER_MAKEFILE=$KERNEL_ROOT/drivers/Makefile
|
||||||
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
|
DRIVER_KCONFIG=$KERNEL_ROOT/drivers/Kconfig
|
||||||
|
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE"
|
||||||
|
grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG"
|
||||||
echo "[+] Apply KernelSU patches"
|
echo "[+] Apply KernelSU patches"
|
||||||
cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/5.15/*.patch || echo "[-] No patch found"
|
cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/5.15/*.patch || echo "[-] No patch found"
|
||||||
echo "[+] KernelSU setup done."
|
echo "[+] KernelSU setup done."
|
||||||
@@ -101,35 +103,4 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: kernel-WSA-${{ inputs.arch }}-${{ inputs.version }}
|
name: kernel-WSA-${{ inputs.arch }}-${{ inputs.version }}
|
||||||
path: "${{ env.file_path }}"
|
path: "${{ env.file_path }}"
|
||||||
|
|
||||||
- name: Bot session cache
|
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.ref_type == 'tag'
|
|
||||||
id: bot_session_cache
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: scripts/ksubot.session
|
|
||||||
key: ${{ runner.os }}-bot-session
|
|
||||||
|
|
||||||
- name: Post to Telegram
|
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.ref_type == 'tag'
|
|
||||||
env:
|
|
||||||
CHAT_ID: ${{ secrets.CHAT_ID }}
|
|
||||||
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
|
||||||
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
|
|
||||||
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
|
||||||
COMMIT_URL: ${{ github.event.head_commit.url }}
|
|
||||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
||||||
run: |
|
|
||||||
TITLE=kernel-${{ inputs.arch }}-WSA-${{ inputs.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 telethon==1.31.1
|
|
||||||
python3 "$GITHUB_WORKSPACE/KernelSU/scripts/ksubot.py" "${{ env.file_path }}.gz"
|
|
||||||
fi
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
**English** | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
**English** | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||||
|
|
||||||
# KernelSU
|
# KernelSU
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[English](README.md) | [Español](README_ES.md) | **简体中文** | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
[English](README.md) | [Español](README_ES.md) | **简体中文** | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||||
|
|
||||||
# KernelSU
|
# KernelSU
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[English](README.md) | **Español** | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
[English](README.md) | **Español** | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||||
|
|
||||||
# KernelSU
|
# KernelSU
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | **Indonesia** | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | **Indonesia** | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||||
|
|
||||||
# KernelSU
|
# KernelSU
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | **हिंदी**
|
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | **हिंदी**
|
||||||
|
|
||||||
# KernelSU
|
# KernelSU
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | **עברית** | [हिंदी](README_IN.md)
|
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | **עברית** | [हिंदी](README_IN.md)
|
||||||
|
|
||||||
# KernelSU
|
# KernelSU
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | **日本語** | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | **日本語** | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||||
|
|
||||||
# KernelSU
|
# KernelSU
|
||||||
|
|
||||||
|
|||||||
57
docs/README_KR.md
Normal file
57
docs/README_KR.md
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | **한국어** | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||||
|
|
||||||
|
# KernelSU
|
||||||
|
|
||||||
|
<img src="https://kernelsu.org/logo.png" style="width: 96px;" alt="logo">
|
||||||
|
|
||||||
|
안드로이드 기기에서 사용되는 커널 기반 루팅 솔루션입니다.
|
||||||
|
|
||||||
|
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||||
|
[](https://hosted.weblate.org/engage/kernelsu)
|
||||||
|
[](https://t.me/KernelSU)
|
||||||
|
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||||
|
[](/LICENSE)
|
||||||
|
|
||||||
|
## 기능들
|
||||||
|
|
||||||
|
1. 커널 기반 `su` 및 루트 액세스 관리.
|
||||||
|
2. [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) 기반 모듈 시스템.
|
||||||
|
3. [App Profile](https://kernelsu.org/guide/app-profile.html): 루트 권한을 케이지에 가둡니다.
|
||||||
|
|
||||||
|
## 호환 상태
|
||||||
|
|
||||||
|
KernelSU는 공식적으로 안드로이드 GKI 2.0 디바이스(커널 5.10 이상)를 지원합니다. 오래된 커널(4.14 이상)도 사용할 수 있지만, 커널을 수동으로 빌드해야 합니다.
|
||||||
|
|
||||||
|
KernelSU는 WSA, ChromeOS, 컨테이너 기반 안드로이드 모두를 지원합니다.
|
||||||
|
|
||||||
|
현재는 `arm64-v8a`와 `x86_64`만 지원됩니다.
|
||||||
|
|
||||||
|
## 사용 방법
|
||||||
|
|
||||||
|
- [설치 방법](https://kernelsu.org/guide/installation.html)
|
||||||
|
- [어떻게 빌드하나요?](https://kernelsu.org/guide/how-to-build.html)
|
||||||
|
- [공식 웹사이트](https://kernelsu.org/)
|
||||||
|
|
||||||
|
## 번역
|
||||||
|
|
||||||
|
KernelSU 번역을 돕거나 기존 번역을 개선하려면 [Weblate](https://hosted.weblate.org/engage/kernelsu/)를 이용해 주세요. 매니저의 번역은 Weblate와 충돌할 수 있으므로 더 이상 허용되지 않습니다.
|
||||||
|
|
||||||
|
## 토론
|
||||||
|
|
||||||
|
- 텔레그램: [@KernelSU](https://t.me/KernelSU)
|
||||||
|
|
||||||
|
## 보안
|
||||||
|
|
||||||
|
KernelSU의 보안 취약점 보고에 대한 자세한 내용은 [SECURITY.md](/SECURITY.md)를 참조하세요.
|
||||||
|
|
||||||
|
## 저작권
|
||||||
|
|
||||||
|
- `kernel` 디렉터리 아래의 파일은 [GPL-2.0 전용](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)입니다.
|
||||||
|
- `kernel` 디렉토리를 제외한 다른 모든 부분은 [GPL-3.0-이상](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): 강력한 루팅 도구.
|
||||||
|
- [genuine](https://github.com/brevent/genuine/): apk v2 서명 유효성 검사.
|
||||||
|
- [Diamorphine](https://github.com/m0nad/Diamorphine): 일부 rootkit 스킬.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | **Polski** | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | **Polski** | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||||
|
|
||||||
# KernelSU
|
# KernelSU
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | **Português (Brasil)** | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | **Português (Brasil)** | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||||
|
|
||||||
# KernelSU
|
# KernelSU
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ Atualmente, apenas `arm64-v8a` e `x86_64` são suportados.
|
|||||||
## Uso
|
## Uso
|
||||||
|
|
||||||
- [Instalação](https://kernelsu.org/pt_BR/guide/installation.html)
|
- [Instalação](https://kernelsu.org/pt_BR/guide/installation.html)
|
||||||
- [Como construir o KernelSU?](https://kernelsu.org/pt_BR/guide/how-to-build.html)
|
- [Como compilar o KernelSU?](https://kernelsu.org/pt_BR/guide/how-to-build.html)
|
||||||
- [Site oficial](https://kernelsu.org/pt_BR/)
|
- [Site oficial](https://kernelsu.org/pt_BR/)
|
||||||
|
|
||||||
## Tradução
|
## Tradução
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | **Русский** | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | **Русский** | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||||
|
|
||||||
# KernelSU
|
# KernelSU
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | **Türkçe** | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | **Türkçe** | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||||
|
|
||||||
# KernelSU
|
# KernelSU
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ Bununla birlikte; WSA, ChromeOS ve konteyner tabanlı Android'in tamamı destekl
|
|||||||
|
|
||||||
## Çeviri
|
## Çeviri
|
||||||
|
|
||||||
KernelSU'nun çevirisine veya mevcut çevirilerin iyileştirilmesine yardımcı olmak için lütfen [Weblate](https://hosted.weblate.org/engage/kernelsu/) kullanın. Yönetici uygulamasının PR ile çevirisi, Weblate ile çakışacağından artık kabul edilmeyecektir.
|
KernelSU'nun başka dillere çevrilmesine veya mevcut çevirilerin iyileştirilmesine yardımcı olmak için lütfen [Weblate](https://hosted.weblate.org/engage/kernelsu/) kullanın. Yönetici uygulamasının PR ile çevirisi, Weblate ile çakışacağından artık kabul edilmeyecektir.
|
||||||
|
|
||||||
## Tartışma
|
## Tartışma
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | **繁體中文** | [日本語](README_JP.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | **繁體中文** | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||||
|
|
||||||
# KernelSU
|
# KernelSU
|
||||||
|
|
||||||
@@ -16,12 +16,13 @@
|
|||||||
|
|
||||||
- 基於核心的 `su` 和 Root 存取權管理。
|
- 基於核心的 `su` 和 Root 存取權管理。
|
||||||
- 基於 [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) 的模組系統。
|
- 基於 [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) 的模組系統。
|
||||||
|
- [App Profile](https://kernelsu.org/zh_TW/guide/app-profile.html): 將 Root 的權限鎖在牢籠中.
|
||||||
|
|
||||||
## 相容性狀態
|
## 相容性狀態
|
||||||
|
|
||||||
KernelSU 官方支援 Android GKI 2.0 的裝置 (核心版本 5.10+);舊版核心同樣相容 (最低 4.14+),但需要自行編譯核心。
|
KernelSU 官方支援 Android GKI 2.0 的裝置 (核心版本 5.10+ );舊版核心同樣相容 (最低 4.14+ ),但需要自行編譯核心。
|
||||||
|
|
||||||
WSA 和執行在容器中的 Android 也可以與 KernelSU 一同運作。
|
WSA和ChromeOS和執行在容器中的 Android 也可以與 KernelSU 一同運作。
|
||||||
|
|
||||||
目前支援架構:`arm64-v8a` 和 `x86_64`。
|
目前支援架構:`arm64-v8a` 和 `x86_64`。
|
||||||
|
|
||||||
@@ -29,11 +30,19 @@ WSA 和執行在容器中的 Android 也可以與 KernelSU 一同運作。
|
|||||||
|
|
||||||
- [安裝教學](https://kernelsu.org/zh_TW/guide/installation.html)
|
- [安裝教學](https://kernelsu.org/zh_TW/guide/installation.html)
|
||||||
- [如何建置?](https://kernelsu.org/zh_TW/guide/how-to-build.html)
|
- [如何建置?](https://kernelsu.org/zh_TW/guide/how-to-build.html)
|
||||||
|
- [官方網站](https://kernelsu.org/zh_TW/)
|
||||||
|
|
||||||
|
## 翻譯
|
||||||
|
|
||||||
|
若要協助翻譯 KernelSU 或改進現有翻譯,請使用 [Weblate](https://hosted.weblate.org/engage/kernelsu/)。 翻譯管理器的PR不再被接受,因為它會與Weblate衝突。
|
||||||
|
|
||||||
### 討論
|
### 討論
|
||||||
|
|
||||||
- Telegram:[@KernelSU](https://t.me/KernelSU)
|
- Telegram:[@KernelSU](https://t.me/KernelSU)
|
||||||
|
|
||||||
|
## 安全
|
||||||
|
有關報告 KernelSU 中的安全漏洞的資訊,請參閱 [SECURITY.md](/SECURITY.md)。
|
||||||
|
|
||||||
## 授權
|
## 授權
|
||||||
|
|
||||||
- 目錄 `kernel` 下所有檔案為 [GPL-2-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)。
|
- 目錄 `kernel` 下所有檔案為 [GPL-2-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)。
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | **Tiếng Việt** | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | **Tiếng Việt** | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md)
|
||||||
|
|
||||||
# KernelSU
|
# KernelSU
|
||||||
|
|
||||||
|
|||||||
45
js/index.d.ts
vendored
Normal file
45
js/index.d.ts
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
interface ExecOptions {
|
||||||
|
cwd?: string,
|
||||||
|
env?: { [key: string]: string }
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ExecResults {
|
||||||
|
errno: number,
|
||||||
|
stdout: string,
|
||||||
|
stderr: string
|
||||||
|
}
|
||||||
|
|
||||||
|
declare function exec(command: string): Promise<ExecResults>;
|
||||||
|
declare function exec(command: string, options: ExecOptions): Promise<ExecResults>;
|
||||||
|
|
||||||
|
interface SpawnOptions {
|
||||||
|
cwd?: string,
|
||||||
|
env?: { [key: string]: string }
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Stdio {
|
||||||
|
on(event: 'data', callback: (data: string) => void)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChildProcess {
|
||||||
|
stdout: Stdio,
|
||||||
|
stderr: Stdio,
|
||||||
|
on(event: 'exit', callback: (code: number) => void)
|
||||||
|
on(event: 'error', callback: (err: any) => void)
|
||||||
|
}
|
||||||
|
|
||||||
|
declare function spawn(command: string): ChildProcess;
|
||||||
|
declare function spawn(command: string, args: string[]): ChildProcess;
|
||||||
|
declare function spawn(command: string, options: SpawnOptions): ChildProcess;
|
||||||
|
declare function spawn(command: string, args: string[], options: SpawnOptions): ChildProcess;
|
||||||
|
|
||||||
|
declare function fullScreen(isFullScreen: boolean);
|
||||||
|
|
||||||
|
declare function toast(message: string);
|
||||||
|
|
||||||
|
export {
|
||||||
|
exec,
|
||||||
|
spawn,
|
||||||
|
fullScreen,
|
||||||
|
toast
|
||||||
|
}
|
||||||
@@ -71,7 +71,7 @@ function Stdio() {
|
|||||||
export function spawn(command, args, options) {
|
export function spawn(command, args, options) {
|
||||||
if (typeof args === "undefined") {
|
if (typeof args === "undefined") {
|
||||||
args = [];
|
args = [];
|
||||||
} else if (typeof args === "object") {
|
} else if (!(args instanceof Array)) {
|
||||||
// allow for (command, options) signature
|
// allow for (command, options) signature
|
||||||
options = args;
|
options = args;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"description": "Library for KernelSU's module WebUI",
|
"description": "Library for KernelSU's module WebUI",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
"types": "index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "npm run test"
|
"test": "npm run test"
|
||||||
},
|
},
|
||||||
|
|||||||
4
justfile
4
justfile
@@ -8,3 +8,7 @@ build_manager: build_ksud
|
|||||||
cp userspace/ksud/target/aarch64-linux-android/release/ksud manager/app/src/main/jniLibs/arm64-v8a/libksud.so
|
cp userspace/ksud/target/aarch64-linux-android/release/ksud manager/app/src/main/jniLibs/arm64-v8a/libksud.so
|
||||||
cd manager && ./gradlew aDebug
|
cd manager && ./gradlew aDebug
|
||||||
|
|
||||||
|
clippy:
|
||||||
|
cargo fmt --manifest-path ./userspace/ksud/Cargo.toml
|
||||||
|
cross clippy --target x86_64-pc-windows-gnu --release --manifest-path ./userspace/ksud/Cargo.toml
|
||||||
|
cross clippy --target aarch64-linux-android --release --manifest-path ./userspace/ksud/Cargo.toml
|
||||||
|
|||||||
@@ -5,13 +5,15 @@ config KSU
|
|||||||
depends on OVERLAY_FS
|
depends on OVERLAY_FS
|
||||||
default y
|
default y
|
||||||
help
|
help
|
||||||
Enable kernel-level root privileges on Android System.
|
Enable kernel-level root privileges on Android System.
|
||||||
|
To compile as a module, choose M here: the
|
||||||
|
module will be called kernelsu.
|
||||||
|
|
||||||
config KSU_DEBUG
|
config KSU_DEBUG
|
||||||
bool "KernelSU debug mode"
|
bool "KernelSU debug mode"
|
||||||
depends on KSU
|
depends on KSU
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
Enable KernelSU debug mode
|
Enable KernelSU debug mode.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|||||||
@@ -1,19 +1,24 @@
|
|||||||
obj-y += ksu.o
|
kernelsu-objs := ksu.o
|
||||||
obj-y += allowlist.o
|
kernelsu-objs += allowlist.o
|
||||||
kernelsu-objs := apk_sign.o
|
kernelsu-objs += apk_sign.o
|
||||||
obj-y += kernelsu.o
|
kernelsu-objs += sucompat.o
|
||||||
obj-y += module_api.o
|
kernelsu-objs += throne_tracker.o
|
||||||
obj-y += sucompat.o
|
kernelsu-objs += core_hook.o
|
||||||
obj-y += uid_observer.o
|
kernelsu-objs += ksud.o
|
||||||
obj-y += manager.o
|
kernelsu-objs += embed_ksud.o
|
||||||
obj-y += core_hook.o
|
kernelsu-objs += kernel_compat.o
|
||||||
obj-y += ksud.o
|
|
||||||
obj-y += embed_ksud.o
|
kernelsu-objs += selinux/selinux.o
|
||||||
obj-y += kernel_compat.o
|
kernelsu-objs += selinux/sepolicy.o
|
||||||
|
kernelsu-objs += selinux/rules.o
|
||||||
|
ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
|
||||||
|
ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h
|
||||||
|
|
||||||
|
obj-$(CONFIG_KSU) += kernelsu.o
|
||||||
|
|
||||||
obj-y += selinux/
|
|
||||||
# .git is a text file while the module is imported by 'git submodule add'.
|
# .git is a text file while the module is imported by 'git submodule add'.
|
||||||
ifeq ($(shell test -e $(srctree)/$(src)/../.git; echo $$?),0)
|
ifeq ($(shell test -e $(srctree)/$(src)/../.git; echo $$?),0)
|
||||||
|
$(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin [ -f ../.git/shallow ] && git fetch --unshallow)
|
||||||
KSU_GIT_VERSION := $(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git rev-list --count HEAD)
|
KSU_GIT_VERSION := $(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git rev-list --count HEAD)
|
||||||
# ksu_version: major * 10000 + git version + 200 for historical reasons
|
# ksu_version: major * 10000 + git version + 200 for historical reasons
|
||||||
$(eval KSU_VERSION=$(shell expr 10000 + $(KSU_GIT_VERSION) + 200))
|
$(eval KSU_VERSION=$(shell expr 10000 + $(KSU_GIT_VERSION) + 200))
|
||||||
@@ -24,6 +29,14 @@ $(warning "KSU_GIT_VERSION not defined! It is better to make KernelSU a git subm
|
|||||||
ccflags-y += -DKSU_VERSION=16
|
ccflags-y += -DKSU_VERSION=16
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(shell grep -q " current_sid(void)" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_COMPAT_HAS_CURRENT_SID
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(shell grep -q "struct selinux_state " $(srctree)/security/selinux/include/security.h; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_COMPAT_HAS_SELINUX_STATE
|
||||||
|
endif
|
||||||
|
|
||||||
ifndef KSU_EXPECTED_SIZE
|
ifndef KSU_EXPECTED_SIZE
|
||||||
KSU_EXPECTED_SIZE := 0x033b
|
KSU_EXPECTED_SIZE := 0x033b
|
||||||
endif
|
endif
|
||||||
@@ -42,5 +55,15 @@ $(info -- KernelSU Manager signature hash: $(KSU_EXPECTED_HASH))
|
|||||||
|
|
||||||
ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE)
|
ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE)
|
||||||
ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\"
|
ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\"
|
||||||
|
|
||||||
|
ifeq ($(shell grep -q "int path_umount" $(srctree)/fs/namespace.c; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_UMOUNT
|
||||||
|
else
|
||||||
|
$(info -- Did you know you can backport path_umount to fs/namespace.c from 5.9?)
|
||||||
|
$(info -- Read: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#how-to-backport-path-umount)
|
||||||
|
endif
|
||||||
|
|
||||||
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
||||||
ccflags-y += -Wno-declaration-after-statement
|
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
|
||||||
|
|
||||||
|
# Keep a new line here!! Because someone may append config
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
#include "ksu.h"
|
#include <linux/compiler.h>
|
||||||
#include "linux/compiler.h"
|
#include <linux/fs.h>
|
||||||
#include "linux/fs.h"
|
#include <linux/gfp.h>
|
||||||
#include "linux/gfp.h"
|
#include <linux/kernel.h>
|
||||||
#include "linux/kernel.h"
|
#include <linux/list.h>
|
||||||
#include "linux/list.h"
|
#include <linux/printk.h>
|
||||||
#include "linux/printk.h"
|
#include <linux/slab.h>
|
||||||
#include "linux/slab.h"
|
#include <linux/types.h>
|
||||||
#include "linux/types.h"
|
#include <linux/version.h>
|
||||||
#include "linux/version.h"
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
||||||
#include "linux/compiler_types.h"
|
#include <linux/compiler_types.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "ksu.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "selinux/selinux.h"
|
#include "selinux/selinux.h"
|
||||||
#include "kernel_compat.h"
|
#include "kernel_compat.h"
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
|
#include "manager.h"
|
||||||
|
|
||||||
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
|
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
|
||||||
#define FILE_FORMAT_VERSION 3 // u32
|
#define FILE_FORMAT_VERSION 3 // u32
|
||||||
@@ -274,6 +275,11 @@ bool __ksu_is_allow_uid(uid_t uid)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) {
|
||||||
|
// manager is always allowed!
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (likely(uid <= BITMAP_UID_MAX)) {
|
if (likely(uid <= BITMAP_UID_MAX)) {
|
||||||
return !!(allow_list_bitmap[uid / BITS_PER_BYTE] & (1 << (uid % BITS_PER_BYTE)));
|
return !!(allow_list_bitmap[uid / BITS_PER_BYTE] & (1 << (uid % BITS_PER_BYTE)));
|
||||||
} else {
|
} else {
|
||||||
@@ -289,6 +295,10 @@ bool __ksu_is_allow_uid(uid_t uid)
|
|||||||
bool ksu_uid_should_umount(uid_t uid)
|
bool ksu_uid_should_umount(uid_t uid)
|
||||||
{
|
{
|
||||||
struct app_profile profile = { .current_uid = uid };
|
struct app_profile profile = { .current_uid = uid };
|
||||||
|
if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) {
|
||||||
|
// we should not umount on manager!
|
||||||
|
return false;
|
||||||
|
}
|
||||||
bool found = ksu_get_app_profile(&profile);
|
bool found = ksu_get_app_profile(&profile);
|
||||||
if (!found) {
|
if (!found) {
|
||||||
// no app profile found, it must be non root app
|
// no app profile found, it must be non root app
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef __KSU_H_ALLOWLIST
|
#ifndef __KSU_H_ALLOWLIST
|
||||||
#define __KSU_H_ALLOWLIST
|
#define __KSU_H_ALLOWLIST
|
||||||
|
|
||||||
#include "linux/types.h"
|
#include <linux/types.h>
|
||||||
#include "ksu.h"
|
#include "ksu.h"
|
||||||
|
|
||||||
void ksu_allowlist_init(void);
|
void ksu_allowlist_init(void);
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
#include "linux/err.h"
|
#include <linux/err.h>
|
||||||
#include "linux/fs.h"
|
#include <linux/fs.h>
|
||||||
#include "linux/gfp.h"
|
#include <linux/gfp.h>
|
||||||
#include "linux/kernel.h"
|
#include <linux/kernel.h>
|
||||||
#include "linux/moduleparam.h"
|
#include <linux/slab.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
|
#include <linux/moduleparam.h>
|
||||||
|
#endif
|
||||||
|
#include <crypto/hash.h>
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||||
|
#include <crypto/sha2.h>
|
||||||
|
#else
|
||||||
|
#include <crypto/sha.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "apk_sign.h"
|
#include "apk_sign.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "kernel_compat.h"
|
#include "kernel_compat.h"
|
||||||
#include "crypto/hash.h"
|
|
||||||
#include "linux/slab.h"
|
|
||||||
#include "linux/version.h"
|
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
|
||||||
#include "crypto/sha2.h"
|
|
||||||
#else
|
|
||||||
#include "crypto/sha.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct sdesc {
|
struct sdesc {
|
||||||
struct shash_desc shash;
|
struct shash_desc shash;
|
||||||
@@ -188,7 +190,7 @@ static __always_inline bool check_v2_signature(char *path,
|
|||||||
struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0);
|
struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0);
|
||||||
if (IS_ERR(fp)) {
|
if (IS_ERR(fp)) {
|
||||||
pr_err("open %s error.\n", path);
|
pr_err("open %s error.\n", path);
|
||||||
return PTR_ERR(fp);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// disable inotify for this file
|
// disable inotify for this file
|
||||||
@@ -229,7 +231,8 @@ static __always_inline bool check_v2_signature(char *path,
|
|||||||
goto clean;
|
goto clean;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (;;) {
|
int loop_count = 0;
|
||||||
|
while (loop_count++ < 10) {
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
ksu_kernel_read_compat(fp, &size8, 0x8,
|
ksu_kernel_read_compat(fp, &size8, 0x8,
|
||||||
@@ -239,7 +242,6 @@ static __always_inline bool check_v2_signature(char *path,
|
|||||||
}
|
}
|
||||||
ksu_kernel_read_compat(fp, &id, 0x4, &pos); // id
|
ksu_kernel_read_compat(fp, &id, 0x4, &pos); // id
|
||||||
offset = 4;
|
offset = 4;
|
||||||
pr_info("id: 0x%08x\n", id);
|
|
||||||
if (id == 0x7109871au) {
|
if (id == 0x7109871au) {
|
||||||
v2_signing_blocks++;
|
v2_signing_blocks++;
|
||||||
v2_signing_valid =
|
v2_signing_valid =
|
||||||
@@ -251,13 +253,19 @@ static __always_inline bool check_v2_signature(char *path,
|
|||||||
} else if (id == 0x1b93ad61u) {
|
} else if (id == 0x1b93ad61u) {
|
||||||
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#74
|
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#74
|
||||||
v3_1_signing_exist = true;
|
v3_1_signing_exist = true;
|
||||||
|
} else {
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
|
pr_info("Unknown id: 0x%08x\n", id);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
pos += (size8 - offset);
|
pos += (size8 - offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v2_signing_blocks != 1) {
|
if (v2_signing_blocks != 1) {
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_err("Unexpected v2 signature count: %d\n",
|
pr_err("Unexpected v2 signature count: %d\n",
|
||||||
v2_signing_blocks);
|
v2_signing_blocks);
|
||||||
|
#endif
|
||||||
v2_signing_valid = false;
|
v2_signing_valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +281,9 @@ clean:
|
|||||||
filp_close(fp, 0);
|
filp_close(fp, 0);
|
||||||
|
|
||||||
if (v3_signing_exist || v3_1_signing_exist) {
|
if (v3_signing_exist || v3_1_signing_exist) {
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_err("Unexpected v3 signature scheme found!\n");
|
pr_err("Unexpected v3 signature scheme found!\n");
|
||||||
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,25 +292,15 @@ clean:
|
|||||||
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
|
|
||||||
unsigned ksu_expected_size = EXPECTED_SIZE;
|
int ksu_debug_manager_uid = -1;
|
||||||
const char *ksu_expected_hash = EXPECTED_HASH;
|
|
||||||
|
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
|
||||||
static int set_expected_size(const char *val, const struct kernel_param *kp)
|
static int set_expected_size(const char *val, const struct kernel_param *kp)
|
||||||
{
|
{
|
||||||
int rv = param_set_uint(val, kp);
|
int rv = param_set_uint(val, kp);
|
||||||
ksu_invalidate_manager_uid();
|
ksu_set_manager_uid(ksu_debug_manager_uid);
|
||||||
pr_info("ksu_expected_size set to %x\n", ksu_expected_size);
|
pr_info("ksu_manager_uid set to %d\n", ksu_debug_manager_uid);
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int set_expected_hash(const char *val, const struct kernel_param *kp)
|
|
||||||
{
|
|
||||||
pr_info("set_expected_hash: %s\n", val);
|
|
||||||
int rv = param_set_charp(val, kp);
|
|
||||||
ksu_invalidate_manager_uid();
|
|
||||||
pr_info("ksu_expected_hash set to %s\n", ksu_expected_hash);
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,27 +309,12 @@ static struct kernel_param_ops expected_size_ops = {
|
|||||||
.get = param_get_uint,
|
.get = param_get_uint,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct kernel_param_ops expected_hash_ops = {
|
module_param_cb(ksu_debug_manager_uid, &expected_size_ops,
|
||||||
.set = set_expected_hash,
|
&ksu_debug_manager_uid, S_IRUSR | S_IWUSR);
|
||||||
.get = param_get_charp,
|
|
||||||
.free = param_free_charp,
|
|
||||||
};
|
|
||||||
|
|
||||||
module_param_cb(ksu_expected_size, &expected_size_ops, &ksu_expected_size,
|
#endif
|
||||||
S_IRUSR | S_IWUSR);
|
|
||||||
module_param_cb(ksu_expected_hash, &expected_hash_ops, &ksu_expected_hash,
|
|
||||||
S_IRUSR | S_IWUSR);
|
|
||||||
|
|
||||||
bool is_manager_apk(char *path)
|
|
||||||
{
|
|
||||||
return check_v2_signature(path, ksu_expected_size, ksu_expected_hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
bool is_manager_apk(char *path)
|
bool is_manager_apk(char *path)
|
||||||
{
|
{
|
||||||
return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH);
|
return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef __KSU_H_APK_V2_SIGN
|
#ifndef __KSU_H_APK_V2_SIGN
|
||||||
#define __KSU_H_APK_V2_SIGN
|
#define __KSU_H_APK_V2_SIGN
|
||||||
|
|
||||||
#include "linux/types.h"
|
#include <linux/types.h>
|
||||||
|
|
||||||
bool is_manager_apk(char *path);
|
bool is_manager_apk(char *path);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef __KSU_H_ARCH
|
#ifndef __KSU_H_ARCH
|
||||||
#define __KSU_H_ARCH
|
#define __KSU_H_ARCH
|
||||||
|
|
||||||
#include "linux/version.h"
|
#include <linux/version.h>
|
||||||
|
|
||||||
#if defined(__aarch64__)
|
#if defined(__aarch64__)
|
||||||
|
|
||||||
@@ -20,8 +20,16 @@
|
|||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||||
#define PRCTL_SYMBOL "__arm64_sys_prctl"
|
#define PRCTL_SYMBOL "__arm64_sys_prctl"
|
||||||
|
#define SYS_READ_SYMBOL "__arm64_sys_read"
|
||||||
|
#define SYS_NEWFSTATAT_SYMBOL "__arm64_sys_newfstatat"
|
||||||
|
#define SYS_FACCESSAT_SYMBOL "__arm64_sys_faccessat"
|
||||||
|
#define SYS_EXECVE_SYMBOL "__arm64_sys_execve"
|
||||||
#else
|
#else
|
||||||
#define PRCTL_SYMBOL "sys_prctl"
|
#define PRCTL_SYMBOL "sys_prctl"
|
||||||
|
#define SYS_READ_SYMBOL "sys_read"
|
||||||
|
#define SYS_NEWFSTATAT_SYMBOL "sys_newfstatat"
|
||||||
|
#define SYS_FACCESSAT_SYMBOL "sys_faccessat"
|
||||||
|
#define SYS_EXECVE_SYMBOL "sys_execve"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#elif defined(__x86_64__)
|
#elif defined(__x86_64__)
|
||||||
@@ -41,8 +49,16 @@
|
|||||||
#define __PT_IP_REG ip
|
#define __PT_IP_REG ip
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||||
#define PRCTL_SYMBOL "__x64_sys_prctl"
|
#define PRCTL_SYMBOL "__x64_sys_prctl"
|
||||||
|
#define SYS_READ_SYMBOL "__x64_sys_read"
|
||||||
|
#define SYS_NEWFSTATAT_SYMBOL "__x64_sys_newfstatat"
|
||||||
|
#define SYS_FACCESSAT_SYMBOL "__x64_sys_faccessat"
|
||||||
|
#define SYS_EXECVE_SYMBOL "__x64_sys_execve"
|
||||||
#else
|
#else
|
||||||
#define PRCTL_SYMBOL "sys_prctl"
|
#define PRCTL_SYMBOL "sys_prctl"
|
||||||
|
#define SYS_READ_SYMBOL "sys_read"
|
||||||
|
#define SYS_NEWFSTATAT_SYMBOL "sys_newfstatat"
|
||||||
|
#define SYS_FACCESSAT_SYMBOL "sys_faccessat"
|
||||||
|
#define SYS_EXECVE_SYMBOL "sys_execve"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#else
|
#else
|
||||||
@@ -67,4 +83,10 @@
|
|||||||
#define PT_REGS_SP(x) (__PT_REGS_CAST(x)->__PT_SP_REG)
|
#define PT_REGS_SP(x) (__PT_REGS_CAST(x)->__PT_SP_REG)
|
||||||
#define PT_REGS_IP(x) (__PT_REGS_CAST(x)->__PT_IP_REG)
|
#define PT_REGS_IP(x) (__PT_REGS_CAST(x)->__PT_IP_REG)
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||||
|
#define PT_REAL_REGS(regs) ((struct pt_regs *)PT_REGS_PARM1(regs))
|
||||||
|
#else
|
||||||
|
#define PT_REAL_REGS(regs) ((regs))
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,23 +1,36 @@
|
|||||||
#include "linux/capability.h"
|
#include <linux/capability.h>
|
||||||
#include "linux/cred.h"
|
#include <linux/cred.h>
|
||||||
#include "linux/dcache.h"
|
#include <linux/dcache.h>
|
||||||
#include "linux/err.h"
|
#include <linux/err.h>
|
||||||
#include "linux/init.h"
|
#include <linux/init.h>
|
||||||
#include "linux/init_task.h"
|
#include <linux/init_task.h>
|
||||||
#include "linux/kernel.h"
|
#include <linux/kallsyms.h>
|
||||||
#include "linux/kprobes.h"
|
#include <linux/kernel.h>
|
||||||
#include "linux/lsm_hooks.h"
|
#include <linux/kprobes.h>
|
||||||
#include "linux/nsproxy.h"
|
#include <linux/lsm_hooks.h>
|
||||||
#include "linux/path.h"
|
#include <linux/mm.h>
|
||||||
#include "linux/printk.h"
|
#include <linux/nsproxy.h>
|
||||||
#include "linux/uaccess.h"
|
#include <linux/path.h>
|
||||||
#include "linux/uidgid.h"
|
#include <linux/printk.h>
|
||||||
#include "linux/version.h"
|
#include <linux/sched.h>
|
||||||
#include "linux/mount.h"
|
#include <linux/security.h>
|
||||||
|
#include <linux/stddef.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/uidgid.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/mount.h>
|
||||||
|
|
||||||
#include "linux/fs.h"
|
#include <linux/fs.h>
|
||||||
#include "linux/namei.h"
|
#include <linux/namei.h>
|
||||||
#include "linux/rcupdate.h"
|
|
||||||
|
#ifdef MODULE
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/irqflags.h>
|
||||||
|
#include <linux/mm_types.h>
|
||||||
|
#include <linux/rcupdate.h>
|
||||||
|
#include <linux/vmalloc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
@@ -27,7 +40,8 @@
|
|||||||
#include "ksud.h"
|
#include "ksud.h"
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
#include "selinux/selinux.h"
|
#include "selinux/selinux.h"
|
||||||
#include "uid_observer.h"
|
#include "throne_tracker.h"
|
||||||
|
#include "throne_tracker.h"
|
||||||
#include "kernel_compat.h"
|
#include "kernel_compat.h"
|
||||||
|
|
||||||
static bool ksu_module_mounted = false;
|
static bool ksu_module_mounted = false;
|
||||||
@@ -43,16 +57,11 @@ static inline bool is_allow_su()
|
|||||||
return ksu_is_allow_uid(current_uid().val);
|
return ksu_is_allow_uid(current_uid().val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool is_isolated_uid(uid_t uid)
|
static inline bool is_unsupported_uid(uid_t uid)
|
||||||
{
|
{
|
||||||
#define FIRST_ISOLATED_UID 99000
|
#define LAST_APPLICATION_UID 19999
|
||||||
#define LAST_ISOLATED_UID 99999
|
|
||||||
#define FIRST_APP_ZYGOTE_ISOLATED_UID 90000
|
|
||||||
#define LAST_APP_ZYGOTE_ISOLATED_UID 98999
|
|
||||||
uid_t appid = uid % 100000;
|
uid_t appid = uid % 100000;
|
||||||
return (appid >= FIRST_ISOLATED_UID && appid <= LAST_ISOLATED_UID) ||
|
return appid > LAST_APPLICATION_UID;
|
||||||
(appid >= FIRST_APP_ZYGOTE_ISOLATED_UID &&
|
|
||||||
appid <= LAST_APP_ZYGOTE_ISOLATED_UID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
|
static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
|
||||||
@@ -194,7 +203,7 @@ int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry)
|
|||||||
pr_info("renameat: %s -> %s, new path: %s\n", old_dentry->d_iname,
|
pr_info("renameat: %s -> %s, new path: %s\n", old_dentry->d_iname,
|
||||||
new_dentry->d_iname, buf);
|
new_dentry->d_iname, buf);
|
||||||
|
|
||||||
update_uid();
|
track_throne();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -210,86 +219,33 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// always ignore isolated app uid
|
// TODO: find it in throne tracker!
|
||||||
if (is_isolated_uid(current_uid().val)) {
|
uid_t current_uid_val = current_uid().val;
|
||||||
|
uid_t manager_uid = ksu_get_manager_uid();
|
||||||
|
if (current_uid_val != manager_uid &&
|
||||||
|
current_uid_val % 100000 == manager_uid) {
|
||||||
|
ksu_set_manager_uid(current_uid_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool from_root = 0 == current_uid().val;
|
||||||
|
bool from_manager = is_manager();
|
||||||
|
|
||||||
|
if (!from_root && !from_manager) {
|
||||||
|
// only root or manager can access this interface
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uid_t last_failed_uid = -1;
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
if (last_failed_uid == current_uid().val) {
|
pr_info("option: 0x%x, cmd: %ld\n", option, arg2);
|
||||||
return 0;
|
#endif
|
||||||
}
|
|
||||||
|
|
||||||
// pr_info("option: 0x%x, cmd: %ld\n", option, arg2);
|
|
||||||
|
|
||||||
if (arg2 == CMD_BECOME_MANAGER) {
|
if (arg2 == CMD_BECOME_MANAGER) {
|
||||||
// quick check
|
if (from_manager) {
|
||||||
if (is_manager()) {
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||||
pr_err("become_manager: prctl reply error\n");
|
pr_err("become_manager: prctl reply error\n");
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (ksu_is_manager_uid_valid()) {
|
|
||||||
pr_info("manager already exist: %d\n",
|
|
||||||
ksu_get_manager_uid());
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// someone wants to be root manager, just check it!
|
|
||||||
// arg3 should be `/data/user/<userId>/<manager_package_name>`
|
|
||||||
char param[128];
|
|
||||||
if (ksu_strncpy_from_user_nofault(param, arg3, sizeof(param)) ==
|
|
||||||
-EFAULT) {
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
|
||||||
pr_err("become_manager: copy param err\n");
|
|
||||||
#endif
|
|
||||||
goto block;
|
|
||||||
}
|
|
||||||
|
|
||||||
// for user 0, it is /data/data
|
|
||||||
// for user 999, it is /data/user/999
|
|
||||||
const char *prefix;
|
|
||||||
char prefixTmp[64];
|
|
||||||
int userId = current_uid().val / 100000;
|
|
||||||
if (userId == 0) {
|
|
||||||
prefix = "/data/data";
|
|
||||||
} else {
|
|
||||||
snprintf(prefixTmp, sizeof(prefixTmp), "/data/user/%d",
|
|
||||||
userId);
|
|
||||||
prefix = prefixTmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startswith(param, (char *)prefix) != 0) {
|
|
||||||
pr_info("become_manager: invalid param: %s\n", param);
|
|
||||||
goto block;
|
|
||||||
}
|
|
||||||
|
|
||||||
// stat the param, app must have permission to do this
|
|
||||||
// otherwise it may fake the path!
|
|
||||||
struct path path;
|
|
||||||
if (kern_path(param, LOOKUP_DIRECTORY, &path)) {
|
|
||||||
pr_err("become_manager: kern_path err\n");
|
|
||||||
goto block;
|
|
||||||
}
|
|
||||||
uid_t inode_uid = path.dentry->d_inode->i_uid.val;
|
|
||||||
path_put(&path);
|
|
||||||
if (inode_uid != current_uid().val) {
|
|
||||||
pr_err("become_manager: path uid != current uid\n");
|
|
||||||
goto block;
|
|
||||||
}
|
|
||||||
char *pkg = param + strlen(prefix);
|
|
||||||
pr_info("become_manager: param pkg: %s\n", pkg);
|
|
||||||
|
|
||||||
bool success = become_manager(pkg);
|
|
||||||
if (success) {
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
|
||||||
pr_err("become_manager: prctl reply error\n");
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
block:
|
|
||||||
last_failed_uid = current_uid().val;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,17 +262,23 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
|||||||
|
|
||||||
// Both root manager and root processes should be allowed to get version
|
// Both root manager and root processes should be allowed to get version
|
||||||
if (arg2 == CMD_GET_VERSION) {
|
if (arg2 == CMD_GET_VERSION) {
|
||||||
if (is_manager() || 0 == current_uid().val) {
|
u32 version = KERNEL_SU_VERSION;
|
||||||
u32 version = KERNEL_SU_VERSION;
|
if (copy_to_user(arg3, &version, sizeof(version))) {
|
||||||
if (copy_to_user(arg3, &version, sizeof(version))) {
|
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
}
|
||||||
}
|
#ifdef MODULE
|
||||||
|
u32 is_lkm = 0x1;
|
||||||
|
#else
|
||||||
|
u32 is_lkm = 0x0;
|
||||||
|
#endif
|
||||||
|
if (arg4 && copy_to_user(arg4, &is_lkm, sizeof(is_lkm))) {
|
||||||
|
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arg2 == CMD_REPORT_EVENT) {
|
if (arg2 == CMD_REPORT_EVENT) {
|
||||||
if (0 != current_uid().val) {
|
if (!from_root) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
switch (arg3) {
|
switch (arg3) {
|
||||||
@@ -349,7 +311,7 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (arg2 == CMD_SET_SEPOLICY) {
|
if (arg2 == CMD_SET_SEPOLICY) {
|
||||||
if (0 != current_uid().val) {
|
if (!from_root) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!handle_sepolicy(arg3, arg4)) {
|
if (!handle_sepolicy(arg3, arg4)) {
|
||||||
@@ -362,9 +324,6 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (arg2 == CMD_CHECK_SAFEMODE) {
|
if (arg2 == CMD_CHECK_SAFEMODE) {
|
||||||
if (!is_manager() && 0 != current_uid().val) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (ksu_is_safe_mode()) {
|
if (ksu_is_safe_mode()) {
|
||||||
pr_warn("safemode enabled!\n");
|
pr_warn("safemode enabled!\n");
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||||
@@ -375,57 +334,49 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (arg2 == CMD_GET_ALLOW_LIST || arg2 == CMD_GET_DENY_LIST) {
|
if (arg2 == CMD_GET_ALLOW_LIST || arg2 == CMD_GET_DENY_LIST) {
|
||||||
if (is_manager() || 0 == current_uid().val) {
|
u32 array[128];
|
||||||
u32 array[128];
|
u32 array_length;
|
||||||
u32 array_length;
|
bool success = ksu_get_allow_list(array, &array_length,
|
||||||
bool success =
|
arg2 == CMD_GET_ALLOW_LIST);
|
||||||
ksu_get_allow_list(array, &array_length,
|
if (success) {
|
||||||
arg2 == CMD_GET_ALLOW_LIST);
|
if (!copy_to_user(arg4, &array_length,
|
||||||
if (success) {
|
sizeof(array_length)) &&
|
||||||
if (!copy_to_user(arg4, &array_length,
|
!copy_to_user(arg3, array,
|
||||||
sizeof(array_length)) &&
|
sizeof(u32) * array_length)) {
|
||||||
!copy_to_user(arg3, array,
|
|
||||||
sizeof(u32) * array_length)) {
|
|
||||||
if (copy_to_user(result, &reply_ok,
|
|
||||||
sizeof(reply_ok))) {
|
|
||||||
pr_err("prctl reply error, cmd: %lu\n",
|
|
||||||
arg2);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pr_err("prctl copy allowlist error\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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: %lu\n", arg2);
|
|
||||||
}
|
|
||||||
if (!copy_to_user(arg4, &allow, sizeof(allow))) {
|
|
||||||
if (copy_to_user(result, &reply_ok,
|
if (copy_to_user(result, &reply_ok,
|
||||||
sizeof(reply_ok))) {
|
sizeof(reply_ok))) {
|
||||||
pr_err("prctl reply error, cmd: %lu\n",
|
pr_err("prctl reply error, cmd: %lu\n",
|
||||||
arg2);
|
arg2);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pr_err("prctl copy err, cmd: %lu\n", arg2);
|
pr_err("prctl copy allowlist error\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arg2 == CMD_UID_GRANTED_ROOT || arg2 == CMD_UID_SHOULD_UMOUNT) {
|
||||||
|
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: %lu\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: %lu\n", arg2);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pr_err("prctl copy err, cmd: %lu\n", arg2);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// all other cmds are for 'root manager'
|
// all other cmds are for 'root manager'
|
||||||
if (!is_manager()) {
|
if (!from_manager) {
|
||||||
last_failed_uid = current_uid().val;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -498,15 +449,13 @@ static bool should_umount(struct path *path)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ksu_umount_mnt(struct path *path, int flags)
|
static int ksu_umount_mnt(struct path *path, int flags)
|
||||||
{
|
{
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || defined(KSU_UMOUNT)
|
||||||
int err = path_umount(path, flags);
|
return path_umount(path, flags);
|
||||||
if (err) {
|
|
||||||
pr_info("umount %s failed: %d\n", path->dentry->d_iname, err);
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
// TODO: umount for non GKI kernel
|
// TODO: umount for non GKI kernel
|
||||||
|
return -ENOSYS;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -528,7 +477,10 @@ static void try_umount(const char *mnt, bool check_mnt, int flags)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ksu_umount_mnt(&path, flags);
|
err = ksu_umount_mnt(&path, flags);
|
||||||
|
if (err) {
|
||||||
|
pr_warn("umount %s failed: %d\n", mnt, err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
||||||
@@ -550,7 +502,7 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_appuid(new_uid) || is_isolated_uid(new_uid.val)) {
|
if (!is_appuid(new_uid) || is_unsupported_uid(new_uid.val)) {
|
||||||
// pr_info("handle setuid ignore non application or isolated uid: %d\n", new_uid.val);
|
// pr_info("handle setuid ignore non application or isolated uid: %d\n", new_uid.val);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -577,9 +529,11 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
|||||||
current->pid);
|
current->pid);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
// umount the target mnt
|
// umount the target mnt
|
||||||
pr_info("handle umount for uid: %d, pid: %d\n", new_uid.val,
|
pr_info("handle umount for uid: %d, pid: %d\n", new_uid.val,
|
||||||
current->pid);
|
current->pid);
|
||||||
|
#endif
|
||||||
|
|
||||||
// fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and
|
// fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and
|
||||||
// filter the mountpoint whose target is `/data/adb`
|
// filter the mountpoint whose target is `/data/adb`
|
||||||
@@ -599,11 +553,7 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
|||||||
|
|
||||||
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
|
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||||
struct pt_regs *real_regs = (struct pt_regs *)PT_REGS_PARM1(regs);
|
|
||||||
#else
|
|
||||||
struct pt_regs *real_regs = regs;
|
|
||||||
#endif
|
|
||||||
int option = (int)PT_REGS_PARM1(real_regs);
|
int option = (int)PT_REGS_PARM1(real_regs);
|
||||||
unsigned long arg2 = (unsigned long)PT_REGS_PARM2(real_regs);
|
unsigned long arg2 = (unsigned long)PT_REGS_PARM2(real_regs);
|
||||||
unsigned long arg3 = (unsigned long)PT_REGS_PARM3(real_regs);
|
unsigned long arg3 = (unsigned long)PT_REGS_PARM3(real_regs);
|
||||||
@@ -675,7 +625,7 @@ static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
|||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
}
|
}
|
||||||
// kernel 4.4 and 4.9
|
// kernel 4.4 and 4.9
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI)
|
||||||
static int ksu_key_permission(key_ref_t key_ref, const struct cred *cred,
|
static int ksu_key_permission(key_ref_t key_ref, const struct cred *cred,
|
||||||
unsigned perm)
|
unsigned perm)
|
||||||
{
|
{
|
||||||
@@ -703,11 +653,12 @@ static int ksu_task_fix_setuid(struct cred *new, const struct cred *old,
|
|||||||
return ksu_handle_setuid(new, old);
|
return ksu_handle_setuid(new, old);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef MODULE
|
||||||
static struct security_hook_list ksu_hooks[] = {
|
static struct security_hook_list ksu_hooks[] = {
|
||||||
LSM_HOOK_INIT(task_prctl, ksu_task_prctl),
|
LSM_HOOK_INIT(task_prctl, ksu_task_prctl),
|
||||||
LSM_HOOK_INIT(inode_rename, ksu_inode_rename),
|
LSM_HOOK_INIT(inode_rename, ksu_inode_rename),
|
||||||
LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid),
|
LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid),
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI)
|
||||||
LSM_HOOK_INIT(key_permission, ksu_key_permission)
|
LSM_HOOK_INIT(key_permission, ksu_key_permission)
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
@@ -722,21 +673,184 @@ void __init ksu_lsm_hook_init(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
static int override_security_head(void *head, const void *new_head, size_t len)
|
||||||
|
{
|
||||||
|
unsigned long base = (unsigned long)head & PAGE_MASK;
|
||||||
|
unsigned long offset = offset_in_page(head);
|
||||||
|
|
||||||
|
// this is impossible for our case because the page alignment
|
||||||
|
// but be careful for other cases!
|
||||||
|
BUG_ON(offset + len > PAGE_SIZE);
|
||||||
|
struct page *page = phys_to_page(__pa(base));
|
||||||
|
if (!page) {
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *addr = vmap(&page, 1, VM_MAP, PAGE_KERNEL);
|
||||||
|
if (!addr) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
local_irq_disable();
|
||||||
|
memcpy(addr + offset, new_head, len);
|
||||||
|
local_irq_enable();
|
||||||
|
vunmap(addr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_security_hook_list(struct hlist_head *head)
|
||||||
|
{
|
||||||
|
struct hlist_node *temp;
|
||||||
|
struct security_hook_list *entry;
|
||||||
|
|
||||||
|
if (!head)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hlist_for_each_entry_safe (entry, temp, head, list) {
|
||||||
|
hlist_del(&entry->list);
|
||||||
|
kfree(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(head);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct hlist_head *copy_security_hlist(struct hlist_head *orig)
|
||||||
|
{
|
||||||
|
struct hlist_head *new_head = kmalloc(sizeof(*new_head), GFP_KERNEL);
|
||||||
|
if (!new_head)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
INIT_HLIST_HEAD(new_head);
|
||||||
|
|
||||||
|
struct security_hook_list *entry;
|
||||||
|
struct security_hook_list *new_entry;
|
||||||
|
|
||||||
|
hlist_for_each_entry (entry, orig, list) {
|
||||||
|
new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
|
||||||
|
if (!new_entry) {
|
||||||
|
free_security_hook_list(new_head);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*new_entry = *entry;
|
||||||
|
|
||||||
|
hlist_add_tail_rcu(&new_entry->list, new_head);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_head;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LSM_SEARCH_MAX 180 // This should be enough to iterate
|
||||||
|
static void *find_head_addr(void *security_ptr, int *index)
|
||||||
|
{
|
||||||
|
if (!security_ptr) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
struct hlist_head *head_start =
|
||||||
|
(struct hlist_head *)&security_hook_heads;
|
||||||
|
|
||||||
|
for (int i = 0; i < LSM_SEARCH_MAX; i++) {
|
||||||
|
struct hlist_head *head = head_start + i;
|
||||||
|
struct security_hook_list *pos;
|
||||||
|
hlist_for_each_entry (pos, head, list) {
|
||||||
|
if (pos->hook.capget == security_ptr) {
|
||||||
|
if (index) {
|
||||||
|
*index = i;
|
||||||
|
}
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GET_SYMBOL_ADDR(sym) \
|
||||||
|
({ \
|
||||||
|
void *addr = kallsyms_lookup_name(#sym ".cfi_jt"); \
|
||||||
|
if (!addr) { \
|
||||||
|
addr = kallsyms_lookup_name(#sym); \
|
||||||
|
} \
|
||||||
|
addr; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define KSU_LSM_HOOK_HACK_INIT(head_ptr, name, func) \
|
||||||
|
do { \
|
||||||
|
static struct security_hook_list hook = { \
|
||||||
|
.hook = { .name = func } \
|
||||||
|
}; \
|
||||||
|
hook.head = head_ptr; \
|
||||||
|
hook.lsm = "ksu"; \
|
||||||
|
struct hlist_head *new_head = copy_security_hlist(hook.head); \
|
||||||
|
if (!new_head) { \
|
||||||
|
pr_err("Failed to copy security list: %s\n", #name); \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
hlist_add_tail_rcu(&hook.list, new_head); \
|
||||||
|
if (override_security_head(hook.head, new_head, \
|
||||||
|
sizeof(*new_head))) { \
|
||||||
|
free_security_hook_list(new_head); \
|
||||||
|
pr_err("Failed to hack lsm for: %s\n", #name); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
void __init ksu_lsm_hook_init(void)
|
||||||
|
{
|
||||||
|
void *cap_prctl = GET_SYMBOL_ADDR(cap_task_prctl);
|
||||||
|
void *prctl_head = find_head_addr(cap_prctl, NULL);
|
||||||
|
if (prctl_head) {
|
||||||
|
if (prctl_head != &security_hook_heads.task_prctl) {
|
||||||
|
pr_warn("prctl's address has shifted!\n");
|
||||||
|
}
|
||||||
|
KSU_LSM_HOOK_HACK_INIT(prctl_head, task_prctl, ksu_task_prctl);
|
||||||
|
} else {
|
||||||
|
pr_warn("Failed to find task_prctl!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int inode_killpriv_index = -1;
|
||||||
|
void *cap_killpriv = GET_SYMBOL_ADDR(cap_inode_killpriv);
|
||||||
|
find_head_addr(cap_killpriv, &inode_killpriv_index);
|
||||||
|
if (inode_killpriv_index < 0) {
|
||||||
|
pr_warn("Failed to find inode_rename, use kprobe instead!\n");
|
||||||
|
register_kprobe(&renameat_kp);
|
||||||
|
} else {
|
||||||
|
int inode_rename_index = inode_killpriv_index +
|
||||||
|
&security_hook_heads.inode_rename -
|
||||||
|
&security_hook_heads.inode_killpriv;
|
||||||
|
struct hlist_head *head_start =
|
||||||
|
(struct hlist_head *)&security_hook_heads;
|
||||||
|
void *inode_rename_head = head_start + inode_rename_index;
|
||||||
|
if (inode_rename_head != &security_hook_heads.inode_rename) {
|
||||||
|
pr_warn("inode_rename's address has shifted!\n");
|
||||||
|
}
|
||||||
|
KSU_LSM_HOOK_HACK_INIT(inode_rename_head, inode_rename,
|
||||||
|
ksu_inode_rename);
|
||||||
|
}
|
||||||
|
void *cap_setuid = GET_SYMBOL_ADDR(cap_task_fix_setuid);
|
||||||
|
void *setuid_head = find_head_addr(cap_setuid, NULL);
|
||||||
|
if (setuid_head) {
|
||||||
|
if (setuid_head != &security_hook_heads.task_fix_setuid) {
|
||||||
|
pr_warn("setuid's address has shifted!\n");
|
||||||
|
}
|
||||||
|
KSU_LSM_HOOK_HACK_INIT(setuid_head, task_fix_setuid,
|
||||||
|
ksu_task_fix_setuid);
|
||||||
|
} else {
|
||||||
|
pr_warn("Failed to find task_fix_setuid!\n");
|
||||||
|
}
|
||||||
|
smp_mb();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void __init ksu_core_init(void)
|
void __init ksu_core_init(void)
|
||||||
{
|
{
|
||||||
#ifndef MODULE
|
|
||||||
pr_info("ksu_lsm_hook_init\n");
|
|
||||||
ksu_lsm_hook_init();
|
ksu_lsm_hook_init();
|
||||||
#else
|
|
||||||
pr_info("ksu_kprobe_init\n");
|
|
||||||
ksu_kprobe_init();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_core_exit(void)
|
void ksu_core_exit(void)
|
||||||
{
|
{
|
||||||
#ifndef MODULE
|
#ifdef CONFIG_KPROBES
|
||||||
pr_info("ksu_kprobe_exit\n");
|
pr_info("ksu_core_kprobe_exit\n");
|
||||||
ksu_kprobe_exit();
|
// we dont use this now
|
||||||
|
// ksu_kprobe_exit();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef __KSU_H_KSU_CORE
|
#ifndef __KSU_H_KSU_CORE
|
||||||
#define __KSU_H_KSU_CORE
|
#define __KSU_H_KSU_CORE
|
||||||
|
|
||||||
#include "linux/init.h"
|
#include <linux/init.h>
|
||||||
|
|
||||||
void __init ksu_core_init(void);
|
void __init ksu_core_init(void);
|
||||||
void ksu_core_exit(void);
|
void ksu_core_exit(void);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#ifndef __KSU_H_KSHOOK
|
#ifndef __KSU_H_KSHOOK
|
||||||
#define __KSU_H_KSHOOK
|
#define __KSU_H_KSHOOK
|
||||||
|
|
||||||
#include "linux/fs.h"
|
#include <linux/fs.h>
|
||||||
#include "linux/types.h"
|
#include <linux/types.h>
|
||||||
|
|
||||||
// For sucompat
|
// For sucompat
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,19 @@
|
|||||||
#include "linux/version.h"
|
#include <linux/version.h>
|
||||||
#include "linux/fs.h"
|
#include <linux/fs.h>
|
||||||
#include "linux/nsproxy.h"
|
#include <linux/nsproxy.h>
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||||
#include "linux/sched/task.h"
|
#include <linux/sched/task.h>
|
||||||
#include "linux/uaccess.h"
|
|
||||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
|
|
||||||
#include "linux/uaccess.h"
|
|
||||||
#include "linux/sched.h"
|
|
||||||
#else
|
#else
|
||||||
#include "linux/sched.h"
|
#include <linux/sched.h>
|
||||||
#endif
|
#endif
|
||||||
|
#include <linux/uaccess.h>
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "kernel_compat.h" // Add check Huawei Device
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI)
|
||||||
#include "linux/key.h"
|
#include <linux/key.h>
|
||||||
#include "linux/errno.h"
|
#include <linux/errno.h>
|
||||||
#include "linux/cred.h"
|
#include <linux/cred.h>
|
||||||
struct key *init_session_keyring = NULL;
|
struct key *init_session_keyring = NULL;
|
||||||
|
|
||||||
static inline int install_session_keyring(struct key *keyring)
|
static inline int install_session_keyring(struct key *keyring)
|
||||||
@@ -81,7 +79,7 @@ void ksu_android_ns_fs_check()
|
|||||||
|
|
||||||
struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
|
struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
|
||||||
{
|
{
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI)
|
||||||
if (init_session_keyring != NULL && !current_cred()->session_keyring &&
|
if (init_session_keyring != NULL && !current_cred()->session_keyring &&
|
||||||
(current->flags & PF_WQ_WORKER)) {
|
(current->flags & PF_WQ_WORKER)) {
|
||||||
pr_info("installing init session keyring for older kernel\n");
|
pr_info("installing init session keyring for older kernel\n");
|
||||||
|
|||||||
@@ -1,15 +1,30 @@
|
|||||||
#ifndef __KSU_H_KERNEL_COMPAT
|
#ifndef __KSU_H_KERNEL_COMPAT
|
||||||
#define __KSU_H_KERNEL_COMPAT
|
#define __KSU_H_KERNEL_COMPAT
|
||||||
|
|
||||||
#include "linux/fs.h"
|
#include <linux/fs.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include "ss/policydb.h"
|
||||||
#include "linux/key.h"
|
#include "linux/key.h"
|
||||||
#include "linux/version.h"
|
|
||||||
|
/*
|
||||||
|
* Adapt to Huawei HISI kernel without affecting other kernels ,
|
||||||
|
* Huawei Hisi Kernel EBITMAP Enable or Disable Flag ,
|
||||||
|
* From ss/ebitmap.h
|
||||||
|
*/
|
||||||
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) && \
|
||||||
|
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)) || \
|
||||||
|
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) && \
|
||||||
|
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0))
|
||||||
|
#ifdef HISI_SELINUX_EBITMAP_RO
|
||||||
|
#define CONFIG_IS_HW_HISI
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
extern long ksu_strncpy_from_user_nofault(char *dst,
|
extern long ksu_strncpy_from_user_nofault(char *dst,
|
||||||
const void __user *unsafe_addr,
|
const void __user *unsafe_addr,
|
||||||
long count);
|
long count);
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI)
|
||||||
extern struct key *init_session_keyring;
|
extern struct key *init_session_keyring;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
34
kernel/ksu.c
34
kernel/ksu.c
@@ -1,13 +1,15 @@
|
|||||||
#include "linux/fs.h"
|
#include <linux/export.h>
|
||||||
#include "linux/module.h"
|
#include <linux/fs.h>
|
||||||
#include "linux/workqueue.h"
|
#include <linux/kobject.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
#include "core_hook.h"
|
#include "core_hook.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "ksu.h"
|
#include "ksu.h"
|
||||||
#include "uid_observer.h"
|
#include "throne_tracker.h"
|
||||||
|
|
||||||
static struct workqueue_struct *ksu_workqueue;
|
static struct workqueue_struct *ksu_workqueue;
|
||||||
|
|
||||||
@@ -30,8 +32,10 @@ int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
|||||||
flags);
|
flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void ksu_enable_sucompat();
|
extern void ksu_sucompat_init();
|
||||||
extern void ksu_enable_ksud();
|
extern void ksu_sucompat_exit();
|
||||||
|
extern void ksu_ksud_init();
|
||||||
|
extern void ksu_ksud_exit();
|
||||||
|
|
||||||
int __init kernelsu_init(void)
|
int __init kernelsu_init(void)
|
||||||
{
|
{
|
||||||
@@ -51,15 +55,20 @@ int __init kernelsu_init(void)
|
|||||||
|
|
||||||
ksu_allowlist_init();
|
ksu_allowlist_init();
|
||||||
|
|
||||||
ksu_uid_observer_init();
|
ksu_throne_tracker_init();
|
||||||
|
|
||||||
#ifdef CONFIG_KPROBES
|
#ifdef CONFIG_KPROBES
|
||||||
ksu_enable_sucompat();
|
ksu_sucompat_init();
|
||||||
ksu_enable_ksud();
|
ksu_ksud_init();
|
||||||
#else
|
#else
|
||||||
pr_alert("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
|
#endif
|
||||||
|
|
||||||
|
#ifdef MODULE
|
||||||
|
#ifndef CONFIG_KSU_DEBUG
|
||||||
|
kobject_del(&THIS_MODULE->mkobj.kobj);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,10 +76,15 @@ void kernelsu_exit(void)
|
|||||||
{
|
{
|
||||||
ksu_allowlist_exit();
|
ksu_allowlist_exit();
|
||||||
|
|
||||||
ksu_uid_observer_exit();
|
ksu_throne_tracker_exit();
|
||||||
|
|
||||||
destroy_workqueue(ksu_workqueue);
|
destroy_workqueue(ksu_workqueue);
|
||||||
|
|
||||||
|
#ifdef CONFIG_KPROBES
|
||||||
|
ksu_ksud_exit();
|
||||||
|
ksu_sucompat_exit();
|
||||||
|
#endif
|
||||||
|
|
||||||
ksu_core_exit();
|
ksu_core_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#ifndef __KSU_H_KSU
|
#ifndef __KSU_H_KSU
|
||||||
#define __KSU_H_KSU
|
#define __KSU_H_KSU
|
||||||
|
|
||||||
#include "linux/types.h"
|
#include <linux/types.h>
|
||||||
#include "linux/workqueue.h"
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
#define KERNEL_SU_VERSION KSU_VERSION
|
#define KERNEL_SU_VERSION KSU_VERSION
|
||||||
#define KERNEL_SU_OPTION 0xDEADBEEF
|
#define KERNEL_SU_OPTION 0xDEADBEEF
|
||||||
|
|||||||
171
kernel/ksud.c
171
kernel/ksud.c
@@ -1,15 +1,24 @@
|
|||||||
#include "asm/current.h"
|
#include <asm/current.h>
|
||||||
#include "linux/compat.h"
|
#include <linux/compat.h>
|
||||||
#include "linux/dcache.h"
|
#include <linux/cred.h>
|
||||||
#include "linux/err.h"
|
#include <linux/dcache.h>
|
||||||
#include "linux/fs.h"
|
#include <linux/err.h>
|
||||||
#include "linux/input-event-codes.h"
|
#include <linux/file.h>
|
||||||
#include "linux/kprobes.h"
|
#include <linux/fs.h>
|
||||||
#include "linux/printk.h"
|
#include <linux/version.h>
|
||||||
#include "linux/types.h"
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
|
||||||
#include "linux/uaccess.h"
|
#include <linux/input-event-codes.h>
|
||||||
#include "linux/version.h"
|
#else
|
||||||
#include "linux/workqueue.h"
|
#include <uapi/linux/input.h>
|
||||||
|
#endif
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)
|
||||||
|
#include <linux/aio.h>
|
||||||
|
#endif
|
||||||
|
#include <linux/kprobes.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
@@ -55,6 +64,8 @@ bool ksu_execveat_hook __read_mostly = true;
|
|||||||
bool ksu_input_hook __read_mostly = true;
|
bool ksu_input_hook __read_mostly = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
u32 ksu_devpts_sid;
|
||||||
|
|
||||||
void on_post_fs_data(void)
|
void on_post_fs_data(void)
|
||||||
{
|
{
|
||||||
static bool done = false;
|
static bool done = false;
|
||||||
@@ -67,6 +78,9 @@ void on_post_fs_data(void)
|
|||||||
ksu_load_allow_list();
|
ksu_load_allow_list();
|
||||||
// sanity check, this may influence the performance
|
// sanity check, this may influence the performance
|
||||||
stop_input_hook();
|
stop_input_hook();
|
||||||
|
|
||||||
|
ksu_devpts_sid = ksu_get_devpts_sid();
|
||||||
|
pr_info("devpts sid: %d\n", ksu_devpts_sid);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAX_ARG_STRINGS 0x7FFFFFFF
|
#define MAX_ARG_STRINGS 0x7FFFFFFF
|
||||||
@@ -107,7 +121,7 @@ static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr)
|
|||||||
* count() counts the number of strings in array ARGV.
|
* count() counts the number of strings in array ARGV.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure old GCC compiler can use __maybe_unused,
|
* Make sure old GCC compiler can use __maybe_unused,
|
||||||
* Test passed in 4.4.x ~ 4.9.x when use GCC.
|
* Test passed in 4.4.x ~ 4.9.x when use GCC.
|
||||||
*/
|
*/
|
||||||
@@ -140,7 +154,8 @@ static int __maybe_unused count(struct user_arg_ptr argv, int max)
|
|||||||
|
|
||||||
// IMPORTANT NOTE: the call from execve_handler_pre WON'T provided correct value for envp and flags in GKI version
|
// IMPORTANT NOTE: the call from execve_handler_pre WON'T provided correct value for envp and flags in GKI version
|
||||||
int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||||
struct user_arg_ptr *argv, struct user_arg_ptr *envp, int *flags)
|
struct user_arg_ptr *argv,
|
||||||
|
struct user_arg_ptr *envp, int *flags)
|
||||||
{
|
{
|
||||||
#ifndef CONFIG_KPROBES
|
#ifndef CONFIG_KPROBES
|
||||||
if (!ksu_execveat_hook) {
|
if (!ksu_execveat_hook) {
|
||||||
@@ -166,8 +181,9 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(!memcmp(filename->name, system_bin_init,
|
if (unlikely(!memcmp(filename->name, system_bin_init,
|
||||||
sizeof(system_bin_init) - 1) && argv)) {
|
sizeof(system_bin_init) - 1) &&
|
||||||
|
argv)) {
|
||||||
// /system/bin/init executed
|
// /system/bin/init executed
|
||||||
int argc = count(*argv, MAX_ARG_STRINGS);
|
int argc = count(*argv, MAX_ARG_STRINGS);
|
||||||
pr_info("/system/bin/init argc: %d\n", argc);
|
pr_info("/system/bin/init argc: %d\n", argc);
|
||||||
@@ -175,8 +191,10 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
|||||||
const char __user *p = get_user_arg_ptr(*argv, 1);
|
const char __user *p = get_user_arg_ptr(*argv, 1);
|
||||||
if (p && !IS_ERR(p)) {
|
if (p && !IS_ERR(p)) {
|
||||||
char first_arg[16];
|
char first_arg[16];
|
||||||
ksu_strncpy_from_user_nofault(first_arg, p, sizeof(first_arg));
|
ksu_strncpy_from_user_nofault(
|
||||||
pr_info("/system/bin/init first arg: %s\n", first_arg);
|
first_arg, p, sizeof(first_arg));
|
||||||
|
pr_info("/system/bin/init first arg: %s\n",
|
||||||
|
first_arg);
|
||||||
if (!strcmp(first_arg, "second_stage")) {
|
if (!strcmp(first_arg, "second_stage")) {
|
||||||
pr_info("/system/bin/init second_stage executed\n");
|
pr_info("/system/bin/init second_stage executed\n");
|
||||||
apply_kernelsu_rules();
|
apply_kernelsu_rules();
|
||||||
@@ -188,7 +206,8 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (unlikely(!memcmp(filename->name, old_system_init,
|
} else if (unlikely(!memcmp(filename->name, old_system_init,
|
||||||
sizeof(old_system_init) - 1) && argv)) {
|
sizeof(old_system_init) - 1) &&
|
||||||
|
argv)) {
|
||||||
// /init executed
|
// /init executed
|
||||||
int argc = count(*argv, MAX_ARG_STRINGS);
|
int argc = count(*argv, MAX_ARG_STRINGS);
|
||||||
pr_info("/init argc: %d\n", argc);
|
pr_info("/init argc: %d\n", argc);
|
||||||
@@ -197,7 +216,8 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
|||||||
const char __user *p = get_user_arg_ptr(*argv, 1);
|
const char __user *p = get_user_arg_ptr(*argv, 1);
|
||||||
if (p && !IS_ERR(p)) {
|
if (p && !IS_ERR(p)) {
|
||||||
char first_arg[16];
|
char first_arg[16];
|
||||||
ksu_strncpy_from_user_nofault(first_arg, p, sizeof(first_arg));
|
ksu_strncpy_from_user_nofault(
|
||||||
|
first_arg, p, sizeof(first_arg));
|
||||||
pr_info("/init first arg: %s\n", first_arg);
|
pr_info("/init first arg: %s\n", first_arg);
|
||||||
if (!strcmp(first_arg, "--second-stage")) {
|
if (!strcmp(first_arg, "--second-stage")) {
|
||||||
pr_info("/init second_stage executed\n");
|
pr_info("/init second_stage executed\n");
|
||||||
@@ -214,13 +234,15 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
|||||||
if (envc > 0) {
|
if (envc > 0) {
|
||||||
int n;
|
int n;
|
||||||
for (n = 1; n <= envc; n++) {
|
for (n = 1; n <= envc; n++) {
|
||||||
const char __user *p = get_user_arg_ptr(*envp, n);
|
const char __user *p =
|
||||||
|
get_user_arg_ptr(*envp, n);
|
||||||
if (!p || IS_ERR(p)) {
|
if (!p || IS_ERR(p)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
char env[256];
|
char env[256];
|
||||||
// Reading environment variable strings from user space
|
// Reading environment variable strings from user space
|
||||||
if (ksu_strncpy_from_user_nofault(env, p, sizeof(env)) < 0)
|
if (ksu_strncpy_from_user_nofault(
|
||||||
|
env, p, sizeof(env)) < 0)
|
||||||
continue;
|
continue;
|
||||||
// Parsing environment variable names and values
|
// Parsing environment variable names and values
|
||||||
char *env_name = env;
|
char *env_name = env;
|
||||||
@@ -231,10 +253,14 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
|||||||
*env_value = '\0';
|
*env_value = '\0';
|
||||||
env_value++;
|
env_value++;
|
||||||
// Check if the environment variable name and value are matching
|
// Check if the environment variable name and value are matching
|
||||||
if (!strcmp(env_name, "INIT_SECOND_STAGE") && (!strcmp(env_value, "1") || !strcmp(env_value, "true"))) {
|
if (!strcmp(env_name,
|
||||||
|
"INIT_SECOND_STAGE") &&
|
||||||
|
(!strcmp(env_value, "1") ||
|
||||||
|
!strcmp(env_value, "true"))) {
|
||||||
pr_info("/init second_stage executed\n");
|
pr_info("/init second_stage executed\n");
|
||||||
apply_kernelsu_rules();
|
apply_kernelsu_rules();
|
||||||
init_second_stage_executed = true;
|
init_second_stage_executed =
|
||||||
|
true;
|
||||||
ksu_android_ns_fs_check();
|
ksu_android_ns_fs_check();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -242,10 +268,11 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(first_app_process &&
|
if (unlikely(first_app_process && !memcmp(filename->name, app_process,
|
||||||
!memcmp(filename->name, app_process, sizeof(app_process) - 1))) {
|
sizeof(app_process) - 1))) {
|
||||||
first_app_process = false;
|
first_app_process = false;
|
||||||
pr_info("exec app_process, /data prepared, second_stage: %d\n", init_second_stage_executed);
|
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
|
on_post_fs_data(); // we keep this for old ksud
|
||||||
stop_execve_hook();
|
stop_execve_hook();
|
||||||
}
|
}
|
||||||
@@ -264,7 +291,8 @@ static ssize_t read_proxy(struct file *file, char __user *buf, size_t count,
|
|||||||
bool first_read = file->f_pos == 0;
|
bool first_read = file->f_pos == 0;
|
||||||
ssize_t ret = orig_read(file, buf, count, pos);
|
ssize_t ret = orig_read(file, buf, count, pos);
|
||||||
if (first_read) {
|
if (first_read) {
|
||||||
pr_info("read_proxy append %ld + %ld\n", ret, read_count_append);
|
pr_info("read_proxy append %ld + %ld\n", ret,
|
||||||
|
read_count_append);
|
||||||
ret += read_count_append;
|
ret += read_count_append;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@@ -376,6 +404,18 @@ int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr,
|
||||||
|
size_t *count_ptr)
|
||||||
|
{
|
||||||
|
struct file *file = fget(fd);
|
||||||
|
if (!file) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int result = ksu_handle_vfs_read(&file, buf_ptr, count_ptr, NULL);
|
||||||
|
fput(file);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static unsigned int volumedown_pressed_count = 0;
|
static unsigned int volumedown_pressed_count = 0;
|
||||||
|
|
||||||
static bool is_volumedown_enough(unsigned int count)
|
static bool is_volumedown_enough(unsigned int count)
|
||||||
@@ -451,7 +491,32 @@ static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
|||||||
return ksu_handle_execveat_ksud(fd, filename_ptr, &argv, NULL, NULL);
|
return ksu_handle_execveat_ksud(fd, filename_ptr, &argv, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||||
|
const char __user **filename_user =
|
||||||
|
(const char **)&PT_REGS_PARM1(real_regs);
|
||||||
|
const char __user *const __user *__argv =
|
||||||
|
(const char __user *const __user *)PT_REGS_PARM2(real_regs);
|
||||||
|
struct user_arg_ptr argv = { .ptr.native = __argv };
|
||||||
|
struct filename filename_in, *filename_p;
|
||||||
|
char path[32];
|
||||||
|
|
||||||
|
if (!filename_user)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
memset(path, 0, sizeof(path));
|
||||||
|
ksu_strncpy_from_user_nofault(path, *filename_user, 32);
|
||||||
|
filename_in.name = path;
|
||||||
|
|
||||||
|
filename_p = &filename_in;
|
||||||
|
return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, &argv, NULL,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove this later!
|
||||||
|
__maybe_unused static int vfs_read_handler_pre(struct kprobe *p,
|
||||||
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
struct file **file_ptr = (struct file **)&PT_REGS_PARM1(regs);
|
struct file **file_ptr = (struct file **)&PT_REGS_PARM1(regs);
|
||||||
char __user **buf_ptr = (char **)&PT_REGS_PARM2(regs);
|
char __user **buf_ptr = (char **)&PT_REGS_PARM2(regs);
|
||||||
@@ -461,6 +526,16 @@ static int read_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
|||||||
return ksu_handle_vfs_read(file_ptr, buf_ptr, count_ptr, pos_ptr);
|
return ksu_handle_vfs_read(file_ptr, buf_ptr, count_ptr, pos_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||||
|
unsigned int fd = PT_REGS_PARM1(real_regs);
|
||||||
|
char __user **buf_ptr = (char __user **)&PT_REGS_PARM2(real_regs);
|
||||||
|
size_t count_ptr = (size_t *)&PT_REGS_PARM3(real_regs);
|
||||||
|
|
||||||
|
return ksu_handle_sys_read(fd, buf_ptr, count_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
static int input_handle_event_handler_pre(struct kprobe *p,
|
static int input_handle_event_handler_pre(struct kprobe *p,
|
||||||
struct pt_regs *regs)
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
@@ -470,6 +545,12 @@ static int input_handle_event_handler_pre(struct kprobe *p,
|
|||||||
return ksu_handle_input_handle_event(type, code, value);
|
return ksu_handle_input_handle_event(type, code, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
static struct kprobe execve_kp = {
|
||||||
|
.symbol_name = SYS_EXECVE_SYMBOL,
|
||||||
|
.pre_handler = sys_execve_handler_pre,
|
||||||
|
};
|
||||||
|
#else
|
||||||
static struct kprobe execve_kp = {
|
static struct kprobe execve_kp = {
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
||||||
.symbol_name = "do_execveat_common",
|
.symbol_name = "do_execveat_common",
|
||||||
@@ -480,14 +561,22 @@ static struct kprobe execve_kp = {
|
|||||||
#endif
|
#endif
|
||||||
.pre_handler = execve_handler_pre,
|
.pre_handler = execve_handler_pre,
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
static struct kprobe vfs_read_kp = {
|
||||||
|
.symbol_name = SYS_READ_SYMBOL,
|
||||||
|
.pre_handler = sys_read_handler_pre,
|
||||||
|
};
|
||||||
|
#else
|
||||||
static struct kprobe vfs_read_kp = {
|
static struct kprobe vfs_read_kp = {
|
||||||
.symbol_name = "vfs_read",
|
.symbol_name = "vfs_read",
|
||||||
.pre_handler = read_handler_pre,
|
.pre_handler = vfs_read_handler_pre,
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
static struct kprobe input_handle_event_kp = {
|
static struct kprobe input_event_kp = {
|
||||||
.symbol_name = "input_handle_event",
|
.symbol_name = "input_event",
|
||||||
.pre_handler = input_handle_event_handler_pre,
|
.pre_handler = input_handle_event_handler_pre,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -503,7 +592,7 @@ static void do_stop_execve_hook(struct work_struct *work)
|
|||||||
|
|
||||||
static void do_stop_input_hook(struct work_struct *work)
|
static void do_stop_input_hook(struct work_struct *work)
|
||||||
{
|
{
|
||||||
unregister_kprobe(&input_handle_event_kp);
|
unregister_kprobe(&input_event_kp);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -546,7 +635,7 @@ static void stop_input_hook()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ksud: module support
|
// ksud: module support
|
||||||
void ksu_enable_ksud()
|
void ksu_ksud_init()
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KPROBES
|
#ifdef CONFIG_KPROBES
|
||||||
int ret;
|
int ret;
|
||||||
@@ -557,11 +646,21 @@ void ksu_enable_ksud()
|
|||||||
ret = register_kprobe(&vfs_read_kp);
|
ret = register_kprobe(&vfs_read_kp);
|
||||||
pr_info("ksud: vfs_read_kp: %d\n", ret);
|
pr_info("ksud: vfs_read_kp: %d\n", ret);
|
||||||
|
|
||||||
ret = register_kprobe(&input_handle_event_kp);
|
ret = register_kprobe(&input_event_kp);
|
||||||
pr_info("ksud: input_handle_event_kp: %d\n", ret);
|
pr_info("ksud: input_event_kp: %d\n", ret);
|
||||||
|
|
||||||
INIT_WORK(&stop_vfs_read_work, do_stop_vfs_read_hook);
|
INIT_WORK(&stop_vfs_read_work, do_stop_vfs_read_hook);
|
||||||
INIT_WORK(&stop_execve_hook_work, do_stop_execve_hook);
|
INIT_WORK(&stop_execve_hook_work, do_stop_execve_hook);
|
||||||
INIT_WORK(&stop_input_hook_work, do_stop_input_hook);
|
INIT_WORK(&stop_input_hook_work, do_stop_input_hook);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ksu_ksud_exit()
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_KPROBES
|
||||||
|
unregister_kprobe(&execve_kp);
|
||||||
|
// this should be done before unregister vfs_read_kp
|
||||||
|
// unregister_kprobe(&vfs_read_kp);
|
||||||
|
unregister_kprobe(&input_event_kp);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
#ifndef __KSU_H_KSUD
|
#ifndef __KSU_H_KSUD
|
||||||
#define __KSU_H_KSUD
|
#define __KSU_H_KSUD
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
#define KSUD_PATH "/data/adb/ksud"
|
#define KSUD_PATH "/data/adb/ksud"
|
||||||
|
|
||||||
void on_post_fs_data(void);
|
void on_post_fs_data(void);
|
||||||
|
|
||||||
bool ksu_is_safe_mode(void);
|
bool ksu_is_safe_mode(void);
|
||||||
|
|
||||||
|
extern u32 ksu_devpts_sid;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
102
kernel/manager.c
102
kernel/manager.c
@@ -1,102 +0,0 @@
|
|||||||
#include "linux/cred.h"
|
|
||||||
#include "linux/gfp.h"
|
|
||||||
#include "linux/slab.h"
|
|
||||||
#include "linux/uidgid.h"
|
|
||||||
#include "linux/version.h"
|
|
||||||
|
|
||||||
#include "linux/fdtable.h"
|
|
||||||
#include "linux/fs.h"
|
|
||||||
#include "linux/rcupdate.h"
|
|
||||||
|
|
||||||
#include "apk_sign.h"
|
|
||||||
#include "klog.h" // IWYU pragma: keep
|
|
||||||
#include "ksu.h"
|
|
||||||
#include "manager.h"
|
|
||||||
|
|
||||||
uid_t ksu_manager_uid = KSU_INVALID_UID;
|
|
||||||
|
|
||||||
bool become_manager(char *pkg)
|
|
||||||
{
|
|
||||||
struct fdtable *files_table;
|
|
||||||
int i = 0;
|
|
||||||
struct path files_path;
|
|
||||||
char *cwd;
|
|
||||||
char *buf;
|
|
||||||
bool result = false;
|
|
||||||
|
|
||||||
#ifdef KSU_MANAGER_PACKAGE
|
|
||||||
// pkg is `/<real package>`
|
|
||||||
if (strncmp(pkg + 1, KSU_MANAGER_PACKAGE,
|
|
||||||
sizeof(KSU_MANAGER_PACKAGE)) != 0) {
|
|
||||||
pr_info("manager package is inconsistent with kernel build: %s\n",
|
|
||||||
KSU_MANAGER_PACKAGE);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// must be zygote's direct child, otherwise any app can fork a new process and
|
|
||||||
// open manager's apk
|
|
||||||
if (task_uid(current->real_parent).val != 0) {
|
|
||||||
pr_info("parent is not zygote!\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = (char *)kmalloc(PATH_MAX, GFP_ATOMIC);
|
|
||||||
if (!buf) {
|
|
||||||
pr_err("kalloc path failed.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
files_table = files_fdtable(current->files);
|
|
||||||
|
|
||||||
int pkg_len = strlen(pkg);
|
|
||||||
// todo: use iterate_fd
|
|
||||||
for (i = 0; files_table->fd[i] != NULL; i++) {
|
|
||||||
files_path = files_table->fd[i]->f_path;
|
|
||||||
if (!d_is_reg(files_path.dentry)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
cwd = d_path(&files_path, buf, PATH_MAX);
|
|
||||||
if (startswith(cwd, "/data/app/") != 0 ||
|
|
||||||
endswith(cwd, "==/base.apk") != 0) {
|
|
||||||
// AOSP generate ramdom base64 with 16bit, without NO_PADDING, so it must have two "="
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// we have found the apk!
|
|
||||||
pr_info("found apk: %s\n", 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)) {
|
|
||||||
// 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!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
clean:
|
|
||||||
kfree(buf);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
#ifndef __KSU_H_KSU_MANAGER
|
#ifndef __KSU_H_KSU_MANAGER
|
||||||
#define __KSU_H_KSU_MANAGER
|
#define __KSU_H_KSU_MANAGER
|
||||||
|
|
||||||
#include "linux/cred.h"
|
#include <linux/cred.h>
|
||||||
#include "linux/types.h"
|
#include <linux/types.h>
|
||||||
|
|
||||||
#define KSU_INVALID_UID -1
|
#define KSU_INVALID_UID -1
|
||||||
|
|
||||||
@@ -33,6 +33,4 @@ static inline void ksu_invalidate_manager_uid()
|
|||||||
ksu_manager_uid = KSU_INVALID_UID;
|
ksu_manager_uid = KSU_INVALID_UID;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool become_manager(char *pkg);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
#include "linux/kallsyms.h"
|
|
||||||
|
|
||||||
#define RE_EXPORT_SYMBOL1(ret, func, t1, v1) \
|
|
||||||
ret ksu_##func(t1 v1) \
|
|
||||||
{ \
|
|
||||||
return func(v1); \
|
|
||||||
} \
|
|
||||||
EXPORT_SYMBOL(ksu_##func);
|
|
||||||
|
|
||||||
#define RE_EXPORT_SYMBOL2(ret, func, t1, v1, t2, v2) \
|
|
||||||
ret ksu_##func(t1 v1, t2 v2) \
|
|
||||||
{ \
|
|
||||||
return func(v1, v2); \
|
|
||||||
} \
|
|
||||||
EXPORT_SYMBOL(ksu_##func);
|
|
||||||
|
|
||||||
RE_EXPORT_SYMBOL1(unsigned long, kallsyms_lookup_name, const char *, name)
|
|
||||||
|
|
||||||
// RE_EXPORT_SYMBOL2(int, register_kprobe, struct kprobe *, p)
|
|
||||||
// RE_EXPORT_SYMBOL2(void, unregister_kprobe, struct kprobe *, p)
|
|
||||||
|
|
||||||
// RE_EXPORT_SYMBOL2(int, register_kprobe, struct kprobe *, p)
|
|
||||||
// RE_EXPORT_SYMBOL2(void, unregister_kprobe, struct kprobe *, p)
|
|
||||||
|
|
||||||
// int ksu_register_kprobe(struct kprobe *p);
|
|
||||||
// void ksu_unregister_kprobe(struct kprobe *p);
|
|
||||||
// int ksu_register_kprobes(struct kprobe **kps, int num);
|
|
||||||
// void ksu_unregister_kprobes(struct kprobe **kps, int num);
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "linux/uaccess.h"
|
#include <linux/uaccess.h>
|
||||||
#include "linux/types.h"
|
#include <linux/types.h>
|
||||||
#include "linux/version.h"
|
#include <linux/version.h>
|
||||||
|
|
||||||
#include "../klog.h" // IWYU pragma: keep
|
#include "../klog.h" // IWYU pragma: keep
|
||||||
#include "selinux.h"
|
#include "selinux.h"
|
||||||
@@ -69,6 +69,11 @@ void apply_kernelsu_rules()
|
|||||||
// we need to save allowlist in /data/adb/ksu
|
// we need to save allowlist in /data/adb/ksu
|
||||||
ksu_allow(db, "kernel", "adb_data_file", "dir", ALL);
|
ksu_allow(db, "kernel", "adb_data_file", "dir", ALL);
|
||||||
ksu_allow(db, "kernel", "adb_data_file", "file", ALL);
|
ksu_allow(db, "kernel", "adb_data_file", "file", ALL);
|
||||||
|
// we need to search /data/app
|
||||||
|
ksu_allow(db, "kernel", "apk_data_file", "file", "open");
|
||||||
|
ksu_allow(db, "kernel", "apk_data_file", "dir", "open");
|
||||||
|
ksu_allow(db, "kernel", "apk_data_file", "dir", "read");
|
||||||
|
ksu_allow(db, "kernel", "apk_data_file", "dir", "search");
|
||||||
// we may need to do mount on shell
|
// we may need to do mount on shell
|
||||||
ksu_allow(db, "kernel", "shell_data_file", "file", ALL);
|
ksu_allow(db, "kernel", "shell_data_file", "file", ALL);
|
||||||
// we need to read /data/system/packages.list
|
// we need to read /data/system/packages.list
|
||||||
@@ -125,12 +130,6 @@ void apply_kernelsu_rules()
|
|||||||
// Allow all binder transactions
|
// Allow all binder transactions
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "binder", ALL);
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "binder", ALL);
|
||||||
|
|
||||||
// Allow system server devpts
|
|
||||||
ksu_allow(db, "system_server", "untrusted_app_all_devpts", "chr_file",
|
|
||||||
"read");
|
|
||||||
ksu_allow(db, "system_server", "untrusted_app_all_devpts", "chr_file",
|
|
||||||
"write");
|
|
||||||
|
|
||||||
// Allow system server kill su process
|
// Allow system server kill su process
|
||||||
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid");
|
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid");
|
||||||
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill");
|
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill");
|
||||||
|
|||||||
@@ -129,4 +129,17 @@ bool is_zygote(void *sec)
|
|||||||
result = strncmp("u:r:zygote:s0", domain, seclen) == 0;
|
result = strncmp("u:r:zygote:s0", domain, seclen) == 0;
|
||||||
security_release_secctx(domain, seclen);
|
security_release_secctx(domain, seclen);
|
||||||
return result;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DEVPTS_DOMAIN "u:object_r:devpts:s0"
|
||||||
|
|
||||||
|
u32 ksu_get_devpts_sid()
|
||||||
|
{
|
||||||
|
u32 devpts_sid = 0;
|
||||||
|
int err = security_secctx_to_secid(DEVPTS_DOMAIN, strlen(DEVPTS_DOMAIN),
|
||||||
|
&devpts_sid);
|
||||||
|
if (err) {
|
||||||
|
pr_info("get devpts sid err %d\n", err);
|
||||||
|
}
|
||||||
|
return devpts_sid;
|
||||||
}
|
}
|
||||||
@@ -20,4 +20,6 @@ bool is_zygote(void *cred);
|
|||||||
|
|
||||||
void apply_kernelsu_rules();
|
void apply_kernelsu_rules();
|
||||||
|
|
||||||
|
u32 ksu_get_devpts_sid();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,26 +1,15 @@
|
|||||||
#include "sepolicy.h"
|
#include <linux/gfp.h>
|
||||||
#include "linux/gfp.h"
|
#include <linux/printk.h>
|
||||||
#include "linux/printk.h"
|
#include <linux/slab.h>
|
||||||
#include "linux/slab.h"
|
#include <linux/version.h>
|
||||||
#include "linux/version.h"
|
|
||||||
|
|
||||||
|
#include "sepolicy.h"
|
||||||
#include "../klog.h" // IWYU pragma: keep
|
#include "../klog.h" // IWYU pragma: keep
|
||||||
#include "ss/symtab.h"
|
#include "ss/symtab.h"
|
||||||
|
#include "../kernel_compat.h" // Add check Huawei Device
|
||||||
|
|
||||||
#define KSU_SUPPORT_ADD_TYPE
|
#define KSU_SUPPORT_ADD_TYPE
|
||||||
|
|
||||||
/*
|
|
||||||
* Adapt to Huawei HISI kernel without affecting other kernels ,
|
|
||||||
* Huawei Hisi Kernel EBITMAP Enable or Disable Flag ,
|
|
||||||
* From ss/ebitmap.h
|
|
||||||
*/
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) && \
|
|
||||||
LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
|
|
||||||
#ifdef HISI_SELINUX_EBITMAP_RO
|
|
||||||
#define CONFIG_IS_HW_HISI
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////
|
||||||
// Declaration
|
// Declaration
|
||||||
//////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////
|
||||||
@@ -619,6 +608,22 @@ static bool add_genfscon(struct policydb *db, const char *fs_name,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void *ksu_realloc(void *old, size_t new_size, size_t old_size)
|
||||||
|
{
|
||||||
|
// we can't use krealloc, because it may be read-only
|
||||||
|
void *new = kzalloc(new_size, GFP_ATOMIC);
|
||||||
|
if (!new) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (old_size) {
|
||||||
|
memcpy(new, old, old_size);
|
||||||
|
}
|
||||||
|
// we can't use kfree, because it may be read-only
|
||||||
|
// there maybe some leaks, maybe we can check ptr_write, but it's not a big deal
|
||||||
|
// kfree(old);
|
||||||
|
return new;
|
||||||
|
}
|
||||||
|
|
||||||
static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||||
{
|
{
|
||||||
#ifdef KSU_SUPPORT_ADD_TYPE
|
#ifdef KSU_SUPPORT_ADD_TYPE
|
||||||
@@ -652,29 +657,30 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
|
||||||
size_t new_size = sizeof(struct ebitmap) * db->p_types.nprim;
|
|
||||||
struct ebitmap *new_type_attr_map_array =
|
struct ebitmap *new_type_attr_map_array =
|
||||||
(krealloc(db->type_attr_map_array, new_size, GFP_ATOMIC));
|
ksu_realloc(db->type_attr_map_array,
|
||||||
|
value * sizeof(struct ebitmap),
|
||||||
struct type_datum **new_type_val_to_struct =
|
(value - 1) * sizeof(struct ebitmap));
|
||||||
krealloc(db->type_val_to_struct,
|
|
||||||
sizeof(*db->type_val_to_struct) * db->p_types.nprim,
|
|
||||||
GFP_ATOMIC);
|
|
||||||
|
|
||||||
if (!new_type_attr_map_array) {
|
if (!new_type_attr_map_array) {
|
||||||
pr_err("add_type: alloc type_attr_map_array failed\n");
|
pr_err("add_type: alloc type_attr_map_array failed\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct type_datum **new_type_val_to_struct =
|
||||||
|
ksu_realloc(db->type_val_to_struct,
|
||||||
|
sizeof(*db->type_val_to_struct) * value,
|
||||||
|
sizeof(*db->type_val_to_struct) * (value - 1));
|
||||||
|
|
||||||
if (!new_type_val_to_struct) {
|
if (!new_type_val_to_struct) {
|
||||||
pr_err("add_type: alloc type_val_to_struct failed\n");
|
pr_err("add_type: alloc type_val_to_struct failed\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char **new_val_to_name_types =
|
char **new_val_to_name_types =
|
||||||
krealloc(db->sym_val_to_name[SYM_TYPES],
|
ksu_realloc(db->sym_val_to_name[SYM_TYPES],
|
||||||
sizeof(char *) * db->symtab[SYM_TYPES].nprim,
|
sizeof(char *) * value,
|
||||||
GFP_KERNEL);
|
sizeof(char *) * (value - 1));
|
||||||
if (!new_val_to_name_types) {
|
if (!new_val_to_name_types) {
|
||||||
pr_err("add_type: alloc val_to_name failed\n");
|
pr_err("add_type: alloc val_to_name failed\n");
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef __KSU_H_SEPOLICY
|
#ifndef __KSU_H_SEPOLICY
|
||||||
#define __KSU_H_SEPOLICY
|
#define __KSU_H_SEPOLICY
|
||||||
|
|
||||||
#include "linux/types.h"
|
#include <linux/types.h>
|
||||||
|
|
||||||
#include "ss/policydb.h"
|
#include "ss/policydb.h"
|
||||||
|
|
||||||
|
|||||||
111
kernel/setup.sh
111
kernel/setup.sh
@@ -1,50 +1,75 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -eux
|
set -eu
|
||||||
|
|
||||||
GKI_ROOT=$(pwd)
|
GKI_ROOT=$(pwd)
|
||||||
|
|
||||||
echo "[+] GKI_ROOT: $GKI_ROOT"
|
display_usage() {
|
||||||
|
echo "Usage: $0 [--cleanup | <commit-or-tag>]"
|
||||||
|
echo " --cleanup: Cleans up previous modifications made by the script."
|
||||||
|
echo " <commit-or-tag>: Sets up or updates the KernelSU to specified tag or commit."
|
||||||
|
echo " -h, --help: Displays this usage information."
|
||||||
|
echo " (no args): Sets up or updates the KernelSU environment to the latest tagged version."
|
||||||
|
}
|
||||||
|
|
||||||
if test -d "$GKI_ROOT/common/drivers"; then
|
initialize_variables() {
|
||||||
DRIVER_DIR="$GKI_ROOT/common/drivers"
|
if test -d "$GKI_ROOT/common/drivers"; then
|
||||||
elif test -d "$GKI_ROOT/drivers"; then
|
DRIVER_DIR="$GKI_ROOT/common/drivers"
|
||||||
DRIVER_DIR="$GKI_ROOT/drivers"
|
elif test -d "$GKI_ROOT/drivers"; then
|
||||||
|
DRIVER_DIR="$GKI_ROOT/drivers"
|
||||||
|
else
|
||||||
|
echo '[ERROR] "drivers/" directory not found.'
|
||||||
|
exit 127
|
||||||
|
fi
|
||||||
|
|
||||||
|
DRIVER_MAKEFILE=$DRIVER_DIR/Makefile
|
||||||
|
DRIVER_KCONFIG=$DRIVER_DIR/Kconfig
|
||||||
|
}
|
||||||
|
|
||||||
|
# Reverts modifications made by this script
|
||||||
|
perform_cleanup() {
|
||||||
|
echo "[+] Cleaning up..."
|
||||||
|
[ -L "$DRIVER_DIR/kernelsu" ] && rm "$DRIVER_DIR/kernelsu" && echo "[-] Symlink removed."
|
||||||
|
grep -q "kernelsu" "$DRIVER_MAKEFILE" && sed -i '/kernelsu/d' "$DRIVER_MAKEFILE" && echo "[-] Makefile reverted."
|
||||||
|
grep -q "drivers/kernelsu/Kconfig" "$DRIVER_KCONFIG" && sed -i '/drivers\/kernelsu\/Kconfig/d' "$DRIVER_KCONFIG" && echo "[-] Kconfig reverted."
|
||||||
|
if [ -d "$GKI_ROOT/KernelSU" ]; then
|
||||||
|
rm -rf "$GKI_ROOT/KernelSU" && echo "[-] KernelSU directory deleted."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Sets up or update KernelSU environment
|
||||||
|
setup_kernelsu() {
|
||||||
|
echo "[+] Setting up KernelSU..."
|
||||||
|
test -d "$GKI_ROOT/KernelSU" || git clone https://github.com/tiann/KernelSU && echo "[+] Repository cloned."
|
||||||
|
cd "$GKI_ROOT/KernelSU"
|
||||||
|
git stash && echo "[-] Stashed current changes."
|
||||||
|
if [ "$(git status | grep -Po 'v\d+(\.\d+)*' | head -n1)" ]; then
|
||||||
|
git checkout main && echo "[-] Switched to main branch."
|
||||||
|
fi
|
||||||
|
git pull && echo "[+] Repository updated."
|
||||||
|
if [ -z "${1-}" ]; then
|
||||||
|
git checkout "$(git describe --abbrev=0 --tags)" && echo "[-] Checked out latest tag."
|
||||||
|
else
|
||||||
|
git checkout "$1" && echo "[-] Checked out $1." || echo "[-] Checkout default branch"
|
||||||
|
fi
|
||||||
|
cd "$DRIVER_DIR"
|
||||||
|
ln -sf "$(realpath --relative-to="$DRIVER_DIR" "$GKI_ROOT/KernelSU/kernel")" "kernelsu" && echo "[+] Symlink created."
|
||||||
|
|
||||||
|
# Add entries in Makefile and Kconfig if not already existing
|
||||||
|
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE" && echo "[+] Modified Makefile."
|
||||||
|
grep -q "source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" && echo "[+] Modified Kconfig."
|
||||||
|
echo '[+] Done.'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Process command-line arguments
|
||||||
|
if [ "$#" -eq 0 ]; then
|
||||||
|
initialize_variables
|
||||||
|
setup_kernelsu
|
||||||
|
elif [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
|
||||||
|
display_usage
|
||||||
|
elif [ "$1" = "--cleanup" ]; then
|
||||||
|
initialize_variables
|
||||||
|
perform_cleanup
|
||||||
else
|
else
|
||||||
echo '[ERROR] "drivers/" directory is not found.'
|
initialize_variables
|
||||||
echo '[+] You should modify this script by yourself.'
|
setup_kernelsu "$@"
|
||||||
exit 127
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
test -d "$GKI_ROOT/KernelSU" || git clone https://github.com/tiann/KernelSU
|
|
||||||
cd "$GKI_ROOT/KernelSU"
|
|
||||||
git stash
|
|
||||||
if [ "$(git status | grep -Po 'v\d+(\.\d+)*' | head -n1)" ]; then
|
|
||||||
git checkout main
|
|
||||||
fi
|
|
||||||
git pull
|
|
||||||
if [ -z "${1-}" ]; then
|
|
||||||
git checkout "$(git describe --abbrev=0 --tags)"
|
|
||||||
else
|
|
||||||
git checkout "$1"
|
|
||||||
fi
|
|
||||||
cd "$GKI_ROOT"
|
|
||||||
|
|
||||||
echo "[+] GKI_ROOT: $GKI_ROOT"
|
|
||||||
echo "[+] Copy kernel su driver to $DRIVER_DIR"
|
|
||||||
|
|
||||||
cd "$DRIVER_DIR"
|
|
||||||
if test -d "$GKI_ROOT/common/drivers"; then
|
|
||||||
ln -sf "../../KernelSU/kernel" "kernelsu"
|
|
||||||
elif test -d "$GKI_ROOT/drivers"; then
|
|
||||||
ln -sf "../KernelSU/kernel" "kernelsu"
|
|
||||||
fi
|
|
||||||
cd "$GKI_ROOT"
|
|
||||||
|
|
||||||
echo '[+] Add kernel su driver to Makefile'
|
|
||||||
|
|
||||||
DRIVER_MAKEFILE=$DRIVER_DIR/Makefile
|
|
||||||
DRIVER_KCONFIG=$DRIVER_DIR/Kconfig
|
|
||||||
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE"
|
|
||||||
grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG"
|
|
||||||
|
|
||||||
echo '[+] Done.'
|
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
#include "asm/current.h"
|
#include <linux/dcache.h>
|
||||||
#include "linux/cred.h"
|
#include <linux/security.h>
|
||||||
#include "linux/err.h"
|
#include <asm/current.h>
|
||||||
#include "linux/fs.h"
|
#include <linux/cred.h>
|
||||||
#include "linux/kprobes.h"
|
#include <linux/err.h>
|
||||||
#include "linux/types.h"
|
#include <linux/fs.h>
|
||||||
#include "linux/uaccess.h"
|
#include <linux/kprobes.h>
|
||||||
#include "linux/version.h"
|
#include <linux/types.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/version.h>
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||||
#include "linux/sched/task_stack.h"
|
#include <linux/sched/task_stack.h>
|
||||||
#else
|
#else
|
||||||
#include "linux/sched.h"
|
#include <linux/sched.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "objsec.h"
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
@@ -39,8 +42,15 @@ static char __user *sh_user_path(void)
|
|||||||
return userspace_stack_buffer(sh_path, sizeof(sh_path));
|
return userspace_stack_buffer(sh_path, sizeof(sh_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char __user *ksud_user_path(void)
|
||||||
|
{
|
||||||
|
static const char ksud_path[] = KSUD_PATH;
|
||||||
|
|
||||||
|
return userspace_stack_buffer(ksud_path, sizeof(ksud_path));
|
||||||
|
}
|
||||||
|
|
||||||
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
||||||
int *flags)
|
int *__unused_flags)
|
||||||
{
|
{
|
||||||
const char su[] = SU_PATH;
|
const char su[] = SU_PATH;
|
||||||
|
|
||||||
@@ -75,11 +85,12 @@ int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
|
|||||||
|
|
||||||
char path[sizeof(su) + 1];
|
char path[sizeof(su) + 1];
|
||||||
memset(path, 0, sizeof(path));
|
memset(path, 0, sizeof(path));
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0)
|
// Remove this later!! we use syscall hook, so this will never happen!!!!!
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) && 0
|
||||||
// it becomes a `struct filename *` after 5.18
|
// it becomes a `struct filename *` after 5.18
|
||||||
// https://elixir.bootlin.com/linux/v5.18/source/fs/stat.c#L216
|
// https://elixir.bootlin.com/linux/v5.18/source/fs/stat.c#L216
|
||||||
const char sh[] = SH_PATH;
|
const char sh[] = SH_PATH;
|
||||||
struct filename *filename = * ((struct filename **) filename_user);
|
struct filename *filename = *((struct filename **)filename_user);
|
||||||
if (IS_ERR(filename)) {
|
if (IS_ERR(filename)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -101,7 +112,8 @@ int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
|
|||||||
|
|
||||||
// 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
|
// 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,
|
int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
||||||
void *__never_use_argv, void *__never_use_envp, int *__never_use_flags)
|
void *__never_use_argv, void *__never_use_envp,
|
||||||
|
int *__never_use_flags)
|
||||||
{
|
{
|
||||||
struct filename *filename;
|
struct filename *filename;
|
||||||
const char sh[] = KSUD_PATH;
|
const char sh[] = KSUD_PATH;
|
||||||
@@ -129,11 +141,69 @@ int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
|
||||||
|
void *__never_use_argv, void *__never_use_envp,
|
||||||
|
int *__never_use_flags)
|
||||||
|
{
|
||||||
|
const char su[] = SU_PATH;
|
||||||
|
char path[sizeof(su) + 1];
|
||||||
|
|
||||||
|
if (unlikely(!filename_user))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
memset(path, 0, sizeof(path));
|
||||||
|
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
||||||
|
|
||||||
|
if (likely(memcmp(path, su, sizeof(su))))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!ksu_is_allow_uid(current_uid().val))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
pr_info("sys_execve su found\n");
|
||||||
|
*filename_user = ksud_user_path();
|
||||||
|
|
||||||
|
escape_to_root();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_handle_devpts(struct inode *inode)
|
||||||
|
{
|
||||||
|
if (!current->mm) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uid_t uid = current_uid().val;
|
||||||
|
if (uid % 100000 < 10000) {
|
||||||
|
// not untrusted_app, ignore it
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_is_allow_uid(uid))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (ksu_devpts_sid) {
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
|
||||||
|
struct inode_security_struct *sec = selinux_inode(inode);
|
||||||
|
#else
|
||||||
|
struct inode_security_struct *sec =
|
||||||
|
(struct inode_security_struct *)inode->i_security;
|
||||||
|
#endif
|
||||||
|
if (sec) {
|
||||||
|
sec->sid = ksu_devpts_sid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_KPROBES
|
#ifdef CONFIG_KPROBES
|
||||||
|
|
||||||
static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
__maybe_unused static int faccessat_handler_pre(struct kprobe *p,
|
||||||
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
int *dfd = (int *)PT_REGS_PARM1(regs);
|
int *dfd = (int *)&PT_REGS_PARM1(regs);
|
||||||
const char __user **filename_user = (const char **)&PT_REGS_PARM2(regs);
|
const char __user **filename_user = (const char **)&PT_REGS_PARM2(regs);
|
||||||
int *mode = (int *)&PT_REGS_PARM3(regs);
|
int *mode = (int *)&PT_REGS_PARM3(regs);
|
||||||
// Both sys_ and do_ is C function
|
// Both sys_ and do_ is C function
|
||||||
@@ -142,21 +212,44 @@ static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
|||||||
return ksu_handle_faccessat(dfd, filename_user, mode, flags);
|
return ksu_handle_faccessat(dfd, filename_user, mode, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
static int sys_faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||||
|
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
|
||||||
|
const char __user **filename_user =
|
||||||
|
(const char **)&PT_REGS_PARM2(real_regs);
|
||||||
|
int *mode = (int *)&PT_REGS_PARM3(real_regs);
|
||||||
|
|
||||||
|
return ksu_handle_faccessat(dfd, filename_user, mode, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
__maybe_unused static int newfstatat_handler_pre(struct kprobe *p,
|
||||||
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
int *dfd = (int *)&PT_REGS_PARM1(regs);
|
int *dfd = (int *)&PT_REGS_PARM1(regs);
|
||||||
const char __user **filename_user = (const char **)&PT_REGS_PARM2(regs);
|
const char __user **filename_user = (const char **)&PT_REGS_PARM2(regs);
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||||
// static int vfs_statx(int dfd, const char __user *filename, int flags, struct kstat *stat, u32 request_mask)
|
// static int vfs_statx(int dfd, const char __user *filename, int flags, struct kstat *stat, u32 request_mask)
|
||||||
int *flags = (int *)&PT_REGS_PARM3(regs);
|
int *flags = (int *)&PT_REGS_PARM3(regs);
|
||||||
#else
|
#else
|
||||||
// int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,int flag)
|
// int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,int flag)
|
||||||
int *flags = (int *)&PT_REGS_CCALL_PARM4(regs);
|
int *flags = (int *)&PT_REGS_CCALL_PARM4(regs);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return ksu_handle_stat(dfd, filename_user, flags);
|
return ksu_handle_stat(dfd, filename_user, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sys_newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||||
|
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
|
||||||
|
const char __user **filename_user =
|
||||||
|
(const char **)&PT_REGS_PARM2(real_regs);
|
||||||
|
int *flags = (int *)&PT_REGS_SYSCALL_PARM4(real_regs);
|
||||||
|
|
||||||
|
return ksu_handle_stat(dfd, filename_user, flags);
|
||||||
|
}
|
||||||
|
|
||||||
// https://elixir.bootlin.com/linux/v5.10.158/source/fs/exec.c#L1864
|
// https://elixir.bootlin.com/linux/v5.10.158/source/fs/exec.c#L1864
|
||||||
static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
@@ -167,6 +260,22 @@ static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
|||||||
return ksu_handle_execveat_sucompat(fd, filename_ptr, NULL, NULL, NULL);
|
return ksu_handle_execveat_sucompat(fd, filename_ptr, NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||||
|
const char __user **filename_user =
|
||||||
|
(const char **)&PT_REGS_PARM1(real_regs);
|
||||||
|
|
||||||
|
return ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL, NULL,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
static struct kprobe faccessat_kp = {
|
||||||
|
.symbol_name = SYS_FACCESSAT_SYMBOL,
|
||||||
|
.pre_handler = sys_faccessat_handler_pre,
|
||||||
|
};
|
||||||
|
#else
|
||||||
static struct kprobe faccessat_kp = {
|
static struct kprobe faccessat_kp = {
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
|
||||||
.symbol_name = "do_faccessat",
|
.symbol_name = "do_faccessat",
|
||||||
@@ -175,7 +284,14 @@ static struct kprobe faccessat_kp = {
|
|||||||
#endif
|
#endif
|
||||||
.pre_handler = faccessat_handler_pre,
|
.pre_handler = faccessat_handler_pre,
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
static struct kprobe newfstatat_kp = {
|
||||||
|
.symbol_name = SYS_NEWFSTATAT_SYMBOL,
|
||||||
|
.pre_handler = sys_newfstatat_handler_pre,
|
||||||
|
};
|
||||||
|
#else
|
||||||
static struct kprobe newfstatat_kp = {
|
static struct kprobe newfstatat_kp = {
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||||
.symbol_name = "vfs_statx",
|
.symbol_name = "vfs_statx",
|
||||||
@@ -184,7 +300,14 @@ static struct kprobe newfstatat_kp = {
|
|||||||
#endif
|
#endif
|
||||||
.pre_handler = newfstatat_handler_pre,
|
.pre_handler = newfstatat_handler_pre,
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
static struct kprobe execve_kp = {
|
||||||
|
.symbol_name = SYS_EXECVE_SYMBOL,
|
||||||
|
.pre_handler = sys_execve_handler_pre,
|
||||||
|
};
|
||||||
|
#else
|
||||||
static struct kprobe execve_kp = {
|
static struct kprobe execve_kp = {
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
||||||
.symbol_name = "do_execveat_common",
|
.symbol_name = "do_execveat_common",
|
||||||
@@ -195,11 +318,30 @@ static struct kprobe execve_kp = {
|
|||||||
#endif
|
#endif
|
||||||
.pre_handler = execve_handler_pre,
|
.pre_handler = execve_handler_pre,
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int pts_unix98_lookup_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct inode *inode;
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
|
||||||
|
struct file *file = (struct file *)PT_REGS_PARM2(regs);
|
||||||
|
inode = file->f_path.dentry->d_inode;
|
||||||
|
#else
|
||||||
|
inode = (struct inode *)PT_REGS_PARM2(regs);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ksu_handle_devpts(inode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct kprobe pts_unix98_lookup_kp = { .symbol_name =
|
||||||
|
"pts_unix98_lookup",
|
||||||
|
.pre_handler =
|
||||||
|
pts_unix98_lookup_pre };
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// sucompat: permited process can execute 'su' to gain root access.
|
// sucompat: permited process can execute 'su' to gain root access.
|
||||||
void ksu_enable_sucompat()
|
void ksu_sucompat_init()
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KPROBES
|
#ifdef CONFIG_KPROBES
|
||||||
int ret;
|
int ret;
|
||||||
@@ -209,5 +351,17 @@ void ksu_enable_sucompat()
|
|||||||
pr_info("sucompat: newfstatat_kp: %d\n", ret);
|
pr_info("sucompat: newfstatat_kp: %d\n", ret);
|
||||||
ret = register_kprobe(&faccessat_kp);
|
ret = register_kprobe(&faccessat_kp);
|
||||||
pr_info("sucompat: faccessat_kp: %d\n", ret);
|
pr_info("sucompat: faccessat_kp: %d\n", ret);
|
||||||
|
ret = register_kprobe(&pts_unix98_lookup_kp);
|
||||||
|
pr_info("sucompat: devpts_kp: %d\n", ret);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_sucompat_exit()
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_KPROBES
|
||||||
|
unregister_kprobe(&execve_kp);
|
||||||
|
unregister_kprobe(&newfstatat_kp);
|
||||||
|
unregister_kprobe(&faccessat_kp);
|
||||||
|
unregister_kprobe(&pts_unix98_lookup_kp);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
382
kernel/throne_tracker.c
Normal file
382
kernel/throne_tracker.c
Normal file
@@ -0,0 +1,382 @@
|
|||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "ksu.h"
|
||||||
|
#include "manager.h"
|
||||||
|
#include "throne_tracker.h"
|
||||||
|
#include "kernel_compat.h"
|
||||||
|
|
||||||
|
uid_t ksu_manager_uid = KSU_INVALID_UID;
|
||||||
|
|
||||||
|
#define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list.tmp"
|
||||||
|
|
||||||
|
struct uid_data {
|
||||||
|
struct list_head list;
|
||||||
|
u32 uid;
|
||||||
|
char package[KSU_MAX_PACKAGE_NAME];
|
||||||
|
};
|
||||||
|
|
||||||
|
static int get_pkg_from_apk_path(char *pkg, const char *path)
|
||||||
|
{
|
||||||
|
int len = strlen(path);
|
||||||
|
if (len >= KSU_MAX_PACKAGE_NAME || len < 1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
const char *last_slash = NULL;
|
||||||
|
const char *second_last_slash = NULL;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = len - 1; i >= 0; i--) {
|
||||||
|
if (path[i] == '/') {
|
||||||
|
if (!last_slash) {
|
||||||
|
last_slash = &path[i];
|
||||||
|
} else {
|
||||||
|
second_last_slash = &path[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!last_slash || !second_last_slash)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
const char *last_hyphen = strchr(second_last_slash, '-');
|
||||||
|
if (!last_hyphen || last_hyphen > last_slash)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int pkg_len = last_hyphen - second_last_slash - 1;
|
||||||
|
if (pkg_len >= KSU_MAX_PACKAGE_NAME || pkg_len <= 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// Copying the package name
|
||||||
|
strncpy(pkg, second_last_slash + 1, pkg_len);
|
||||||
|
pkg[pkg_len] = '\0';
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void crown_manager(const char *apk, struct list_head *uid_data)
|
||||||
|
{
|
||||||
|
char pkg[KSU_MAX_PACKAGE_NAME];
|
||||||
|
if (get_pkg_from_apk_path(pkg, apk) < 0) {
|
||||||
|
pr_err("Failed to get package name from apk path: %s\n", apk);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("manager pkg: %s\n", pkg);
|
||||||
|
|
||||||
|
#ifdef KSU_MANAGER_PACKAGE
|
||||||
|
// pkg is `/<real package>`
|
||||||
|
if (strncmp(pkg, KSU_MANAGER_PACKAGE, sizeof(KSU_MANAGER_PACKAGE))) {
|
||||||
|
pr_info("manager package is inconsistent with kernel build: %s\n",
|
||||||
|
KSU_MANAGER_PACKAGE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
struct list_head *list = (struct list_head *)uid_data;
|
||||||
|
struct uid_data *np;
|
||||||
|
|
||||||
|
list_for_each_entry (np, list, list) {
|
||||||
|
if (strncmp(np->package, pkg, KSU_MAX_PACKAGE_NAME) == 0) {
|
||||||
|
pr_info("Crowning manager: %s(uid=%d)\n", pkg, np->uid);
|
||||||
|
ksu_set_manager_uid(np->uid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DATA_PATH_LEN 384 // 384 is enough for /data/app/<package>/base.apk
|
||||||
|
|
||||||
|
struct data_path {
|
||||||
|
char dirpath[DATA_PATH_LEN];
|
||||||
|
int depth;
|
||||||
|
struct list_head list;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct apk_path_hash {
|
||||||
|
unsigned int hash;
|
||||||
|
bool exists;
|
||||||
|
struct list_head list;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct list_head apk_path_hash_list = LIST_HEAD_INIT(apk_path_hash_list);
|
||||||
|
|
||||||
|
struct my_dir_context {
|
||||||
|
struct dir_context ctx;
|
||||||
|
struct list_head *data_path_list;
|
||||||
|
char *parent_dir;
|
||||||
|
void *private_data;
|
||||||
|
int depth;
|
||||||
|
int *stop;
|
||||||
|
};
|
||||||
|
// https://docs.kernel.org/filesystems/porting.html
|
||||||
|
// filldir_t (readdir callbacks) calling conventions have changed. Instead of returning 0 or -E... it returns bool now. false means "no more" (as -E... used to) and true - "keep going" (as 0 in old calling conventions). Rationale: callers never looked at specific -E... values anyway. -> iterate_shared() instances require no changes at all, all filldir_t ones in the tree converted.
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
|
||||||
|
#define FILLDIR_RETURN_TYPE bool
|
||||||
|
#define FILLDIR_ACTOR_CONTINUE true
|
||||||
|
#define FILLDIR_ACTOR_STOP false
|
||||||
|
#else
|
||||||
|
#define FILLDIR_RETURN_TYPE int
|
||||||
|
#define FILLDIR_ACTOR_CONTINUE 0
|
||||||
|
#define FILLDIR_ACTOR_STOP -EINVAL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||||
|
int namelen, loff_t off, u64 ino,
|
||||||
|
unsigned int d_type)
|
||||||
|
{
|
||||||
|
struct my_dir_context *my_ctx =
|
||||||
|
container_of(ctx, struct my_dir_context, ctx);
|
||||||
|
char dirpath[DATA_PATH_LEN];
|
||||||
|
|
||||||
|
if (!my_ctx) {
|
||||||
|
pr_err("Invalid context\n");
|
||||||
|
return FILLDIR_ACTOR_STOP;
|
||||||
|
}
|
||||||
|
if (my_ctx->stop && *my_ctx->stop) {
|
||||||
|
pr_info("Stop searching\n");
|
||||||
|
return FILLDIR_ACTOR_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strncmp(name, "..", namelen) || !strncmp(name, ".", namelen))
|
||||||
|
return FILLDIR_ACTOR_CONTINUE; // Skip "." and ".."
|
||||||
|
|
||||||
|
if (snprintf(dirpath, DATA_PATH_LEN, "%s/%.*s", my_ctx->parent_dir,
|
||||||
|
namelen, name) >= DATA_PATH_LEN) {
|
||||||
|
pr_err("Path too long: %s/%.*s\n", my_ctx->parent_dir, namelen,
|
||||||
|
name);
|
||||||
|
return FILLDIR_ACTOR_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d_type == DT_DIR && my_ctx->depth > 0 &&
|
||||||
|
(my_ctx->stop && !*my_ctx->stop)) {
|
||||||
|
struct data_path *data = kmalloc(sizeof(struct data_path), GFP_ATOMIC);
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
pr_err("Failed to allocate memory for %s\n", dirpath);
|
||||||
|
return FILLDIR_ACTOR_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
strscpy(data->dirpath, dirpath, DATA_PATH_LEN);
|
||||||
|
data->depth = my_ctx->depth - 1;
|
||||||
|
list_add_tail(&data->list, my_ctx->data_path_list);
|
||||||
|
} else {
|
||||||
|
if ((namelen == 8) && (strncmp(name, "base.apk", namelen) == 0)) {
|
||||||
|
struct apk_path_hash *pos, *n;
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)
|
||||||
|
unsigned int hash = full_name_hash(dirpath, strlen(dirpath));
|
||||||
|
#else
|
||||||
|
unsigned int hash = full_name_hash(NULL, dirpath, strlen(dirpath));
|
||||||
|
#endif
|
||||||
|
list_for_each_entry(pos, &apk_path_hash_list, list) {
|
||||||
|
if (hash == pos->hash) {
|
||||||
|
pos->exists = true;
|
||||||
|
return FILLDIR_ACTOR_CONTINUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_manager = is_manager_apk(dirpath);
|
||||||
|
pr_info("Found new base.apk at path: %s, is_manager: %d\n",
|
||||||
|
dirpath, is_manager);
|
||||||
|
if (is_manager) {
|
||||||
|
crown_manager(dirpath, my_ctx->private_data);
|
||||||
|
*my_ctx->stop = 1;
|
||||||
|
|
||||||
|
// Manager found, clear APK cache list
|
||||||
|
list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) {
|
||||||
|
list_del(&pos->list);
|
||||||
|
kfree(pos);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
struct apk_path_hash *apk_data = kmalloc(sizeof(struct apk_path_hash), GFP_ATOMIC);
|
||||||
|
apk_data->hash = hash;
|
||||||
|
apk_data->exists = true;
|
||||||
|
list_add_tail(&apk_data->list, &apk_path_hash_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FILLDIR_ACTOR_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void search_manager(const char *path, int depth, struct list_head *uid_data)
|
||||||
|
{
|
||||||
|
int i, stop = 0;
|
||||||
|
struct list_head data_path_list;
|
||||||
|
INIT_LIST_HEAD(&data_path_list);
|
||||||
|
|
||||||
|
// Initialize APK cache list
|
||||||
|
struct apk_path_hash *pos, *n;
|
||||||
|
list_for_each_entry(pos, &apk_path_hash_list, list) {
|
||||||
|
pos->exists = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First depth
|
||||||
|
struct data_path data;
|
||||||
|
strscpy(data.dirpath, path, DATA_PATH_LEN);
|
||||||
|
data.depth = depth;
|
||||||
|
list_add_tail(&data.list, &data_path_list);
|
||||||
|
|
||||||
|
for (i = depth; i > 0; i--) {
|
||||||
|
struct data_path *pos, *n;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(pos, n, &data_path_list, list) {
|
||||||
|
struct my_dir_context ctx = { .ctx.actor = my_actor,
|
||||||
|
.data_path_list = &data_path_list,
|
||||||
|
.parent_dir = pos->dirpath,
|
||||||
|
.private_data = uid_data,
|
||||||
|
.depth = pos->depth,
|
||||||
|
.stop = &stop };
|
||||||
|
struct file *file;
|
||||||
|
|
||||||
|
if (!stop) {
|
||||||
|
file = ksu_filp_open_compat(pos->dirpath, O_RDONLY | O_NOFOLLOW, 0);
|
||||||
|
if (IS_ERR(file)) {
|
||||||
|
pr_err("Failed to open directory: %s, err: %ld\n", pos->dirpath, PTR_ERR(file));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterate_dir(file, &ctx.ctx);
|
||||||
|
filp_close(file, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
list_del(&pos->list);
|
||||||
|
if (pos != &data)
|
||||||
|
kfree(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove stale cached APK entries
|
||||||
|
list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) {
|
||||||
|
if (!pos->exists) {
|
||||||
|
list_del(&pos->list);
|
||||||
|
kfree(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_uid_exist(uid_t uid, char *package, void *data)
|
||||||
|
{
|
||||||
|
struct list_head *list = (struct list_head *)data;
|
||||||
|
struct uid_data *np;
|
||||||
|
|
||||||
|
bool exist = false;
|
||||||
|
list_for_each_entry (np, list, list) {
|
||||||
|
if (np->uid == uid % 100000 &&
|
||||||
|
strncmp(np->package, package, KSU_MAX_PACKAGE_NAME) == 0) {
|
||||||
|
exist = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return exist;
|
||||||
|
}
|
||||||
|
|
||||||
|
void track_throne()
|
||||||
|
{
|
||||||
|
struct file *fp =
|
||||||
|
ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0);
|
||||||
|
if (IS_ERR(fp)) {
|
||||||
|
pr_err("%s: open " SYSTEM_PACKAGES_LIST_PATH " failed: %ld\n",
|
||||||
|
__func__, PTR_ERR(fp));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct list_head uid_list;
|
||||||
|
INIT_LIST_HEAD(&uid_list);
|
||||||
|
|
||||||
|
char chr = 0;
|
||||||
|
loff_t pos = 0;
|
||||||
|
loff_t line_start = 0;
|
||||||
|
char buf[KSU_MAX_PACKAGE_NAME];
|
||||||
|
for (;;) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
struct uid_data *data =
|
||||||
|
kzalloc(sizeof(struct uid_data), GFP_ATOMIC);
|
||||||
|
if (!data) {
|
||||||
|
filp_close(fp, 0);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *tmp = buf;
|
||||||
|
const char *delim = " ";
|
||||||
|
char *package = strsep(&tmp, delim);
|
||||||
|
char *uid = strsep(&tmp, delim);
|
||||||
|
if (!uid || !package) {
|
||||||
|
pr_err("update_uid: package or uid is NULL!\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 res;
|
||||||
|
if (kstrtou32(uid, 10, &res)) {
|
||||||
|
pr_err("update_uid: uid parse err\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
data->uid = res;
|
||||||
|
strncpy(data->package, package, KSU_MAX_PACKAGE_NAME);
|
||||||
|
list_add_tail(&data->list, &uid_list);
|
||||||
|
// reset line start
|
||||||
|
line_start = pos;
|
||||||
|
}
|
||||||
|
filp_close(fp, 0);
|
||||||
|
|
||||||
|
// now update uid list
|
||||||
|
struct uid_data *np;
|
||||||
|
struct uid_data *n;
|
||||||
|
|
||||||
|
// first, check if manager_uid exist!
|
||||||
|
bool manager_exist = false;
|
||||||
|
list_for_each_entry (np, &uid_list, list) {
|
||||||
|
// if manager is installed in work profile, the uid in packages.list is still equals main profile
|
||||||
|
// don't delete it in this case!
|
||||||
|
int manager_uid = ksu_get_manager_uid() % 100000;
|
||||||
|
if (np->uid == manager_uid) {
|
||||||
|
manager_exist = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!manager_exist) {
|
||||||
|
if (ksu_is_manager_uid_valid()) {
|
||||||
|
pr_info("manager is uninstalled, invalidate it!\n");
|
||||||
|
ksu_invalidate_manager_uid();
|
||||||
|
}
|
||||||
|
pr_info("Searching manager...\n");
|
||||||
|
search_manager("/data/app", 2, &uid_list);
|
||||||
|
pr_info("Search manager finished\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// then prune the allowlist
|
||||||
|
ksu_prune_allowlist(is_uid_exist, &uid_list);
|
||||||
|
out:
|
||||||
|
// free uid_list
|
||||||
|
list_for_each_entry_safe (np, n, &uid_list, list) {
|
||||||
|
list_del(&np->list);
|
||||||
|
kfree(np);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_throne_tracker_init()
|
||||||
|
{
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_throne_tracker_exit()
|
||||||
|
{
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
10
kernel/throne_tracker.h
Normal file
10
kernel/throne_tracker.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef __KSU_H_UID_OBSERVER
|
||||||
|
#define __KSU_H_UID_OBSERVER
|
||||||
|
|
||||||
|
void ksu_throne_tracker_init();
|
||||||
|
|
||||||
|
void ksu_throne_tracker_exit();
|
||||||
|
|
||||||
|
void track_throne();
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
#include "linux/err.h"
|
|
||||||
#include "linux/fs.h"
|
|
||||||
#include "linux/list.h"
|
|
||||||
#include "linux/slab.h"
|
|
||||||
#include "linux/string.h"
|
|
||||||
#include "linux/types.h"
|
|
||||||
#include "linux/version.h"
|
|
||||||
#include "linux/workqueue.h"
|
|
||||||
|
|
||||||
#include "allowlist.h"
|
|
||||||
#include "klog.h" // IWYU pragma: keep
|
|
||||||
#include "ksu.h"
|
|
||||||
#include "manager.h"
|
|
||||||
#include "uid_observer.h"
|
|
||||||
#include "kernel_compat.h"
|
|
||||||
|
|
||||||
#define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list"
|
|
||||||
static struct work_struct ksu_update_uid_work;
|
|
||||||
|
|
||||||
struct uid_data {
|
|
||||||
struct list_head list;
|
|
||||||
u32 uid;
|
|
||||||
char package[KSU_MAX_PACKAGE_NAME];
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool is_uid_exist(uid_t uid, char *package, void *data)
|
|
||||||
{
|
|
||||||
struct list_head *list = (struct list_head *)data;
|
|
||||||
struct uid_data *np;
|
|
||||||
|
|
||||||
bool exist = false;
|
|
||||||
list_for_each_entry (np, list, list) {
|
|
||||||
if (np->uid == uid % 100000 &&
|
|
||||||
strncmp(np->package, package, KSU_MAX_PACKAGE_NAME) == 0) {
|
|
||||||
exist = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return exist;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void do_update_uid(struct work_struct *work)
|
|
||||||
{
|
|
||||||
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: %ld\n",
|
|
||||||
PTR_ERR(fp));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct list_head uid_list;
|
|
||||||
INIT_LIST_HEAD(&uid_list);
|
|
||||||
|
|
||||||
char chr = 0;
|
|
||||||
loff_t pos = 0;
|
|
||||||
loff_t line_start = 0;
|
|
||||||
char buf[KSU_MAX_PACKAGE_NAME];
|
|
||||||
for (;;) {
|
|
||||||
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);
|
|
||||||
|
|
||||||
struct uid_data *data =
|
|
||||||
kzalloc(sizeof(struct uid_data), GFP_ATOMIC);
|
|
||||||
if (!data) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *tmp = buf;
|
|
||||||
const char *delim = " ";
|
|
||||||
char *package = strsep(&tmp, delim);
|
|
||||||
char *uid = strsep(&tmp, delim);
|
|
||||||
if (!uid || !package) {
|
|
||||||
pr_err("update_uid: package or uid is NULL!\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 res;
|
|
||||||
if (kstrtou32(uid, 10, &res)) {
|
|
||||||
pr_err("update_uid: uid parse err\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
data->uid = res;
|
|
||||||
strncpy(data->package, package, KSU_MAX_PACKAGE_NAME);
|
|
||||||
list_add_tail(&data->list, &uid_list);
|
|
||||||
// reset line start
|
|
||||||
line_start = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
// now update uid list
|
|
||||||
struct uid_data *np;
|
|
||||||
struct uid_data *n;
|
|
||||||
|
|
||||||
// first, check if manager_uid exist!
|
|
||||||
bool manager_exist = false;
|
|
||||||
list_for_each_entry (np, &uid_list, list) {
|
|
||||||
// if manager is installed in work profile, the uid in packages.list is still equals main profile
|
|
||||||
// don't delete it in this case!
|
|
||||||
int manager_uid = ksu_get_manager_uid() % 100000;
|
|
||||||
if (np->uid == manager_uid) {
|
|
||||||
manager_exist = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!manager_exist && ksu_is_manager_uid_valid()) {
|
|
||||||
pr_info("manager is uninstalled, invalidate it!\n");
|
|
||||||
ksu_invalidate_manager_uid();
|
|
||||||
}
|
|
||||||
|
|
||||||
// then prune the allowlist
|
|
||||||
ksu_prune_allowlist(is_uid_exist, &uid_list);
|
|
||||||
out:
|
|
||||||
// free uid_list
|
|
||||||
list_for_each_entry_safe (np, n, &uid_list, list) {
|
|
||||||
list_del(&np->list);
|
|
||||||
kfree(np);
|
|
||||||
}
|
|
||||||
filp_close(fp, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void update_uid()
|
|
||||||
{
|
|
||||||
ksu_queue_work(&ksu_update_uid_work);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ksu_uid_observer_init()
|
|
||||||
{
|
|
||||||
INIT_WORK(&ksu_update_uid_work, do_update_uid);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ksu_uid_observer_exit()
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#ifndef __KSU_H_UID_OBSERVER
|
|
||||||
#define __KSU_H_UID_OBSERVER
|
|
||||||
|
|
||||||
int ksu_uid_observer_init();
|
|
||||||
|
|
||||||
int ksu_uid_observer_exit();
|
|
||||||
|
|
||||||
void update_uid();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -3,6 +3,7 @@ import com.android.build.gradle.internal.api.BaseVariantOutputImpl
|
|||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.agp.app)
|
alias(libs.plugins.agp.app)
|
||||||
alias(libs.plugins.kotlin)
|
alias(libs.plugins.kotlin)
|
||||||
|
alias(libs.plugins.compose.compiler)
|
||||||
alias(libs.plugins.ksp)
|
alias(libs.plugins.ksp)
|
||||||
alias(libs.plugins.lsplugin.apksign)
|
alias(libs.plugins.lsplugin.apksign)
|
||||||
id("kotlin-parcelize")
|
id("kotlin-parcelize")
|
||||||
@@ -36,11 +37,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "17"
|
jvmTarget = "21"
|
||||||
}
|
|
||||||
|
|
||||||
composeOptions {
|
|
||||||
kotlinCompilerExtensionVersion = "1.4.3"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
packaging {
|
packaging {
|
||||||
@@ -116,4 +113,4 @@ dependencies {
|
|||||||
|
|
||||||
implementation(libs.markdown)
|
implementation(libs.markdown)
|
||||||
implementation(libs.androidx.webkit)
|
implementation(libs.androidx.webkit)
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:networkSecurityConfig="@xml/network_security_config"
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
android:theme="@style/Theme.KernelSU"
|
android:theme="@style/Theme.KernelSU"
|
||||||
tools:targetApi="33">
|
tools:targetApi="34">
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.MainActivity"
|
android:name=".ui.MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
@@ -31,6 +31,12 @@
|
|||||||
android:value="" />
|
android:value="" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity android:name=".ui.webui.WebUIActivity"
|
||||||
|
android:autoRemoveFromRecents="true"
|
||||||
|
android:documentLaunchMode="intoExisting"
|
||||||
|
android:exported="false"
|
||||||
|
android:theme="@style/Theme.KernelSU.WebUI" />
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="${applicationId}.fileprovider"
|
android:authorities="${applicationId}.fileprovider"
|
||||||
@@ -42,4 +48,4 @@
|
|||||||
</provider>
|
</provider>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -46,6 +46,12 @@ Java_me_weishu_kernelsu_Natives_isSafeMode(JNIEnv *env, jclass clazz) {
|
|||||||
return is_safe_mode();
|
return is_safe_mode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jboolean JNICALL
|
||||||
|
Java_me_weishu_kernelsu_Natives_isLkmMode(JNIEnv *env, jclass clazz) {
|
||||||
|
return is_lkm_mode();
|
||||||
|
}
|
||||||
|
|
||||||
static void fillIntArray(JNIEnv *env, jobject list, int *data, int count) {
|
static void fillIntArray(JNIEnv *env, jobject list, int *data, int count) {
|
||||||
auto cls = env->GetObjectClass(list);
|
auto cls = env->GetObjectClass(list);
|
||||||
auto add = env->GetMethodID(cls, "add", "(Ljava/lang/Object;)Z");
|
auto add = env->GetMethodID(cls, "add", "(Ljava/lang/Object;)Z");
|
||||||
|
|||||||
@@ -47,10 +47,14 @@ bool become_manager(const char* pkg) {
|
|||||||
return ksuctl(CMD_BECOME_MANAGER, param, nullptr);
|
return ksuctl(CMD_BECOME_MANAGER, param, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cache the result to avoid unnecessary syscall
|
||||||
|
static bool is_lkm;
|
||||||
int get_version() {
|
int get_version() {
|
||||||
int32_t version = -1;
|
int32_t version = -1;
|
||||||
if (ksuctl(CMD_GET_VERSION, &version, nullptr)) {
|
int32_t lkm = 0;
|
||||||
return version;
|
ksuctl(CMD_GET_VERSION, &version, &lkm);
|
||||||
|
if (!is_lkm && lkm != 0) {
|
||||||
|
is_lkm = true;
|
||||||
}
|
}
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
@@ -63,6 +67,11 @@ bool is_safe_mode() {
|
|||||||
return ksuctl(CMD_CHECK_SAFEMODE, nullptr, nullptr);
|
return ksuctl(CMD_CHECK_SAFEMODE, nullptr, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_lkm_mode() {
|
||||||
|
// you should call get_version first!
|
||||||
|
return is_lkm;
|
||||||
|
}
|
||||||
|
|
||||||
bool uid_should_umount(int uid) {
|
bool uid_should_umount(int uid) {
|
||||||
bool should;
|
bool should;
|
||||||
return ksuctl(CMD_IS_UID_SHOULD_UMOUNT, reinterpret_cast<void*>(uid), &should) && should;
|
return ksuctl(CMD_IS_UID_SHOULD_UMOUNT, reinterpret_cast<void*>(uid), &should) && should;
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ bool uid_should_umount(int uid);
|
|||||||
|
|
||||||
bool is_safe_mode();
|
bool is_safe_mode();
|
||||||
|
|
||||||
|
bool is_lkm_mode();
|
||||||
|
|
||||||
#define KSU_APP_PROFILE_VER 2
|
#define KSU_APP_PROFILE_VER 2
|
||||||
#define KSU_MAX_PACKAGE_NAME 256
|
#define KSU_MAX_PACKAGE_NAME 256
|
||||||
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
|
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ object Natives {
|
|||||||
// 11071: Fix the issue of failing to set a custom SELinux type.
|
// 11071: Fix the issue of failing to set a custom SELinux type.
|
||||||
const val MINIMAL_SUPPORTED_KERNEL = 11071
|
const val MINIMAL_SUPPORTED_KERNEL = 11071
|
||||||
|
|
||||||
|
// 11640: Support query working mode, LKM or GKI
|
||||||
|
// when MINIMAL_SUPPORTED_KERNEL > 11640, we can remove this constant.
|
||||||
|
const val MINIMAL_SUPPORTED_KERNEL_LKM = 11648
|
||||||
const val KERNEL_SU_DOMAIN = "u:r:su:s0"
|
const val KERNEL_SU_DOMAIN = "u:r:su:s0"
|
||||||
|
|
||||||
const val ROOT_UID = 0
|
const val ROOT_UID = 0
|
||||||
@@ -39,6 +42,9 @@ object Natives {
|
|||||||
val isSafeMode: Boolean
|
val isSafeMode: Boolean
|
||||||
external get
|
external get
|
||||||
|
|
||||||
|
val isLkmMode: Boolean
|
||||||
|
external get
|
||||||
|
|
||||||
external fun uidShouldUmount(uid: Int): Boolean
|
external fun uidShouldUmount(uid: Int): Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package me.weishu.kernelsu.ui
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.NavigationBar
|
import androidx.compose.material3.NavigationBar
|
||||||
@@ -20,41 +19,33 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
import androidx.navigation.compose.rememberNavController
|
||||||
import com.google.accompanist.navigation.animation.rememberAnimatedNavController
|
|
||||||
import com.ramcosta.composedestinations.DestinationsNavHost
|
import com.ramcosta.composedestinations.DestinationsNavHost
|
||||||
import com.ramcosta.composedestinations.navigation.popBackStack
|
import com.ramcosta.composedestinations.navigation.popBackStack
|
||||||
import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState
|
import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState
|
||||||
import me.weishu.kernelsu.Natives
|
import me.weishu.kernelsu.Natives
|
||||||
import me.weishu.kernelsu.ksuApp
|
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.BottomBarDestination
|
||||||
import me.weishu.kernelsu.ui.screen.NavGraphs
|
import me.weishu.kernelsu.ui.screen.NavGraphs
|
||||||
import me.weishu.kernelsu.ui.theme.KernelSUTheme
|
import me.weishu.kernelsu.ui.theme.KernelSUTheme
|
||||||
import me.weishu.kernelsu.ui.util.LocalDialogHost
|
|
||||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||||
import me.weishu.kernelsu.ui.util.rootAvailable
|
import me.weishu.kernelsu.ui.util.rootAvailable
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
@OptIn(ExperimentalAnimationApi::class)
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
KernelSUTheme {
|
KernelSUTheme {
|
||||||
val navController = rememberAnimatedNavController()
|
val navController = rememberNavController()
|
||||||
val snackbarHostState = remember { SnackbarHostState() }
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
|
||||||
val route = navBackStackEntry?.destination?.route
|
|
||||||
val showBottomBar = route == null || !route.startsWith("web_screen")
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
bottomBar = { if (showBottomBar) BottomBar(navController) },
|
bottomBar = { BottomBar(navController) },
|
||||||
snackbarHost = { SnackbarHost(snackbarHostState) }
|
snackbarHost = { SnackbarHost(snackbarHostState) }
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalSnackbarHost provides snackbarHostState,
|
LocalSnackbarHost provides snackbarHostState,
|
||||||
LocalDialogHost provides rememberDialogHostState(),
|
|
||||||
) {
|
) {
|
||||||
DestinationsNavHost(
|
DestinationsNavHost(
|
||||||
modifier = Modifier.padding(innerPadding),
|
modifier = Modifier.padding(innerPadding),
|
||||||
@@ -73,7 +64,7 @@ private fun BottomBar(navController: NavHostController) {
|
|||||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||||
val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable()
|
val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable()
|
||||||
NavigationBar(tonalElevation = 8.dp) {
|
NavigationBar(tonalElevation = 8.dp) {
|
||||||
BottomBarDestination.values().forEach { destination ->
|
BottomBarDestination.entries.forEach { destination ->
|
||||||
if (!fullFeatured && destination.rootRequired) return@forEach
|
if (!fullFeatured && destination.rootRequired) return@forEach
|
||||||
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
|
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
|
||||||
NavigationBarItem(
|
NavigationBarItem(
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import androidx.compose.material3.LocalContentColor
|
|||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.toArgb
|
import androidx.compose.ui.graphics.toArgb
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
@@ -52,11 +51,9 @@ fun AboutCard() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AboutDialog(showAboutDialog: MutableState<Boolean>) {
|
fun AboutDialog(dismiss: () -> Unit) {
|
||||||
if (showAboutDialog.value) {
|
Dialog(onDismissRequest = { dismiss() }) {
|
||||||
Dialog(onDismissRequest = { showAboutDialog.value = false }) {
|
AboutCard()
|
||||||
AboutCard()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package me.weishu.kernelsu.ui.component
|
package me.weishu.kernelsu.ui.component
|
||||||
|
|
||||||
import android.graphics.text.LineBreaker
|
import android.graphics.text.LineBreaker
|
||||||
|
import android.os.Parcelable
|
||||||
import android.text.Layout
|
import android.text.Layout
|
||||||
import android.text.method.LinkMovementMethod
|
import android.text.method.LinkMovementMethod
|
||||||
|
import android.util.Log
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@@ -10,14 +12,10 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.wrapContentHeight
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.LocalContentColor
|
|
||||||
import androidx.compose.material3.Surface
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.TextButton
|
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.runtime.saveable.Saver
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.toArgb
|
import androidx.compose.ui.graphics.toArgb
|
||||||
@@ -28,48 +26,48 @@ import androidx.compose.ui.window.Dialog
|
|||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import io.noties.markwon.Markwon
|
import io.noties.markwon.Markwon
|
||||||
import io.noties.markwon.utils.NoCopySpannableFactory
|
import io.noties.markwon.utils.NoCopySpannableFactory
|
||||||
import kotlinx.coroutines.CancellableContinuation
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.channels.ReceiveChannel
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.flow.FlowCollector
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.flow.consumeAsFlow
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.flow.onEach
|
||||||
import me.weishu.kernelsu.ui.util.LocalDialogHost
|
import kotlinx.parcelize.Parcelize
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
|
||||||
interface DialogVisuals
|
private const val TAG = "DialogComponent"
|
||||||
|
|
||||||
interface LoadingDialogVisuals : DialogVisuals
|
interface ConfirmDialogVisuals : Parcelable {
|
||||||
|
|
||||||
interface PromptDialogVisuals : DialogVisuals {
|
|
||||||
val title: String
|
val title: String
|
||||||
val content: String
|
val content: String
|
||||||
}
|
val isMarkdown: Boolean
|
||||||
|
|
||||||
interface ConfirmDialogVisuals : PromptDialogVisuals {
|
|
||||||
val confirm: String?
|
val confirm: String?
|
||||||
val dismiss: String?
|
val dismiss: String?
|
||||||
val isMarkdown: Boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
sealed interface DialogData {
|
private data class ConfirmDialogVisualsImpl(
|
||||||
val visuals: DialogVisuals
|
override val title: String,
|
||||||
|
override val content: String,
|
||||||
|
override val isMarkdown: Boolean,
|
||||||
|
override val confirm: String?,
|
||||||
|
override val dismiss: String?,
|
||||||
|
) : ConfirmDialogVisuals {
|
||||||
|
companion object {
|
||||||
|
val Empty: ConfirmDialogVisuals = ConfirmDialogVisualsImpl("", "", false, null, null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LoadingDialogData : DialogData {
|
interface DialogHandle {
|
||||||
override val visuals: LoadingDialogVisuals
|
val isShown: Boolean
|
||||||
fun dismiss()
|
val dialogType: String
|
||||||
|
fun show()
|
||||||
|
fun hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PromptDialogData : DialogData {
|
interface LoadingDialogHandle : DialogHandle {
|
||||||
override val visuals: PromptDialogVisuals
|
suspend fun <R> withLoading(block: suspend () -> R): R
|
||||||
fun dismiss()
|
fun showLoading()
|
||||||
}
|
|
||||||
|
|
||||||
interface ConfirmDialogData : PromptDialogData {
|
|
||||||
override val visuals: ConfirmDialogVisuals
|
|
||||||
fun confirm()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed interface ConfirmResult {
|
sealed interface ConfirmResult {
|
||||||
@@ -77,143 +75,313 @@ sealed interface ConfirmResult {
|
|||||||
object Canceled : ConfirmResult
|
object Canceled : ConfirmResult
|
||||||
}
|
}
|
||||||
|
|
||||||
class DialogHostState {
|
interface ConfirmDialogHandle : DialogHandle {
|
||||||
|
val visuals: ConfirmDialogVisuals
|
||||||
|
|
||||||
private object LoadingDialogVisualsImpl : LoadingDialogVisuals
|
fun showConfirm(
|
||||||
|
|
||||||
private data class PromptDialogVisualsImpl(
|
|
||||||
override val title: String, override val content: String
|
|
||||||
) : PromptDialogVisuals
|
|
||||||
|
|
||||||
private data class ConfirmDialogVisualsImpl(
|
|
||||||
override val title: String,
|
|
||||||
override val content: String,
|
|
||||||
override val confirm: String?,
|
|
||||||
override val dismiss: String?,
|
|
||||||
override val isMarkdown: Boolean,
|
|
||||||
) : ConfirmDialogVisuals
|
|
||||||
|
|
||||||
private data class LoadingDialogDataImpl(
|
|
||||||
override val visuals: LoadingDialogVisuals,
|
|
||||||
private val continuation: CancellableContinuation<Unit>,
|
|
||||||
) : LoadingDialogData {
|
|
||||||
override fun dismiss() {
|
|
||||||
if (continuation.isActive) continuation.resume(Unit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private data class PromptDialogDataImpl(
|
|
||||||
override val visuals: PromptDialogVisuals,
|
|
||||||
private val continuation: CancellableContinuation<Unit>,
|
|
||||||
) : PromptDialogData {
|
|
||||||
override fun dismiss() {
|
|
||||||
if (continuation.isActive) continuation.resume(Unit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private data class ConfirmDialogDataImpl(
|
|
||||||
override val visuals: ConfirmDialogVisuals,
|
|
||||||
private val continuation: CancellableContinuation<ConfirmResult>
|
|
||||||
) : ConfirmDialogData {
|
|
||||||
|
|
||||||
override fun confirm() {
|
|
||||||
if (continuation.isActive) continuation.resume(ConfirmResult.Confirmed)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun dismiss() {
|
|
||||||
if (continuation.isActive) continuation.resume(ConfirmResult.Canceled)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val mutex = Mutex()
|
|
||||||
|
|
||||||
var currentDialogData by mutableStateOf<DialogData?>(null)
|
|
||||||
private set
|
|
||||||
|
|
||||||
suspend fun showLoading() {
|
|
||||||
try {
|
|
||||||
mutex.withLock {
|
|
||||||
suspendCancellableCoroutine { continuation ->
|
|
||||||
currentDialogData = LoadingDialogDataImpl(
|
|
||||||
visuals = LoadingDialogVisualsImpl, continuation = continuation
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
currentDialogData = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun <R> withLoading(block: suspend () -> R) = coroutineScope {
|
|
||||||
val showLoading = launch {
|
|
||||||
showLoading()
|
|
||||||
}
|
|
||||||
|
|
||||||
val result = block()
|
|
||||||
|
|
||||||
showLoading.cancel()
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun showPrompt(title: String, content: String) {
|
|
||||||
try {
|
|
||||||
mutex.withLock {
|
|
||||||
suspendCancellableCoroutine { continuation ->
|
|
||||||
currentDialogData = PromptDialogDataImpl(
|
|
||||||
visuals = PromptDialogVisualsImpl(title, content),
|
|
||||||
continuation = continuation
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
currentDialogData = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun showConfirm(
|
|
||||||
title: String,
|
title: String,
|
||||||
content: String,
|
content: String,
|
||||||
markdown: Boolean = false,
|
markdown: Boolean = false,
|
||||||
confirm: String? = null,
|
confirm: String? = null,
|
||||||
dismiss: String? = null
|
dismiss: String? = null
|
||||||
): ConfirmResult = mutex.withLock {
|
)
|
||||||
try {
|
|
||||||
return@withLock suspendCancellableCoroutine { continuation ->
|
suspend fun awaitConfirm(
|
||||||
currentDialogData = ConfirmDialogDataImpl(
|
title: String,
|
||||||
visuals = ConfirmDialogVisualsImpl(title, content, confirm, dismiss, markdown),
|
content: String,
|
||||||
continuation = continuation
|
markdown: Boolean = false,
|
||||||
)
|
confirm: String? = null,
|
||||||
|
dismiss: String? = null
|
||||||
|
): ConfirmResult
|
||||||
|
}
|
||||||
|
|
||||||
|
private abstract class DialogHandleBase(
|
||||||
|
protected val visible: MutableState<Boolean>,
|
||||||
|
protected val coroutineScope: CoroutineScope
|
||||||
|
) : DialogHandle {
|
||||||
|
override val isShown: Boolean
|
||||||
|
get() = visible.value
|
||||||
|
|
||||||
|
override fun show() {
|
||||||
|
coroutineScope.launch {
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun hide() {
|
||||||
|
coroutineScope.launch {
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return dialogType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LoadingDialogHandleImpl(
|
||||||
|
visible: MutableState<Boolean>,
|
||||||
|
coroutineScope: CoroutineScope
|
||||||
|
) : LoadingDialogHandle, DialogHandleBase(visible, coroutineScope) {
|
||||||
|
override suspend fun <R> withLoading(block: suspend () -> R): R {
|
||||||
|
return coroutineScope.async {
|
||||||
|
try {
|
||||||
|
visible.value = true
|
||||||
|
block()
|
||||||
|
} finally {
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
}.await()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showLoading() {
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val dialogType: String get() = "LoadingDialog"
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias NullableCallback = (() -> Unit)?
|
||||||
|
|
||||||
|
interface ConfirmCallback {
|
||||||
|
|
||||||
|
val onConfirm: NullableCallback
|
||||||
|
|
||||||
|
val onDismiss: NullableCallback
|
||||||
|
|
||||||
|
val isEmpty: Boolean get() = onConfirm == null && onDismiss == null
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
operator fun invoke(onConfirmProvider: () -> NullableCallback, onDismissProvider: () -> NullableCallback): ConfirmCallback {
|
||||||
|
return object : ConfirmCallback {
|
||||||
|
override val onConfirm: NullableCallback
|
||||||
|
get() = onConfirmProvider()
|
||||||
|
override val onDismiss: NullableCallback
|
||||||
|
get() = onDismissProvider()
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
currentDialogData = null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class ConfirmDialogHandleImpl(
|
||||||
|
visible: MutableState<Boolean>,
|
||||||
|
coroutineScope: CoroutineScope,
|
||||||
|
callback: ConfirmCallback,
|
||||||
|
override var visuals: ConfirmDialogVisuals = ConfirmDialogVisualsImpl.Empty,
|
||||||
|
private val resultFlow: ReceiveChannel<ConfirmResult>
|
||||||
|
) : ConfirmDialogHandle, DialogHandleBase(visible, coroutineScope) {
|
||||||
|
private class ResultCollector(
|
||||||
|
private val callback: ConfirmCallback
|
||||||
|
) : FlowCollector<ConfirmResult> {
|
||||||
|
fun handleResult(result: ConfirmResult) {
|
||||||
|
Log.d(TAG, "handleResult: ${result.javaClass.simpleName}")
|
||||||
|
when (result) {
|
||||||
|
ConfirmResult.Confirmed -> onConfirm()
|
||||||
|
ConfirmResult.Canceled -> onDismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onConfirm() {
|
||||||
|
callback.onConfirm?.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onDismiss() {
|
||||||
|
callback.onDismiss?.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun emit(value: ConfirmResult) {
|
||||||
|
handleResult(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val resultCollector = ResultCollector(callback)
|
||||||
|
|
||||||
|
private var awaitContinuation: CancellableContinuation<ConfirmResult>? = null
|
||||||
|
|
||||||
|
private val isCallbackEmpty = callback.isEmpty
|
||||||
|
|
||||||
|
init {
|
||||||
|
coroutineScope.launch {
|
||||||
|
resultFlow
|
||||||
|
.consumeAsFlow()
|
||||||
|
.onEach { result ->
|
||||||
|
awaitContinuation?.let {
|
||||||
|
awaitContinuation = null
|
||||||
|
if (it.isActive) {
|
||||||
|
it.resume(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onEach { hide() }
|
||||||
|
.collect(resultCollector)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun awaitResult(): ConfirmResult {
|
||||||
|
return suspendCancellableCoroutine {
|
||||||
|
awaitContinuation = it.apply {
|
||||||
|
if (isCallbackEmpty) {
|
||||||
|
invokeOnCancellation {
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateVisuals(visuals: ConfirmDialogVisuals) {
|
||||||
|
this.visuals = visuals
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun show() {
|
||||||
|
if (visuals !== ConfirmDialogVisualsImpl.Empty) {
|
||||||
|
super.show()
|
||||||
|
} else {
|
||||||
|
throw UnsupportedOperationException("can't show confirm dialog with the Empty visuals")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showConfirm(
|
||||||
|
title: String,
|
||||||
|
content: String,
|
||||||
|
markdown: Boolean,
|
||||||
|
confirm: String?,
|
||||||
|
dismiss: String?
|
||||||
|
) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
updateVisuals(ConfirmDialogVisualsImpl(title, content, markdown, confirm, dismiss))
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun awaitConfirm(
|
||||||
|
title: String,
|
||||||
|
content: String,
|
||||||
|
markdown: Boolean,
|
||||||
|
confirm: String?,
|
||||||
|
dismiss: String?
|
||||||
|
): ConfirmResult {
|
||||||
|
coroutineScope.launch {
|
||||||
|
updateVisuals(ConfirmDialogVisualsImpl(title, content, markdown, confirm, dismiss))
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
return awaitResult()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val dialogType: String get() = "ConfirmDialog"
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "${super.toString()}(visuals: $visuals)"
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun Saver(
|
||||||
|
visible: MutableState<Boolean>,
|
||||||
|
coroutineScope: CoroutineScope,
|
||||||
|
callback: ConfirmCallback,
|
||||||
|
resultChannel: ReceiveChannel<ConfirmResult>
|
||||||
|
) = Saver<ConfirmDialogHandle, ConfirmDialogVisuals>(
|
||||||
|
save = {
|
||||||
|
it.visuals
|
||||||
|
},
|
||||||
|
restore = {
|
||||||
|
Log.d(TAG, "ConfirmDialog restore, visuals: $it")
|
||||||
|
ConfirmDialogHandleImpl(visible, coroutineScope, callback, it, resultChannel)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CustomDialogHandleImpl(
|
||||||
|
visible: MutableState<Boolean>,
|
||||||
|
coroutineScope: CoroutineScope
|
||||||
|
) : DialogHandleBase(visible, coroutineScope) {
|
||||||
|
override val dialogType: String get() = "CustomDialog"
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun rememberDialogHostState(): DialogHostState {
|
fun rememberLoadingDialog(): LoadingDialogHandle {
|
||||||
|
val visible = remember {
|
||||||
|
mutableStateOf(false)
|
||||||
|
}
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
if (visible.value) {
|
||||||
|
LoadingDialog()
|
||||||
|
}
|
||||||
|
|
||||||
return remember {
|
return remember {
|
||||||
DialogHostState()
|
LoadingDialogHandleImpl(visible, coroutineScope)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun <reified T : DialogData> DialogData?.tryInto(): T? {
|
|
||||||
return when (this) {
|
|
||||||
is T -> this
|
|
||||||
else -> null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LoadingDialog(
|
private fun rememberConfirmDialog(visuals: ConfirmDialogVisuals, callback: ConfirmCallback): ConfirmDialogHandle {
|
||||||
state: DialogHostState = LocalDialogHost.current,
|
val visible = rememberSaveable {
|
||||||
) {
|
mutableStateOf(false)
|
||||||
state.currentDialogData.tryInto<LoadingDialogData>() ?: return
|
|
||||||
val dialogProperties = remember {
|
|
||||||
DialogProperties(dismissOnClickOutside = false, dismissOnBackPress = false)
|
|
||||||
}
|
}
|
||||||
Dialog(onDismissRequest = {}, properties = dialogProperties) {
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val resultChannel = remember {
|
||||||
|
Channel<ConfirmResult>()
|
||||||
|
}
|
||||||
|
|
||||||
|
val handle = rememberSaveable(
|
||||||
|
saver = ConfirmDialogHandleImpl.Saver(visible, coroutineScope, callback, resultChannel),
|
||||||
|
init = {
|
||||||
|
ConfirmDialogHandleImpl(visible, coroutineScope, callback, visuals, resultChannel)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (visible.value) {
|
||||||
|
ConfirmDialog(
|
||||||
|
handle.visuals,
|
||||||
|
confirm = { coroutineScope.launch { resultChannel.send(ConfirmResult.Confirmed) } },
|
||||||
|
dismiss = { coroutineScope.launch { resultChannel.send(ConfirmResult.Canceled) } }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return handle
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun rememberConfirmCallback(onConfirm: NullableCallback, onDismiss: NullableCallback): ConfirmCallback {
|
||||||
|
val currentOnConfirm by rememberUpdatedState(newValue = onConfirm)
|
||||||
|
val currentOnDismiss by rememberUpdatedState(newValue = onDismiss)
|
||||||
|
return remember {
|
||||||
|
ConfirmCallback({ currentOnConfirm }, { currentOnDismiss })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun rememberConfirmDialog(onConfirm: NullableCallback = null, onDismiss: NullableCallback = null): ConfirmDialogHandle {
|
||||||
|
return rememberConfirmDialog(rememberConfirmCallback(onConfirm, onDismiss))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun rememberConfirmDialog(callback: ConfirmCallback): ConfirmDialogHandle {
|
||||||
|
return rememberConfirmDialog(ConfirmDialogVisualsImpl.Empty, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun rememberCustomDialog(composable: @Composable (dismiss: () -> Unit) -> Unit): DialogHandle {
|
||||||
|
val visible = rememberSaveable {
|
||||||
|
mutableStateOf(false)
|
||||||
|
}
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
if (visible.value) {
|
||||||
|
composable { visible.value = false }
|
||||||
|
}
|
||||||
|
return remember {
|
||||||
|
CustomDialogHandleImpl(visible, coroutineScope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun LoadingDialog() {
|
||||||
|
Dialog(
|
||||||
|
onDismissRequest = {},
|
||||||
|
properties = DialogProperties(dismissOnClickOutside = false, dismissOnBackPress = false)
|
||||||
|
) {
|
||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier.size(100.dp), shape = RoundedCornerShape(8.dp)
|
modifier = Modifier.size(100.dp), shape = RoundedCornerShape(8.dp)
|
||||||
) {
|
) {
|
||||||
@@ -227,41 +395,10 @@ fun LoadingDialog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PromptDialog(
|
private fun ConfirmDialog(visuals: ConfirmDialogVisuals, confirm: () -> Unit, dismiss: () -> Unit) {
|
||||||
state: DialogHostState = LocalDialogHost.current,
|
|
||||||
) {
|
|
||||||
val promptDialogData = state.currentDialogData.tryInto<PromptDialogData>() ?: return
|
|
||||||
|
|
||||||
val visuals = promptDialogData.visuals
|
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = {
|
onDismissRequest = {
|
||||||
promptDialogData.dismiss()
|
dismiss()
|
||||||
},
|
|
||||||
title = {
|
|
||||||
Text(text = visuals.title)
|
|
||||||
},
|
|
||||||
text = {
|
|
||||||
Text(text = visuals.content)
|
|
||||||
},
|
|
||||||
confirmButton = {
|
|
||||||
TextButton(onClick = { promptDialogData.dismiss() }) {
|
|
||||||
Text(text = stringResource(id = android.R.string.ok))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dismissButton = null,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
fun ConfirmDialog(state: DialogHostState = LocalDialogHost.current) {
|
|
||||||
val confirmDialogData = state.currentDialogData.tryInto<ConfirmDialogData>() ?: return
|
|
||||||
|
|
||||||
val visuals = confirmDialogData.visuals
|
|
||||||
|
|
||||||
AlertDialog(
|
|
||||||
onDismissRequest = {
|
|
||||||
confirmDialogData.dismiss()
|
|
||||||
},
|
},
|
||||||
title = {
|
title = {
|
||||||
Text(text = visuals.title)
|
Text(text = visuals.title)
|
||||||
@@ -274,17 +411,18 @@ fun ConfirmDialog(state: DialogHostState = LocalDialogHost.current) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(onClick = { confirmDialogData.confirm() }) {
|
TextButton(onClick = confirm) {
|
||||||
Text(text = visuals.confirm ?: stringResource(id = android.R.string.ok))
|
Text(text = visuals.confirm ?: stringResource(id = android.R.string.ok))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dismissButton = {
|
dismissButton = {
|
||||||
TextButton(onClick = { confirmDialogData.dismiss() }) {
|
TextButton(onClick = dismiss) {
|
||||||
Text(text = visuals.dismiss ?: stringResource(id = android.R.string.cancel))
|
Text(text = visuals.dismiss ?: stringResource(id = android.R.string.cancel))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun MarkdownContent(content: String) {
|
private fun MarkdownContent(content: String) {
|
||||||
val contentColor = LocalContentColor.current
|
val contentColor = LocalContentColor.current
|
||||||
@@ -307,5 +445,6 @@ private fun MarkdownContent(content: String) {
|
|||||||
update = {
|
update = {
|
||||||
Markwon.create(it.context).setMarkdown(it, content)
|
Markwon.create(it.context).setMarkdown(it, content)
|
||||||
it.setTextColor(contentColor.toArgb())
|
it.setTextColor(contentColor.toArgb())
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
@@ -10,9 +10,9 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.Close
|
import androidx.compose.material.icons.filled.Close
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material.icons.outlined.ArrowBack
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
@@ -27,7 +27,6 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
@@ -39,7 +38,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
|
|
||||||
private const val TAG = "SearchBar"
|
private const val TAG = "SearchBar"
|
||||||
|
|
||||||
@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun SearchAppBar(
|
fun SearchAppBar(
|
||||||
title: @Composable () -> Unit,
|
title: @Composable () -> Unit,
|
||||||
@@ -115,7 +114,7 @@ fun SearchAppBar(
|
|||||||
if (onBackClick != null) {
|
if (onBackClick != null) {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = onBackClick,
|
onClick = onBackClick,
|
||||||
content = { Icon(Icons.Outlined.ArrowBack, null) }
|
content = { Icon(Icons.AutoMirrored.Outlined.ArrowBack, null) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package me.weishu.kernelsu.ui.component
|
package me.weishu.kernelsu.ui.component
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
import androidx.compose.material3.RadioButton
|
import androidx.compose.material3.RadioButton
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -18,6 +20,9 @@ fun SwitchItem(
|
|||||||
onCheckedChange: (Boolean) -> Unit
|
onCheckedChange: (Boolean) -> Unit
|
||||||
) {
|
) {
|
||||||
ListItem(
|
ListItem(
|
||||||
|
modifier = Modifier.clickable {
|
||||||
|
onCheckedChange.invoke(!checked)
|
||||||
|
},
|
||||||
headlineContent = {
|
headlineContent = {
|
||||||
Text(title)
|
Text(title)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -22,14 +22,13 @@ import androidx.compose.material3.ListItem
|
|||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedCard
|
import androidx.compose.material3.OutlinedCard
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextFieldDefaults
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@@ -53,6 +52,7 @@ import me.weishu.kernelsu.Natives
|
|||||||
import me.weishu.kernelsu.R
|
import me.weishu.kernelsu.R
|
||||||
import me.weishu.kernelsu.profile.Capabilities
|
import me.weishu.kernelsu.profile.Capabilities
|
||||||
import me.weishu.kernelsu.profile.Groups
|
import me.weishu.kernelsu.profile.Groups
|
||||||
|
import me.weishu.kernelsu.ui.component.rememberCustomDialog
|
||||||
import me.weishu.kernelsu.ui.util.isSepolicyValid
|
import me.weishu.kernelsu.ui.util.isSepolicyValid
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@@ -146,7 +146,7 @@ fun RootProfileConfig(
|
|||||||
|
|
||||||
val selectedGroups = profile.groups.ifEmpty { listOf(0) }.let { e ->
|
val selectedGroups = profile.groups.ifEmpty { listOf(0) }.let { e ->
|
||||||
e.mapNotNull { g ->
|
e.mapNotNull { g ->
|
||||||
Groups.values().find { it.gid == g }
|
Groups.entries.find { it.gid == g }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GroupsPanel(selectedGroups) {
|
GroupsPanel(selectedGroups) {
|
||||||
@@ -159,7 +159,7 @@ fun RootProfileConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val selectedCaps = profile.capabilities.mapNotNull { e ->
|
val selectedCaps = profile.capabilities.mapNotNull { e ->
|
||||||
Capabilities.values().find { it.cap == e }
|
Capabilities.entries.find { it.cap == e }
|
||||||
}
|
}
|
||||||
|
|
||||||
CapsPanel(selectedCaps) {
|
CapsPanel(selectedCaps) {
|
||||||
@@ -187,11 +187,8 @@ fun RootProfileConfig(
|
|||||||
@OptIn(ExperimentalLayoutApi::class)
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun GroupsPanel(selected: List<Groups>, closeSelection: (selection: Set<Groups>) -> Unit) {
|
fun GroupsPanel(selected: List<Groups>, closeSelection: (selection: Set<Groups>) -> Unit) {
|
||||||
|
val selectGroupsDialog = rememberCustomDialog { dismiss: () -> Unit ->
|
||||||
var showDialog by remember { mutableStateOf(false) }
|
val groups = Groups.entries.toTypedArray().sortedWith(
|
||||||
|
|
||||||
if (showDialog) {
|
|
||||||
val groups = Groups.values().sortedWith(
|
|
||||||
compareBy<Groups> { if (selected.contains(it)) 0 else 1 }
|
compareBy<Groups> { if (selected.contains(it)) 0 else 1 }
|
||||||
.then(compareBy {
|
.then(compareBy {
|
||||||
when (it) {
|
when (it) {
|
||||||
@@ -217,7 +214,7 @@ fun GroupsPanel(selected: List<Groups>, closeSelection: (selection: Set<Groups>)
|
|||||||
state = rememberUseCaseState(visible = true, onFinishedRequest = {
|
state = rememberUseCaseState(visible = true, onFinishedRequest = {
|
||||||
closeSelection(selection)
|
closeSelection(selection)
|
||||||
}, onCloseRequest = {
|
}, onCloseRequest = {
|
||||||
showDialog = false
|
dismiss()
|
||||||
}),
|
}),
|
||||||
header = Header.Default(
|
header = Header.Default(
|
||||||
title = stringResource(R.string.profile_groups),
|
title = stringResource(R.string.profile_groups),
|
||||||
@@ -241,7 +238,7 @@ fun GroupsPanel(selected: List<Groups>, closeSelection: (selection: Set<Groups>)
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
.clickable {
|
.clickable {
|
||||||
showDialog = true
|
selectGroupsDialog.show()
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
Column(modifier = Modifier.padding(16.dp)) {
|
Column(modifier = Modifier.padding(16.dp)) {
|
||||||
@@ -265,11 +262,8 @@ fun CapsPanel(
|
|||||||
selected: Collection<Capabilities>,
|
selected: Collection<Capabilities>,
|
||||||
closeSelection: (selection: Set<Capabilities>) -> Unit
|
closeSelection: (selection: Set<Capabilities>) -> Unit
|
||||||
) {
|
) {
|
||||||
|
val selectCapabilitiesDialog = rememberCustomDialog { dismiss ->
|
||||||
var showDialog by remember { mutableStateOf(false) }
|
val caps = Capabilities.entries.toTypedArray().sortedWith(
|
||||||
|
|
||||||
if (showDialog) {
|
|
||||||
val caps = Capabilities.values().sortedWith(
|
|
||||||
compareBy<Capabilities> { if (selected.contains(it)) 0 else 1 }
|
compareBy<Capabilities> { if (selected.contains(it)) 0 else 1 }
|
||||||
.then(compareBy { it.name })
|
.then(compareBy { it.name })
|
||||||
)
|
)
|
||||||
@@ -286,7 +280,7 @@ fun CapsPanel(
|
|||||||
state = rememberUseCaseState(visible = true, onFinishedRequest = {
|
state = rememberUseCaseState(visible = true, onFinishedRequest = {
|
||||||
closeSelection(selection)
|
closeSelection(selection)
|
||||||
}, onCloseRequest = {
|
}, onCloseRequest = {
|
||||||
showDialog = false
|
dismiss()
|
||||||
}),
|
}),
|
||||||
header = Header.Default(
|
header = Header.Default(
|
||||||
title = stringResource(R.string.profile_capabilities),
|
title = stringResource(R.string.profile_capabilities),
|
||||||
@@ -309,7 +303,7 @@ fun CapsPanel(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
.clickable {
|
.clickable {
|
||||||
showDialog = true
|
selectCapabilitiesDialog.show()
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
Column(modifier = Modifier.padding(16.dp)) {
|
Column(modifier = Modifier.padding(16.dp)) {
|
||||||
@@ -327,7 +321,6 @@ fun CapsPanel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalComposeUiApi::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun UidPanel(uid: Int, label: String, onUidChange: (Int) -> Unit) {
|
private fun UidPanel(uid: Int, label: String, onUidChange: (Int) -> Unit) {
|
||||||
|
|
||||||
@@ -377,8 +370,7 @@ private fun SELinuxPanel(
|
|||||||
profile: Natives.Profile,
|
profile: Natives.Profile,
|
||||||
onSELinuxChange: (domain: String, rules: String) -> Unit
|
onSELinuxChange: (domain: String, rules: String) -> Unit
|
||||||
) {
|
) {
|
||||||
var showDialog by remember { mutableStateOf(false) }
|
val editSELinuxDialog = rememberCustomDialog { dismiss ->
|
||||||
if (showDialog) {
|
|
||||||
var domain by remember { mutableStateOf(profile.context) }
|
var domain by remember { mutableStateOf(profile.context) }
|
||||||
var rules by remember { mutableStateOf(profile.rules) }
|
var rules by remember { mutableStateOf(profile.rules) }
|
||||||
|
|
||||||
@@ -430,7 +422,7 @@ private fun SELinuxPanel(
|
|||||||
onSELinuxChange(domain, rules)
|
onSELinuxChange(domain, rules)
|
||||||
},
|
},
|
||||||
onCloseRequest = {
|
onCloseRequest = {
|
||||||
showDialog = false
|
dismiss()
|
||||||
}),
|
}),
|
||||||
header = Header.Default(
|
header = Header.Default(
|
||||||
title = stringResource(R.string.profile_selinux_context),
|
title = stringResource(R.string.profile_selinux_context),
|
||||||
@@ -449,10 +441,10 @@ private fun SELinuxPanel(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable {
|
.clickable {
|
||||||
showDialog = true
|
editSELinuxDialog.show()
|
||||||
},
|
},
|
||||||
enabled = false,
|
enabled = false,
|
||||||
colors = TextFieldDefaults.outlinedTextFieldColors(
|
colors = OutlinedTextFieldDefaults.colors(
|
||||||
disabledTextColor = MaterialTheme.colorScheme.onSurface,
|
disabledTextColor = MaterialTheme.colorScheme.onSurface,
|
||||||
disabledBorderColor = MaterialTheme.colorScheme.outline,
|
disabledBorderColor = MaterialTheme.colorScheme.outline,
|
||||||
disabledPlaceholderColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
disabledPlaceholderColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ package me.weishu.kernelsu.ui.component.profile
|
|||||||
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ReadMore
|
||||||
import androidx.compose.material.icons.filled.ArrowDropDown
|
import androidx.compose.material.icons.filled.ArrowDropDown
|
||||||
import androidx.compose.material.icons.filled.ArrowDropUp
|
import androidx.compose.material.icons.filled.ArrowDropUp
|
||||||
import androidx.compose.material.icons.filled.Create
|
import androidx.compose.material.icons.filled.Create
|
||||||
import androidx.compose.material.icons.filled.ReadMore
|
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||||
@@ -105,7 +105,7 @@ fun TemplateConfig(
|
|||||||
IconButton(onClick = {
|
IconButton(onClick = {
|
||||||
onViewTemplate(tid)
|
onViewTemplate(tid)
|
||||||
}) {
|
}) {
|
||||||
Icon(Icons.Filled.ReadMore, null)
|
Icon(Icons.AutoMirrored.Filled.ReadMore, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -15,15 +15,15 @@ import androidx.compose.foundation.layout.width
|
|||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.AccountCircle
|
import androidx.compose.material.icons.filled.AccountCircle
|
||||||
import androidx.compose.material.icons.filled.Android
|
import androidx.compose.material.icons.filled.Android
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
|
||||||
import androidx.compose.material.icons.filled.Security
|
import androidx.compose.material.icons.filled.Security
|
||||||
import androidx.compose.material3.Divider
|
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.material3.DropdownMenu
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.FilterChip
|
import androidx.compose.material3.FilterChip
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
@@ -183,7 +183,7 @@ private fun AppProfileInner(
|
|||||||
} else {
|
} else {
|
||||||
Mode.Custom
|
Mode.Custom
|
||||||
}
|
}
|
||||||
var mode by remember {
|
var mode by rememberSaveable {
|
||||||
mutableStateOf(initialMode)
|
mutableStateOf(initialMode)
|
||||||
}
|
}
|
||||||
ProfileBox(mode, true) {
|
ProfileBox(mode, true) {
|
||||||
@@ -246,12 +246,11 @@ private fun TopBar(onBack: () -> Unit) {
|
|||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = onBack
|
onClick = onBack
|
||||||
) { Icon(Icons.Filled.ArrowBack, contentDescription = null) }
|
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ProfileBox(
|
private fun ProfileBox(
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
@@ -263,7 +262,7 @@ private fun ProfileBox(
|
|||||||
supportingContent = { Text(mode.text) },
|
supportingContent = { Text(mode.text) },
|
||||||
leadingContent = { Icon(Icons.Filled.AccountCircle, null) },
|
leadingContent = { Icon(Icons.Filled.AccountCircle, null) },
|
||||||
)
|
)
|
||||||
Divider(thickness = Dp.Hairline)
|
HorizontalDivider(thickness = Dp.Hairline)
|
||||||
ListItem(headlineContent = {
|
ListItem(headlineContent = {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly
|
modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly
|
||||||
|
|||||||
238
manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt
Normal file
238
manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
package me.weishu.kernelsu.ui.screen
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Environment
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
|
import androidx.compose.material.icons.filled.Refresh
|
||||||
|
import androidx.compose.material.icons.filled.Save
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.input.key.Key
|
||||||
|
import androidx.compose.ui.input.key.key
|
||||||
|
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.navigation.DestinationsNavigator
|
||||||
|
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import me.weishu.kernelsu.R
|
||||||
|
import me.weishu.kernelsu.ui.component.KeyEventBlocker
|
||||||
|
import me.weishu.kernelsu.ui.util.LkmSelection
|
||||||
|
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||||
|
import me.weishu.kernelsu.ui.util.flashModule
|
||||||
|
import me.weishu.kernelsu.ui.util.installBoot
|
||||||
|
import me.weishu.kernelsu.ui.util.reboot
|
||||||
|
import me.weishu.kernelsu.ui.util.restoreBoot
|
||||||
|
import me.weishu.kernelsu.ui.util.uninstallPermanently
|
||||||
|
import java.io.File
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
enum class FlashingStatus {
|
||||||
|
FLASHING,
|
||||||
|
SUCCESS,
|
||||||
|
FAILED
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author weishu
|
||||||
|
* @date 2023/1/1.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
@Destination
|
||||||
|
fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||||
|
|
||||||
|
var text by rememberSaveable { mutableStateOf("") }
|
||||||
|
val logContent = rememberSaveable { StringBuilder() }
|
||||||
|
var showFloatAction by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
||||||
|
val snackBarHost = LocalSnackbarHost.current
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val scrollState = rememberScrollState()
|
||||||
|
var flashing by rememberSaveable {
|
||||||
|
mutableStateOf(FlashingStatus.FLASHING)
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
if (text.isNotEmpty()) {
|
||||||
|
return@LaunchedEffect
|
||||||
|
}
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
flashIt(flashIt, onFinish = { showReboot, code ->
|
||||||
|
if (code != 0) {
|
||||||
|
text += "Error: exit code = $code.\nPlease save and check the log.\n"
|
||||||
|
}
|
||||||
|
if (showReboot) {
|
||||||
|
text += "\n\n\n"
|
||||||
|
showFloatAction = true
|
||||||
|
}
|
||||||
|
flashing = if (code == 0) FlashingStatus.SUCCESS else FlashingStatus.FAILED
|
||||||
|
}, onStdout = {
|
||||||
|
text += "$it\n"
|
||||||
|
logContent.append(it).append("\n")
|
||||||
|
}, onStderr = {
|
||||||
|
logContent.append(it).append("\n")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
TopBar(
|
||||||
|
flashing,
|
||||||
|
onBack = {
|
||||||
|
navigator.popBackStack()
|
||||||
|
},
|
||||||
|
onSave = {
|
||||||
|
scope.launch {
|
||||||
|
val format = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault())
|
||||||
|
val date = format.format(Date())
|
||||||
|
val file = File(
|
||||||
|
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
||||||
|
"KernelSU_install_log_${date}.log"
|
||||||
|
)
|
||||||
|
file.writeText(logContent.toString())
|
||||||
|
snackBarHost.showSnackbar("Log saved to ${file.absolutePath}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
floatingActionButton = {
|
||||||
|
if (showFloatAction) {
|
||||||
|
val reboot = stringResource(id = R.string.reboot)
|
||||||
|
ExtendedFloatingActionButton(
|
||||||
|
onClick = {
|
||||||
|
scope.launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
reboot()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon = { Icon(Icons.Filled.Refresh, reboot) },
|
||||||
|
text = { Text(text = reboot) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
) { innerPadding ->
|
||||||
|
KeyEventBlocker {
|
||||||
|
it.key == Key.VolumeDown || it.key == Key.VolumeUp
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize(1f)
|
||||||
|
.padding(innerPadding)
|
||||||
|
.verticalScroll(scrollState),
|
||||||
|
) {
|
||||||
|
LaunchedEffect(text) {
|
||||||
|
scrollState.animateScrollTo(scrollState.maxValue)
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(8.dp),
|
||||||
|
text = text,
|
||||||
|
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||||
|
fontFamily = FontFamily.Monospace,
|
||||||
|
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
sealed class FlashIt : Parcelable {
|
||||||
|
data class FlashBoot(val boot: Uri? = null, val lkm: LkmSelection, val ota: Boolean) :
|
||||||
|
FlashIt()
|
||||||
|
|
||||||
|
data class FlashModule(val uri: Uri) : FlashIt()
|
||||||
|
|
||||||
|
data object FlashRestore : FlashIt()
|
||||||
|
|
||||||
|
data object FlashUninstall : FlashIt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun flashIt(
|
||||||
|
flashIt: FlashIt, onFinish: (Boolean, Int) -> Unit,
|
||||||
|
onStdout: (String) -> Unit,
|
||||||
|
onStderr: (String) -> Unit
|
||||||
|
) {
|
||||||
|
when (flashIt) {
|
||||||
|
is FlashIt.FlashBoot -> installBoot(
|
||||||
|
flashIt.boot,
|
||||||
|
flashIt.lkm,
|
||||||
|
flashIt.ota,
|
||||||
|
onFinish,
|
||||||
|
onStdout,
|
||||||
|
onStderr
|
||||||
|
)
|
||||||
|
|
||||||
|
is FlashIt.FlashModule -> flashModule(flashIt.uri, onFinish, onStdout, onStderr)
|
||||||
|
|
||||||
|
FlashIt.FlashRestore -> restoreBoot(onFinish, onStdout, onStderr)
|
||||||
|
|
||||||
|
FlashIt.FlashUninstall -> uninstallPermanently(onFinish, onStdout, onStderr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
private fun TopBar(status: FlashingStatus, onBack: () -> Unit = {}, onSave: () -> Unit = {}) {
|
||||||
|
TopAppBar(
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
stringResource(
|
||||||
|
when (status) {
|
||||||
|
FlashingStatus.FLASHING -> R.string.flashing
|
||||||
|
FlashingStatus.SUCCESS -> R.string.flash_success
|
||||||
|
FlashingStatus.FAILED -> R.string.flash_failed
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(
|
||||||
|
onClick = onBack
|
||||||
|
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||||
|
},
|
||||||
|
actions = {
|
||||||
|
IconButton(onClick = onSave) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.Save,
|
||||||
|
contentDescription = "Localized description"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun InstallPreview() {
|
||||||
|
InstallScreen(EmptyDestinationsNavigator)
|
||||||
|
}
|
||||||
@@ -5,11 +5,13 @@ import android.os.Build
|
|||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import android.system.Os
|
import android.system.Os
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.animation.*
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Archive
|
||||||
import androidx.compose.material.icons.filled.Refresh
|
import androidx.compose.material.icons.filled.Refresh
|
||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.material.icons.outlined.Block
|
import androidx.compose.material.icons.outlined.Block
|
||||||
@@ -29,22 +31,26 @@ import com.ramcosta.composedestinations.annotation.Destination
|
|||||||
import com.ramcosta.composedestinations.annotation.RootNavGraph
|
import com.ramcosta.composedestinations.annotation.RootNavGraph
|
||||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import me.weishu.kernelsu.*
|
import me.weishu.kernelsu.*
|
||||||
import me.weishu.kernelsu.R
|
import me.weishu.kernelsu.R
|
||||||
import me.weishu.kernelsu.ui.component.ConfirmDialog
|
import me.weishu.kernelsu.ui.component.rememberConfirmDialog
|
||||||
import me.weishu.kernelsu.ui.component.ConfirmResult
|
import me.weishu.kernelsu.ui.screen.destinations.InstallScreenDestination
|
||||||
import me.weishu.kernelsu.ui.screen.destinations.SettingScreenDestination
|
import me.weishu.kernelsu.ui.screen.destinations.SettingScreenDestination
|
||||||
import me.weishu.kernelsu.ui.util.*
|
import me.weishu.kernelsu.ui.util.*
|
||||||
|
import me.weishu.kernelsu.ui.util.module.LatestVersionInfo
|
||||||
|
|
||||||
@RootNavGraph(start = true)
|
@RootNavGraph(start = true)
|
||||||
@Destination
|
@Destination
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeScreen(navigator: DestinationsNavigator) {
|
fun HomeScreen(navigator: DestinationsNavigator) {
|
||||||
|
val kernelVersion = getKernelVersion()
|
||||||
|
|
||||||
Scaffold(topBar = {
|
Scaffold(topBar = {
|
||||||
TopBar(onSettingsClick = {
|
TopBar(kernelVersion, onSettingsClick = {
|
||||||
navigator.navigate(SettingScreenDestination)
|
navigator.navigate(SettingScreenDestination)
|
||||||
|
}, onInstallClick = {
|
||||||
|
navigator.navigate(InstallScreenDestination)
|
||||||
})
|
})
|
||||||
}) { innerPadding ->
|
}) { innerPadding ->
|
||||||
Column(
|
Column(
|
||||||
@@ -54,14 +60,18 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
|||||||
.verticalScroll(rememberScrollState()),
|
.verticalScroll(rememberScrollState()),
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
val kernelVersion = getKernelVersion()
|
|
||||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||||
SideEffect {
|
SideEffect {
|
||||||
if (isManager) install()
|
if (isManager) install()
|
||||||
}
|
}
|
||||||
val ksuVersion = if (isManager) Natives.version else null
|
val ksuVersion = if (isManager) Natives.version else null
|
||||||
|
val lkmMode = ksuVersion?.let {
|
||||||
|
if (it >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && kernelVersion.isGKI()) Natives.isLkmMode else null
|
||||||
|
}
|
||||||
|
|
||||||
StatusCard(kernelVersion, ksuVersion)
|
StatusCard(kernelVersion, ksuVersion, lkmMode) {
|
||||||
|
navigator.navigate(InstallScreenDestination)
|
||||||
|
}
|
||||||
if (isManager && Natives.requireNewKernel()) {
|
if (isManager && Natives.requireNewKernel()) {
|
||||||
WarningCard(
|
WarningCard(
|
||||||
stringResource(id = R.string.require_kernel_version).format(
|
stringResource(id = R.string.require_kernel_version).format(
|
||||||
@@ -69,7 +79,7 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (!rootAvailable()) {
|
if (ksuVersion != null && !rootAvailable()) {
|
||||||
WarningCard(
|
WarningCard(
|
||||||
stringResource(id = R.string.grant_root_failed)
|
stringResource(id = R.string.grant_root_failed)
|
||||||
)
|
)
|
||||||
@@ -84,7 +94,6 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
|||||||
DonateCard()
|
DonateCard()
|
||||||
LearnMoreCard()
|
LearnMoreCard()
|
||||||
Spacer(Modifier)
|
Spacer(Modifier)
|
||||||
ConfirmDialog()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,35 +101,40 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
|||||||
@Composable
|
@Composable
|
||||||
fun UpdateCard() {
|
fun UpdateCard() {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val newVersion by produceState(initialValue = Triple(0, "", "")) {
|
val latestVersionInfo = LatestVersionInfo()
|
||||||
value = withContext(Dispatchers.IO) { checkNewVersion() }
|
val newVersion by produceState(initialValue = latestVersionInfo) {
|
||||||
}
|
value = withContext(Dispatchers.IO){
|
||||||
val currentVersionCode = getManagerVersion(context).second
|
checkNewVersion()
|
||||||
val newVersionCode = newVersion.first
|
}
|
||||||
val newVersionUrl = newVersion.second
|
|
||||||
val changelog = newVersion.third
|
|
||||||
if (newVersionCode <= currentVersionCode) {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val currentVersionCode = getManagerVersion(context).second
|
||||||
|
val newVersionCode = newVersion.versionCode
|
||||||
|
val newVersionUrl = newVersion.downloadUrl
|
||||||
|
val changelog = newVersion.changelog
|
||||||
|
|
||||||
val uriHandler = LocalUriHandler.current
|
val uriHandler = LocalUriHandler.current
|
||||||
val dialogHost = LocalDialogHost.current
|
|
||||||
val title = stringResource(id = R.string.module_changelog)
|
val title = stringResource(id = R.string.module_changelog)
|
||||||
val updateText = stringResource(id = R.string.module_update)
|
val updateText = stringResource(id = R.string.module_update)
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
WarningCard(
|
AnimatedVisibility(
|
||||||
message = stringResource(id = R.string.new_version_available).format(newVersionCode),
|
visible = newVersionCode > currentVersionCode,
|
||||||
MaterialTheme.colorScheme.outlineVariant
|
enter = fadeIn() + expandVertically(),
|
||||||
|
exit = shrinkVertically() + fadeOut()
|
||||||
) {
|
) {
|
||||||
scope.launch {
|
val updateDialog = rememberConfirmDialog(onConfirm = { uriHandler.openUri(newVersionUrl) })
|
||||||
if (changelog.isEmpty() || dialogHost.showConfirm(
|
WarningCard(
|
||||||
|
message = stringResource(id = R.string.new_version_available).format(newVersionCode),
|
||||||
|
MaterialTheme.colorScheme.outlineVariant
|
||||||
|
) {
|
||||||
|
if (changelog.isNotEmpty()) {
|
||||||
|
updateDialog.showConfirm(
|
||||||
title = title,
|
title = title,
|
||||||
content = changelog,
|
content = changelog,
|
||||||
markdown = true,
|
markdown = true,
|
||||||
confirm = updateText,
|
confirm = updateText
|
||||||
) == ConfirmResult.Confirmed
|
)
|
||||||
) {
|
|
||||||
uriHandler.openUri(newVersionUrl)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,8 +151,21 @@ fun RebootDropdownItem(@StringRes id: Int, reason: String = "") {
|
|||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun TopBar(onSettingsClick: () -> Unit) {
|
private fun TopBar(
|
||||||
|
kernelVersion: KernelVersion,
|
||||||
|
onInstallClick: () -> Unit,
|
||||||
|
onSettingsClick: () -> Unit
|
||||||
|
) {
|
||||||
TopAppBar(title = { Text(stringResource(R.string.app_name)) }, actions = {
|
TopAppBar(title = { Text(stringResource(R.string.app_name)) }, actions = {
|
||||||
|
if (kernelVersion.isGKI()) {
|
||||||
|
IconButton(onClick = onInstallClick) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.Archive,
|
||||||
|
contentDescription = stringResource(id = R.string.install)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var showDropdown by remember { mutableStateOf(false) }
|
var showDropdown by remember { mutableStateOf(false) }
|
||||||
IconButton(onClick = {
|
IconButton(onClick = {
|
||||||
showDropdown = true
|
showDropdown = true
|
||||||
@@ -176,33 +203,46 @@ private fun TopBar(onSettingsClick: () -> Unit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
|
private fun StatusCard(
|
||||||
|
kernelVersion: KernelVersion,
|
||||||
|
ksuVersion: Int?,
|
||||||
|
lkmMode: Boolean?,
|
||||||
|
onClickInstall: () -> Unit = {}
|
||||||
|
) {
|
||||||
ElevatedCard(
|
ElevatedCard(
|
||||||
colors = CardDefaults.elevatedCardColors(containerColor = run {
|
colors = CardDefaults.elevatedCardColors(containerColor = run {
|
||||||
if (ksuVersion != null) MaterialTheme.colorScheme.secondaryContainer
|
if (ksuVersion != null) MaterialTheme.colorScheme.secondaryContainer
|
||||||
else MaterialTheme.colorScheme.errorContainer
|
else MaterialTheme.colorScheme.errorContainer
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
val uriHandler = LocalUriHandler.current
|
|
||||||
Row(modifier = Modifier
|
Row(modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable {
|
.clickable {
|
||||||
if (kernelVersion.isGKI() && ksuVersion == null) {
|
if (kernelVersion.isGKI()) {
|
||||||
uriHandler.openUri("https://kernelsu.org/guide/installation.html")
|
onClickInstall()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
|
.padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||||
when {
|
when {
|
||||||
ksuVersion != null -> {
|
ksuVersion != null -> {
|
||||||
val appendText = if (Natives.isSafeMode) {
|
val safeMode = when {
|
||||||
" [${stringResource(id = R.string.safe_mode)}]"
|
Natives.isSafeMode -> " [${stringResource(id = R.string.safe_mode)}]"
|
||||||
} else {
|
else -> ""
|
||||||
""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val workingMode = when (lkmMode) {
|
||||||
|
null -> ""
|
||||||
|
true -> " <LKM>"
|
||||||
|
else -> " <GKI>"
|
||||||
|
}
|
||||||
|
|
||||||
|
val workingText =
|
||||||
|
"${stringResource(id = R.string.home_working)}$workingMode$safeMode"
|
||||||
|
|
||||||
Icon(Icons.Outlined.CheckCircle, stringResource(R.string.home_working))
|
Icon(Icons.Outlined.CheckCircle, stringResource(R.string.home_working))
|
||||||
Column(Modifier.padding(start = 20.dp)) {
|
Column(Modifier.padding(start = 20.dp)) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.home_working) + appendText,
|
text = workingText,
|
||||||
style = MaterialTheme.typography.titleMedium
|
style = MaterialTheme.typography.titleMedium
|
||||||
)
|
)
|
||||||
Spacer(Modifier.height(4.dp))
|
Spacer(Modifier.height(4.dp))
|
||||||
@@ -293,7 +333,7 @@ fun LearnMoreCard() {
|
|||||||
uriHandler.openUri(url)
|
uriHandler.openUri(url)
|
||||||
}
|
}
|
||||||
.padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
|
.padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||||
Column() {
|
Column {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.home_learn_kernelsu),
|
text = stringResource(R.string.home_learn_kernelsu),
|
||||||
style = MaterialTheme.typography.titleSmall
|
style = MaterialTheme.typography.titleSmall
|
||||||
@@ -320,7 +360,7 @@ fun DonateCard() {
|
|||||||
uriHandler.openUri("https://patreon.com/weishu")
|
uriHandler.openUri("https://patreon.com/weishu")
|
||||||
}
|
}
|
||||||
.padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
|
.padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||||
Column() {
|
Column {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.home_support_title),
|
text = stringResource(R.string.home_support_title),
|
||||||
style = MaterialTheme.typography.titleSmall
|
style = MaterialTheme.typography.titleSmall
|
||||||
@@ -382,9 +422,10 @@ fun getManagerVersion(context: Context): Pair<String, Int> {
|
|||||||
@Composable
|
@Composable
|
||||||
private fun StatusCardPreview() {
|
private fun StatusCardPreview() {
|
||||||
Column {
|
Column {
|
||||||
StatusCard(KernelVersion(5, 10, 101), 1)
|
StatusCard(KernelVersion(5, 10, 101), 1, null)
|
||||||
StatusCard(KernelVersion(5, 10, 101), null)
|
StatusCard(KernelVersion(5, 10, 101), 20000, true)
|
||||||
StatusCard(KernelVersion(4, 10, 101), null)
|
StatusCard(KernelVersion(5, 10, 101), null, true)
|
||||||
|
StatusCard(KernelVersion(4, 10, 101), null, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,160 +1,311 @@
|
|||||||
package me.weishu.kernelsu.ui.screen
|
package me.weishu.kernelsu.ui.screen
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Environment
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
|
||||||
import androidx.compose.foundation.verticalScroll
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.Refresh
|
import androidx.compose.material.icons.filled.FileUpload
|
||||||
import androidx.compose.material.icons.filled.Save
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.RadioButton
|
||||||
|
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.produceState
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
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.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.maxkeppeker.sheets.core.models.base.Header
|
||||||
|
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
|
||||||
|
import com.maxkeppeler.sheets.list.ListDialog
|
||||||
|
import com.maxkeppeler.sheets.list.models.ListOption
|
||||||
|
import com.maxkeppeler.sheets.list.models.ListSelection
|
||||||
import com.ramcosta.composedestinations.annotation.Destination
|
import com.ramcosta.composedestinations.annotation.Destination
|
||||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||||
import kotlinx.coroutines.Dispatchers
|
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import me.weishu.kernelsu.R
|
import me.weishu.kernelsu.R
|
||||||
import me.weishu.kernelsu.ui.component.KeyEventBlocker
|
import me.weishu.kernelsu.ui.component.DialogHandle
|
||||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
import me.weishu.kernelsu.ui.component.rememberConfirmDialog
|
||||||
import me.weishu.kernelsu.ui.util.installModule
|
import me.weishu.kernelsu.ui.component.rememberCustomDialog
|
||||||
import me.weishu.kernelsu.ui.util.reboot
|
import me.weishu.kernelsu.ui.screen.destinations.FlashScreenDestination
|
||||||
import java.io.File
|
import me.weishu.kernelsu.ui.util.LkmSelection
|
||||||
import java.text.SimpleDateFormat
|
import me.weishu.kernelsu.ui.util.getCurrentKmi
|
||||||
import java.util.*
|
import me.weishu.kernelsu.ui.util.getSupportedKmis
|
||||||
|
import me.weishu.kernelsu.ui.util.isAbDevice
|
||||||
|
import me.weishu.kernelsu.ui.util.isInitBoot
|
||||||
|
import me.weishu.kernelsu.ui.util.rootAvailable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author weishu
|
* @author weishu
|
||||||
* @date 2023/1/1.
|
* @date 2024/3/12.
|
||||||
*/
|
*/
|
||||||
@OptIn(ExperimentalComposeUiApi::class)
|
|
||||||
@Composable
|
|
||||||
@Destination
|
@Destination
|
||||||
fun InstallScreen(navigator: DestinationsNavigator, uri: Uri) {
|
@Composable
|
||||||
|
fun InstallScreen(navigator: DestinationsNavigator) {
|
||||||
|
var installMethod by remember {
|
||||||
|
mutableStateOf<InstallMethod?>(null)
|
||||||
|
}
|
||||||
|
|
||||||
var text by rememberSaveable { mutableStateOf("") }
|
var lkmSelection by remember {
|
||||||
val logContent = StringBuilder()
|
mutableStateOf<LkmSelection>(LkmSelection.KmiNone)
|
||||||
var showFloatAction by rememberSaveable { mutableStateOf(false) }
|
}
|
||||||
|
|
||||||
val snackBarHost = LocalSnackbarHost.current
|
val onInstall = {
|
||||||
val scope = rememberCoroutineScope()
|
installMethod?.let { method ->
|
||||||
val scrollState = rememberScrollState()
|
val flashIt = FlashIt.FlashBoot(
|
||||||
|
boot = if (method is InstallMethod.SelectFile) method.uri else null,
|
||||||
LaunchedEffect(Unit) {
|
lkm = lkmSelection,
|
||||||
if (text.isNotEmpty()) {
|
ota = method is InstallMethod.DirectInstallToInactiveSlot
|
||||||
return@LaunchedEffect
|
)
|
||||||
}
|
navigator.navigate(FlashScreenDestination(flashIt))
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
installModule(uri, onFinish = { success ->
|
|
||||||
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")
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
val currentKmi by produceState(initialValue = "") { value = getCurrentKmi() }
|
||||||
topBar = {
|
|
||||||
TopBar(
|
val selectKmiDialog = rememberSelectKmiDialog { kmi ->
|
||||||
onBack = {
|
kmi?.let {
|
||||||
navigator.popBackStack()
|
lkmSelection = LkmSelection.KmiString(it)
|
||||||
},
|
onInstall()
|
||||||
onSave = {
|
}
|
||||||
scope.launch {
|
}
|
||||||
val format = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault())
|
|
||||||
val date = format.format(Date())
|
val onClickNext = {
|
||||||
val file = File(
|
if (lkmSelection == LkmSelection.KmiNone && currentKmi.isBlank()) {
|
||||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
// no lkm file selected and cannot get current kmi
|
||||||
"KernelSU_install_log_${date}.log"
|
selectKmiDialog.show()
|
||||||
)
|
} else {
|
||||||
file.writeText(logContent.toString())
|
onInstall()
|
||||||
snackBarHost.showSnackbar("Log saved to ${file.absolutePath}")
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val selectLkmLauncher =
|
||||||
|
rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) {
|
||||||
|
if (it.resultCode == Activity.RESULT_OK) {
|
||||||
|
it.data?.data?.let { uri ->
|
||||||
|
lkmSelection = LkmSelection.LkmUri(uri)
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
},
|
}
|
||||||
floatingActionButton = {
|
|
||||||
if (showFloatAction) {
|
val onLkmUpload = {
|
||||||
val reboot = stringResource(id = R.string.reboot)
|
selectLkmLauncher.launch(Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||||
ExtendedFloatingActionButton(
|
type = "application/octet-stream"
|
||||||
onClick = {
|
})
|
||||||
scope.launch {
|
}
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
reboot()
|
Scaffold(topBar = {
|
||||||
}
|
TopBar(
|
||||||
}
|
onBack = { navigator.popBackStack() }, onLkmUpload = onLkmUpload
|
||||||
},
|
)
|
||||||
icon = { Icon(Icons.Filled.Refresh, reboot) },
|
}) {
|
||||||
text = { Text(text = reboot) },
|
Column(modifier = Modifier.padding(it)) {
|
||||||
)
|
SelectInstallMethod { method ->
|
||||||
|
installMethod = method
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
(lkmSelection as? LkmSelection.LkmUri)?.let {
|
||||||
|
Text(
|
||||||
|
stringResource(
|
||||||
|
id = R.string.selected_lkm,
|
||||||
|
it.uri.lastPathSegment ?: "(file)"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Button(modifier = Modifier.fillMaxWidth(),
|
||||||
|
enabled = installMethod != null,
|
||||||
|
onClick = {
|
||||||
|
onClickNext()
|
||||||
|
}) {
|
||||||
|
Text(
|
||||||
|
stringResource(id = R.string.install_next),
|
||||||
|
fontSize = MaterialTheme.typography.bodyMedium.fontSize
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
) { innerPadding ->
|
}
|
||||||
KeyEventBlocker {
|
}
|
||||||
it.key == Key.VolumeDown || it.key == Key.VolumeUp
|
|
||||||
|
sealed class InstallMethod {
|
||||||
|
data class SelectFile(
|
||||||
|
val uri: Uri? = null,
|
||||||
|
@StringRes override val label: Int = R.string.select_file,
|
||||||
|
override val summary: String?
|
||||||
|
) : InstallMethod()
|
||||||
|
|
||||||
|
data object DirectInstall : InstallMethod() {
|
||||||
|
override val label: Int
|
||||||
|
get() = R.string.direct_install
|
||||||
|
}
|
||||||
|
|
||||||
|
data object DirectInstallToInactiveSlot : InstallMethod() {
|
||||||
|
override val label: Int
|
||||||
|
get() = R.string.install_inactive_slot
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract val label: Int
|
||||||
|
open val summary: String? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SelectInstallMethod(onSelected: (InstallMethod) -> Unit = {}) {
|
||||||
|
val rootAvailable = rootAvailable()
|
||||||
|
val isAbDevice = isAbDevice()
|
||||||
|
val selectFileTip = stringResource(
|
||||||
|
id = R.string.select_file_tip, if (isInitBoot()) "init_boot" else "boot"
|
||||||
|
)
|
||||||
|
val radioOptions =
|
||||||
|
mutableListOf<InstallMethod>(InstallMethod.SelectFile(summary = selectFileTip))
|
||||||
|
if (rootAvailable) {
|
||||||
|
radioOptions.add(InstallMethod.DirectInstall)
|
||||||
|
|
||||||
|
if (isAbDevice) {
|
||||||
|
radioOptions.add(InstallMethod.DirectInstallToInactiveSlot)
|
||||||
}
|
}
|
||||||
Column(
|
}
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize(1f)
|
var selectedOption by remember { mutableStateOf<InstallMethod?>(null) }
|
||||||
.padding(innerPadding)
|
val selectImageLauncher = rememberLauncherForActivityResult(
|
||||||
.verticalScroll(scrollState),
|
contract = ActivityResultContracts.StartActivityForResult()
|
||||||
) {
|
) {
|
||||||
Text(
|
if (it.resultCode == Activity.RESULT_OK) {
|
||||||
modifier = Modifier.padding(8.dp),
|
it.data?.data?.let { uri ->
|
||||||
text = text,
|
val option = InstallMethod.SelectFile(uri, summary = selectFileTip)
|
||||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
selectedOption = option
|
||||||
fontFamily = FontFamily.Monospace,
|
onSelected(option)
|
||||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
}
|
||||||
)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val confirmDialog = rememberConfirmDialog(onConfirm = {
|
||||||
|
selectedOption = InstallMethod.DirectInstallToInactiveSlot
|
||||||
|
onSelected(InstallMethod.DirectInstallToInactiveSlot)
|
||||||
|
}, onDismiss = null)
|
||||||
|
val dialogTitle = stringResource(id = android.R.string.dialog_alert_title)
|
||||||
|
val dialogContent = stringResource(id = R.string.install_inactive_slot_warning)
|
||||||
|
|
||||||
|
val onClick = { option: InstallMethod ->
|
||||||
|
|
||||||
|
when (option) {
|
||||||
|
is InstallMethod.SelectFile -> {
|
||||||
|
selectImageLauncher.launch(Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||||
|
type = "application/octet-stream"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
is InstallMethod.DirectInstall -> {
|
||||||
|
selectedOption = option
|
||||||
|
onSelected(option)
|
||||||
|
}
|
||||||
|
|
||||||
|
is InstallMethod.DirectInstallToInactiveSlot -> {
|
||||||
|
confirmDialog.showConfirm(dialogTitle, dialogContent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
radioOptions.forEach { option ->
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable {
|
||||||
|
onClick(option)
|
||||||
|
}) {
|
||||||
|
RadioButton(selected = option.javaClass == selectedOption?.javaClass, onClick = {
|
||||||
|
onClick(option)
|
||||||
|
})
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = option.label),
|
||||||
|
fontSize = MaterialTheme.typography.titleMedium.fontSize,
|
||||||
|
fontFamily = MaterialTheme.typography.titleMedium.fontFamily,
|
||||||
|
fontStyle = MaterialTheme.typography.titleMedium.fontStyle
|
||||||
|
)
|
||||||
|
option.summary?.let {
|
||||||
|
Text(
|
||||||
|
text = it,
|
||||||
|
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||||
|
fontFamily = MaterialTheme.typography.bodySmall.fontFamily,
|
||||||
|
fontStyle = MaterialTheme.typography.bodySmall.fontStyle
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun TopBar(onBack: () -> Unit = {}, onSave: () -> Unit = {}) {
|
fun rememberSelectKmiDialog(onSelected: (String?) -> Unit): DialogHandle {
|
||||||
TopAppBar(
|
return rememberCustomDialog { dismiss ->
|
||||||
title = { Text(stringResource(R.string.install)) },
|
val supportedKmi by produceState(initialValue = emptyList<String>()) {
|
||||||
navigationIcon = {
|
value = getSupportedKmis()
|
||||||
IconButton(
|
|
||||||
onClick = onBack
|
|
||||||
) { Icon(Icons.Filled.ArrowBack, contentDescription = null) }
|
|
||||||
},
|
|
||||||
actions = {
|
|
||||||
IconButton(onClick = onSave) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Filled.Save,
|
|
||||||
contentDescription = "Localized description"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
val options = supportedKmi.map { value ->
|
||||||
|
ListOption(
|
||||||
|
titleText = value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var selection by remember { mutableStateOf<String?>(null) }
|
||||||
|
ListDialog(state = rememberUseCaseState(visible = true, onFinishedRequest = {
|
||||||
|
onSelected(selection)
|
||||||
|
}, onCloseRequest = {
|
||||||
|
dismiss()
|
||||||
|
}), header = Header.Default(
|
||||||
|
title = stringResource(R.string.select_kmi),
|
||||||
|
), selection = ListSelection.Single(
|
||||||
|
showRadioButtons = true,
|
||||||
|
options = options,
|
||||||
|
) { _, option ->
|
||||||
|
selection = option.titleText
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun InstallPreview() {
|
private fun TopBar(onBack: () -> Unit = {}, onLkmUpload: () -> Unit = {}) {
|
||||||
// InstallScreen(DestinationsNavigator(), uri = Uri.EMPTY)
|
TopAppBar(title = { Text(stringResource(R.string.install)) }, navigationIcon = {
|
||||||
|
IconButton(
|
||||||
|
onClick = onBack
|
||||||
|
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||||
|
}, actions = {
|
||||||
|
IconButton(onClick = onLkmUpload) {
|
||||||
|
Icon(Icons.Filled.FileUpload, contentDescription = null)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Preview
|
||||||
|
fun SelectInstall_Preview() {
|
||||||
|
InstallScreen(EmptyDestinationsNavigator)
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,17 @@ import android.widget.Toast
|
|||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.defaultMinSize
|
||||||
|
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.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
@@ -18,9 +28,29 @@ import androidx.compose.material.icons.filled.Add
|
|||||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||||
import androidx.compose.material.pullrefresh.pullRefresh
|
import androidx.compose.material.pullrefresh.pullRefresh
|
||||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.material3.CardDefaults
|
||||||
|
import androidx.compose.material3.ElevatedCard
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.SnackbarResult
|
||||||
|
import androidx.compose.material3.Switch
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.produceState
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
@@ -40,19 +70,26 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import me.weishu.kernelsu.Natives
|
import me.weishu.kernelsu.Natives
|
||||||
import me.weishu.kernelsu.R
|
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.ConfirmResult
|
||||||
import me.weishu.kernelsu.ui.component.LoadingDialog
|
import me.weishu.kernelsu.ui.component.rememberConfirmDialog
|
||||||
import me.weishu.kernelsu.ui.screen.destinations.InstallScreenDestination
|
import me.weishu.kernelsu.ui.component.rememberLoadingDialog
|
||||||
import me.weishu.kernelsu.ui.screen.destinations.WebScreenDestination
|
import me.weishu.kernelsu.ui.screen.destinations.FlashScreenDestination
|
||||||
import me.weishu.kernelsu.ui.util.*
|
import me.weishu.kernelsu.ui.util.DownloadListener
|
||||||
|
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||||
|
import me.weishu.kernelsu.ui.util.download
|
||||||
|
import me.weishu.kernelsu.ui.util.hasMagisk
|
||||||
|
import me.weishu.kernelsu.ui.util.reboot
|
||||||
|
import me.weishu.kernelsu.ui.util.toggleModule
|
||||||
|
import me.weishu.kernelsu.ui.util.uninstallModule
|
||||||
import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel
|
import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel
|
||||||
|
import me.weishu.kernelsu.ui.webui.WebUIActivity
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
|
|
||||||
@Destination
|
@Destination
|
||||||
@Composable
|
@Composable
|
||||||
fun ModuleScreen(navigator: DestinationsNavigator) {
|
fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||||
val viewModel = viewModel<ModuleViewModel>()
|
val viewModel = viewModel<ModuleViewModel>()
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
if (viewModel.moduleList.isEmpty() || viewModel.isNeedRefresh) {
|
if (viewModel.moduleList.isEmpty() || viewModel.isNeedRefresh) {
|
||||||
@@ -81,7 +118,7 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
val data = it.data ?: return@rememberLauncherForActivityResult
|
val data = it.data ?: return@rememberLauncherForActivityResult
|
||||||
val uri = data.data ?: return@rememberLauncherForActivityResult
|
val uri = data.data ?: return@rememberLauncherForActivityResult
|
||||||
|
|
||||||
navigator.navigate(InstallScreenDestination(uri))
|
navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(uri)))
|
||||||
|
|
||||||
viewModel.markNeedRefresh()
|
viewModel.markNeedRefresh()
|
||||||
|
|
||||||
@@ -101,10 +138,6 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
}
|
}
|
||||||
}) { innerPadding ->
|
}) { innerPadding ->
|
||||||
|
|
||||||
ConfirmDialog()
|
|
||||||
|
|
||||||
LoadingDialog()
|
|
||||||
|
|
||||||
when {
|
when {
|
||||||
hasMagisk -> {
|
hasMagisk -> {
|
||||||
Box(
|
Box(
|
||||||
@@ -127,10 +160,14 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
onInstallModule =
|
onInstallModule =
|
||||||
{
|
{
|
||||||
navigator.navigate(InstallScreenDestination(it))
|
navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(it)))
|
||||||
}, onClickModule = { id, name, hasWebUi ->
|
}, onClickModule = { id, name, hasWebUi ->
|
||||||
if (hasWebUi) {
|
if (hasWebUi) {
|
||||||
navigator.navigate(WebScreenDestination(id, name))
|
context.startActivity(Intent(context, WebUIActivity::class.java)
|
||||||
|
.setData(Uri.parse("kernelsu://webui/$id"))
|
||||||
|
.putExtra("id", id)
|
||||||
|
.putExtra("name", name)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -162,17 +199,19 @@ private fun ModuleList(
|
|||||||
val startDownloadingText = stringResource(R.string.module_start_downloading)
|
val startDownloadingText = stringResource(R.string.module_start_downloading)
|
||||||
val fetchChangeLogFailed = stringResource(R.string.module_changelog_failed)
|
val fetchChangeLogFailed = stringResource(R.string.module_changelog_failed)
|
||||||
|
|
||||||
val dialogHost = LocalDialogHost.current
|
|
||||||
val snackBarHost = LocalSnackbarHost.current
|
val snackBarHost = LocalSnackbarHost.current
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
val loadingDialog = rememberLoadingDialog()
|
||||||
|
val confirmDialog = rememberConfirmDialog()
|
||||||
|
|
||||||
suspend fun onModuleUpdate(
|
suspend fun onModuleUpdate(
|
||||||
module: ModuleViewModel.ModuleInfo,
|
module: ModuleViewModel.ModuleInfo,
|
||||||
changelogUrl: String,
|
changelogUrl: String,
|
||||||
downloadUrl: String,
|
downloadUrl: String,
|
||||||
fileName: String
|
fileName: String
|
||||||
) {
|
) {
|
||||||
val changelogResult = dialogHost.withLoading {
|
val changelogResult = loadingDialog.withLoading {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
runCatching {
|
runCatching {
|
||||||
OkHttpClient().newCall(
|
OkHttpClient().newCall(
|
||||||
@@ -201,7 +240,7 @@ private fun ModuleList(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// changelog is not empty, show it and wait for confirm
|
// changelog is not empty, show it and wait for confirm
|
||||||
val confirmResult = dialogHost.showConfirm(
|
val confirmResult = confirmDialog.awaitConfirm(
|
||||||
changelogText,
|
changelogText,
|
||||||
content = changelog,
|
content = changelog,
|
||||||
markdown = true,
|
markdown = true,
|
||||||
@@ -232,7 +271,7 @@ private fun ModuleList(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun onModuleUninstall(module: ModuleViewModel.ModuleInfo) {
|
suspend fun onModuleUninstall(module: ModuleViewModel.ModuleInfo) {
|
||||||
val confirmResult = dialogHost.showConfirm(
|
val confirmResult = confirmDialog.awaitConfirm(
|
||||||
moduleStr,
|
moduleStr,
|
||||||
content = moduleUninstallConfirm.format(module.name),
|
content = moduleUninstallConfirm.format(module.name),
|
||||||
confirm = uninstall,
|
confirm = uninstall,
|
||||||
@@ -242,7 +281,7 @@ private fun ModuleList(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val success = dialogHost.withLoading {
|
val success = loadingDialog.withLoading {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
uninstallModule(module.id)
|
uninstallModule(module.id)
|
||||||
}
|
}
|
||||||
@@ -327,7 +366,7 @@ private fun ModuleList(
|
|||||||
scope.launch { onModuleUninstall(module) }
|
scope.launch { onModuleUninstall(module) }
|
||||||
}, onCheckChanged = {
|
}, onCheckChanged = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val success = dialogHost.withLoading {
|
val success = loadingDialog.withLoading {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
toggleModule(module.id, !isChecked)
|
toggleModule(module.id, !isChecked)
|
||||||
}
|
}
|
||||||
@@ -394,15 +433,13 @@ private fun ModuleItem(
|
|||||||
onClick: (ModuleViewModel.ModuleInfo) -> Unit
|
onClick: (ModuleViewModel.ModuleInfo) -> Unit
|
||||||
) {
|
) {
|
||||||
ElevatedCard(
|
ElevatedCard(
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxWidth(),
|
||||||
.fillMaxWidth()
|
|
||||||
.clickable { onClick(module) },
|
|
||||||
colors = CardDefaults.elevatedCardColors(containerColor = MaterialTheme.colorScheme.surface)
|
colors = CardDefaults.elevatedCardColors(containerColor = MaterialTheme.colorScheme.surface)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val textDecoration = if (!module.remove) null else TextDecoration.LineThrough
|
val textDecoration = if (!module.remove) null else TextDecoration.LineThrough
|
||||||
|
|
||||||
Column(modifier = Modifier.padding(24.dp, 16.dp, 24.dp, 0.dp)) {
|
Column(modifier = Modifier.clickable { onClick(module) }.padding(24.dp, 16.dp, 24.dp, 0.dp)) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
@@ -467,7 +504,7 @@ private fun ModuleItem(
|
|||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
Divider(thickness = Dp.Hairline)
|
HorizontalDivider(thickness = Dp.Hairline)
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
|||||||
@@ -1,27 +1,69 @@
|
|||||||
package me.weishu.kernelsu.ui.screen
|
package me.weishu.kernelsu.ui.screen
|
||||||
|
|
||||||
|
import android.content.ContentResolver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.database.Cursor
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.provider.OpenableColumns
|
||||||
|
import android.util.Log
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.Undo
|
||||||
import androidx.compose.material.icons.filled.BugReport
|
import androidx.compose.material.icons.filled.BugReport
|
||||||
|
import androidx.compose.material.icons.filled.Compress
|
||||||
import androidx.compose.material.icons.filled.ContactPage
|
import androidx.compose.material.icons.filled.ContactPage
|
||||||
|
import androidx.compose.material.icons.filled.Delete
|
||||||
|
import androidx.compose.material.icons.filled.DeleteForever
|
||||||
|
import androidx.compose.material.icons.filled.DeveloperMode
|
||||||
import androidx.compose.material.icons.filled.Fence
|
import androidx.compose.material.icons.filled.Fence
|
||||||
import androidx.compose.material.icons.filled.RemoveModerator
|
import androidx.compose.material.icons.filled.RemoveModerator
|
||||||
|
import androidx.compose.material.icons.filled.Save
|
||||||
|
import androidx.compose.material.icons.filled.Share
|
||||||
import androidx.compose.material.icons.filled.Update
|
import androidx.compose.material.icons.filled.Update
|
||||||
import androidx.compose.material.icons.filled.Upgrade
|
import androidx.compose.material3.BottomSheetScaffold
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.ListItem
|
||||||
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
|
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.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.LineHeightStyle
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
|
import com.maxkeppeker.sheets.core.models.base.Header
|
||||||
|
import com.maxkeppeker.sheets.core.models.base.IconSource
|
||||||
|
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
|
||||||
|
import com.maxkeppeler.sheets.list.ListDialog
|
||||||
|
import com.maxkeppeler.sheets.list.models.ListOption
|
||||||
|
import com.maxkeppeler.sheets.list.models.ListSelection
|
||||||
import com.ramcosta.composedestinations.annotation.Destination
|
import com.ramcosta.composedestinations.annotation.Destination
|
||||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||||
|
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -29,20 +71,28 @@ import me.weishu.kernelsu.BuildConfig
|
|||||||
import me.weishu.kernelsu.Natives
|
import me.weishu.kernelsu.Natives
|
||||||
import me.weishu.kernelsu.R
|
import me.weishu.kernelsu.R
|
||||||
import me.weishu.kernelsu.ui.component.AboutDialog
|
import me.weishu.kernelsu.ui.component.AboutDialog
|
||||||
import me.weishu.kernelsu.ui.component.LoadingDialog
|
import me.weishu.kernelsu.ui.component.ConfirmResult
|
||||||
|
import me.weishu.kernelsu.ui.component.DialogHandle
|
||||||
import me.weishu.kernelsu.ui.component.SwitchItem
|
import me.weishu.kernelsu.ui.component.SwitchItem
|
||||||
|
import me.weishu.kernelsu.ui.component.rememberConfirmDialog
|
||||||
|
import me.weishu.kernelsu.ui.component.rememberCustomDialog
|
||||||
|
import me.weishu.kernelsu.ui.component.rememberLoadingDialog
|
||||||
import me.weishu.kernelsu.ui.screen.destinations.AppProfileTemplateScreenDestination
|
import me.weishu.kernelsu.ui.screen.destinations.AppProfileTemplateScreenDestination
|
||||||
import me.weishu.kernelsu.ui.util.LocalDialogHost
|
import me.weishu.kernelsu.ui.screen.destinations.FlashScreenDestination
|
||||||
import me.weishu.kernelsu.ui.util.getBugreportFile
|
import me.weishu.kernelsu.ui.util.getBugreportFile
|
||||||
|
import me.weishu.kernelsu.ui.util.getFileNameFromUri
|
||||||
|
import me.weishu.kernelsu.ui.util.shrinkModules
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author weishu
|
* @author weishu
|
||||||
* @date 2023/1/1.
|
* @date 2023/1/1.
|
||||||
*/
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Destination
|
@Destination
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingScreen(navigator: DestinationsNavigator) {
|
fun SettingScreen(navigator: DestinationsNavigator) {
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopBar(onBack = {
|
TopBar(onBack = {
|
||||||
@@ -50,16 +100,20 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
LoadingDialog()
|
val aboutDialog = rememberCustomDialog {
|
||||||
|
AboutDialog(it)
|
||||||
|
}
|
||||||
|
val loadingDialog = rememberLoadingDialog()
|
||||||
|
val shrinkDialog = rememberConfirmDialog()
|
||||||
|
|
||||||
val showAboutDialog = remember { mutableStateOf(false) }
|
Column(
|
||||||
AboutDialog(showAboutDialog)
|
modifier = Modifier
|
||||||
|
.padding(paddingValues)
|
||||||
Column(modifier = Modifier.padding(paddingValues)) {
|
.verticalScroll(rememberScrollState())
|
||||||
|
) {
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val dialogHost = LocalDialogHost.current
|
|
||||||
|
|
||||||
val profileTemplate = stringResource(id = R.string.settings_profile_template)
|
val profileTemplate = stringResource(id = R.string.settings_profile_template)
|
||||||
ListItem(
|
ListItem(
|
||||||
@@ -101,6 +155,22 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
checkUpdate = it
|
checkUpdate = it
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var enableWebDebugging by rememberSaveable {
|
||||||
|
mutableStateOf(
|
||||||
|
prefs.getBoolean("enable_web_debugging", false)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
SwitchItem(
|
||||||
|
icon = Icons.Filled.DeveloperMode,
|
||||||
|
title = stringResource(id = R.string.enable_web_debugging),
|
||||||
|
summary = stringResource(id = R.string.enable_web_debugging_summary),
|
||||||
|
checked = enableWebDebugging
|
||||||
|
) {
|
||||||
|
prefs.edit().putBoolean("enable_web_debugging", it).apply()
|
||||||
|
enableWebDebugging = it
|
||||||
|
}
|
||||||
|
|
||||||
|
var showBottomsheet by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
ListItem(
|
ListItem(
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
@@ -111,35 +181,159 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
},
|
},
|
||||||
headlineContent = { Text(stringResource(id = R.string.send_log)) },
|
headlineContent = { Text(stringResource(id = R.string.send_log)) },
|
||||||
modifier = Modifier.clickable {
|
modifier = Modifier.clickable {
|
||||||
scope.launch {
|
showBottomsheet = true
|
||||||
val bugreport = dialogHost.withLoading {
|
}
|
||||||
withContext(Dispatchers.IO) {
|
)
|
||||||
getBugreportFile(context)
|
if (showBottomsheet){
|
||||||
|
ModalBottomSheet(
|
||||||
|
onDismissRequest = { showBottomsheet = false },
|
||||||
|
content = {
|
||||||
|
Row(modifier = Modifier.padding(10.dp)
|
||||||
|
.align(Alignment.CenterHorizontally)
|
||||||
|
|
||||||
|
) {
|
||||||
|
Box{
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(16.dp)
|
||||||
|
.clickable {
|
||||||
|
scope.launch {
|
||||||
|
val bugreport = loadingDialog.withLoading {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
getBugreportFile(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val uri: Uri =
|
||||||
|
FileProvider.getUriForFile(
|
||||||
|
context,
|
||||||
|
"${BuildConfig.APPLICATION_ID}.fileprovider",
|
||||||
|
bugreport
|
||||||
|
)
|
||||||
|
val filename = getFileNameFromUri(context , uri)
|
||||||
|
val savefile = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||||
|
addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
type = "application/zip"
|
||||||
|
putExtra(Intent.EXTRA_STREAM, uri)
|
||||||
|
putExtra(Intent.EXTRA_TITLE, filename)
|
||||||
|
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
|
}
|
||||||
|
context.startActivity(
|
||||||
|
Intent.createChooser(
|
||||||
|
savefile,
|
||||||
|
context.getString(R.string.save_log)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.Filled.Save,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.save_log),
|
||||||
|
modifier = Modifier.padding(top = 16.dp),
|
||||||
|
textAlign = TextAlign.Center.also {
|
||||||
|
LineHeightStyle(
|
||||||
|
alignment = LineHeightStyle.Alignment.Center,
|
||||||
|
trim = LineHeightStyle.Trim.None
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Box{
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(16.dp)
|
||||||
|
.clickable {
|
||||||
|
scope.launch {
|
||||||
|
val bugreport = loadingDialog.withLoading {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
getBugreportFile(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val uri: Uri =
|
||||||
|
FileProvider.getUriForFile(
|
||||||
|
context,
|
||||||
|
"${BuildConfig.APPLICATION_ID}.fileprovider",
|
||||||
|
bugreport
|
||||||
|
)
|
||||||
|
|
||||||
|
val shareIntent = Intent(Intent.ACTION_SEND)
|
||||||
|
shareIntent.putExtra(Intent.EXTRA_STREAM, uri)
|
||||||
|
shareIntent.setDataAndType(uri, "application/zip")
|
||||||
|
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
|
||||||
|
context.startActivity(
|
||||||
|
Intent.createChooser(
|
||||||
|
shareIntent,
|
||||||
|
context.getString(R.string.send_log)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.Filled.Share,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.send_log),
|
||||||
|
modifier = Modifier.padding(top = 16.dp),
|
||||||
|
textAlign = TextAlign.Center.also {
|
||||||
|
LineHeightStyle(
|
||||||
|
alignment = LineHeightStyle.Alignment.Center,
|
||||||
|
trim = LineHeightStyle.Trim.None
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
val uri: Uri =
|
|
||||||
FileProvider.getUriForFile(
|
|
||||||
context,
|
|
||||||
"${BuildConfig.APPLICATION_ID}.fileprovider",
|
|
||||||
bugreport
|
|
||||||
)
|
|
||||||
|
|
||||||
val shareIntent = Intent(Intent.ACTION_SEND)
|
}
|
||||||
shareIntent.putExtra(Intent.EXTRA_STREAM, uri)
|
|
||||||
shareIntent.setDataAndType(uri, "application/zip")
|
|
||||||
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
||||||
|
|
||||||
context.startActivity(
|
val shrink = stringResource(id = R.string.shrink_sparse_image)
|
||||||
Intent.createChooser(
|
val shrinkMessage = stringResource(id = R.string.shrink_sparse_image_message)
|
||||||
shareIntent,
|
ListItem(
|
||||||
context.getString(R.string.send_log)
|
leadingContent = {
|
||||||
)
|
Icon(
|
||||||
)
|
Icons.Filled.Compress,
|
||||||
|
shrink
|
||||||
|
)
|
||||||
|
},
|
||||||
|
headlineContent = { Text(shrink) },
|
||||||
|
modifier = Modifier.clickable {
|
||||||
|
scope.launch {
|
||||||
|
val result =
|
||||||
|
shrinkDialog.awaitConfirm(title = shrink, content = shrinkMessage)
|
||||||
|
if (result == ConfirmResult.Confirmed) {
|
||||||
|
loadingDialog.withLoading {
|
||||||
|
shrinkModules()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val lkmMode =
|
||||||
|
Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode
|
||||||
|
if (lkmMode) {
|
||||||
|
UninstallItem(navigator) {
|
||||||
|
loadingDialog.withLoading(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val about = stringResource(id = R.string.about)
|
val about = stringResource(id = R.string.about)
|
||||||
ListItem(
|
ListItem(
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
@@ -150,12 +344,115 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
},
|
},
|
||||||
headlineContent = { Text(about) },
|
headlineContent = { Text(about) },
|
||||||
modifier = Modifier.clickable {
|
modifier = Modifier.clickable {
|
||||||
showAboutDialog.value = true
|
aboutDialog.show()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@Composable
|
||||||
|
fun UninstallItem(
|
||||||
|
navigator: DestinationsNavigator,
|
||||||
|
withLoading: suspend (suspend () -> Unit) -> Unit
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val uninstallConfirmDialog = rememberConfirmDialog()
|
||||||
|
val showTodo = {
|
||||||
|
Toast.makeText(context, "TODO", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
val uninstallDialog = rememberUninstallDialog { uninstallType ->
|
||||||
|
scope.launch {
|
||||||
|
val result = uninstallConfirmDialog.awaitConfirm(
|
||||||
|
title = context.getString(uninstallType.title),
|
||||||
|
content = context.getString(uninstallType.message)
|
||||||
|
)
|
||||||
|
if (result == ConfirmResult.Confirmed) {
|
||||||
|
withLoading {
|
||||||
|
when (uninstallType) {
|
||||||
|
UninstallType.TEMPORARY -> showTodo()
|
||||||
|
UninstallType.PERMANENT -> navigator.navigate(
|
||||||
|
FlashScreenDestination(FlashIt.FlashUninstall)
|
||||||
|
)
|
||||||
|
|
||||||
|
UninstallType.RESTORE_STOCK_IMAGE -> navigator.navigate(
|
||||||
|
FlashScreenDestination(FlashIt.FlashRestore)
|
||||||
|
)
|
||||||
|
|
||||||
|
UninstallType.NONE -> Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val uninstall = stringResource(id = R.string.settings_uninstall)
|
||||||
|
ListItem(
|
||||||
|
leadingContent = {
|
||||||
|
Icon(
|
||||||
|
Icons.Filled.Delete,
|
||||||
|
uninstall
|
||||||
|
)
|
||||||
|
},
|
||||||
|
headlineContent = { Text(uninstall) },
|
||||||
|
modifier = Modifier.clickable {
|
||||||
|
uninstallDialog.show()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class UninstallType(val title: Int, val message: Int, val icon: ImageVector) {
|
||||||
|
TEMPORARY(
|
||||||
|
R.string.settings_uninstall_temporary,
|
||||||
|
R.string.settings_uninstall_temporary_message,
|
||||||
|
Icons.Filled.Delete
|
||||||
|
),
|
||||||
|
PERMANENT(
|
||||||
|
R.string.settings_uninstall_permanent,
|
||||||
|
R.string.settings_uninstall_permanent_message,
|
||||||
|
Icons.Filled.DeleteForever
|
||||||
|
),
|
||||||
|
RESTORE_STOCK_IMAGE(
|
||||||
|
R.string.settings_restore_stock_image,
|
||||||
|
R.string.settings_restore_stock_image_message,
|
||||||
|
Icons.AutoMirrored.Filled.Undo
|
||||||
|
),
|
||||||
|
NONE(0, 0, Icons.Filled.Delete)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle {
|
||||||
|
return rememberCustomDialog { dismiss ->
|
||||||
|
val options = listOf(
|
||||||
|
// UninstallType.TEMPORARY,
|
||||||
|
UninstallType.PERMANENT,
|
||||||
|
UninstallType.RESTORE_STOCK_IMAGE
|
||||||
|
)
|
||||||
|
val listOptions = options.map {
|
||||||
|
ListOption(
|
||||||
|
titleText = stringResource(it.title),
|
||||||
|
subtitleText = if (it.message != 0) stringResource(it.message) else null,
|
||||||
|
icon = IconSource(it.icon)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var selection = UninstallType.NONE
|
||||||
|
ListDialog(state = rememberUseCaseState(visible = true, onFinishedRequest = {
|
||||||
|
if (selection != UninstallType.NONE) {
|
||||||
|
onSelected(selection)
|
||||||
|
}
|
||||||
|
}, onCloseRequest = {
|
||||||
|
dismiss()
|
||||||
|
}), header = Header.Default(
|
||||||
|
title = stringResource(R.string.settings_uninstall),
|
||||||
|
), selection = ListSelection.Single(
|
||||||
|
showRadioButtons = false,
|
||||||
|
options = listOptions,
|
||||||
|
) { index, _ ->
|
||||||
|
selection = options[index]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -165,7 +462,13 @@ private fun TopBar(onBack: () -> Unit = {}) {
|
|||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = onBack
|
onClick = onBack
|
||||||
) { Icon(Icons.Filled.ArrowBack, contentDescription = null) }
|
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun SettingsPreview() {
|
||||||
|
SettingScreen(EmptyDestinationsNavigator)
|
||||||
|
}
|
||||||
@@ -30,7 +30,6 @@ import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import me.weishu.kernelsu.Natives
|
import me.weishu.kernelsu.Natives
|
||||||
import me.weishu.kernelsu.R
|
import me.weishu.kernelsu.R
|
||||||
import me.weishu.kernelsu.ui.component.ConfirmDialog
|
|
||||||
import me.weishu.kernelsu.ui.component.SearchAppBar
|
import me.weishu.kernelsu.ui.component.SearchAppBar
|
||||||
import me.weishu.kernelsu.ui.screen.destinations.AppProfileScreenDestination
|
import me.weishu.kernelsu.ui.screen.destinations.AppProfileScreenDestination
|
||||||
import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel
|
import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel
|
||||||
@@ -95,9 +94,6 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
|
|
||||||
ConfirmDialog()
|
|
||||||
|
|
||||||
val refreshState = rememberPullRefreshState(
|
val refreshState = rememberPullRefreshState(
|
||||||
refreshing = viewModel.isRefreshing,
|
refreshing = viewModel.isRefreshing,
|
||||||
onRefresh = { scope.launch { viewModel.fetchAppList() } },
|
onRefresh = { scope.launch { viewModel.fetchAppList() } },
|
||||||
|
|||||||
@@ -6,14 +6,15 @@ import androidx.compose.foundation.layout.Box
|
|||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||||
import androidx.compose.foundation.layout.FlowRow
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.ExperimentalMaterialApi
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
|
||||||
import androidx.compose.material.icons.filled.ImportExport
|
import androidx.compose.material.icons.filled.ImportExport
|
||||||
import androidx.compose.material.icons.filled.Sync
|
import androidx.compose.material.icons.filled.Sync
|
||||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||||
@@ -43,6 +44,7 @@ import androidx.compose.ui.platform.LocalClipboardManager
|
|||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.ramcosta.composedestinations.annotation.Destination
|
import com.ramcosta.composedestinations.annotation.Destination
|
||||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||||
@@ -149,7 +151,9 @@ fun AppProfileTemplateScreen(
|
|||||||
.padding(innerPadding)
|
.padding(innerPadding)
|
||||||
.pullRefresh(refreshState)
|
.pullRefresh(refreshState)
|
||||||
) {
|
) {
|
||||||
LazyColumn(Modifier.fillMaxSize()) {
|
LazyColumn(Modifier.fillMaxSize(), contentPadding = remember {
|
||||||
|
PaddingValues(bottom = 16.dp + 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */)
|
||||||
|
}) {
|
||||||
items(viewModel.templateList, key = { it.id }) { app ->
|
items(viewModel.templateList, key = { it.id }) { app ->
|
||||||
TemplateItem(navigator, app)
|
TemplateItem(navigator, app)
|
||||||
}
|
}
|
||||||
@@ -214,7 +218,7 @@ private fun TopBar(
|
|||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = onBack
|
onClick = onBack
|
||||||
) { Icon(Icons.Filled.ArrowBack, contentDescription = null) }
|
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
IconButton(onClick = onSync) {
|
IconButton(onClick = onSync) {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import androidx.compose.foundation.text.KeyboardActions
|
|||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.DeleteForever
|
import androidx.compose.material.icons.filled.DeleteForever
|
||||||
import androidx.compose.material.icons.filled.Save
|
import androidx.compose.material.icons.filled.Save
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
@@ -40,16 +40,12 @@ import com.ramcosta.composedestinations.annotation.Destination
|
|||||||
import com.ramcosta.composedestinations.result.ResultBackNavigator
|
import com.ramcosta.composedestinations.result.ResultBackNavigator
|
||||||
import me.weishu.kernelsu.Natives
|
import me.weishu.kernelsu.Natives
|
||||||
import me.weishu.kernelsu.R
|
import me.weishu.kernelsu.R
|
||||||
import me.weishu.kernelsu.profile.Capabilities
|
|
||||||
import me.weishu.kernelsu.profile.Groups
|
|
||||||
import me.weishu.kernelsu.ui.component.profile.RootProfileConfig
|
import me.weishu.kernelsu.ui.component.profile.RootProfileConfig
|
||||||
import me.weishu.kernelsu.ui.util.deleteAppProfileTemplate
|
import me.weishu.kernelsu.ui.util.deleteAppProfileTemplate
|
||||||
import me.weishu.kernelsu.ui.util.getAppProfileTemplate
|
import me.weishu.kernelsu.ui.util.getAppProfileTemplate
|
||||||
import me.weishu.kernelsu.ui.util.setAppProfileTemplate
|
import me.weishu.kernelsu.ui.util.setAppProfileTemplate
|
||||||
import me.weishu.kernelsu.ui.viewmodel.TemplateViewModel
|
import me.weishu.kernelsu.ui.viewmodel.TemplateViewModel
|
||||||
import me.weishu.kernelsu.ui.viewmodel.toJSON
|
import me.weishu.kernelsu.ui.viewmodel.toJSON
|
||||||
import org.json.JSONArray
|
|
||||||
import org.json.JSONObject
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author weishu
|
* @author weishu
|
||||||
@@ -259,7 +255,7 @@ private fun TopBar(
|
|||||||
}, navigationIcon = {
|
}, navigationIcon = {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = onBack
|
onClick = onBack
|
||||||
) { Icon(Icons.Filled.ArrowBack, contentDescription = null) }
|
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
|
||||||
}, actions = {
|
}, actions = {
|
||||||
if (readOnly) {
|
if (readOnly) {
|
||||||
return@TopAppBar
|
return@TopAppBar
|
||||||
@@ -279,7 +275,6 @@ private fun TopBar(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalComposeUiApi::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun TextEdit(
|
private fun TextEdit(
|
||||||
label: String,
|
label: String,
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
package me.weishu.kernelsu.ui.screen
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.Activity
|
|
||||||
import android.webkit.WebResourceRequest
|
|
||||||
import android.webkit.WebResourceResponse
|
|
||||||
import android.webkit.WebView
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.DisposableEffect
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.webkit.WebViewAssetLoader
|
|
||||||
import com.google.accompanist.web.AccompanistWebViewClient
|
|
||||||
import com.google.accompanist.web.WebView
|
|
||||||
import com.google.accompanist.web.rememberWebViewState
|
|
||||||
import com.ramcosta.composedestinations.annotation.Destination
|
|
||||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
|
||||||
import me.weishu.kernelsu.ui.webui.SuFilePathHandler
|
|
||||||
import me.weishu.kernelsu.ui.webui.WebViewInterface
|
|
||||||
import me.weishu.kernelsu.ui.webui.showSystemUI
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
|
||||||
@Destination
|
|
||||||
@Composable
|
|
||||||
fun WebScreen(navigator: DestinationsNavigator, moduleId: String, moduleName: String) {
|
|
||||||
|
|
||||||
val context = LocalContext.current
|
|
||||||
|
|
||||||
DisposableEffect(Unit) {
|
|
||||||
onDispose {
|
|
||||||
if (WebViewInterface.isHideSystemUI && context is Activity) {
|
|
||||||
showSystemUI(context.window)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Scaffold { innerPadding ->
|
|
||||||
val webRoot = File("/data/adb/modules/${moduleId}/webroot")
|
|
||||||
val webViewAssetLoader = WebViewAssetLoader.Builder()
|
|
||||||
.setDomain("mui.kernelsu.org")
|
|
||||||
.addPathHandler("/",
|
|
||||||
SuFilePathHandler(context, webRoot)
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val webViewClient = object : AccompanistWebViewClient() {
|
|
||||||
override fun shouldInterceptRequest(
|
|
||||||
view: WebView,
|
|
||||||
request: WebResourceRequest
|
|
||||||
): WebResourceResponse? {
|
|
||||||
return webViewAssetLoader.shouldInterceptRequest(request.url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WebView(
|
|
||||||
state = rememberWebViewState(url = "https://mui.kernelsu.org/index.html"),
|
|
||||||
Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(innerPadding),
|
|
||||||
client = webViewClient,
|
|
||||||
factory = { context ->
|
|
||||||
WebView(context).apply {
|
|
||||||
settings.javaScriptEnabled = true
|
|
||||||
settings.domStorageEnabled = true
|
|
||||||
settings.allowFileAccess = false
|
|
||||||
addJavascriptInterface(WebViewInterface(context, this), "ksu")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
package me.weishu.kernelsu.ui.theme
|
package me.weishu.kernelsu.ui.theme
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.darkColorScheme
|
||||||
|
import androidx.compose.material3.dynamicDarkColorScheme
|
||||||
|
import androidx.compose.material3.dynamicLightColorScheme
|
||||||
|
import androidx.compose.material3.lightColorScheme
|
||||||
|
import androidx.compose.material3.surfaceColorAtElevation
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.SideEffect
|
import androidx.compose.runtime.SideEffect
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.graphics.toArgb
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalView
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.view.ViewCompat
|
|
||||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
|
|
||||||
private val DarkColorScheme = darkColorScheme(
|
private val DarkColorScheme = darkColorScheme(
|
||||||
|
|||||||
@@ -2,12 +2,7 @@ package me.weishu.kernelsu.ui.util
|
|||||||
|
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.runtime.compositionLocalOf
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
import me.weishu.kernelsu.ui.component.DialogHostState
|
|
||||||
|
|
||||||
val LocalSnackbarHost = compositionLocalOf<SnackbarHostState> {
|
val LocalSnackbarHost = compositionLocalOf<SnackbarHostState> {
|
||||||
error("CompositionLocal LocalSnackbarController not present")
|
error("CompositionLocal LocalSnackbarController not present")
|
||||||
}
|
|
||||||
|
|
||||||
val LocalDialogHost = compositionLocalOf<DialogHostState> {
|
|
||||||
error("CompositionLocal LocalDialogController not present")
|
|
||||||
}
|
}
|
||||||
@@ -7,9 +7,11 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
|
import me.weishu.kernelsu.ui.util.module.LatestVersionInfo
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author weishu
|
* @author weishu
|
||||||
@@ -60,9 +62,10 @@ fun download(
|
|||||||
downloadManager.enqueue(request)
|
downloadManager.enqueue(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkNewVersion(): Triple<Int, String, String> {
|
fun checkNewVersion(): LatestVersionInfo {
|
||||||
val url = "https://api.github.com/repos/tiann/KernelSU/releases/latest"
|
val url = "https://api.github.com/repos/tiann/KernelSU/releases/latest"
|
||||||
val defaultValue = Triple(0, "", "")
|
// default null value if failed
|
||||||
|
val defaultValue = LatestVersionInfo()
|
||||||
runCatching {
|
runCatching {
|
||||||
okhttp3.OkHttpClient().newCall(okhttp3.Request.Builder().url(url).build()).execute()
|
okhttp3.OkHttpClient().newCall(okhttp3.Request.Builder().url(url).build()).execute()
|
||||||
.use { response ->
|
.use { response ->
|
||||||
@@ -87,7 +90,11 @@ fun checkNewVersion(): Triple<Int, String, String> {
|
|||||||
val versionCode = matchResult.groupValues[2].toInt()
|
val versionCode = matchResult.groupValues[2].toInt()
|
||||||
val downloadUrl = asset.getString("browser_download_url")
|
val downloadUrl = asset.getString("browser_download_url")
|
||||||
|
|
||||||
return Triple(versionCode, downloadUrl, changelog)
|
return LatestVersionInfo(
|
||||||
|
versionCode,
|
||||||
|
downloadUrl,
|
||||||
|
changelog
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -123,12 +130,20 @@ fun DownloadListener(context: Context, onDownloaded: (Uri) -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
context.registerReceiver(
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
receiver,
|
context.registerReceiver(
|
||||||
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
|
receiver,
|
||||||
)
|
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE),
|
||||||
|
Context.RECEIVER_EXPORTED
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
context.registerReceiver(
|
||||||
|
receiver,
|
||||||
|
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
|
||||||
|
)
|
||||||
|
}
|
||||||
onDispose {
|
onDispose {
|
||||||
context.unregisterReceiver(receiver)
|
context.unregisterReceiver(receiver)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user