Compare commits

...

248 Commits

Author SHA1 Message Date
weishu
3ad27c5999 kernel: use macro for default domain 2023-06-06 21:07:25 +08:00
weishu
5b27f6c010 kernel: Fix shell permission under KSU_DEBUG 2023-06-06 21:05:37 +08:00
weishu
be44fad288 ci: android13-5.15-2022-11 is deprecated: https://android.googlesource.com/kernel/common/+refs 2023-06-06 20:59:15 +08:00
weishu
2a5fb76f95 manager: sync kernel struct and bump profile version 2023-06-06 20:56:27 +08:00
weishu
5db7075432 kernel: bump format version to force clear allowlist, prepare to release a new version 2023-06-06 20:49:39 +08:00
weishu
1408175a35 kernel: fix set groups for kernel 4.9- 2023-06-06 20:45:42 +08:00
weishu
ab1dc894e1 manager: disable template ui temporarily 2023-06-06 19:06:03 +08:00
weishu
c5bc1c8b15 manager: make template ui work 2023-06-06 17:54:57 +08:00
Soo-Hwan Na
6d1ee60d67 kernel/selinux: Need to force include errno.h sometimes (#572)
- Seen with Linux 4.14 kernel with error message:

In file included from ../drivers/android/kernelsu/selinux/sepolicy.c:1:
In file included from ../drivers/android/kernelsu/selinux/sepolicy.h:6:
In file included from ../security/selinux/ss/policydb.h:30: In file
included from ../security/selinux/ss/avtab.h:26:
../security/selinux/include/security.h:240:10: error: use of undeclared
identifier 'EIDRM'
        return -EIDRM;
                ^
  CC      drivers/base/transport_class.o
  CC      kernel/rcu/update.o
../security/selinux/include/security.h:246:10: error: use of undeclared
identifier 'ENOENT'
        return -ENOENT;
                ^
2023-06-06 16:42:45 +08:00
weishu
a58e929205 manager: Default selinux context should be "u:r:su:s0" 2023-06-06 16:37:14 +08:00
weishu
53be8612c8 kernel: support setting selinux context for profile 2023-06-06 16:35:25 +08:00
weishu
c7f6a7d11b kernel: support settings supplementary groups for profile 2023-06-06 13:19:11 +08:00
weishu
e9011041c5 manager: Fix groups overflow, close #598 2023-06-06 12:40:07 +08:00
weishu
9803371fdb ci: respect the CONFIG_KSU options (#599)
close #596, #597
2023-06-06 12:12:27 +08:00
weishu
710edb72fa kernel: prevent root process to exec su, which makes app can escape root profile by exec it twice 2023-06-06 11:08:57 +08:00
Igor Sorocean
a4ddf59562 Update ro translation (#595) 2023-06-05 22:40:44 +08:00
weishu
37dc9a27a7 kernel: fix incorrect prune uid for work profile app, close #587 2023-06-05 17:41:41 +08:00
weishu
27ccfa6395 manager: change minimal CAP to DAC_READ_SEARCH 2023-06-05 10:17:10 +08:00
weishu
116bc56cfe manager: Fix set profile failed. 2023-06-05 10:10:16 +08:00
weishu
9ae0126be5 manager: Add default minimal caps for default root profile 2023-06-05 09:08:41 +08:00
weishu
e8755f8ae0 manager: Fix groups and caps selection, close #592 2023-06-05 08:31:15 +08:00
Gustavo Mendes
6ba2bd3af9 Add Portuguese brazilian translation (#593)
Signed-off-by: Gustavo Mendes <gusttavo.me@outlook.com>
2023-06-05 08:24:41 +08:00
raystef66
4c5e485e76 Update Flemish/Dutch translation (#590) 2023-06-05 05:48:10 +08:00
Womsxd
05c6892f43 ksubot.py: Increase timeout (#589)
Increase the timeout duration and try to resolve the
'telegram.error.TimedOut: Timed out' issue when sending messages in a
Telegram group.
2023-06-04 23:31:37 +08:00
weishu
670a20c37f manager: Fix selection 2023-06-04 23:24:19 +08:00
weishu
75509aba72 ksud: fix gid is override by uid 2023-06-04 23:14:44 +08:00
weishu
887f02e742 manager: optimize uid/gid panel 2023-06-04 23:14:44 +08:00
Hikari-no-Tenshi
ef8da39d34 manager: Update Ukrainian translation (#584) 2023-06-04 21:41:46 +08:00
Jia-Bin
b7c759ece1 Update Traditional Chinese (#586)
Update Traditional Chinese
Modify the translation to meet localization and usage habits
2023-06-04 21:36:32 +08:00
weishu
019da4a634 ksud: respect the kernel default uid 2023-06-04 18:09:58 +08:00
weishu
33aa6de50b manager: Add desc for umount modules. 2023-06-04 17:42:58 +08:00
weishu
0b3688c3b1 manager: unify kernel and user structs 2023-06-04 17:29:31 +08:00
weishu
076e5d3655 kernel: make the profile uid, gid, capabilities really work 2023-06-04 17:29:12 +08:00
weishu
c9608af0c8 manager: fix capabilities and namespace save/load 2023-06-04 16:12:03 +08:00
weishu
908fbadaf5 manager: remove unused resources 2023-06-04 16:12:02 +08:00
weishu
ee97fdfc56 kernel: add more capailities 2023-06-04 16:09:27 +08:00
weishu
4ac137313f manager: rename "umount modules" -> "Hide modules" 2023-06-04 14:25:50 +08:00
weishu
11d0029a4b manager: Add default groups when empty 2023-06-04 14:25:50 +08:00
weishu
8af5a9038b kernel: fix set app profile may override other user's 2023-06-04 13:59:22 +08:00
weishu
5d449988fb manager: Fix app profile umount default 2023-06-04 09:58:02 +08:00
weishu
e969563df0 manager: Add UMOUNT badge 2023-06-04 09:52:51 +08:00
weishu
990626cf7d kernel: Fixing the issue of a delay in updating the default profile. 2023-06-04 09:47:31 +08:00
weishu
fc77ca989f kernel: use uid instead of package to compare allowlist, fix #580 2023-06-04 09:18:39 +08:00
diphons
2ce3976023 Fixup: build under linux 4.14 (#581)
Split compiler_types.h from compiler.h only available for linux 4.14
2023-06-04 09:15:03 +08:00
weishu
63ec531814 kernel: dont prune uid used for app profile 2023-06-04 01:01:39 +08:00
weishu
a4b55b30ca manager: Add log for fallbacked app profile 2023-06-04 00:59:59 +08:00
weishu
807556f361 manager: show default value for app profile. 2023-06-04 00:22:36 +08:00
weishu
2d854f2f37 manager: the default value of umountModules is actually "true" in kernel 2023-06-04 00:05:19 +08:00
weishu
5980c113fe kernel: respect the app profile's umount modules field for kernel umounting 2023-06-04 00:03:56 +08:00
weishu
1491465b55 manager: require kernel 10931 2023-06-03 23:58:23 +08:00
weishu
728380222a kernel: Add version field for struct app_profile 2023-06-03 23:57:05 +08:00
weishu
005404f552 kernel: support load/restore default profiles 2023-06-03 23:57:05 +08:00
weishu
b55c229038 manager: add support for setting default non root profile. 2023-06-03 23:54:15 +08:00
weishu
32538c9833 manager: remove unused 2023-06-03 23:54:15 +08:00
weishu
b31fc47197 kernel: support CMD_IS_UID_GRANTED_ROOT and CMD_IS_UID_SHOULD_UMOUNT 2023-06-03 22:44:01 +08:00
s1204IT
5003824fa8 Update WSA Kernel (#575)
Remove: 5.10.117.2
Change: 5.15.98.1 -> 5.15.98.**4**
2023-06-03 19:29:04 +08:00
Hikari-no-Tenshi
eea75b72a4 manager: Update Ukrainian translation (#579) 2023-06-03 19:28:38 +08:00
weishu
82d5ec3cc1 kernel: fix CMD_GET_ALLOW_LIST error 2023-06-03 19:23:51 +08:00
weishu
75721be8c0 manager: use default profile when kernel return null. 2023-06-03 17:26:38 +08:00
weishu
de72eedb46 kernel: don't copy profile when not found 2023-06-03 17:26:00 +08:00
weishu
66827ab7de manager: show profile info in superuser list 2023-06-03 17:17:19 +08:00
weishu
2a33433272 manager: don't obfuscate Profile 2023-06-03 16:20:13 +08:00
weishu
581dff8a5f manager: only show warning when installed. 2023-06-03 15:30:51 +08:00
weishu
fc425cbba2 manager: remove semicolon for require new version 2023-06-03 15:25:54 +08:00
weishu
87f55c1acb manager: Add require new kernel version. 2023-06-03 15:23:35 +08:00
weishu
68d639e325 manager: Add groups and caps for profile 2023-06-03 15:01:57 +08:00
weishu
89f6cd044e kernel: minor fixes for profiles 2023-06-03 10:18:11 +08:00
weishu
bd3a1291da kernel: don't fill default value for app profile 2023-06-03 00:06:58 +08:00
weishu
3abb7e4ca2 kernel: baby version of profile 2023-06-03 00:01:00 +08:00
weishu
41265b0203 manager: implement app profile api call 2023-06-02 23:58:07 +08:00
sajadasadollahi83
f2cb841b8a Update Persian Translation. (#569) 2023-06-02 16:01:46 +08:00
dabao1955
c69da29081 Kconfig:Update text information (#567)
![Screenshot_2023-05-31-12-56-27-57_84d3000e3f4017145260f7618db1d683](https://github.com/tiann/KernelSU/assets/79307765/84ab8d8e-c034-4567-b815-ebeccde1fdbc)

![Screenshot_2023-05-31-12-56-09-68_84d3000e3f4017145260f7618db1d683](https://github.com/tiann/KernelSU/assets/79307765/a4209212-997f-4e2f-bbcb-2c5136ab6e71)
2023-06-02 10:41:40 +08:00
github-actions[bot]
e304ef8cfb [add device]: Kernel 4.14.194 exynos9610 Non-GKI Device, Added KernelSu using manual method (#574)
Kernel 4.14.194 exynos9610 Non-GKI Device, Added KernelSu using manual
method has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/573

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-06-02 10:31:41 +08:00
Hikari-no-Tenshi
2ce76351da manager: Scroll app profile view (#565)
close #536
2023-05-31 13:04:22 +08:00
sajadasadollahi83
66cbd931a7 Create strings.xml (#561)
add translation for persian.
2023-05-31 12:44:06 +08:00
s1204IT
07bc28e386 Fix README languages (#564) 2023-05-31 12:08:58 +08:00
AndroPlus
2ef4ffe5eb Update Japanese readme (#560)
Changed the installation link to Japanese one (build page is English)
2023-05-29 20:17:43 +08:00
Murat Kozan
128e7e394e Update Turkish translation (#553) 2023-05-29 10:30:59 +08:00
Akari
998dc9b94b added support(README.md) for Brazilian Portuguese (#558)
added README_PT-BR.md support as mentioned before, for Brazilian
Portuguese.
2023-05-29 10:30:23 +08:00
Hikari-no-Tenshi
c6dafb1333 manager: Shorten long Ukrainian strings (#550) 2023-05-25 11:46:47 +08:00
Hikari-no-Tenshi
8ae7299d59 manager: Update Ukrainian translation (#540) 2023-05-23 10:49:39 +08:00
raystef66
8f1b9c579b Update Flemish/Dutch translation (#541) 2023-05-23 10:43:33 +08:00
ahmeda9a
4b6573b521 Fine tuning for Arabic translation. (#543)
Done some changes to make the translation Arabic clearer
2023-05-23 10:43:03 +08:00
Eduardo Novella
325e843569 (scripts) check_v2.c -add missing header (#548) 2023-05-23 10:24:42 +08:00
dabao1955
d014947a54 change warning about disable kprobes (#549)
#479


Co-authored-by: dabao1955 <195328750@qq.com>
2023-05-23 10:24:18 +08:00
weishu
8858cc899e kernel: remove no-macro-redefined 2023-05-22 10:50:04 +08:00
weishu
8f3e59803f kernel: fix format warnning 2023-05-22 10:39:54 +08:00
weishu
95044bb551 kernel: fix incorrect prune uid in allowlist for work profile app. close #535 2023-05-22 10:32:20 +08:00
Aquarius223
b56448a929 kernel: Modify and adjust KernelSU Kconfig (#538)
1. List KSU as a "KernelSU" menu
2. Overlayfs[1] may be disabled for some early kernels(4.4.y), so
choosing depends on OVERLAY_FS here is useless.
3. KSU_DEBUG is a bool option, it is not a module, fixed from tristate
to bool.
4. Adjust the config order

[1]: github.com/LineageOS/android_kernel_xiaomi_msm8998/commit/dcc27641

Change-Id: I49c77ba30f0a8ed00efc44afef8a12f9ad039d7e

Co-authored-by: admin <paper@localhost>
2023-05-21 20:28:59 +08:00
Mohamed Elsayed
c2b981dbdb Update Arabic translation (#534) 2023-05-20 11:36:29 +08:00
Muhammad Fadlyas
28fb482720 Update Indonesian translation (#532) 2023-05-20 11:28:37 +08:00
Muhammad Fadlyas
d744a705a8 Fix little typo in README.md (#531) 2023-05-20 09:53:38 +08:00
Nipin NA
c62b89f02b [add device]: Xiaomi Redmi Note 7 Pro (violet) (#524) 2023-05-17 17:17:00 +08:00
AndroPlus
f876b0114e Update Japanese translation (#518) 2023-05-17 17:16:24 +08:00
diphons
9965988f26 [add device]: Xiaomi Poco F1 (#520)
Also update link device Xiaomi Poco X3 Pro
2023-05-17 12:43:39 +08:00
github-actions[bot]
828bb6fbb4 [add device]: OPPO Reno6 on ColorOS11.x/ColorOS12.x,kernel version4.14.186+ (#522)
OPPO Reno6 on ColorOS11.x/ColorOS12.x,kernel version4.14.186+ has been
added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/521

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-17 12:43:06 +08:00
Howard Wu
10d8d9efcd ci: upgrade android13 kernel version (#516)
+ android13-5.10.168
+ android13-5.15.94
2023-05-17 10:48:13 +08:00
AndroPlus
e59f3333cb Update and fix Japanese translation (#517) 2023-05-17 10:00:46 +08:00
Nullptr
31a9189d80 manager: several UI improvements (#515) 2023-05-17 09:34:08 +08:00
dabao1955
d162221fac a little change (#513)
Though it is a little not suitable?
2023-05-17 09:31:38 +08:00
weishu
0cdca0d053 website: fix build error 2023-05-16 23:54:36 +08:00
AndroPlus
4c934d460b Update Japanese translation (#508)
I'm not familiar with vitepress, so there may be some mistakes in the
settings.
2023-05-16 23:40:54 +08:00
Nullptr
76612b9cf7 manager: several updates (#510)
+ update deps
+ update app profile page
+ don't show su and module page if no root
2023-05-16 22:32:48 +08:00
github-actions[bot]
9cf8ac9c51 [add device]: (#512)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/511

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-16 22:31:19 +08:00
tiann
b80e06256d Revert "Add option to enable global namespace (#497)"
This reverts commit f81caf75a9.
2023-05-16 22:27:01 +08:00
tiann
08d9e5d6bc manager: add app_profile set/get 2023-05-16 20:33:09 +08:00
tiann
ed0cfd231e ci: fix debug build 2023-05-16 19:46:49 +08:00
weishu
538d3f06f4 ci: Add debug kernel build (#506) 2023-05-16 18:30:33 +08:00
Vayruz Rafli
f5d3fb6217 Fix little typo in setup script. (#505) 2023-05-16 18:12:16 +08:00
tiann
ffa3579e6f manager: minor fixes for app profile 2023-05-16 17:31:57 +08:00
tiann
c7adb8e3b1 manager: Add app profile implementation 2023-05-16 17:15:01 +08:00
tiann
c1427f658a manager: Add working mode UI 2023-05-16 16:31:56 +08:00
tiann
eccce7b31f manager: Add app profile UI 2023-05-16 15:07:26 +08:00
Hikari-no-Tenshi
f81caf75a9 Add option to enable global namespace (#497)
Disabled by default.
To enable/disable use terminal with root.

To enable:
echo 1 > /sys/module/ksu/parameters/global_namespace_enable

To disable:
echo 0 > /sys/module/ksu/parameters/global_namespace_enable

Value will be reset to 0 (disabled) on reboot.
2023-05-16 11:39:48 +08:00
dabao1955
d4680c6de7 Add Japanese language for Readme (#501)
Co-authored-by: dabao1955 <195328750@qq.com>
2023-05-16 10:40:39 +08:00
浅秋枫影
2716ec58a0 Revert 67667b6 (#500)
确实会导致卡住问题,一旦卡住,其他程序就无法正确卸载。
可能是,卸载目录那里一直在等待前面的空闲后再卸载,然后把后面的事件全部阻塞了?
2023-05-15 23:18:34 +08:00
TheNoFace
f25dbd8fed [Add Device]: OnePlus 5/5T (cheeseburger/dumpling) (#499)
Issue https://github.com/tiann/KernelSU/issues/498 closed without any
explicit reason, so made a pull request.
2023-05-15 18:13:58 +08:00
weishu
e35180c919 Update build-manager.yml 2023-05-15 12:14:30 +08:00
Howard Wu
2f54ceb7c4 ci: android12-5.10 drop deprecated tag (#495) 2023-05-15 09:58:25 +08:00
Mohamed Elsayed
6506ef468d Add Arabic translation (#491) 2023-05-13 16:25:10 +08:00
5ec1cff
da46dfbde1 ksud: use MS_RDONLY when mounting overlayfs (#490)
fix https://github.com/tiann/KernelSU/issues/489
2023-05-13 16:17:22 +08:00
Alessandro Paluzzi
09ecc2c9b5 Add italian translation (#486) 2023-05-13 07:07:34 +08:00
github-actions[bot]
0b2899a961 [add device]: (#485)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/484

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-13 06:03:40 +08:00
dabao1955
c6ed3fa27f Change the device supported by the kernel source code I maintain. (#483) 2023-05-12 23:34:25 +08:00
TheNoFace
43cd3b9cad Add Korean translation (#480) 2023-05-12 20:27:43 +08:00
tiann
294d6fa05e kernel: don't umount for process in global namespace 2023-05-12 12:32:35 +08:00
Syuugo
009a479c17 WSA: 5.15.94.1 (#475)
![](https://github.com/tiann/KernelSU/assets/52069677/99edfd12-c0a1-4430-bf2f-b17b76dbc44f)

Co-authored-by: weishu <twsxtd@gmail.com>
2023-05-11 18:45:15 +08:00
tiann
72ee14e6be kernel: revert init stage for x86_64 temporarily 2023-05-11 17:20:55 +08:00
Murat Kozan
2b01a1c395 Add Turkish translation (#474) 2023-05-11 11:14:38 +08:00
tiann
f69793d38e ksud: REPLACE must be directory, if you need to replace normal file, just put it to module's system corresponding location, don't need extra steps. fix #466 2023-05-10 14:36:02 +08:00
github-actions[bot]
eda0d6d23b [add device]: OPPO Reno 6 (#473)
OPPO Reno 6 has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/472

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-10 07:12:39 +08:00
S M Mahbub Hossain
abba36d786 Add Bangla/Bengali translation (#471)
Signed-off-by: S M Mahbub Hossain  <118460787+smmahbubhossain@users.noreply.github.com>
2023-05-09 19:03:58 +08:00
raystef66
33ea9a6f33 Add Flemish/Dutch translation (#470) 2023-05-08 23:21:20 +08:00
Kurisu Cat
9a0da7270b document: fix typo (#467)
修正文档中的错别字
2023-05-08 00:12:38 +08:00
github-actions[bot]
9caf440200 [add device]: (#462)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/461

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-05 18:57:39 +08:00
github-actions[bot]
c9e53cf355 [add device]: (#459)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/458

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-05 10:42:42 +08:00
RyuujiX
432eb318ac kernel: Kconfig: Remove KPROBES dependancy (#453)
For those who want to Implement KernelSU Manually with KPROBES disabled.

When KPROBES Broken and Still enabled, this will causing Loop at splash
logo even Already Manually Imported because this
842c0b674f/kernel/ksu.c (L57).
And when KPROBES is disabled, KSU also will not compiled (I Know it'll
still build if obj-y is set for KSU, but it's better to remove, who
knows if someone set as obj-$(CONFIG_KSU)).

Signed-off-by: RyuujiX <saputradenny712@gmail.com>
2023-05-04 22:57:49 +08:00
セリカ・シルフィル
cea0db4e0d manager: Open guide in the current language if it exist (#452)
If not present, the English page will be opened by default.
2023-05-04 21:10:33 +08:00
weishu
842c0b674f ci: Fix setlocalversion patch failed (#451) 2023-05-04 15:37:06 +08:00
Huy Minh
36ed99d1f6 ksud.c : Include compat.h (#450)
Fix compiling on Android-x86

Signed-off-by: hmtheboy154 <buingoc67@gmail.com>
2023-05-04 11:49:21 +08:00
shygos
86d0f37b37 kernel: Use a better way to link submodule directory (#449)
Use ../ instead of absolute path
2023-05-03 22:05:36 +08:00
github-actions[bot]
5303c9c461 [add device]: (#448)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/447

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-02 06:56:35 +08:00
tiann
67667b6df2 kernel: umount by lazy 2023-05-02 00:12:01 +08:00
Lvc Revincx
d9d27b4229 readme: fix credits link title of genuine project (#446)
The credit link title of [genuine](https://github.com/brevent/genuine)
in `README_CN.md` and `README_TW.md` is `true`.
2023-05-01 16:56:31 +08:00
Sreeshankar K
a2a1b19758 Add OnePlus Nord (avicii) (#444) 2023-04-29 23:45:07 +08:00
github-actions[bot]
fb9d39d6d5 [add device]: (#443)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/442

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-29 12:03:07 +08:00
github-actions[bot]
0f3d425f64 [add device]: (#439)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/438

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-28 08:35:02 +08:00
Abdelhay-Ali
08e3580646 make huawei hi6250 4.9.319 kernel compile (#431) 2023-04-27 13:30:04 +08:00
Igor Sorocean
0fa4a4c6db Update ro strings (#434) 2023-04-26 17:22:53 +08:00
diphons
d452e01a3d [add device]: Xiaomi Poco X3 Pro | Vayu - Bhima (#433) 2023-04-26 11:45:50 +08:00
diphons
f7da373f8b [add device]: Xiaomi Poco F3 - F4 - MI10T/Pro (#432) 2023-04-25 14:55:32 +08:00
Koinu
b80cf7ba15 Update repos.json (#430)
他的内核已经不支持KernelSU了
2023-04-24 11:22:48 +08:00
Kung-chih
fe1cd4b27a website: add Traditional Chinese (#429)
Also README_TW added, hope it works :)
2023-04-23 18:54:33 +08:00
Hikari-no-Tenshi
925206f9c8 Add Ukrainian translation (#427) 2023-04-23 13:55:43 +08:00
Abdelhay-Ali
f24a317e2d Update repos.json (#428)
add Huawei P20 lite (hi6250) (Linux 4.9)
2023-04-23 10:28:29 +08:00
TireX228
aeaa3ce982 manager: Update Russian language (#423) 2023-04-22 23:26:53 +08:00
Kung-chih
8c6913a9af Update zh-rHK and zh-rTW (#425)
@cracky5322 For zh-rTW with Android Style: 

> Log: 記錄(檔)
Developer: 開發人員
Access: 存取(權)
Support: 支援(For users) 支持(For devs)
2023-04-22 23:26:37 +08:00
TinyHai
7795232a58 manager: fix ConfirmDialog (#426) 2023-04-22 23:26:21 +08:00
TinyHai
057330c68f manager: refine dialog component and make a small fix in AboutCard (#422)
Co-authored-by: weishu <twsxtd@gmail.com>
2023-04-22 18:40:11 +08:00
github-actions[bot]
91c80279bd [add device]: (#421)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/420

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-22 15:25:07 +08:00
Nullptr
5715df0b10 manager: small fix for navigation (#416) 2023-04-21 23:52:46 +08:00
github-actions[bot]
9f0c540fba [add device]: (#418)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/417

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-21 23:52:16 +08:00
Nullptr
f9d19a957a manager: refactor to use AGP 8.0.0 (#414) 2023-04-21 16:19:49 +08:00
tiann
76b1165572 kernel: fix incorrect invalidate for manager in work profile 2023-04-21 14:45:47 +08:00
tiann
80c85b3bb9 manager: update libsu to make it work in work profile standalone! 2023-04-21 14:34:40 +08:00
Amicia De Rune
900652a82b update id translation (#412)
Signed-off-by: RooGhz720 <rooghz720@gmail.com>
2023-04-21 10:26:14 +08:00
Jia-Bin
6cbef7d987 Update Traditional Chinese (#413)
Update Traditional Chinese
Optimize the text for easier reading
2023-04-21 10:25:46 +08:00
TireX228
01d66834f0 manager: Update Russian language (#411) 2023-04-20 19:15:14 +08:00
Trịnh Văn Lợi
a40eae9b8c Update vi strings (#408) 2023-04-20 16:16:08 +08:00
github-actions[bot]
23b46bde2b [add device]: (#410)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/409

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-20 16:15:28 +08:00
tiann
b38ada30e6 manager: remove html-text dependency 2023-04-20 15:46:05 +08:00
3452841752 selinux: use grep to check api supports (#402)
This checks `selinux_state` and `current_sid` supports in a raw way.
Feels more reliable than the version checks.

Supersedes #401, fixes #280, fixes #400.
2023-04-20 15:22:03 +08:00
tiann
3b9cab3432 manager: refine about dialog 2023-04-20 15:20:10 +08:00
tiann
2f6df20085 manager: remake home page 2023-04-20 13:52:56 +08:00
tiann
609ea40d1c manager: show loading dialog when collect bugreport 2023-04-20 11:40:09 +08:00
tiann
515a309831 Revert "ci: Add version to build artifacts"
This reverts commit 0b4f4683b3.
2023-04-20 10:55:41 +08:00
tiann
02c2228ab7 Revert "ci: Fix img upload"
This reverts commit ea93140b8a.
2023-04-20 10:55:41 +08:00
Trịnh Văn Lợi
ca246ba67d Update vi strings (#406) 2023-04-20 10:01:03 +08:00
easterNday
f9008b67c4 [add device]: Xiaomi 10S (#405)
增加小米10S的非GKi内核KSU支持。
2023-04-19 23:22:19 +08:00
tiann
e228197906 Revert "manager: manager: disable user scroll when refreshing"
This reverts commit d86b524558.
2023-04-19 21:16:23 +08:00
tiann
d98e324618 manager: upgrade compose dependencies to fix some weird bugs. close #367 2023-04-19 21:15:59 +08:00
Igor Sorocean
1880fdfda9 Update ro strings (#403) 2023-04-19 21:10:08 +08:00
tiann
d86b524558 manager: manager: disable user scroll when refreshing 2023-04-19 18:14:09 +08:00
tiann
70fa38a589 manager: upgrade AGP 2023-04-19 18:10:48 +08:00
tiann
ea93140b8a ci: Fix img upload 2023-04-19 14:02:39 +08:00
tiann
45c229dc9f kernel: fix snprintf 2023-04-18 19:38:58 +08:00
tiann
2b5ac95165 manager: Fix snprintf 2023-04-18 19:35:42 +08:00
Howard Wu
38c65e7e4b [skip ci] ci: fix job name typo (#398) 2023-04-18 18:44:04 +08:00
tiann
f40eaf4499 manager: Fix loading state 2023-04-18 18:42:41 +08:00
tiann
3a667ef389 manager: allow become_manager in work profile. 2023-04-18 18:40:32 +08:00
tiann
f35e03d816 kernel: allow manager to be installed in work profile 2023-04-18 18:32:33 +08:00
tiann
2bfd4d71d8 manager: use ParceledListSlice to transport List<PackageInfo>, avoiding TransactionTooLargeException. 2023-04-18 18:04:49 +08:00
tiann
2bc84014c2 manager: supports grant root access to cross profile. close #271 2023-04-18 17:38:48 +08:00
tiann
a3c72c22c1 manager: add selinux state to bugreport. 2023-04-18 14:47:21 +08:00
tiann
ddf2b7e9ff manager: add allowlist to bugreport. 2023-04-18 14:47:21 +08:00
tiann
04d1bee7b4 manager: remove directory tree in bugreport. 2023-04-18 14:47:21 +08:00
tiann
0c9ecf3abc kernel: always apply sepolicy rules even if selinux is permissive 2023-04-18 14:15:37 +08:00
Kung-chih
c8b4798b61 Update zh-rHK and zh-rTW (#396) 2023-04-18 13:25:55 +08:00
tiann
550c8ae45d Revert "ksud: update faked Magisk version to 26.1 to make some modules happy"
This reverts commit 48cd6e112b.
2023-04-18 13:24:35 +08:00
tiann
0b4f4683b3 ci: Add version to build artifacts 2023-04-18 13:24:35 +08:00
tiann
5504b0dd29 Revert "kernel: don't save allowlist in worker"
This reverts commit c569d803c5.
2023-04-18 13:19:49 +08:00
tiann
f0dcddd391 kernel: don't show log when get allowlist 2023-04-18 13:19:49 +08:00
Howard Wu
75be1e1bd6 ci: avoid ccache key conflict (#397) 2023-04-18 12:12:03 +08:00
tiann
c569d803c5 kernel: don't save allowlist in worker 2023-04-18 09:18:06 +08:00
tiann
48cd6e112b ksud: update faked Magisk version to 26.1 to make some modules happy 2023-04-17 23:20:10 +08:00
tiann
d41354e1d7 manager: show confirm dialog when grant root to app. 2023-04-17 18:19:33 +08:00
tiann
9a5e36c0a4 ksud: remove unused dependency 2023-04-17 18:19:18 +08:00
5ec1cff
f963e40a5f ksud: fix bind mount failed again (#395)
This fixes https://github.com/Dr-TSNG/ZygiskOnKernelSU/issues/18
2023-04-17 12:05:13 +08:00
tiann
f1e2402316 ksud: avoid using master commit dependency 2023-04-17 11:37:54 +08:00
tiann
31bb8c75f5 ksud: fix clippy 2023-04-16 22:14:53 +08:00
tiann
fb4ab44aa5 manager: add bootlog for bugreport 2023-04-15 17:45:20 +08:00
tiann
71216b1458 ksud: catch bootlog when post-fs-data 2023-04-15 17:45:20 +08:00
aiamadeus
c969ff4011 website: fix web link address of taichi (#391)
Co-authored-by: AmadeusGhost <42570690+AmadeusGhost@users.noreply.github.com>
2023-04-15 15:32:07 +08:00
tiann
dc45eb6ed4 kernel: Make sure logd has started before post-fs-data. 2023-04-15 00:17:28 +08:00
tiann
29aaaae4b4 ksud: fix clippy 2023-04-14 23:06:11 +08:00
tiann
1fe53b9549 ci: run clippy with release build 2023-04-14 22:59:29 +08:00
weishu
6274dbebc0 Ci (#390) 2023-04-14 22:55:43 +08:00
weishu
a1cd3ab6fa ci: update clippy check (#389) 2023-04-14 22:42:25 +08:00
5ec1cff
c058cb8848 ksud: refactor module mount (#384) 2023-04-14 22:30:34 +08:00
セリカ・シルフィル
029061177b kernel: Fix pull when we are not currently on a branch (#388)
Before fix:
```
[celica@arch msm-5.4]$ curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -
++ pwd
+ GKI_ROOT=/home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4
+ echo '[+] GKI_ROOT: /home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4'
[+] GKI_ROOT: /home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4
+ test -d /home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4/common/drivers
+ test -d /home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4/drivers
+ DRIVER_DIR=/home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4/drivers
+ test -d /home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4/KernelSU
+ cd /home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4/KernelSU
+ git stash
没有要保存的本地修改
+ git pull
您当前不在一个分支上。
请指定您要合并哪一个分支。
详见 git-pull(1)。

    git pull <远程> <分支>
```

After fix:
```
[celica@arch msm-5.4]$ curl -LSs "https://raw.githubusercontent.com/natsumerinchan/KernelSU/main/kernel/setup.sh" | bash -s main
++ pwd
+ GKI_ROOT=/home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4
+ echo '[+] GKI_ROOT: /home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4'
[+] GKI_ROOT: /home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4
+ test -d /home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4/common/drivers
+ test -d /home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4/drivers
+ DRIVER_DIR=/home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4/drivers
+ test -d /home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4/KernelSU
+ cd /home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4/KernelSU
+ git stash
没有要保存的本地修改
++ git status
++ grep -Po 'v\d+(\.\d+)*'
++ head -n1
+ '[' v0.5.2 ']'
+ git checkout main
之前的 HEAD 位置是 0bda101 ci: fix WSA upload path (#372)
切换到分支 'main'
您的分支与上游分支 'origin/main' 一致。
+ git pull
已经是最新的。
+ '[' -z main ']'
+ git checkout main
已经位于 'main'
您的分支与上游分支 'origin/main' 一致。
+ cd /home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4
+ echo '[+] GKI_ROOT: /home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4'
[+] GKI_ROOT: /home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4
+ echo '[+] Copy kernel su driver to /home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4/drivers'
[+] Copy kernel su driver to /home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4/drivers
+ test -e /home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4/drivers/kernelsu
+ echo '[+] Add kernel su driver to Makefile'
[+] Add kernel su driver to Makefile
+ DRIVER_MAKEFILE=/home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4/drivers/Makefile
+ grep -q kernelsu /home/celica/workdir/mvaisakh-kernel/kernel/msm-5.4/drivers/Makefile
+ echo '[+] Done.'
[+] Done.
```
2023-04-14 15:31:30 +08:00
セリカ・シルフィル
9c8e813642 Kernel: Fix "bash: line 21: $1: unbound variable" (#385) 2023-04-13 22:15:17 +08:00
Coconut
7be61b9657 Make Huawei 4.9 Kernel compiler Happy and Does not affect other kernels (#374)
Adapt to SELinux with Huawei 4.9.xxx kernel without affecting other
mainstream kernels
2023-04-13 17:08:05 +08:00
セリカ・シルフィル
2f67d24ec9 kernel: Support switch branch or tag (#382) 2023-04-13 14:40:56 +08:00
Amicia De Rune
17d6f1cdf1 manager: update translation (#381)
Signed-off-by: RooGhz720 <rooghz720@gmail.com>
2023-04-13 13:19:39 +08:00
Terminator850
a0c34b40c6 Ensure the stability of use (#380)
I think we can add tag selection commands in the setup.sh script, such
as git checkout v0.5.1, to ensure the stability of use #379
2023-04-13 12:30:32 +08:00
tiann
00856f8cb9 misc: add .clang-format 2023-04-13 10:41:49 +08:00
Mike Evans
3df6387ee0 promotion: KernelSU integrated Bool-X kernel for raphael (#378)
Hi, I have made yet another KernelSU integrated kernel for Redmi #K20Pro
(#Raphael), based on a popular custom & optimized kernel called Bool-X
in the community.

Forked source code:
https://github.com/etnperlong/kernel_xiaomi_raphael_bool-x/tree/13.0-ksu
upstream source: https://github.com/onettboots/bool-x_xiaomi_raphael
(thanks to @onettboots)

I have tested the KernelSU feature on this kernel, everything works
fine.
2023-04-13 08:07:23 +08:00
Howard Wu
fe1c826b1b ci: Install dependency python-telegram-bot (#373) 2023-04-12 20:37:41 +08:00
Howard Wu
0bda101d4d ci: fix WSA upload path (#372) 2023-04-12 18:31:59 +08:00
Howard Wu
5e738129d9 ci: WSA build fix typo (#370) 2023-04-12 17:52:24 +08:00
tiann
39b5014add ksud: ensure parent dir when create file 2023-04-12 17:45:56 +08:00
tiann
3c6560ade9 ksud: format anyhow error 2023-04-12 13:21:13 +08:00
tiann
183d1a91c1 ksud: add error context to log 2023-04-12 13:17:13 +08:00
tiann
d711ab8b1f Revert "ksud: sort the stock mounts correctly."
This reverts commit 273a0b0b99.
2023-04-12 13:12:10 +08:00
Howard Wu
6c1a48952e WSA Kernel update to 5.15 (#369)
Checks: https://github.com/Howard20181/KernelSU/actions/runs/4672228625
2023-04-12 12:16:56 +08:00
chen2021-web
a343aa5eb0 Try to solve 4.14 build don't pass problem (#368) 2023-04-11 23:13:34 +08:00
tiann
273a0b0b99 ksud: sort the stock mounts correctly. 2023-04-11 21:23:30 +08:00
tiann
2f1e64dc1b ksud: make compiler happy 2023-04-11 20:45:21 +08:00
tiann
a46d4ecd3e ksud: use move mount instead of remount for stock mounts
Because some stock mounts are difficult to remount it back, so we bind mount it to somewhere else before overlayfs mount. And then use move mount to mount it back.
2023-04-11 20:32:08 +08:00
tiann
5f04954a5c ksud: don't umount stock overlayfs, just remount it 2023-04-11 16:24:02 +08:00
tiann
d065a7ca22 manager: don't create root shell everytime. this should close #361. MeiZu kernel seems cache something in execve syscall, which will cause double free in kernel. 2023-04-11 12:56:19 +08:00
Trịnh Văn Lợi
2f8373f9c5 Update strings.xml (#366)
Update missing translation
2023-04-11 11:05:35 +08:00
weishu
2d36e98246 kernel: make /system/bin/init second_stage more precisely (#357) 2023-04-11 10:55:17 +08:00
5ec1cff
f2d8f1ee60 kernel: fix search git in path (#363)
Prevent failing to get KSU_GIT_VERSION when PATH contains space.


[Details](https://github.com/5ec1cff/my-notes/blob/master/mi-kernel.md#%E4%B8%80%E4%B8%AA%E7%BC%BA%E5%B0%91%E5%BC%95%E5%8F%B7%E5%BC%95%E5%8F%91%E7%9A%84%E8%A1%80%E6%A1%88)
2023-04-09 17:16:35 +08:00
weishu
e7881c350c ksud: handle partition after REMOVE and REPLACE 2023-04-08 12:51:16 +08:00
Ylarod
ffe3e68c35 ksud: ensure ksud context when install (#356)
Incorrect se context will make module system broken
2023-04-07 12:46:59 +08:00
Xeonacid
4d6fafd778 website: fix typo (#354) 2023-04-06 22:40:47 +08:00
tiann
2bbf77ab4c ci: shortten image file name 2023-04-06 19:23:09 +08:00
151 changed files with 7532 additions and 1831 deletions

View File

@@ -1,43 +0,0 @@
From dbdd2906c0b3a967ca28c6b870b46f905c170661 Mon Sep 17 00:00:00 2001
From: Park Ju Hyung <qkrwngud825@gmail.com>
Date: Wed, 13 Mar 2019 13:36:37 +0900
Subject: [PATCH] setlocalversion: don't check for uncommitted changes
I ofter push after the build is done and I hate seeing "-dirty"
Signed-off-by: Park Ju Hyung <qkrwngud825@gmail.com>
Signed-off-by: Danny Lin <danny@kdrag0n.dev>
Signed-off-by: Divyanshu-Modi <divyan.m05@gmail.com>
Change-Id: I240c516520879da680794fd144b1f273f9e21e13
Signed-off-by: Divyanshu-Modi <divyan.m05@gmail.com>
---
scripts/setlocalversion | 13 -------------
1 file changed, 13 deletions(-)
diff --git a/scripts/setlocalversion b/scripts/setlocalversion
index 842936656b84..ef27a273ebf5 100755
--- a/scripts/setlocalversion
+++ b/scripts/setlocalversion
@@ -107,19 +107,6 @@ scm_version()
printf -- '-svn%s' "$(git svn find-rev $head)"
fi
- # Check for uncommitted changes.
- # First, with git-status, but --no-optional-locks is only
- # supported in git >= 2.14, so fall back to git-diff-index if
- # it fails. Note that git-diff-index does not refresh the
- # index, so it may give misleading results. See
- # git-update-index(1), git-diff-index(1), and git-status(1).
- if {
- git --no-optional-locks status -uno --porcelain 2>/dev/null ||
- git diff-index --name-only HEAD
- } | grep -qvE '^(.. )?scripts/package'; then
- printf '%s' -dirty
- fi
-
# All done with git
return
fi
--
2.37.2

View File

@@ -1,46 +0,0 @@
From bbb9e7fb1ccadac47b58ba615e6874ddeaa9e628 Mon Sep 17 00:00:00 2001
From: Park Ju Hyung <qkrwngud825@gmail.com>
Date: Wed, 13 Mar 2019 13:36:37 +0900
Subject: [PATCH] setlocalversion: don't check for uncommitted changes
I ofter push after the build is done and I hate seeing "-dirty"
Signed-off-by: Park Ju Hyung <qkrwngud825@gmail.com>
Signed-off-by: Danny Lin <danny@kdrag0n.dev>
Signed-off-by: Divyanshu-Modi <divyan.m05@gmail.com>
Change-Id: I240c516520879da680794fd144b1f273f9e21e13
Signed-off-by: Divyanshu-Modi <divyan.m05@gmail.com>
---
scripts/setlocalversion | 16 ----------------
1 file changed, 16 deletions(-)
diff --git a/scripts/setlocalversion b/scripts/setlocalversion
index 1b733ae4c..2a3ea7684 100755
--- a/scripts/setlocalversion
+++ b/scripts/setlocalversion
@@ -90,22 +90,6 @@ scm_version()
printf '%s%s' -g "$(echo $head | cut -c1-12)"
fi
- # Check for uncommitted changes.
- # This script must avoid any write attempt to the source tree,
- # which might be read-only.
- # You cannot use 'git describe --dirty' because it tries to
- # create .git/index.lock .
- # First, with git-status, but --no-optional-locks is only
- # supported in git >= 2.14, so fall back to git-diff-index if
- # it fails. Note that git-diff-index does not refresh the
- # index, so it may give misleading results. See
- # git-update-index(1), git-diff-index(1), and git-status(1).
- if {
- git --no-optional-locks status -uno --porcelain 2>/dev/null ||
- git diff-index --name-only HEAD
- } | read dummy; then
- printf '%s' -dirty
- fi
fi
}
--
2.37.2

View File

@@ -37,7 +37,7 @@ build_from_image() {
echo '[+] Compress images' echo '[+] Compress images'
for image in boot*.img; do for image in boot*.img; do
$GZIP -n -f -9 "$image" $GZIP -n -f -9 "$image"
mv "$image".gz ksu-"$VERSION"-"$1"-"$image".gz mv "$image".gz "${1//Image-/}"-"$image".gz
done done
echo "[+] Images to upload" echo "[+] Images to upload"

View File

@@ -24,7 +24,7 @@ build_from_image() {
echo '[+] Compress images' echo '[+] Compress images'
for image in boot*.img; do for image in boot*.img; do
$GZIP -n -f -9 "$image" $GZIP -n -f -9 "$image"
mv "$image".gz ksu-"$VERSION"-"$1"-"$image".gz mv "$image".gz "${1//Image-/}"-"$image".gz
done done
echo '[+] Images to upload' echo '[+] Images to upload'

View File

@@ -0,0 +1,31 @@
name: Build debug kernel
on:
workflow_dispatch:
jobs:
build-debug-kernel-a12:
uses: ./.github/workflows/gki-kernel.yml
with:
version: android12-5.10
version_name: android12-5.10.168
tag: android12-5.10-2023-04
os_patch_level: 2023-04
patch_path: "5.10"
debug: true
build-debug-kernel-a13:
strategy:
matrix:
include:
- version: "5.10"
sub_level: 168
os_patch_level: 2023-04
- version: "5.15"
sub_level: 94
os_patch_level: 2023-04
uses: ./.github/workflows/gki-kernel.yml
with:
version: android13-${{ matrix.version }}
version_name: android13-${{ matrix.version }}.${{ matrix.sub_level }}
tag: android13-${{ matrix.version }}-${{ matrix.os_patch_level }}
patch_path: ${{ matrix.version }}
debug: true

View File

@@ -21,20 +21,14 @@ jobs:
strategy: strategy:
matrix: matrix:
include: include:
- sub_level: 66
os_patch_level: 2021-11
- 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: 136 - sub_level: 136
os_patch_level: 2022-11 os_patch_level: 2022-11
- sub_level: 149 - sub_level: 149
os_patch_level: 2023-01 os_patch_level: 2023-01
- sub_level: 160 - sub_level: 160
os_patch_level: 2023-02 os_patch_level: 2023-03
- sub_level: 168
os_patch_level: 2023-04
uses: ./.github/workflows/gki-kernel.yml uses: ./.github/workflows/gki-kernel.yml
secrets: inherit secrets: inherit
with: with:
@@ -115,7 +109,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.160 version_name: android12-5.10.168
tag: android12-5.10-2023-02 tag: android12-5.10-2023-04
os_patch_level: 2023-02 os_patch_level: 2023-04
patch_path: "5.10" patch_path: "5.10"

View File

@@ -29,16 +29,19 @@ jobs:
os_patch_level: 2023-01 os_patch_level: 2023-01
- version: "5.10" - version: "5.10"
sub_level: 157 sub_level: 157
os_patch_level: 2023-02 os_patch_level: 2023-03
- version: "5.15" - version: "5.10"
sub_level: 41 sub_level: 168
os_patch_level: 2022-11 os_patch_level: 2023-04
- version: "5.15" - version: "5.15"
sub_level: 74 sub_level: 74
os_patch_level: 2023-01 os_patch_level: 2023-01
- version: "5.15" - version: "5.15"
sub_level: 78 sub_level: 78
os_patch_level: 2023-02 os_patch_level: 2023-03
- version: "5.15"
sub_level: 94
os_patch_level: 2023-04
uses: ./.github/workflows/gki-kernel.yml uses: ./.github/workflows/gki-kernel.yml
secrets: inherit secrets: inherit
with: with:
@@ -119,14 +122,14 @@ jobs:
matrix: matrix:
include: include:
- version: "5.10" - version: "5.10"
sub_level: 149 sub_level: 168
os_patch_level: 2022-11 os_patch_level: 2023-04
- version: "5.15" - version: "5.15"
sub_level: 74 sub_level: 94
os_patch_level: 2023-01 os_patch_level: 2023-04
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 }}
patch_path: ${{ matrix.version }} patch_path: ${{ matrix.version }}

View File

@@ -1,13 +1,14 @@
name: Build WSA-5.10.117-Kernel name: Build Kernel - WSA
on: on:
push: push:
branches: ["main"] branches: ["main"]
paths: paths:
- ".github/workflows/build-WSA-5.10.117-kernel.yml" - ".github/workflows/build-kernel-wsa.yml"
- "kernel/**" - "kernel/**"
pull_request: pull_request:
branches: ["main"] branches: ["main"]
paths: paths:
- ".github/workflows/build-kernel-wsa.yml"
- "kernel/**" - "kernel/**"
workflow_call: workflow_call:
workflow_dispatch: workflow_dispatch:
@@ -17,29 +18,45 @@ jobs:
strategy: strategy:
matrix: matrix:
arch: [x86_64, arm64] arch: [x86_64, arm64]
version: [5.10.117.2] version: ["5.15.78.1", "5.15.94.4"]
include: include:
- file_name: "bzImage" - arch: x86_64
make_config: "config-wsa" file_name: "bzImage"
arch: x86_64 - arch: arm64
- file_name: "Image" file_name: "Image"
cross_compile: "aarch64-linux-gnu" cross_compile: "aarch64-linux-gnu"
make_config: "config-wsa-arm64" - version: "5.15.78.1"
arch: x86_64
make_config: config-wsa-x64
- version: "5.15.78.1"
arch: arm64 arch: arm64
make_config: config-wsa-arm64
- version: "5.15.94.4"
arch: x86_64
make_config: config-wsa-x64
- version: "5.15.94.4"
arch: arm64
make_config: config-wsa-arm64
- version: "5.15.78.1"
device_code: latte-2
kernel_version: "5.15"
- version: "5.15.94.4"
device_code: latte-2
kernel_version: "5.15"
name: Build WSA-Kernel-${{ matrix.version }}-${{ matrix.arch }} name: Build WSA-Kernel-${{ matrix.version }}-${{ matrix.arch }}
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
env: env:
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion" CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
CCACHE_NOHASHDIR: "true" CCACHE_NOHASHDIR: "true"
CCACHE_MAXSIZE: "2G"
CCACHE_HARDLINK: "true" CCACHE_HARDLINK: "true"
steps: steps:
- name: Install Build Tools - name: Install Build Tools
run: | run: |
sudo apt update sudo apt-get update
sudo apt install -y --no-install-recommends bc bison build-essential ca-certificates flex git gnupg libelf-dev libssl-dev lsb-release software-properties-common wget libncurses-dev binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu nuget sudo apt-get install -y --no-install-recommends bc bison build-essential ca-certificates flex git gnupg libelf-dev libssl-dev lsb-release software-properties-common wget libncurses-dev binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu nuget gzip
export LLVM_VERSION=12 export LLVM_VERSION=12
wget https://apt.llvm.org/llvm.sh wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh chmod +x llvm.sh
@@ -66,14 +83,15 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
repository: microsoft/WSA-Linux-Kernel repository: microsoft/WSA-Linux-Kernel
ref: android-lts/latte/${{ matrix.version }} ref: android-lts/${{ matrix.device_code }}/${{ matrix.version }}
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.2
with: with:
key: WSA-Kernel-${{ matrix.version }}-${{ matrix.arch }} key: WSA-Kernel-${{ matrix.version }}-${{ matrix.arch }}
save: ${{ github.event_name != 'pull_request' }} save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
max-size: 2G
- name: Setup KernelSU - name: Setup KernelSU
working-directory: WSA-Linux-Kernel working-directory: WSA-Linux-Kernel
@@ -87,17 +105,47 @@ jobs:
DRIVER_MAKEFILE=$KERNEL_ROOT/drivers/Makefile DRIVER_MAKEFILE=$KERNEL_ROOT/drivers/Makefile
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
echo "[+] Apply KernelSU patches" echo "[+] Apply KernelSU patches"
cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/5.10/*.patch cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/${{ matrix.kernel_version }}/*.patch
echo "[+] KernelSU setup done." echo "[+] KernelSU setup done."
cd $GITHUB_WORKSPACE/KernelSU
VERSION=$(($(git rev-list --count HEAD) + 10200))
echo "VERSION: $VERSION"
echo "kernelsu_version=$VERSION" >> $GITHUB_ENV
- name: Build Kernel - name: Build Kernel
working-directory: WSA-Linux-Kernel working-directory: WSA-Linux-Kernel
run: | run: |
cp configs/wsa/${{ matrix.make_config }}-5.10 .config cp configs/wsa/${{ matrix.make_config }} .config
make olddefconfig
make -j`nproc` LLVM=1 ARCH=${{ matrix.arch }} CROSS_COMPILE=${{ matrix.cross_compile }} ${{ matrix.file_name }} CCACHE="/usr/bin/ccache" make -j`nproc` LLVM=1 ARCH=${{ matrix.arch }} CROSS_COMPILE=${{ matrix.cross_compile }} ${{ matrix.file_name }} CCACHE="/usr/bin/ccache"
echo "file_path=WSA-Linux-Kernel/arch/${{ matrix.arch }}/boot/${{ matrix.file_name }}" >> $GITHUB_ENV
- name: Upload kernel-${{ matrix.arch }}-${{ matrix.version }} - name: Upload kernel-${{ matrix.arch }}-${{ matrix.version }}
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: kernel-WSA-${{ matrix.arch }}-${{ matrix.version }} name: kernel-WSA-${{ matrix.arch }}-${{ matrix.version }}
path: WSA-Linux-Kernel/arch/${{ matrix.arch }}/boot/${{ matrix.file_name }} path: "${{ env.file_path }}"
- name: Post to Telegram
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
env:
CHAT_ID: ${{ secrets.CHAT_ID }}
CACHE_CHAT_ID: ${{ secrets.CACHE_CHAT_ID }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
COMMIT_URL: ${{ github.event.head_commit.url }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
TITLE=kernel-${{ matrix.arch }}-WSA-${{ matrix.version }}
echo "[+] title: $TITLE"
export TITLE
export VERSION="${{ env.kernelsu_version }}"
echo "[+] Compress images"
gzip -n -f -9 "${{ env.file_path }}"
echo "[+] Image to upload"
ls -l "${{ env.file_path }}.gz"
if [ -n "${{ secrets.BOT_TOKEN }}" ]; then
pip3 install python-telegram-bot
python3 "$GITHUB_WORKSPACE/KernelSU/scripts/ksubot.py" "${{ env.file_path }}.gz"
fi

View File

@@ -1,16 +1,18 @@
name: Build Manager name: Build Manager
on: on:
push: push:
branches: [ "main" ] branches: [ "main" ]
paths: paths:
- '.github/workflows/build-manager.yml' - '.github/workflows/build-manager.yml'
- 'manager/**' - 'manager/**'
- 'userspace/ksud/**' - 'userspace/ksud/**'
pull_request: pull_request:
branches: [ "main" ] branches: [ "main" ]
paths: paths:
- 'manager/**' - 'manager/**'
workflow_call: workflow_call:
jobs: jobs:
build-ksud: build-ksud:
strategy: strategy:
@@ -21,87 +23,104 @@ jobs:
uses: ./.github/workflows/ksud.yml uses: ./.github/workflows/ksud.yml
with: with:
target: ${{ matrix.target }} target: ${{ matrix.target }}
build-manager: build-manager:
needs: build-ksud needs: build-ksud
runs-on: ubuntu-latest runs-on: ubuntu-latest
defaults: defaults:
run: run:
working-directory: ./manager working-directory: ./manager
steps: steps:
- uses: actions/checkout@v3 - name: Checkout
with: uses: actions/checkout@v3
fetch-depth: 0 with:
- name: Setup need_upload fetch-depth: 0
id: need_upload
run: | - name: Setup need_upload
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then id: need_upload
echo "UPLOAD=true" >> $GITHUB_OUTPUT run: |
else if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
echo "UPLOAD=false" >> $GITHUB_OUTPUT echo "UPLOAD=true" >> $GITHUB_OUTPUT
fi else
- name: set up JDK 11 echo "UPLOAD=false" >> $GITHUB_OUTPUT
uses: actions/setup-java@v3 fi
with:
java-version: '11' - name: Write key
distribution: 'temurin' if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
cache: gradle run: |
- uses: nttld/setup-ndk@v1 if [ ! -z "${{ secrets.KEYSTORE }}" ]; then
with: echo KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}' >> gradle.properties
ndk-version: r25b echo KEY_ALIAS='${{ secrets.KEY_ALIAS }}' >> gradle.properties
local-cache: true echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}' >> gradle.properties
- name: Extract keystore echo KEYSTORE_FILE='../key.jks' >> gradle.properties
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }} echo ${{ secrets.KEYSTORE }} | base64 --decode > key.jks
run: | fi
if [ ! -z "${{ secrets.KEYSTORE }}" ]; then
echo KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}' >> sign.properties - name: Setup Java
echo KEY_ALIAS='${{ secrets.KEY_ALIAS }}' >> sign.properties uses: actions/setup-java@v3
echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}' >> sign.properties with:
echo KEYSTORE_FILE='../key.jks' >> sign.properties distribution: "temurin"
echo ${{ secrets.KEYSTORE }} | base64 --decode > key.jks java-version: "17"
fi
- name: Download arm64 ksud - name: Setup Gradle
uses: actions/download-artifact@v3 uses: gradle/gradle-build-action@v2
with: with:
name: ksud-aarch64-linux-android gradle-home-cache-cleanup: true
path: .
- name: Download x86_64 ksud - name: Download arm64 ksud
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3
with: with:
name: ksud-x86_64-linux-android name: ksud-aarch64-linux-android
path: . path: .
- name: Copy ksud to app jniLibs
run: | - name: Download x86_64 ksud
mkdir -p app/src/main/jniLibs/arm64-v8a uses: actions/download-artifact@v3
mkdir -p app/src/main/jniLibs/x86_64 with:
cp -f ../aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud.so name: ksud-x86_64-linux-android
cp -f ../x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud.so path: .
- name: Grant execute permission for gradlew
run: chmod +x gradlew - name: Copy ksud to app jniLibs
- name: Build with Gradle run: |
run: ./gradlew clean assembleRelease mkdir -p app/src/main/jniLibs/arm64-v8a
- name: Upload build artifact mkdir -p app/src/main/jniLibs/x86_64
uses: actions/upload-artifact@v3 cp -f ../aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud.so
with: cp -f ../x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud.so
name: manager
path: manager/app/build/outputs/apk/release/*.apk - name: Build with Gradle
- name: Setup mutex for uploading run: |
uses: ben-z/gh-action-mutex@v1.0-alpha-7 echo 'org.gradle.parallel=true' >> gradle.properties
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true' echo 'org.gradle.vfs.watch=true' >> gradle.properties
- name: Upload to telegram echo 'org.gradle.jvmargs=-Xmx2048m' >> gradle.properties
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true' echo 'android.native.buildOutput=verbose' >> gradle.properties
env: sed -i 's/org.gradle.configuration-cache=true//g' gradle.properties
CHAT_ID: ${{ secrets.CHAT_ID }} ./gradlew clean assembleRelease
CACHE_CHAT_ID: ${{ secrets.CACHE_CHAT_ID }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }} - name: Upload build artifact
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }} uses: actions/upload-artifact@v3
COMMIT_MESSAGE: ${{ github.event.head_commit.message }} with:
COMMIT_URL: ${{ github.event.head_commit.url }} name: manager
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} path: manager/app/build/outputs/apk/release/*.apk
TITLE: Manager
run: | - name: Setup mutex for uploading
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then uses: ben-z/gh-action-mutex@v1.0-alpha-7
export VERSION=$(git rev-list --count HEAD) if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
APK=$(find ./app/build/outputs/apk/release -name "*.apk")
pip3 install python-telegram-bot - name: Upload to telegram
python3 $GITHUB_WORKSPACE/scripts/ksubot.py $APK if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
fi env:
CHAT_ID: ${{ secrets.CHAT_ID }}
CACHE_CHAT_ID: ${{ secrets.CACHE_CHAT_ID }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
COMMIT_URL: ${{ github.event.head_commit.url }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
TITLE: Manager
run: |
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
export VERSION=$(git rev-list --count HEAD)
APK=$(find ./app/build/outputs/apk/release -name "*.apk")
pip3 install python-telegram-bot
python3 $GITHUB_WORKSPACE/scripts/ksubot.py $APK
fi

View File

@@ -1,22 +0,0 @@
name: Clippy check for pull request
on:
pull_request:
branches:
- 'main'
paths:
- '.github/workflows/clippy-pr.yml'
- 'userspace/ksud/**'
permissions:
checks: write
jobs:
clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: giraffate/clippy-action@v1
with:
workdir: userspace/ksud

View File

@@ -3,7 +3,13 @@ name: Clippy check
on: on:
push: push:
branches: branches:
- 'main' - main
paths:
- '.github/workflows/clippy.yml'
- 'userspace/ksud/**'
pull_request:
branches:
- main
paths: paths:
- '.github/workflows/clippy.yml' - '.github/workflows/clippy.yml'
- 'userspace/ksud/**' - 'userspace/ksud/**'
@@ -26,5 +32,5 @@ jobs:
- name: Run clippy - name: Run clippy
run: | run: |
cross clippy --manifest-path userspace/ksud/Cargo.toml --target aarch64-linux-android cross clippy --manifest-path userspace/ksud/Cargo.toml --target aarch64-linux-android --release
cross clippy --manifest-path userspace/ksud/Cargo.toml --target x86_64-linux-android cross clippy --manifest-path userspace/ksud/Cargo.toml --target x86_64-linux-android --release

View File

@@ -45,6 +45,10 @@ on:
description: > description: >
Artifact name of prebuilt ksud to be embedded Artifact name of prebuilt ksud to be embedded
for example: ksud-aarch64-linux-android for example: ksud-aarch64-linux-android
debug:
required: false
type: boolean
default: false
secrets: secrets:
BOOT_SIGN_KEY: BOOT_SIGN_KEY:
required: false required: false
@@ -64,7 +68,6 @@ jobs:
env: env:
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion" CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
CCACHE_NOHASHDIR: "true" CCACHE_NOHASHDIR: "true"
CCACHE_MAXSIZE: "2G"
CCACHE_HARDLINK: "true" CCACHE_HARDLINK: "true"
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
@@ -72,13 +75,6 @@ jobs:
path: KernelSU path: KernelSU
fetch-depth: 0 fetch-depth: 0
- uses: hendrikmuhs/ccache-action@v1.2
if: inputs.use_cache == true
with:
key: ccache-aarch64-${{ inputs.version_name }}
append-timestamp: false
save: ${{ github.event_name != 'pull_request' }}
- name: Setup need_upload - name: Setup need_upload
id: need_upload id: need_upload
run: | run: |
@@ -99,6 +95,7 @@ jobs:
- name: Setup KernelSU - name: Setup KernelSU
env: env:
PATCH_PATH: ${{ inputs.patch_path }} PATCH_PATH: ${{ inputs.patch_path }}
IS_DEBUG_KERNEL: ${{ inputs.debug }}
run: | run: |
cd $GITHUB_WORKSPACE/android-kernel cd $GITHUB_WORKSPACE/android-kernel
echo "[+] KernelSU setup" echo "[+] KernelSU setup"
@@ -111,6 +108,13 @@ jobs:
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
echo "[+] Apply KernelSU patches" echo "[+] Apply KernelSU patches"
cd $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/$PATCH_PATH/*.patch cd $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/$PATCH_PATH/*.patch
echo "[+] Patch script/setlocalversion"
sed -i 's/-dirty//g' $GKI_ROOT/common/scripts/setlocalversion
if [ "$IS_DEBUG_KERNEL" = "true" ]; then
echo "[+] Enable debug features for kernel"
echo "ccflags-y += -DCONFIG_KSU_DEBUG" >> $GITHUB_WORKSPACE/KernelSU/kernel/Makefile
fi
echo "[+] KernelSU setup done." echo "[+] KernelSU setup done."
- name: Symbol magic - name: Symbol magic
@@ -124,6 +128,14 @@ jobs:
echo "[+] Add KernelSU symbols" echo "[+] Add KernelSU symbols"
cat $KSU_ROOT/kernel/export_symbol.txt | awk '{sub("[ \t]+","");print " "$0}' >> $SYMBOL_LIST cat $KSU_ROOT/kernel/export_symbol.txt | awk '{sub("[ \t]+","");print " "$0}' >> $SYMBOL_LIST
- name: Setup ccache
if: inputs.use_cache == true
uses: hendrikmuhs/ccache-action@v1.2
with:
key: gki-kernel-aarch64-${{ inputs.version_name }}
max-size: 2G
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
- name: Build boot.img - name: Build boot.img
working-directory: android-kernel working-directory: android-kernel
run: CCACHE="/usr/bin/ccache" LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh run: CCACHE="/usr/bin/ccache" LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
@@ -149,4 +161,4 @@ jobs:
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: AnyKernel3-${{ inputs.version_name }}_${{ inputs.os_patch_level }} name: AnyKernel3-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
path: ./AnyKernel3/* path: ./AnyKernel3/*

View File

@@ -14,7 +14,7 @@ jobs:
build-a13-kernel: build-a13-kernel:
uses: ./.github/workflows/build-kernel-a13.yml uses: ./.github/workflows/build-kernel-a13.yml
build-wsa-kernel: build-wsa-kernel:
uses: ./.github/workflows/build-WSA-5.10.117-kernel.yml uses: ./.github/workflows/build-kernel-wsa.yml
release: release:
needs: needs:
- build-manager - build-manager

3
.gitignore vendored
View File

@@ -1 +1,2 @@
.vscode/ .idea
.vscode

View File

@@ -1,4 +1,4 @@
**English** | [中文](README_CN.md) **English** | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Portuguese-Brazil](README_PT-BR.md)
# KernelSU # KernelSU
@@ -11,7 +11,7 @@ A Kernel based root solution for Android devices.
## Compatibility State ## Compatibility State
KernelSU officially supports Android GKI 2.0 devices(with kernel 5.10+), old kernels(4.14+) is also compatiable, but you need to build kernel yourself. KernelSU officially supports Android GKI 2.0 devices(with kernel 5.10+), old kernels(4.14+) is also compatible, but you need to build kernel yourself.
WSA and containter-based Android should also work with KernelSU integrated. WSA and containter-based Android should also work with KernelSU integrated.

View File

@@ -1,4 +1,4 @@
[English](README.md) | **中文** [English](README.md) | **简体中文** | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Portuguese-Brazil](README_PT-BR.md)
# KernelSU # KernelSU
@@ -37,6 +37,6 @@ WSA 和运行在容器上的 Android 也可以与 KernelSU 一起工作。
## 鸣谢 ## 鸣谢
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/)KernelSU 的灵感。 - [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/)KernelSU 的灵感。
- [true](https://github.com/brevent/genuine/)apk v2 签名验证。 - [genuine](https://github.com/brevent/genuine/)apk v2 签名验证。
- [Diamorphine](https://github.com/m0nad/Diamorphine):一些 rootkit 技巧。 - [Diamorphine](https://github.com/m0nad/Diamorphine):一些 rootkit 技巧。
- [Magisk](https://github.com/topjohnwu/Magisk)sepolicy 的实现。 - [Magisk](https://github.com/topjohnwu/Magisk)sepolicy 的实现。

42
README_JP.md Normal file
View File

@@ -0,0 +1,42 @@
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | **日本語** | [Portuguese-Brazil](README_PT-BR.md)
# KernelSU
Android におけるカーネルベースの root ソリューションです。
## 特徴
1. カーネルベースの `su` と権限管理
2. OverlayFS に基づくモジュールシステム
## 対応状況
KernelSU は GKI 2.0 デバイス(カーネルバージョン 5.10 以上を公式にサポートしています。古いカーネル4.14以上)とも互換性がありますが、自分でカーネルをビルドする必要があります。
WSA とコンテナ上で動作する Android でも KernelSU を統合して動かせます。
現在サポートしているアーキテクチャは `arm64-v8a` および `x86_64` です。
## 使用方法
[インストール方法はこちら](https://kernelsu.org/ja_JP/guide/installation.html)
## ビルド
[ビルド方法はこちら](https://kernelsu.org/guide/how-to-build.html)
### ディスカッション
- Telegram: [@KernelSU](https://t.me/KernelSU)
## ライセンス
- `kernel` ディレクトリの下にあるすべてのファイル: [GPL-2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
- `kernel` ディレクトリ以外のすべてのファイル: [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html)
## クレジット
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/)KernelSU のアイデア元
- [genuine](https://github.com/brevent/genuine/)apk v2 の署名検証
- [Diamorphine](https://github.com/m0nad/Diamorphine): rootkit のスキル
- [Magisk](https://github.com/topjohnwu/Magisk)sepolicy の実装

47
README_PT-BR.md Normal file
View File

@@ -0,0 +1,47 @@
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | **Portuguese-Brazil**
# KernelSU
Uma solução raiz baseada em Kernel para dispositivos Android.
## Características
1. `su` baseado em kernel e gerenciamento de acesso root.
2. Sistema modular baseado em overlayfs.
## Estado de compatibilidade
O KernelSU suporta oficialmente dispositivos Android GKI 2.0 (com kernel 5.10+), kernels antigos (4.14+) também são compatíveis, mas você mesmo precisa construir o kernel.
O Android baseado em WSA e contêiner também deve funcionar com o KernelSU integrado.
E os ABIs atualmente suportados são: `arm64-v8a` e `x86_64`
## Uso
[Instalação](https://kernelsu.org/guide/installation.html)
## Construir
[Como construir?](https://kernelsu.org/guide/how-to-build.html)
### Discussão
- Telegram: [@KernelSU](https://t.me/KernelSU)
## Licença
- Os arquivos no diretório `kernel` são [GPL-2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
- Todas as outras partes, exceto o diretório `kernel`, são [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html)
## Créditos
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): a ideia do KernelSU.
- [genuine](https://github.com/brevent/genuine/): validação de assinatura apk v2.
- [Diamorphine](https://github.com/m0nad/Diamorphine): algumas habilidades de rootkit.
- [Magisk](https://github.com/topjohnwu/Magisk): a implementação da sepolicy.

42
README_TW.md Normal file
View File

@@ -0,0 +1,42 @@
[English](README.md) | [简体中文](README_CN.md) | **繁體中文** | [日本語](README_JP.md) | [Portuguese-Brazil](README_PT-BR.md)
# KernelSU
一個基於核心的 Android 裝置 Root 解決方案
## 功能
- 基於核心的 Su 和 Root 存取權管理。
- 基於 Overlayfs 的模組系統。
## 相容性狀態
KernelSU 官方支援 Android GKI 2.0 的裝置 (核心版本 5.10+);舊版核心同樣相容 (最低 4.14+),但需要自行編譯核心。
WSA 和執行在容器中的 Android 也可以與 KernelSU 一同運作。
目前支援架構:`arm64-v8a``x86_64`
## 使用方法
[安裝教學](https://kernelsu.org/zh_TW/guide/installation.html)
## 建置
[如何建置?](https://kernelsu.org/zh_TW/guide/how-to-build.html)
### 討論
- Telegram[@KernelSU](https://t.me/KernelSU)
## 授權
- 目錄 `kernel` 下所有檔案為 [GPL-2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
-`kernel` 目錄的其他部分均為 [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html)
## 致謝
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/)KernelSU 的靈感。
- [genuine](https://github.com/brevent/genuine/)apk v2 簽章驗證。
- [Diamorphine](https://github.com/m0nad/Diamorphine):一些 rootkit 技巧。
- [Magisk](https://github.com/topjohnwu/Magisk)sepolicy 實作。

548
kernel/.clang-format Normal file
View File

@@ -0,0 +1,548 @@
# SPDX-License-Identifier: GPL-2.0
#
# clang-format configuration file. Intended for clang-format >= 4.
#
# For more information, see:
#
# Documentation/process/clang-format.rst
# https://clang.llvm.org/docs/ClangFormat.html
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
#
---
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
#AlignEscapedNewlines: Left # Unknown to clang-format-4.0
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
#AfterExternBlock: false # Unknown to clang-format-5.0
BeforeCatch: false
BeforeElse: false
IndentBraces: false
#SplitEmptyFunction: true # Unknown to clang-format-4.0
#SplitEmptyRecord: true # Unknown to clang-format-4.0
#SplitEmptyNamespace: true # Unknown to clang-format-4.0
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
#BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
#BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
#CompactNamespaces: false # Unknown to clang-format-4.0
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
#FixNamespaceComments: false # Unknown to clang-format-4.0
# Taken from:
# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \
# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \
# | sort | uniq
ForEachMacros:
- 'apei_estatus_for_each_section'
- 'ata_for_each_dev'
- 'ata_for_each_link'
- '__ata_qc_for_each'
- 'ata_qc_for_each'
- 'ata_qc_for_each_raw'
- 'ata_qc_for_each_with_internal'
- 'ax25_for_each'
- 'ax25_uid_for_each'
- '__bio_for_each_bvec'
- 'bio_for_each_bvec'
- 'bio_for_each_bvec_all'
- 'bio_for_each_integrity_vec'
- '__bio_for_each_segment'
- 'bio_for_each_segment'
- 'bio_for_each_segment_all'
- 'bio_list_for_each'
- 'bip_for_each_vec'
- 'bitmap_for_each_clear_region'
- 'bitmap_for_each_set_region'
- 'blkg_for_each_descendant_post'
- 'blkg_for_each_descendant_pre'
- 'blk_queue_for_each_rl'
- 'bond_for_each_slave'
- 'bond_for_each_slave_rcu'
- 'bpf_for_each_spilled_reg'
- 'btree_for_each_safe128'
- 'btree_for_each_safe32'
- 'btree_for_each_safe64'
- 'btree_for_each_safel'
- 'card_for_each_dev'
- 'cgroup_taskset_for_each'
- 'cgroup_taskset_for_each_leader'
- 'cpufreq_for_each_entry'
- 'cpufreq_for_each_entry_idx'
- 'cpufreq_for_each_valid_entry'
- 'cpufreq_for_each_valid_entry_idx'
- 'css_for_each_child'
- 'css_for_each_descendant_post'
- 'css_for_each_descendant_pre'
- 'device_for_each_child_node'
- 'dma_fence_chain_for_each'
- 'do_for_each_ftrace_op'
- 'drm_atomic_crtc_for_each_plane'
- 'drm_atomic_crtc_state_for_each_plane'
- 'drm_atomic_crtc_state_for_each_plane_state'
- 'drm_atomic_for_each_plane_damage'
- 'drm_client_for_each_connector_iter'
- 'drm_client_for_each_modeset'
- 'drm_connector_for_each_possible_encoder'
- 'drm_for_each_bridge_in_chain'
- 'drm_for_each_connector_iter'
- 'drm_for_each_crtc'
- 'drm_for_each_encoder'
- 'drm_for_each_encoder_mask'
- 'drm_for_each_fb'
- 'drm_for_each_legacy_plane'
- 'drm_for_each_plane'
- 'drm_for_each_plane_mask'
- 'drm_for_each_privobj'
- 'drm_mm_for_each_hole'
- 'drm_mm_for_each_node'
- 'drm_mm_for_each_node_in_range'
- 'drm_mm_for_each_node_safe'
- 'flow_action_for_each'
- 'for_each_active_dev_scope'
- 'for_each_active_drhd_unit'
- 'for_each_active_iommu'
- 'for_each_aggr_pgid'
- 'for_each_available_child_of_node'
- 'for_each_bio'
- 'for_each_board_func_rsrc'
- 'for_each_bvec'
- 'for_each_card_auxs'
- 'for_each_card_auxs_safe'
- 'for_each_card_components'
- 'for_each_card_dapms'
- 'for_each_card_pre_auxs'
- 'for_each_card_prelinks'
- 'for_each_card_rtds'
- 'for_each_card_rtds_safe'
- 'for_each_card_widgets'
- 'for_each_card_widgets_safe'
- 'for_each_cgroup_storage_type'
- 'for_each_child_of_node'
- 'for_each_clear_bit'
- 'for_each_clear_bit_from'
- 'for_each_cmsghdr'
- 'for_each_compatible_node'
- 'for_each_component_dais'
- 'for_each_component_dais_safe'
- 'for_each_comp_order'
- 'for_each_console'
- 'for_each_cpu'
- 'for_each_cpu_and'
- 'for_each_cpu_not'
- 'for_each_cpu_wrap'
- 'for_each_dapm_widgets'
- 'for_each_dev_addr'
- 'for_each_dev_scope'
- 'for_each_displayid_db'
- 'for_each_dma_cap_mask'
- 'for_each_dpcm_be'
- 'for_each_dpcm_be_rollback'
- 'for_each_dpcm_be_safe'
- 'for_each_dpcm_fe'
- 'for_each_drhd_unit'
- 'for_each_dss_dev'
- 'for_each_efi_memory_desc'
- 'for_each_efi_memory_desc_in_map'
- 'for_each_element'
- 'for_each_element_extid'
- 'for_each_element_id'
- 'for_each_endpoint_of_node'
- 'for_each_evictable_lru'
- 'for_each_fib6_node_rt_rcu'
- 'for_each_fib6_walker_rt'
- 'for_each_free_mem_pfn_range_in_zone'
- 'for_each_free_mem_pfn_range_in_zone_from'
- 'for_each_free_mem_range'
- 'for_each_free_mem_range_reverse'
- 'for_each_func_rsrc'
- 'for_each_hstate'
- 'for_each_if'
- 'for_each_iommu'
- 'for_each_ip_tunnel_rcu'
- 'for_each_irq_nr'
- 'for_each_link_codecs'
- 'for_each_link_cpus'
- 'for_each_link_platforms'
- 'for_each_lru'
- 'for_each_matching_node'
- 'for_each_matching_node_and_match'
- 'for_each_member'
- 'for_each_mem_region'
- 'for_each_memblock_type'
- 'for_each_memcg_cache_index'
- 'for_each_mem_pfn_range'
- '__for_each_mem_range'
- 'for_each_mem_range'
- '__for_each_mem_range_rev'
- 'for_each_mem_range_rev'
- 'for_each_migratetype_order'
- 'for_each_msi_entry'
- 'for_each_msi_entry_safe'
- 'for_each_net'
- 'for_each_net_continue_reverse'
- 'for_each_netdev'
- 'for_each_netdev_continue'
- 'for_each_netdev_continue_rcu'
- 'for_each_netdev_continue_reverse'
- 'for_each_netdev_feature'
- 'for_each_netdev_in_bond_rcu'
- 'for_each_netdev_rcu'
- 'for_each_netdev_reverse'
- 'for_each_netdev_safe'
- 'for_each_net_rcu'
- 'for_each_new_connector_in_state'
- 'for_each_new_crtc_in_state'
- 'for_each_new_mst_mgr_in_state'
- 'for_each_new_plane_in_state'
- 'for_each_new_private_obj_in_state'
- 'for_each_node'
- 'for_each_node_by_name'
- 'for_each_node_by_type'
- 'for_each_node_mask'
- 'for_each_node_state'
- 'for_each_node_with_cpus'
- 'for_each_node_with_property'
- 'for_each_nonreserved_multicast_dest_pgid'
- 'for_each_of_allnodes'
- 'for_each_of_allnodes_from'
- 'for_each_of_cpu_node'
- 'for_each_of_pci_range'
- 'for_each_old_connector_in_state'
- 'for_each_old_crtc_in_state'
- 'for_each_old_mst_mgr_in_state'
- 'for_each_oldnew_connector_in_state'
- 'for_each_oldnew_crtc_in_state'
- 'for_each_oldnew_mst_mgr_in_state'
- 'for_each_oldnew_plane_in_state'
- 'for_each_oldnew_plane_in_state_reverse'
- 'for_each_oldnew_private_obj_in_state'
- 'for_each_old_plane_in_state'
- 'for_each_old_private_obj_in_state'
- 'for_each_online_cpu'
- 'for_each_online_node'
- 'for_each_online_pgdat'
- 'for_each_pci_bridge'
- 'for_each_pci_dev'
- 'for_each_pci_msi_entry'
- 'for_each_pcm_streams'
- 'for_each_physmem_range'
- 'for_each_populated_zone'
- 'for_each_possible_cpu'
- 'for_each_present_cpu'
- 'for_each_prime_number'
- 'for_each_prime_number_from'
- 'for_each_process'
- 'for_each_process_thread'
- 'for_each_property_of_node'
- 'for_each_registered_fb'
- 'for_each_requested_gpio'
- 'for_each_requested_gpio_in_range'
- 'for_each_reserved_mem_range'
- 'for_each_reserved_mem_region'
- 'for_each_rtd_codec_dais'
- 'for_each_rtd_codec_dais_rollback'
- 'for_each_rtd_components'
- 'for_each_rtd_cpu_dais'
- 'for_each_rtd_cpu_dais_rollback'
- 'for_each_rtd_dais'
- 'for_each_set_bit'
- 'for_each_set_bit_from'
- 'for_each_set_clump8'
- 'for_each_sg'
- 'for_each_sg_dma_page'
- 'for_each_sg_page'
- 'for_each_sgtable_dma_page'
- 'for_each_sgtable_dma_sg'
- 'for_each_sgtable_page'
- 'for_each_sgtable_sg'
- 'for_each_sibling_event'
- 'for_each_subelement'
- 'for_each_subelement_extid'
- 'for_each_subelement_id'
- '__for_each_thread'
- 'for_each_thread'
- 'for_each_unicast_dest_pgid'
- 'for_each_wakeup_source'
- 'for_each_zone'
- 'for_each_zone_zonelist'
- 'for_each_zone_zonelist_nodemask'
- 'fwnode_for_each_available_child_node'
- 'fwnode_for_each_child_node'
- 'fwnode_graph_for_each_endpoint'
- 'gadget_for_each_ep'
- 'genradix_for_each'
- 'genradix_for_each_from'
- 'hash_for_each'
- 'hash_for_each_possible'
- 'hash_for_each_possible_rcu'
- 'hash_for_each_possible_rcu_notrace'
- 'hash_for_each_possible_safe'
- 'hash_for_each_rcu'
- 'hash_for_each_safe'
- 'hctx_for_each_ctx'
- 'hlist_bl_for_each_entry'
- 'hlist_bl_for_each_entry_rcu'
- 'hlist_bl_for_each_entry_safe'
- 'hlist_for_each'
- 'hlist_for_each_entry'
- 'hlist_for_each_entry_continue'
- 'hlist_for_each_entry_continue_rcu'
- 'hlist_for_each_entry_continue_rcu_bh'
- 'hlist_for_each_entry_from'
- 'hlist_for_each_entry_from_rcu'
- 'hlist_for_each_entry_rcu'
- 'hlist_for_each_entry_rcu_bh'
- 'hlist_for_each_entry_rcu_notrace'
- 'hlist_for_each_entry_safe'
- '__hlist_for_each_rcu'
- 'hlist_for_each_safe'
- 'hlist_nulls_for_each_entry'
- 'hlist_nulls_for_each_entry_from'
- 'hlist_nulls_for_each_entry_rcu'
- 'hlist_nulls_for_each_entry_safe'
- 'i3c_bus_for_each_i2cdev'
- 'i3c_bus_for_each_i3cdev'
- 'ide_host_for_each_port'
- 'ide_port_for_each_dev'
- 'ide_port_for_each_present_dev'
- 'idr_for_each_entry'
- 'idr_for_each_entry_continue'
- 'idr_for_each_entry_continue_ul'
- 'idr_for_each_entry_ul'
- 'in_dev_for_each_ifa_rcu'
- 'in_dev_for_each_ifa_rtnl'
- 'inet_bind_bucket_for_each'
- 'inet_lhash2_for_each_icsk_rcu'
- 'key_for_each'
- 'key_for_each_safe'
- 'klp_for_each_func'
- 'klp_for_each_func_safe'
- 'klp_for_each_func_static'
- 'klp_for_each_object'
- 'klp_for_each_object_safe'
- 'klp_for_each_object_static'
- 'kunit_suite_for_each_test_case'
- 'kvm_for_each_memslot'
- 'kvm_for_each_vcpu'
- 'list_for_each'
- 'list_for_each_codec'
- 'list_for_each_codec_safe'
- 'list_for_each_continue'
- 'list_for_each_entry'
- 'list_for_each_entry_continue'
- 'list_for_each_entry_continue_rcu'
- 'list_for_each_entry_continue_reverse'
- 'list_for_each_entry_from'
- 'list_for_each_entry_from_rcu'
- 'list_for_each_entry_from_reverse'
- 'list_for_each_entry_lockless'
- 'list_for_each_entry_rcu'
- 'list_for_each_entry_reverse'
- 'list_for_each_entry_safe'
- 'list_for_each_entry_safe_continue'
- 'list_for_each_entry_safe_from'
- 'list_for_each_entry_safe_reverse'
- 'list_for_each_prev'
- 'list_for_each_prev_safe'
- 'list_for_each_safe'
- 'llist_for_each'
- 'llist_for_each_entry'
- 'llist_for_each_entry_safe'
- 'llist_for_each_safe'
- 'mci_for_each_dimm'
- 'media_device_for_each_entity'
- 'media_device_for_each_intf'
- 'media_device_for_each_link'
- 'media_device_for_each_pad'
- 'nanddev_io_for_each_page'
- 'netdev_for_each_lower_dev'
- 'netdev_for_each_lower_private'
- 'netdev_for_each_lower_private_rcu'
- 'netdev_for_each_mc_addr'
- 'netdev_for_each_uc_addr'
- 'netdev_for_each_upper_dev_rcu'
- 'netdev_hw_addr_list_for_each'
- 'nft_rule_for_each_expr'
- 'nla_for_each_attr'
- 'nla_for_each_nested'
- 'nlmsg_for_each_attr'
- 'nlmsg_for_each_msg'
- 'nr_neigh_for_each'
- 'nr_neigh_for_each_safe'
- 'nr_node_for_each'
- 'nr_node_for_each_safe'
- 'of_for_each_phandle'
- 'of_property_for_each_string'
- 'of_property_for_each_u32'
- 'pci_bus_for_each_resource'
- 'pcm_for_each_format'
- 'ping_portaddr_for_each_entry'
- 'plist_for_each'
- 'plist_for_each_continue'
- 'plist_for_each_entry'
- 'plist_for_each_entry_continue'
- 'plist_for_each_entry_safe'
- 'plist_for_each_safe'
- 'pnp_for_each_card'
- 'pnp_for_each_dev'
- 'protocol_for_each_card'
- 'protocol_for_each_dev'
- 'queue_for_each_hw_ctx'
- 'radix_tree_for_each_slot'
- 'radix_tree_for_each_tagged'
- 'rbtree_postorder_for_each_entry_safe'
- 'rdma_for_each_block'
- 'rdma_for_each_port'
- 'rdma_umem_for_each_dma_block'
- 'resource_list_for_each_entry'
- 'resource_list_for_each_entry_safe'
- 'rhl_for_each_entry_rcu'
- 'rhl_for_each_rcu'
- 'rht_for_each'
- 'rht_for_each_entry'
- 'rht_for_each_entry_from'
- 'rht_for_each_entry_rcu'
- 'rht_for_each_entry_rcu_from'
- 'rht_for_each_entry_safe'
- 'rht_for_each_from'
- 'rht_for_each_rcu'
- 'rht_for_each_rcu_from'
- '__rq_for_each_bio'
- 'rq_for_each_bvec'
- 'rq_for_each_segment'
- 'scsi_for_each_prot_sg'
- 'scsi_for_each_sg'
- 'sctp_for_each_hentry'
- 'sctp_skb_for_each'
- 'shdma_for_each_chan'
- '__shost_for_each_device'
- 'shost_for_each_device'
- 'sk_for_each'
- 'sk_for_each_bound'
- 'sk_for_each_entry_offset_rcu'
- 'sk_for_each_from'
- 'sk_for_each_rcu'
- 'sk_for_each_safe'
- 'sk_nulls_for_each'
- 'sk_nulls_for_each_from'
- 'sk_nulls_for_each_rcu'
- 'snd_array_for_each'
- 'snd_pcm_group_for_each_entry'
- 'snd_soc_dapm_widget_for_each_path'
- 'snd_soc_dapm_widget_for_each_path_safe'
- 'snd_soc_dapm_widget_for_each_sink_path'
- 'snd_soc_dapm_widget_for_each_source_path'
- 'tb_property_for_each'
- 'tcf_exts_for_each_action'
- 'udp_portaddr_for_each_entry'
- 'udp_portaddr_for_each_entry_rcu'
- 'usb_hub_for_each_child'
- 'v4l2_device_for_each_subdev'
- 'v4l2_m2m_for_each_dst_buf'
- 'v4l2_m2m_for_each_dst_buf_safe'
- 'v4l2_m2m_for_each_src_buf'
- 'v4l2_m2m_for_each_src_buf_safe'
- 'virtio_device_for_each_vq'
- 'while_for_each_ftrace_op'
- 'xa_for_each'
- 'xa_for_each_marked'
- 'xa_for_each_range'
- 'xa_for_each_start'
- 'xas_for_each'
- 'xas_for_each_conflict'
- 'xas_for_each_marked'
- 'xbc_array_for_each_value'
- 'xbc_for_each_key_value'
- 'xbc_node_for_each_array_value'
- 'xbc_node_for_each_child'
- 'xbc_node_for_each_key_value'
- 'zorro_for_each_dev'
#IncludeBlocks: Preserve # Unknown to clang-format-5.0
IncludeCategories:
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
#IndentPPDirectives: None # Unknown to clang-format-5.0
IndentWidth: 8
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
ObjCBlockIndentWidth: 8
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
# Taken from git's rules
#PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0
PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakComment: 10
PenaltyBreakFirstLessLess: 0
PenaltyBreakString: 10
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: false
SortIncludes: false
#SortUsingDeclarations: false # Unknown to clang-format-4.0
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
#SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0
#SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0
SpaceBeforeParens: ControlStatements
#SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp03
TabWidth: 8
UseTab: Always
...

View File

@@ -1,14 +1,17 @@
menu "KernelSU"
config KSU config KSU
tristate "KernelSU module" tristate "KernelSU function support"
select OVERLAY_FS
default y default y
depends on KPROBES
depends on OVERLAY_FS
help help
This is the KSU privilege driver for android system. Enable kernel-level root privileges on Android System.
config KSU_DEBUG config KSU_DEBUG
tristate "KernelSU module debug mode" bool "KernelSU debug mode"
default n
depends on KSU depends on KSU
default n
help help
This enables debug mode for KSU Enable KernelSU debug mode
endmenu

View File

@@ -14,7 +14,7 @@ obj-y += kernel_compat.o
obj-y += selinux/ 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)
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)
ccflags-y += -DKSU_GIT_VERSION=$(KSU_GIT_VERSION) ccflags-y += -DKSU_GIT_VERSION=$(KSU_GIT_VERSION)
endif endif
@@ -29,4 +29,4 @@ endif
ccflags-y += -DEXPECTED_SIZE=$(EXPECTED_SIZE) ccflags-y += -DEXPECTED_SIZE=$(EXPECTED_SIZE)
ccflags-y += -DEXPECTED_HASH=$(EXPECTED_HASH) ccflags-y += -DEXPECTED_HASH=$(EXPECTED_HASH)
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-macro-redefined -Wno-declaration-after-statement ccflags-y += -Wno-declaration-after-statement

View File

@@ -1,23 +1,52 @@
#include "linux/delay.h" #include "ksu.h"
#include "linux/compiler.h"
#include "linux/fs.h" #include "linux/fs.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/version.h" #include "linux/version.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
#include "linux/compiler_types.h"
#endif
#include "klog.h" // IWYU pragma: keep #include "klog.h" // IWYU pragma: keep
#include "selinux/selinux.h" #include "selinux/selinux.h"
#include "kernel_compat.h" #include "kernel_compat.h"
#include "allowlist.h"
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32 #define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
#define FILE_FORMAT_VERSION 1 // u32 #define FILE_FORMAT_VERSION 3 // u32
#define KSU_APP_PROFILE_PRESERVE_UID 9999 // NOBODY_UID
#define KSU_DEFAULT_SELINUX_DOMAIN "u:r:su:s0"
static DEFINE_MUTEX(allowlist_mutex); static DEFINE_MUTEX(allowlist_mutex);
// default profiles, these may be used frequently, so we cache it
static struct root_profile default_root_profile;
static struct non_root_profile default_non_root_profile;
static void init_default_profiles()
{
default_root_profile.uid = 0;
default_root_profile.gid = 0;
default_root_profile.groups_count = 1;
default_root_profile.groups[0] = 0;
memset(&default_root_profile.capabilities, 0xff,
sizeof(default_root_profile.capabilities));
default_root_profile.namespaces = 0;
strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
// This means that we will umount modules by default!
default_non_root_profile.umount_modules = true;
}
struct perm_data { struct perm_data {
struct list_head list; struct list_head list;
uid_t uid; struct app_profile profile;
bool allow;
}; };
static struct list_head allow_list; static struct list_head allow_list;
@@ -36,20 +65,87 @@ void ksu_show_allow_list(void)
pr_info("ksu_show_allow_list"); pr_info("ksu_show_allow_list");
list_for_each (pos, &allow_list) { list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list); p = list_entry(pos, struct perm_data, list);
pr_info("uid :%d, allow: %d\n", p->uid, p->allow); pr_info("uid :%d, allow: %d\n", p->profile.current_uid,
p->profile.allow_su);
} }
} }
bool ksu_allow_uid(uid_t uid, bool allow, bool persist) #ifdef CONFIG_KSU_DEBUG
static void ksu_grant_root_to_shell()
{
struct app_profile profile = {
.allow_su = true,
.current_uid = 2000,
};
strcpy(profile.key, "com.android.shell");
strcpy(profile.rp_config.profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
ksu_set_app_profile(&profile, false);
}
#endif
bool ksu_get_app_profile(struct app_profile *profile)
{
struct perm_data *p = NULL;
struct list_head *pos = NULL;
bool found = false;
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
bool uid_match = profile->current_uid == p->profile.current_uid;
if (uid_match) {
// found it, override it with ours
memcpy(profile, &p->profile, sizeof(*profile));
found = true;
goto exit;
}
}
exit:
return found;
}
static bool profile_valid(struct app_profile *profile)
{
if (!profile) {
return false;
}
if (profile->version < KSU_APP_PROFILE_VER) {
pr_info("Unsupported profile version: %d\n", profile->version);
return false;
}
if (profile->allow_su) {
if (profile->rp_config.profile.groups_count > KSU_MAX_GROUPS) {
return false;
}
if (strlen(profile->rp_config.profile.selinux_domain) == 0) {
return false;
}
}
return true;
}
bool ksu_set_app_profile(struct app_profile *profile, bool persist)
{ {
// find the node first!
struct perm_data *p = NULL; struct perm_data *p = NULL;
struct list_head *pos = NULL; struct list_head *pos = NULL;
bool result = false; bool result = false;
if (!profile_valid(profile)) {
pr_err("Failed to set app profile: invalid profile!\n");
return false;
}
list_for_each (pos, &allow_list) { list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list); p = list_entry(pos, struct perm_data, list);
if (uid == p->uid) { // both uid and package must match, otherwise it will break multiple package with different user id
p->allow = allow; if (profile->current_uid == p->profile.current_uid &&
!strcmp(profile->key, p->profile.key)) {
// found it, just override it all!
memcpy(&p->profile, profile, sizeof(*profile));
result = true; result = true;
goto exit; goto exit;
} }
@@ -58,16 +154,38 @@ bool ksu_allow_uid(uid_t uid, bool allow, bool persist)
// not found, alloc a new node! // not found, alloc a new node!
p = (struct perm_data *)kmalloc(sizeof(struct perm_data), GFP_KERNEL); p = (struct perm_data *)kmalloc(sizeof(struct perm_data), GFP_KERNEL);
if (!p) { if (!p) {
pr_err("alloc allow node failed.\n"); pr_err("ksu_set_app_profile alloc failed\n");
return false; return false;
} }
p->uid = uid;
p->allow = allow;
memcpy(&p->profile, profile, sizeof(*profile));
if (profile->allow_su) {
pr_info("set root profile, key: %s, uid: %d, gid: %d, context: %s\n",
profile->key, profile->current_uid,
profile->rp_config.profile.gid,
profile->rp_config.profile.selinux_domain);
} else {
pr_info("set app profile, key: %s, uid: %d, umount modules: %d\n",
profile->key, profile->current_uid,
profile->nrp_config.profile.umount_modules);
}
list_add_tail(&p->list, &allow_list); list_add_tail(&p->list, &allow_list);
result = true; result = true;
exit: exit:
// check if the default profiles is changed, cache it to a single struct to accelerate access.
if (unlikely(!strcmp(profile->key, "$"))) {
// set default non root profile
memcpy(&default_non_root_profile, &profile->nrp_config.profile,
sizeof(default_non_root_profile));
}
if (unlikely(!strcmp(profile->key, "#"))) {
// set default root profile
memcpy(&default_root_profile, &profile->rp_config.profile,
sizeof(default_root_profile));
}
if (persist) if (persist)
persistent_allow_list(); persistent_allow_list();
@@ -87,14 +205,53 @@ bool ksu_is_allow_uid(uid_t uid)
list_for_each (pos, &allow_list) { list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list); p = list_entry(pos, struct perm_data, list);
// pr_info("is_allow_uid uid :%d, allow: %d\n", p->uid, p->allow); // pr_info("is_allow_uid uid :%d, allow: %d\n", p->uid, p->allow);
if (uid == p->uid) { if (uid == p->profile.current_uid) {
return p->allow; return p->profile.allow_su;
} }
} }
return false; return false;
} }
bool ksu_uid_should_umount(uid_t uid)
{
struct app_profile profile = { .current_uid = uid };
bool found = ksu_get_app_profile(&profile);
if (!found) {
// no app profile found, it must be non root app
return default_non_root_profile.umount_modules;
}
if (profile.allow_su) {
// if found and it is granted to su, we shouldn't umount for it
return false;
} else {
// found an app profile
if (profile.nrp_config.use_default) {
return default_non_root_profile.umount_modules;
} else {
return profile.nrp_config.profile.umount_modules;
}
}
}
struct root_profile *ksu_get_root_profile(uid_t uid)
{
struct perm_data *p = NULL;
struct list_head *pos = NULL;
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
if (uid == p->profile.current_uid && p->profile.allow_su) {
if (!p->profile.rp_config.use_default) {
return &p->profile.rp_config.profile;
}
}
}
// use default profile
return &default_root_profile;
}
bool ksu_get_allow_list(int *array, int *length, bool allow) bool ksu_get_allow_list(int *array, int *length, bool allow)
{ {
struct perm_data *p = NULL; struct perm_data *p = NULL;
@@ -102,9 +259,9 @@ bool ksu_get_allow_list(int *array, int *length, bool allow)
int i = 0; int i = 0;
list_for_each (pos, &allow_list) { list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list); p = list_entry(pos, struct perm_data, list);
pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow); // pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow);
if (p->allow == allow) { if (p->profile.allow_su == allow) {
array[i++] = p->uid; array[i++] = p->profile.current_uid;
} }
} }
*length = i; *length = i;
@@ -112,7 +269,7 @@ bool ksu_get_allow_list(int *array, int *length, bool allow)
return true; return true;
} }
void do_persistent_allow_list(struct work_struct *work) void do_save_allow_list(struct work_struct *work)
{ {
u32 magic = FILE_MAGIC; u32 magic = FILE_MAGIC;
u32 version = FILE_FORMAT_VERSION; u32 version = FILE_FORMAT_VERSION;
@@ -124,12 +281,13 @@ void do_persistent_allow_list(struct work_struct *work)
filp_open(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT, 0644); filp_open(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT, 0644);
if (IS_ERR(fp)) { if (IS_ERR(fp)) {
pr_err("save_allow_list creat file failed: %d\n", PTR_ERR(fp)); pr_err("save_allow_list creat file failed: %ld\n", PTR_ERR(fp));
return; return;
} }
// store magic and version // store magic and version
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic)) { if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) !=
sizeof(magic)) {
pr_err("save_allow_list write magic failed.\n"); pr_err("save_allow_list write magic failed.\n");
goto exit; goto exit;
} }
@@ -142,10 +300,12 @@ void do_persistent_allow_list(struct work_struct *work)
list_for_each (pos, &allow_list) { list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list); p = list_entry(pos, struct perm_data, list);
pr_info("save allow list uid :%d, allow: %d\n", p->uid, pr_info("save allow list, name: %s uid :%d, allow: %d\n",
p->allow); p->profile.key, p->profile.current_uid,
ksu_kernel_write_compat(fp, &p->uid, sizeof(p->uid), &off); p->profile.allow_su);
ksu_kernel_write_compat(fp, &p->allow, sizeof(p->allow), &off);
ksu_kernel_write_compat(fp, &p->profile, sizeof(p->profile),
&off);
} }
exit: exit:
@@ -161,27 +321,21 @@ void do_load_allow_list(struct work_struct *work)
u32 version; u32 version;
KWORKER_INSTALL_KEYRING(); KWORKER_INSTALL_KEYRING();
#ifdef CONFIG_KSU_DEBUG
// always allow adb shell by default
ksu_grant_root_to_shell();
#endif
// load allowlist now! // load allowlist now!
fp = filp_open(KERNEL_SU_ALLOWLIST, O_RDONLY, 0); fp = filp_open(KERNEL_SU_ALLOWLIST, O_RDONLY, 0);
if (IS_ERR(fp)) { if (IS_ERR(fp)) {
#ifdef CONFIG_KSU_DEBUG pr_err("load_allow_list open file failed: %ld\n", PTR_ERR(fp));
int errno = PTR_ERR(fp);
if (errno == -ENOENT) {
ksu_allow_uid(2000, true,
true); // allow adb shell by default
} else {
pr_err("load_allow_list open file failed: %d\n",
PTR_ERR(fp));
}
#else
pr_err("load_allow_list open file failed: %d\n", PTR_ERR(fp));
#endif
return; return;
} }
// verify magic // verify magic
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic) || if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) !=
sizeof(magic) ||
magic != FILE_MAGIC) { magic != FILE_MAGIC) {
pr_err("allowlist file invalid: %d!\n", magic); pr_err("allowlist file invalid: %d!\n", magic);
goto exit; goto exit;
@@ -196,18 +350,19 @@ void do_load_allow_list(struct work_struct *work)
pr_info("allowlist version: %d\n", version); pr_info("allowlist version: %d\n", version);
while (true) { while (true) {
u32 uid; struct app_profile profile;
bool allow = false;
ret = ksu_kernel_read_compat(fp, &uid, sizeof(uid), &off); ret = ksu_kernel_read_compat(fp, &profile, sizeof(profile),
&off);
if (ret <= 0) { if (ret <= 0) {
pr_info("load_allow_list read err: %d\n", ret); pr_info("load_allow_list read err: %zd\n", ret);
break; break;
} }
ret = ksu_kernel_read_compat(fp, &allow, sizeof(allow), &off);
pr_info("load_allow_uid: %d, allow: %d\n", uid, allow); pr_info("load_allow_uid, name: %s, uid: %d, allow: %d\n",
profile.key, profile.current_uid, profile.allow_su);
ksu_allow_uid(uid, allow, false); ksu_set_app_profile(&profile, false);
} }
exit: exit:
@@ -224,8 +379,10 @@ void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, void *), void *data)
// TODO: use RCU! // TODO: use RCU!
mutex_lock(&allowlist_mutex); mutex_lock(&allowlist_mutex);
list_for_each_entry_safe (np, n, &allow_list, list) { list_for_each_entry_safe (np, n, &allow_list, list) {
uid_t uid = np->uid; uid_t uid = np->profile.current_uid;
if (!is_uid_exist(uid, data)) { // we use this uid for special cases, don't prune it!
bool is_preserved_uid = uid == KSU_APP_PROFILE_PRESERVE_UID;
if (!is_preserved_uid && !is_uid_exist(uid, data)) {
modified = true; modified = true;
pr_info("prune uid: %d\n", uid); pr_info("prune uid: %d\n", uid);
list_del(&np->list); list_del(&np->list);
@@ -254,8 +411,10 @@ void ksu_allowlist_init(void)
{ {
INIT_LIST_HEAD(&allow_list); INIT_LIST_HEAD(&allow_list);
INIT_WORK(&ksu_save_work, do_persistent_allow_list); INIT_WORK(&ksu_save_work, do_save_allow_list);
INIT_WORK(&ksu_load_work, do_load_allow_list); INIT_WORK(&ksu_load_work, do_load_allow_list);
init_default_profiles();
} }
void ksu_allowlist_exit(void) void ksu_allowlist_exit(void)
@@ -263,7 +422,7 @@ void ksu_allowlist_exit(void)
struct perm_data *np = NULL; struct perm_data *np = NULL;
struct perm_data *n = NULL; struct perm_data *n = NULL;
do_persistent_allow_list(NULL); do_save_allow_list(NULL);
// free allowlist // free allowlist
mutex_lock(&allowlist_mutex); mutex_lock(&allowlist_mutex);
@@ -272,4 +431,4 @@ void ksu_allowlist_exit(void)
kfree(np); kfree(np);
} }
mutex_unlock(&allowlist_mutex); mutex_unlock(&allowlist_mutex);
} }

View File

@@ -2,6 +2,7 @@
#define __KSU_H_ALLOWLIST #define __KSU_H_ALLOWLIST
#include "linux/types.h" #include "linux/types.h"
#include "ksu.h"
void ksu_allowlist_init(void); void ksu_allowlist_init(void);
@@ -13,10 +14,13 @@ void ksu_show_allow_list(void);
bool ksu_is_allow_uid(uid_t uid); bool ksu_is_allow_uid(uid_t uid);
bool ksu_allow_uid(uid_t uid, bool allow, bool persist);
bool ksu_get_allow_list(int *array, int *length, bool allow); bool ksu_get_allow_list(int *array, int *length, bool allow);
void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, void *), void *data); void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, void *), void *data);
bool ksu_get_app_profile(struct app_profile *);
bool ksu_set_app_profile(struct app_profile *, bool persist);
bool ksu_uid_should_umount(uid_t uid);
struct root_profile *ksu_get_root_profile(uid_t uid);
#endif #endif

View File

@@ -1,3 +1,4 @@
#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"
@@ -5,6 +6,7 @@
#include "linux/kernel.h" #include "linux/kernel.h"
#include "linux/kprobes.h" #include "linux/kprobes.h"
#include "linux/lsm_hooks.h" #include "linux/lsm_hooks.h"
#include "linux/nsproxy.h"
#include "linux/path.h" #include "linux/path.h"
#include "linux/printk.h" #include "linux/printk.h"
#include "linux/uaccess.h" #include "linux/uaccess.h"
@@ -38,17 +40,51 @@ 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_isolated_uid(uid_t uid)
#define FIRST_ISOLATED_UID 99000 {
#define LAST_ISOLATED_UID 99999 #define FIRST_ISOLATED_UID 99000
#define FIRST_APP_ZYGOTE_ISOLATED_UID 90000 #define LAST_ISOLATED_UID 99999
#define LAST_APP_ZYGOTE_ISOLATED_UID 98999 #define FIRST_APP_ZYGOTE_ISOLATED_UID 90000
uid_t appid = uid % 100000; #define LAST_APP_ZYGOTE_ISOLATED_UID 98999
return (appid >= FIRST_ISOLATED_UID && appid <= LAST_ISOLATED_UID) uid_t appid = uid % 100000;
|| (appid >= FIRST_APP_ZYGOTE_ISOLATED_UID && appid <= LAST_APP_ZYGOTE_ISOLATED_UID); return (appid >= FIRST_ISOLATED_UID && appid <= LAST_ISOLATED_UID) ||
(appid >= FIRST_APP_ZYGOTE_ISOLATED_UID &&
appid <= LAST_APP_ZYGOTE_ISOLATED_UID);
} }
static struct group_info root_groups = { .usage = ATOMIC_INIT(2) }; static void setup_groups(struct root_profile *profile, struct cred *cred)
{
if (profile->groups_count > KSU_MAX_GROUPS) {
pr_warn("Failed to setgroups, too large group: %d!\n",
profile->uid);
return;
}
u32 ngroups = profile->groups_count;
struct group_info *group_info = groups_alloc(ngroups);
if (!group_info) {
pr_warn("Failed to setgroups, ENOMEM for: %d\n", profile->uid);
return;
}
for (int i = 0; i < ngroups; i++) {
gid_t gid = profile->groups[i];
kgid_t kgid = make_kgid(current_user_ns(), gid);
if (!gid_valid(kgid)) {
pr_warn("Failed to setgroups, invalid gid: %d\n", gid);
put_group_info(group_info);
return;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
group_info->gid[i] = kgid;
#else
GROUP_AT(group_info, i) = kgid;
#endif
}
groups_sort(group_info);
set_groups(cred, group_info);
}
void escape_to_root(void) void escape_to_root(void)
{ {
@@ -56,18 +92,36 @@ void escape_to_root(void)
cred = (struct cred *)__task_cred(current); cred = (struct cred *)__task_cred(current);
memset(&cred->uid, 0, sizeof(cred->uid)); if (cred->euid.val == 0) {
memset(&cred->gid, 0, sizeof(cred->gid)); pr_warn("Already root, don't escape!\n");
memset(&cred->suid, 0, sizeof(cred->suid)); return;
memset(&cred->euid, 0, sizeof(cred->euid)); }
memset(&cred->egid, 0, sizeof(cred->egid)); struct root_profile *profile = ksu_get_root_profile(cred->uid.val);
memset(&cred->fsuid, 0, sizeof(cred->fsuid));
memset(&cred->fsgid, 0, sizeof(cred->fsgid)); cred->uid.val = profile->uid;
memset(&cred->cap_inheritable, 0xff, sizeof(cred->cap_inheritable)); cred->suid.val = profile->uid;
memset(&cred->cap_permitted, 0xff, sizeof(cred->cap_permitted)); cred->euid.val = profile->uid;
memset(&cred->cap_effective, 0xff, sizeof(cred->cap_effective)); cred->fsuid.val = profile->uid;
memset(&cred->cap_bset, 0xff, sizeof(cred->cap_bset));
memset(&cred->cap_ambient, 0xff, sizeof(cred->cap_ambient)); cred->gid.val = profile->gid;
cred->fsgid.val = profile->gid;
cred->sgid.val = profile->gid;
cred->egid.val = profile->gid;
BUILD_BUG_ON(sizeof(profile->capabilities.effective) !=
sizeof(kernel_cap_t));
// capabilities
memcpy(&cred->cap_effective, &profile->capabilities.effective,
sizeof(cred->cap_effective));
memcpy(&cred->cap_inheritable, &profile->capabilities.effective,
sizeof(cred->cap_inheritable));
memcpy(&cred->cap_permitted, &profile->capabilities.effective,
sizeof(cred->cap_permitted));
memcpy(&cred->cap_bset, &profile->capabilities.effective,
sizeof(cred->cap_bset));
memcpy(&cred->cap_ambient, &profile->capabilities.effective,
sizeof(cred->cap_ambient));
// disable seccomp // disable seccomp
#if defined(CONFIG_GENERIC_ENTRY) && \ #if defined(CONFIG_GENERIC_ENTRY) && \
@@ -83,12 +137,9 @@ void escape_to_root(void)
#else #else
#endif #endif
// setgroup to root setup_groups(profile, cred);
if (cred->group_info)
put_group_info(cred->group_info);
cred->group_info = get_group_info(&root_groups);
setup_selinux(); setup_selinux(profile->selinux_domain);
} }
int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry) int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry)
@@ -168,14 +219,26 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
} }
// someone wants to be root manager, just check it! // someone wants to be root manager, just check it!
// arg3 should be `/data/data/<manager_package_name>` // arg3 should be `/data/user/<userId>/<manager_package_name>`
char param[128]; char param[128];
const char *prefix = "/data/data/";
if (copy_from_user(param, arg3, sizeof(param))) { if (copy_from_user(param, arg3, sizeof(param))) {
pr_err("become_manager: copy param err\n"); pr_err("become_manager: copy param err\n");
return 0; return 0;
} }
// 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) { if (startswith(param, (char *)prefix) != 0) {
pr_info("become_manager: invalid param: %s\n", param); pr_info("become_manager: invalid param: %s\n", param);
return 0; return 0;
@@ -213,10 +276,6 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("grant_root: prctl reply error\n"); pr_err("grant_root: prctl reply error\n");
} }
} else {
pr_info("deny root for: %d\n", current_uid());
// add it to deny list!
ksu_allow_uid(current_uid().val, false, true);
} }
return 0; return 0;
} }
@@ -311,6 +370,30 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
return 0; return 0;
} }
if (arg2 == CMD_UID_GRANTED_ROOT || arg2 == CMD_UID_SHOULD_UMOUNT) {
if (is_manager() || 0 == current_uid().val) {
uid_t target_uid = (uid_t)arg3;
bool allow = false;
if (arg2 == CMD_UID_GRANTED_ROOT) {
allow = ksu_is_allow_uid(target_uid);
} else if (arg2 == CMD_UID_SHOULD_UMOUNT) {
allow = ksu_uid_should_umount(target_uid);
} else {
pr_err("unknown cmd: %d\n", arg2);
}
if (!copy_to_user(arg4, &allow, sizeof(allow))) {
if (copy_to_user(result, &reply_ok,
sizeof(reply_ok))) {
pr_err("prctl reply error, cmd: %d\n",
arg2);
}
} else {
pr_err("prctl copy err, cmd: %d\n", arg2);
}
}
return 0;
}
// all other cmds are for 'root manager' // all other cmds are for 'root manager'
if (!is_manager()) { if (!is_manager()) {
last_failed_uid = current_uid().val; last_failed_uid = current_uid().val;
@@ -318,45 +401,81 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
} }
// we are already manager // we are already manager
if (arg2 == CMD_ALLOW_SU || arg2 == CMD_DENY_SU) { if (arg2 == CMD_GET_APP_PROFILE) {
bool allow = arg2 == CMD_ALLOW_SU; struct app_profile profile;
bool success = false; if (copy_from_user(&profile, arg3, sizeof(profile))) {
uid_t uid = (uid_t)arg3; pr_err("copy profile failed\n");
success = ksu_allow_uid(uid, allow, true); return 0;
}
bool success = ksu_get_app_profile(&profile);
if (success) { if (success) {
if (copy_to_user(arg3, &profile, sizeof(profile))) {
pr_err("copy profile failed\n");
return 0;
}
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("prctl reply error, cmd: %d\n", arg2);
}
}
return 0;
}
if (arg2 == CMD_SET_APP_PROFILE) {
struct app_profile profile;
if (copy_from_user(&profile, arg3, sizeof(profile))) {
pr_err("copy profile failed\n");
return 0;
}
// todo: validate the params
if (ksu_set_app_profile(&profile, true)) {
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("prctl reply error, cmd: %d\n", arg2); pr_err("prctl reply error, cmd: %d\n", arg2);
} }
} }
ksu_show_allow_list();
return 0; return 0;
} }
return 0; return 0;
} }
static bool is_appuid(kuid_t uid) { static bool is_appuid(kuid_t uid)
#define PER_USER_RANGE 100000 {
#define FIRST_APPLICATION_UID 10000 #define PER_USER_RANGE 100000
#define LAST_APPLICATION_UID 19999 #define FIRST_APPLICATION_UID 10000
#define LAST_APPLICATION_UID 19999
uid_t appid = uid.val % PER_USER_RANGE; uid_t appid = uid.val % PER_USER_RANGE;
return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID; return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID;
} }
static bool should_umount(struct path* path) { static bool should_umount(struct path *path)
{
if (!path) { if (!path) {
return false; return false;
} }
if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) {
pr_info("ignore global mnt namespace process: %d\n",
current_uid().val);
return false;
}
if (path->mnt && path->mnt->mnt_sb && path->mnt->mnt_sb->s_type) { if (path->mnt && path->mnt->mnt_sb && path->mnt->mnt_sb->s_type) {
const char* fstype = path->mnt->mnt_sb->s_type->name; const char *fstype = path->mnt->mnt_sb->s_type->name;
return strcmp(fstype, "overlay") == 0; if (strcmp(fstype, "overlay") == 0) {
return ksu_uid_should_umount(current_uid().val);
}
#ifdef CONFIG_KSU_DEBUG
pr_info("uid: %d should not umount!\n", current_uid().val);
#endif
} }
return false; return false;
} }
static void try_umount(const char *mnt) { static void try_umount(const char *mnt)
{
struct path path; struct path path;
int err = kern_path(mnt, 0, &path); int err = kern_path(mnt, 0, &path);
if (err) { if (err) {
@@ -376,7 +495,8 @@ static void try_umount(const char *mnt) {
#endif #endif
} }
int ksu_handle_setuid(struct cred *new, const struct cred *old) { int ksu_handle_setuid(struct cred *new, const struct cred *old)
{
if (!new || !old) { if (!new || !old) {
return 0; return 0;
} }
@@ -509,7 +629,8 @@ static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
} }
static int ksu_task_fix_setuid(struct cred *new, const struct cred *old, static int ksu_task_fix_setuid(struct cred *new, const struct cred *old,
int flags) { int flags)
{
return ksu_handle_setuid(new, old); return ksu_handle_setuid(new, old);
} }

View File

@@ -57,7 +57,7 @@ int __init kernelsu_init(void)
ksu_enable_sucompat(); ksu_enable_sucompat();
ksu_enable_ksud(); ksu_enable_ksud();
#else #else
#warning("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html") pr_alert("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html");
#endif #endif
return 0; return 0;

View File

@@ -1,13 +1,17 @@
#ifndef __KSU_H_KSU #ifndef __KSU_H_KSU
#define __KSU_H_KSU #define __KSU_H_KSU
#include "linux/types.h"
#include "linux/workqueue.h" #include "linux/workqueue.h"
#ifndef KSU_GIT_VERSION #ifndef KSU_GIT_VERSION
#warning "KSU_GIT_VERSION not defined! It is better to make KernelSU a git submodule!" #warning \
"KSU_GIT_VERSION not defined! It is better to make KernelSU a git submodule!"
#define KERNEL_SU_VERSION (16) #define KERNEL_SU_VERSION (16)
#else #else
#define KERNEL_SU_VERSION (10000 + KSU_GIT_VERSION + 200) // major * 10000 + git version + 200 for historical reasons #define KERNEL_SU_VERSION \
(10000 + KSU_GIT_VERSION + \
200) // major * 10000 + git version + 200 for historical reasons
#endif #endif
#define KERNEL_SU_OPTION 0xDEADBEEF #define KERNEL_SU_OPTION 0xDEADBEEF
@@ -22,10 +26,68 @@
#define CMD_REPORT_EVENT 7 #define CMD_REPORT_EVENT 7
#define CMD_SET_SEPOLICY 8 #define CMD_SET_SEPOLICY 8
#define CMD_CHECK_SAFEMODE 9 #define CMD_CHECK_SAFEMODE 9
#define CMD_GET_APP_PROFILE 10
#define CMD_SET_APP_PROFILE 11
#define CMD_UID_GRANTED_ROOT 12
#define CMD_UID_SHOULD_UMOUNT 13
#define EVENT_POST_FS_DATA 1 #define EVENT_POST_FS_DATA 1
#define EVENT_BOOT_COMPLETED 2 #define EVENT_BOOT_COMPLETED 2
#define KSU_APP_PROFILE_VER 2
#define KSU_MAX_PACKAGE_NAME 256
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
#define KSU_MAX_GROUPS 32
#define KSU_SELINUX_DOMAIN 64
struct root_profile {
int32_t uid;
int32_t gid;
int32_t groups_count;
int32_t groups[KSU_MAX_GROUPS];
// kernel_cap_t is u32[2] for capabilities v3
struct {
u64 effective;
u64 permitted;
u64 inheritable;
} capabilities;
char selinux_domain[KSU_SELINUX_DOMAIN];
int32_t namespaces;
};
struct non_root_profile {
bool umount_modules;
};
struct app_profile {
// It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this.
u32 version;
// this is usually the package of the app, but can be other value for special apps
char key[KSU_MAX_PACKAGE_NAME];
int32_t current_uid;
bool allow_su;
union {
struct {
bool use_default;
char template_name[KSU_MAX_PACKAGE_NAME];
struct root_profile profile;
} rp_config;
struct {
bool use_default;
struct non_root_profile profile;
} nrp_config;
};
};
bool ksu_queue_work(struct work_struct *work); bool ksu_queue_work(struct work_struct *work);
static inline int startswith(char *s, char *prefix) static inline int startswith(char *s, char *prefix)

View File

@@ -1,5 +1,6 @@
#include "asm/current.h" #include "asm/current.h"
#include "linux/string.h" #include "linux/string.h"
#include "linux/compat.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"
@@ -23,6 +24,7 @@ static const char KERNEL_SU_RC[] =
"\n" "\n"
"on post-fs-data\n" "on post-fs-data\n"
" start logd\n"
// We should wait for the post-fs-data finish // We should wait for the post-fs-data finish
" exec u:r:su:s0 root -- " KSUD_PATH " post-fs-data\n" " exec u:r:su:s0 root -- " KSUD_PATH " post-fs-data\n"
"\n" "\n"
@@ -69,6 +71,69 @@ void on_post_fs_data(void)
stop_input_hook(); stop_input_hook();
} }
#define MAX_ARG_STRINGS 0x7FFFFFFF
struct user_arg_ptr {
#ifdef CONFIG_COMPAT
bool is_compat;
#endif
union {
const char __user *const __user *native;
#ifdef CONFIG_COMPAT
const compat_uptr_t __user *compat;
#endif
} ptr;
};
static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr)
{
const char __user *native;
#ifdef CONFIG_COMPAT
if (unlikely(argv.is_compat)) {
compat_uptr_t compat;
if (get_user(compat, argv.ptr.compat + nr))
return ERR_PTR(-EFAULT);
return compat_ptr(compat);
}
#endif
if (get_user(native, argv.ptr.native + nr))
return ERR_PTR(-EFAULT);
return native;
}
/*
* count() counts the number of strings in array ARGV.
*/
static int count(struct user_arg_ptr argv, int max)
{
int i = 0;
if (argv.ptr.native != NULL) {
for (;;) {
const char __user *p = get_user_arg_ptr(argv, i);
if (!p)
break;
if (IS_ERR(p))
return -EFAULT;
if (i >= max)
return -E2BIG;
++i;
if (fatal_signal_pending(current))
return -ERESTARTNOHAND;
cond_resched();
}
}
return i;
}
int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
void *argv, void *envp, int *flags) void *argv, void *envp, int *flags)
{ {
@@ -82,7 +147,7 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
static const char app_process[] = "/system/bin/app_process"; static const char app_process[] = "/system/bin/app_process";
static bool first_app_process = true; static bool first_app_process = true;
static const char system_bin_init[] = "/system/bin/init"; static const char system_bin_init[] = "/system/bin/init";
static int init_count = 0; static bool init_second_stage_executed = false;
if (!filename_ptr) if (!filename_ptr)
return 0; return 0;
@@ -94,19 +159,51 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
if (!memcmp(filename->name, system_bin_init, if (!memcmp(filename->name, system_bin_init,
sizeof(system_bin_init) - 1)) { sizeof(system_bin_init) - 1)) {
#ifdef __aarch64__
// /system/bin/init executed // /system/bin/init executed
struct user_arg_ptr *ptr = (struct user_arg_ptr*) argv;
int argc = count(*ptr, MAX_ARG_STRINGS);
pr_info("/system/bin/init argc: %d\n", argc);
if (argc > 1 && !init_second_stage_executed) {
const char __user *p = get_user_arg_ptr(*ptr, 1);
if (p && !IS_ERR(p)) {
char first_arg[16];
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
strncpy_from_user_nofault(first_arg, p, sizeof(first_arg));
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
strncpy_from_unsafe_user(first_arg, p, sizeof(first_arg));
#else
strncpy_from_user(first_arg, p, sizeof(first_arg));
#endif
pr_info("first arg: %s\n", first_arg);
if (!strcmp(first_arg, "second_stage")) {
pr_info("/system/bin/init second_stage executed\n");
apply_kernelsu_rules();
init_second_stage_executed = true;
}
} else {
pr_err("/system/bin/init parse args err!\n");
}
}
#else
// The argument parse is incorrect becuase of the struct user_arg_ptr has 16bytes
// and it is passed by value(not pointer), in arm64, it is correct becuase the register
// is just arranged correct accidentally, but is not correct in x86_64
// i have no device to test, so revert it for x86_64
static int init_count = 0;
if (++init_count == 2) { if (++init_count == 2) {
// 1: /system/bin/init selinux_setup // 1: /system/bin/init selinux_setup
// 2: /system/bin/init second_stage // 2: /system/bin/init 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();
} }
#endif
} }
if (first_app_process && if (first_app_process &&
!memcmp(filename->name, app_process, sizeof(app_process) - 1)) { !memcmp(filename->name, app_process, sizeof(app_process) - 1)) {
first_app_process = false; first_app_process = false;
pr_info("exec app_process, /data prepared!\n"); 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();
} }

View File

@@ -13,7 +13,7 @@
#include "ksu.h" #include "ksu.h"
#include "manager.h" #include "manager.h"
uid_t ksu_manager_uid = INVALID_UID; uid_t ksu_manager_uid = KSU_INVALID_UID;
bool become_manager(char *pkg) bool become_manager(char *pkg)
{ {

View File

@@ -4,13 +4,13 @@
#include "linux/cred.h" #include "linux/cred.h"
#include "linux/types.h" #include "linux/types.h"
#define INVALID_UID -1 #define KSU_INVALID_UID -1
extern uid_t ksu_manager_uid; // DO NOT DIRECT USE extern uid_t ksu_manager_uid; // DO NOT DIRECT USE
static inline bool ksu_is_manager_uid_valid() static inline bool ksu_is_manager_uid_valid()
{ {
return ksu_manager_uid != INVALID_UID; return ksu_manager_uid != KSU_INVALID_UID;
} }
static inline bool is_manager() static inline bool is_manager()
@@ -30,7 +30,7 @@ static inline void ksu_set_manager_uid(uid_t uid)
static inline void ksu_invalidate_manager_uid() static inline void ksu_invalidate_manager_uid()
{ {
ksu_manager_uid = INVALID_UID; ksu_manager_uid = KSU_INVALID_UID;
} }
bool become_manager(char *pkg); bool become_manager(char *pkg);

View File

@@ -2,8 +2,15 @@ obj-y += selinux.o
obj-y += sepolicy.o obj-y += sepolicy.o
obj-y += rules.o obj-y += rules.o
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
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion
ccflags-y += -Wno-macro-redefined -Wno-declaration-after-statement -Wno-unused-function ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
ccflags-y += -I$(objtree)/security/selinux ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h

View File

@@ -22,7 +22,7 @@ static struct policydb *get_policydb(void)
{ {
struct policydb *db; struct policydb *db;
// selinux_state does not exists before 4.19 // selinux_state does not exists before 4.19
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 337) #ifdef KSU_COMPAT_USE_SELINUX_STATE
#ifdef SELINUX_POLICY_INSTEAD_SELINUX_SS #ifdef SELINUX_POLICY_INSTEAD_SELINUX_SS
struct selinux_policy *policy = rcu_dereference(selinux_state.policy); struct selinux_policy *policy = rcu_dereference(selinux_state.policy);
db = &policy->policydb; db = &policy->policydb;
@@ -31,7 +31,7 @@ static struct policydb *get_policydb(void)
db = &ss->policydb; db = &ss->policydb;
#endif #endif
#else #else
db = &policydb; db = &policydb;
#endif #endif
return db; return db;
} }
@@ -39,8 +39,7 @@ static struct policydb *get_policydb(void)
void apply_kernelsu_rules() void apply_kernelsu_rules()
{ {
if (!getenforce()) { if (!getenforce()) {
pr_info("SELinux permissive or disabled, don't apply rules."); pr_info("SELinux permissive or disabled, apply rules!");
return;
} }
rcu_read_lock(); rcu_read_lock();
@@ -169,8 +168,9 @@ static int get_object(char *buf, char __user *user_object, size_t buf_sz,
} }
// reset avc cache table, otherwise the new rules will not take effect if already denied // reset avc cache table, otherwise the new rules will not take effect if already denied
static void reset_avc_cache() { static void reset_avc_cache()
#if ((KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE) && (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 163))) || (LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 337)) {
#ifndef KSU_COMPAT_USE_SELINUX_STATE
avc_ss_reset(0); avc_ss_reset(0);
selnl_notify_policyload(0); selnl_notify_policyload(0);
selinux_status_update_policyload(0); selinux_status_update_policyload(0);
@@ -190,8 +190,7 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
} }
if (!getenforce()) { if (!getenforce()) {
pr_info("SELinux permissive or disabled, don't apply policies."); pr_info("SELinux permissive or disabled when handle policy!\n");
return 0;
} }
struct sepol_data data; struct sepol_data data;

View File

@@ -2,7 +2,7 @@
#include "objsec.h" #include "objsec.h"
#include "linux/version.h" #include "linux/version.h"
#include "../klog.h" // IWYU pragma: keep #include "../klog.h" // IWYU pragma: keep
#if ((KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE) && (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 163))) || (LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 337)) #ifndef KSU_COMPAT_USE_SELINUX_STATE
#include "avc.h" #include "avc.h"
#endif #endif
@@ -39,9 +39,9 @@ static int transive_to_domain(const char *domain)
return error; return error;
} }
void setup_selinux() void setup_selinux(const char *domain)
{ {
if (transive_to_domain(KERNEL_SU_DOMAIN)) { if (transive_to_domain(domain)) {
pr_err("transive domain failed."); pr_err("transive domain failed.");
return; return;
} }
@@ -57,7 +57,7 @@ if (!is_domain_permissive) {
void setenforce(bool enforce) void setenforce(bool enforce)
{ {
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP #ifdef CONFIG_SECURITY_SELINUX_DEVELOP
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 163)) || ((KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 337))) #ifdef KSU_COMPAT_USE_SELINUX_STATE
selinux_state.enforcing = enforce; selinux_state.enforcing = enforce;
#else #else
selinux_enforcing = enforce; selinux_enforcing = enforce;
@@ -68,7 +68,7 @@ void setenforce(bool enforce)
bool getenforce() bool getenforce()
{ {
#ifdef CONFIG_SECURITY_SELINUX_DISABLE #ifdef CONFIG_SECURITY_SELINUX_DISABLE
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 163)) || ((KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 337))) #ifdef KSU_COMPAT_USE_SELINUX_STATE
if (selinux_state.disabled) { if (selinux_state.disabled) {
#else #else
if (selinux_disabled) { if (selinux_disabled) {
@@ -78,7 +78,7 @@ bool getenforce()
#endif #endif
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP #ifdef CONFIG_SECURITY_SELINUX_DEVELOP
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 163)) || ((KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 337))) #ifdef KSU_COMPAT_USE_SELINUX_STATE
return selinux_state.enforcing; return selinux_state.enforcing;
#else #else
return selinux_enforcing; return selinux_enforcing;
@@ -88,7 +88,8 @@ bool getenforce()
#endif #endif
} }
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 337) #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && \
!defined(KSU_COMPAT_HAS_CURRENT_SID)
/* /*
* get the subjective security ID of the current task * get the subjective security ID of the current task
*/ */

View File

@@ -2,8 +2,13 @@
#define __KSU_H_SELINUX #define __KSU_H_SELINUX
#include "linux/types.h" #include "linux/types.h"
#include "linux/version.h"
void setup_selinux(); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) || defined(KSU_COMPAT_HAS_SELINUX_STATE)
#define KSU_COMPAT_USE_SELINUX_STATE
#endif
void setup_selinux(const char *);
void setenforce(bool); void setenforce(bool);

View File

@@ -9,6 +9,18 @@
#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
////////////////////////////////////////////////////// //////////////////////////////////////////////////////
@@ -61,7 +73,7 @@ static bool add_typeattribute(struct policydb *db, const char *type,
// rules // rules
#define strip_av(effect, invert) ((effect == AVTAB_AUDITDENY) == !invert) #define strip_av(effect, invert) ((effect == AVTAB_AUDITDENY) == !invert)
#define hash_for_each(node_ptr, n_slot, cur) \ #define ksu_hash_for_each(node_ptr, n_slot, cur) \
int i; \ int i; \
for (i = 0; i < n_slot; ++i) \ for (i = 0; i < n_slot; ++i) \
for (cur = node_ptr[i]; cur; cur = cur->next) for (cur = node_ptr[i]; cur; cur = cur->next)
@@ -69,10 +81,10 @@ static bool add_typeattribute(struct policydb *db, const char *type,
// htable is a struct instead of pointer above 5.8.0: // htable is a struct instead of pointer above 5.8.0:
// https://elixir.bootlin.com/linux/v5.8-rc1/source/security/selinux/ss/symtab.h // https://elixir.bootlin.com/linux/v5.8-rc1/source/security/selinux/ss/symtab.h
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
#define hashtab_for_each(htab, cur) hash_for_each (htab.htable, htab.size, cur) #define ksu_hashtab_for_each(htab, cur) ksu_hash_for_each (htab.htable, htab.size, cur)
#else #else
#define hashtab_for_each(htab, cur) \ #define ksu_hashtab_for_each(htab, cur) \
hash_for_each (htab->htable, htab->size, cur) ksu_hash_for_each (htab->htable, htab->size, cur)
#endif #endif
// symtab_search is introduced on 5.9.0: // symtab_search is introduced on 5.9.0:
@@ -83,7 +95,7 @@ static bool add_typeattribute(struct policydb *db, const char *type,
#endif #endif
#define avtab_for_each(avtab, cur) \ #define avtab_for_each(avtab, cur) \
hash_for_each (avtab.htable, avtab.nslot, cur) \ ksu_hash_for_each (avtab.htable, avtab.nslot, cur) \
; ;
static struct avtab_node *get_avtab_node(struct policydb *db, static struct avtab_node *get_avtab_node(struct policydb *db,
@@ -198,14 +210,14 @@ static void add_rule_raw(struct policydb *db, struct type_datum *src,
if (src == NULL) { if (src == NULL) {
struct hashtab_node *node; struct hashtab_node *node;
if (strip_av(effect, invert)) { if (strip_av(effect, invert)) {
hashtab_for_each(db->p_types.table, node) ksu_hashtab_for_each(db->p_types.table, node)
{ {
add_rule_raw(db, add_rule_raw(db,
(struct type_datum *)node->datum, (struct type_datum *)node->datum,
tgt, cls, perm, effect, invert); tgt, cls, perm, effect, invert);
}; };
} else { } else {
hashtab_for_each(db->p_types.table, node) ksu_hashtab_for_each(db->p_types.table, node)
{ {
struct type_datum *type = struct type_datum *type =
(struct type_datum *)(node->datum); (struct type_datum *)(node->datum);
@@ -218,14 +230,14 @@ static void add_rule_raw(struct policydb *db, struct type_datum *src,
} else if (tgt == NULL) { } else if (tgt == NULL) {
struct hashtab_node *node; struct hashtab_node *node;
if (strip_av(effect, invert)) { if (strip_av(effect, invert)) {
hashtab_for_each(db->p_types.table, node) ksu_hashtab_for_each(db->p_types.table, node)
{ {
add_rule_raw(db, src, add_rule_raw(db, src,
(struct type_datum *)node->datum, (struct type_datum *)node->datum,
cls, perm, effect, invert); cls, perm, effect, invert);
}; };
} else { } else {
hashtab_for_each(db->p_types.table, node) ksu_hashtab_for_each(db->p_types.table, node)
{ {
struct type_datum *type = struct type_datum *type =
(struct type_datum *)(node->datum); (struct type_datum *)(node->datum);
@@ -237,7 +249,7 @@ static void add_rule_raw(struct policydb *db, struct type_datum *src,
} }
} else if (cls == NULL) { } else if (cls == NULL) {
struct hashtab_node *node; struct hashtab_node *node;
hashtab_for_each(db->p_classes.table, node) ksu_hashtab_for_each(db->p_classes.table, node)
{ {
add_rule_raw(db, src, tgt, add_rule_raw(db, src, tgt,
(struct class_datum *)node->datum, perm, (struct class_datum *)node->datum, perm,
@@ -280,7 +292,7 @@ static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
{ {
if (src == NULL) { if (src == NULL) {
struct hashtab_node *node; struct hashtab_node *node;
hashtab_for_each(db->p_types.table, node) ksu_hashtab_for_each(db->p_types.table, node)
{ {
struct type_datum *type = struct type_datum *type =
(struct type_datum *)(node->datum); (struct type_datum *)(node->datum);
@@ -291,7 +303,7 @@ static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
}; };
} else if (tgt == NULL) { } else if (tgt == NULL) {
struct hashtab_node *node; struct hashtab_node *node;
hashtab_for_each(db->p_types.table, node) ksu_hashtab_for_each(db->p_types.table, node)
{ {
struct type_datum *type = struct type_datum *type =
(struct type_datum *)(node->datum); (struct type_datum *)(node->datum);
@@ -302,7 +314,7 @@ static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
}; };
} else if (cls == NULL) { } else if (cls == NULL) {
struct hashtab_node *node; struct hashtab_node *node;
hashtab_for_each(db->p_classes.table, node) ksu_hashtab_for_each(db->p_classes.table, node)
{ {
add_xperm_rule_raw(db, src, tgt, add_xperm_rule_raw(db, src, tgt,
(struct class_datum *)(node->datum), (struct class_datum *)(node->datum),
@@ -453,8 +465,9 @@ static bool add_type_rule(struct policydb *db, const char *s, const char *t,
return true; return true;
} }
// 5.9.0 : static inline int hashtab_insert(struct hashtab *h, void *key, void *datum, struct hashtab_key_params key_params) // 5.9.0 : static inline int hashtab_insert(struct hashtab *h, void *key, void
// 5.8.0: int hashtab_insert(struct hashtab *h, void *k, void *d); // *datum, struct hashtab_key_params key_params) 5.8.0: int
// hashtab_insert(struct hashtab *h, void *k, void *d);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
static u32 filenametr_hash(const void *k) static u32 filenametr_hash(const void *k)
{ {
@@ -486,7 +499,6 @@ static int filenametr_cmp(const void *k1, const void *k2)
return v; return v;
return strcmp(ft1->name, ft2->name); return strcmp(ft1->name, ft2->name);
} }
static const struct hashtab_key_params filenametr_key_params = { static const struct hashtab_key_params filenametr_key_params = {
@@ -531,13 +543,13 @@ static bool add_filename_trans(struct policydb *db, const char *s,
struct filename_trans_datum *last = NULL; struct filename_trans_datum *last = NULL;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
struct filename_trans_datum *trans = struct filename_trans_datum *trans =
policydb_filenametr_search(db, &key); policydb_filenametr_search(db, &key);
#else #else
struct filename_trans_datum *trans = struct filename_trans_datum *trans =
hashtab_search(&db->filename_trans, &key); hashtab_search(&db->filename_trans, &key);
#endif #endif
while (trans) { while (trans) {
if (ebitmap_get_bit(&trans->stypes, src->value - 1)) { if (ebitmap_get_bit(&trans->stypes, src->value - 1)) {
// Duplicate, overwrite existing data and return // Duplicate, overwrite existing data and return
@@ -551,15 +563,17 @@ static bool add_filename_trans(struct policydb *db, const char *s,
} }
if (trans == NULL) { if (trans == NULL) {
trans = (struct filename_trans_datum*) kcalloc(sizeof(*trans), 1, GFP_ATOMIC); trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans),
1, GFP_ATOMIC);
struct filename_trans_key *new_key = struct filename_trans_key *new_key =
(struct filename_trans_key*) kmalloc(sizeof(*new_key), GFP_ATOMIC); (struct filename_trans_key *)kmalloc(sizeof(*new_key),
GFP_ATOMIC);
*new_key = key; *new_key = key;
new_key->name = kstrdup(key.name, GFP_ATOMIC); new_key->name = kstrdup(key.name, GFP_ATOMIC);
trans->next = last; trans->next = last;
trans->otype = def->value; trans->otype = def->value;
hashtab_insert(&db->filename_trans, new_key, hashtab_insert(&db->filename_trans, new_key, trans,
trans, filenametr_key_params); filenametr_key_params);
} }
db->compat_filename_trans_count++; db->compat_filename_trans_count++;
@@ -575,13 +589,15 @@ static bool add_filename_trans(struct policydb *db, const char *s,
hashtab_search(db->filename_trans, &key); hashtab_search(db->filename_trans, &key);
if (trans == NULL) { if (trans == NULL) {
trans = (struct filename_trans_datum*) kcalloc(sizeof(*trans), 1, GFP_ATOMIC); trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans),
1, GFP_ATOMIC);
if (!trans) { if (!trans) {
pr_err("add_filename_trans: Failed to alloc datum"); pr_err("add_filename_trans: Failed to alloc datum");
return false; return false;
} }
struct filename_trans *new_key = struct filename_trans *new_key =
(struct filename_trans*) kmalloc(sizeof(*new_key), GFP_ATOMIC); (struct filename_trans *)kmalloc(sizeof(*new_key),
GFP_ATOMIC);
if (!new_key) { if (!new_key) {
pr_err("add_filename_trans: Failed to alloc new_key"); pr_err("add_filename_trans: Failed to alloc new_key");
return false; return false;
@@ -592,7 +608,8 @@ static bool add_filename_trans(struct policydb *db, const char *s,
hashtab_insert(db->filename_trans, new_key, trans); hashtab_insert(db->filename_trans, new_key, trans);
} }
return ebitmap_set_bit(&db->filename_trans_ttypes, src->value - 1, 1) == 0; return ebitmap_set_bit(&db->filename_trans_ttypes, src->value - 1, 1) ==
0;
#endif #endif
} }
@@ -679,17 +696,70 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
0); 0);
} }
return true;
#elif defined(CONFIG_IS_HW_HISI)
/*
* Huawei use type_attr_map and type_val_to_struct.
* And use ebitmap not flex_array.
*/
size_t new_size = sizeof(struct ebitmap) * db->p_types.nprim;
struct ebitmap *new_type_attr_map =
(krealloc(db->type_attr_map, new_size, GFP_ATOMIC));
struct type_datum **new_type_val_to_struct =
krealloc(db->type_val_to_struct,
sizeof(*db->type_val_to_struct) * db->p_types.nprim,
GFP_ATOMIC);
if (!new_type_attr_map) {
pr_err("add_type: alloc type_attr_map failed\n");
return false;
}
if (!new_type_val_to_struct) {
pr_err("add_type: alloc type_val_to_struct failed\n");
return false;
}
char **new_val_to_name_types =
krealloc(db->sym_val_to_name[SYM_TYPES],
sizeof(char *) * db->symtab[SYM_TYPES].nprim,
GFP_KERNEL);
if (!new_val_to_name_types) {
pr_err("add_type: alloc val_to_name failed\n");
return false;
}
db->type_attr_map = new_type_attr_map;
ebitmap_init(&db->type_attr_map[value - 1], HISI_SELINUX_EBITMAP_RO);
ebitmap_set_bit(&db->type_attr_map[value - 1], value - 1, 1);
db->type_val_to_struct = new_type_val_to_struct;
db->type_val_to_struct[value - 1] = type;
db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types;
db->sym_val_to_name[SYM_TYPES][value - 1] = key;
int i;
for (i = 0; i < db->p_roles.nprim; ++i) {
ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1,
0);
}
return true; return true;
#else #else
// flex_array is not extensible, we need to create a new bigger one instead // flex_array is not extensible, we need to create a new bigger one instead
struct flex_array *new_type_attr_map_array = flex_array_alloc(sizeof(struct ebitmap), struct flex_array *new_type_attr_map_array =
db->p_types.nprim, GFP_ATOMIC | __GFP_ZERO); flex_array_alloc(sizeof(struct ebitmap), db->p_types.nprim,
GFP_ATOMIC | __GFP_ZERO);
struct flex_array *new_type_val_to_struct = flex_array_alloc(sizeof(struct type_datum *), struct flex_array *new_type_val_to_struct =
db->p_types.nprim, GFP_ATOMIC | __GFP_ZERO); flex_array_alloc(sizeof(struct type_datum *), db->p_types.nprim,
GFP_ATOMIC | __GFP_ZERO);
struct flex_array *new_val_to_name_types = flex_array_alloc(sizeof(char *),
db->symtab[SYM_TYPES].nprim, GFP_ATOMIC | __GFP_ZERO); struct flex_array *new_val_to_name_types =
flex_array_alloc(sizeof(char *), db->symtab[SYM_TYPES].nprim,
GFP_ATOMIC | __GFP_ZERO);
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");
@@ -707,20 +777,21 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
} }
// preallocate so we don't have to worry about the put ever failing // preallocate so we don't have to worry about the put ever failing
if (flex_array_prealloc(new_type_attr_map_array, 0, if (flex_array_prealloc(new_type_attr_map_array, 0, db->p_types.nprim,
db->p_types.nprim, GFP_ATOMIC | __GFP_ZERO)) { GFP_ATOMIC | __GFP_ZERO)) {
pr_err("add_type: prealloc type_attr_map_array failed\n"); pr_err("add_type: prealloc type_attr_map_array failed\n");
return false; return false;
} }
if (flex_array_prealloc(new_type_val_to_struct, 0, if (flex_array_prealloc(new_type_val_to_struct, 0, db->p_types.nprim,
db->p_types.nprim, GFP_ATOMIC | __GFP_ZERO)) { GFP_ATOMIC | __GFP_ZERO)) {
pr_err("add_type: prealloc type_val_to_struct_array failed\n"); pr_err("add_type: prealloc type_val_to_struct_array failed\n");
return false; return false;
} }
if (flex_array_prealloc(new_val_to_name_types, 0, if (flex_array_prealloc(new_val_to_name_types, 0,
db->symtab[SYM_TYPES].nprim, GFP_ATOMIC | __GFP_ZERO)) { db->symtab[SYM_TYPES].nprim,
GFP_ATOMIC | __GFP_ZERO)) {
pr_err("add_type: prealloc val_to_name_types failed\n"); pr_err("add_type: prealloc val_to_name_types failed\n");
return false; return false;
} }
@@ -731,25 +802,27 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
for (j = 0; j < db->type_attr_map_array->total_nr_elements; j++) { for (j = 0; j < db->type_attr_map_array->total_nr_elements; j++) {
old_elem = flex_array_get(db->type_attr_map_array, j); old_elem = flex_array_get(db->type_attr_map_array, j);
if (old_elem) if (old_elem)
flex_array_put(new_type_attr_map_array, j, flex_array_put(new_type_attr_map_array, j, old_elem,
old_elem, GFP_ATOMIC | __GFP_ZERO); GFP_ATOMIC | __GFP_ZERO);
} }
for (j = 0; j < db->type_val_to_struct_array->total_nr_elements; j++) { for (j = 0; j < db->type_val_to_struct_array->total_nr_elements; j++) {
old_elem = flex_array_get_ptr(db->type_val_to_struct_array, j); old_elem = flex_array_get_ptr(db->type_val_to_struct_array, j);
if (old_elem) if (old_elem)
flex_array_put_ptr(new_type_val_to_struct, j, flex_array_put_ptr(new_type_val_to_struct, j, old_elem,
old_elem, GFP_ATOMIC | __GFP_ZERO); GFP_ATOMIC | __GFP_ZERO);
} }
for (j = 0; j < db->symtab[SYM_TYPES].nprim; j++) { for (j = 0; j < db->symtab[SYM_TYPES].nprim; j++) {
old_elem = flex_array_get_ptr(db->sym_val_to_name[SYM_TYPES], j); old_elem =
flex_array_get_ptr(db->sym_val_to_name[SYM_TYPES], j);
if (old_elem) if (old_elem)
flex_array_put_ptr(new_val_to_name_types, j, flex_array_put_ptr(new_val_to_name_types, j, old_elem,
old_elem, GFP_ATOMIC | __GFP_ZERO); GFP_ATOMIC | __GFP_ZERO);
} }
// store the pointer of old flex arrays first, when assigning new ones we should free it // store the pointer of old flex arrays first, when assigning new ones we
// should free it
struct flex_array *old_fa; struct flex_array *old_fa;
old_fa = db->type_attr_map_array; old_fa = db->type_attr_map_array;
@@ -767,16 +840,16 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
if (old_fa) { if (old_fa) {
flex_array_free(old_fa); flex_array_free(old_fa);
} }
flex_array_put_ptr(db->type_val_to_struct_array, value - 1, flex_array_put_ptr(db->type_val_to_struct_array, value - 1, type,
type, GFP_ATOMIC | __GFP_ZERO); GFP_ATOMIC | __GFP_ZERO);
old_fa = db->sym_val_to_name[SYM_TYPES]; old_fa = db->sym_val_to_name[SYM_TYPES];
db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types; db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types;
if (old_fa) { if (old_fa) {
flex_array_free(old_fa); flex_array_free(old_fa);
} }
flex_array_put_ptr(db->sym_val_to_name[SYM_TYPES], value - 1, flex_array_put_ptr(db->sym_val_to_name[SYM_TYPES], value - 1, key,
key, GFP_ATOMIC | __GFP_ZERO); GFP_ATOMIC | __GFP_ZERO);
int i; int i;
for (i = 0; i < db->p_roles.nprim; ++i) { for (i = 0; i < db->p_roles.nprim; ++i) {
@@ -797,7 +870,7 @@ static bool set_type_state(struct policydb *db, const char *type_name,
struct type_datum *type; struct type_datum *type;
if (type_name == NULL) { if (type_name == NULL) {
struct hashtab_node *node; struct hashtab_node *node;
hashtab_for_each(db->p_types.table, node) ksu_hashtab_for_each(db->p_types.table, node)
{ {
type = (struct type_datum *)(node->datum); type = (struct type_datum *)(node->datum);
if (ebitmap_set_bit(&db->permissive_map, type->value, if (ebitmap_set_bit(&db->permissive_map, type->value,
@@ -825,6 +898,12 @@ static void add_typeattribute_raw(struct policydb *db, struct type_datum *type,
{ {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
struct ebitmap *sattr = &db->type_attr_map_array[type->value - 1]; struct ebitmap *sattr = &db->type_attr_map_array[type->value - 1];
#elif defined(CONFIG_IS_HW_HISI)
/*
* HISI_SELINUX_EBITMAP_RO is Huawei's unique features.
*/
struct ebitmap *sattr = &db->type_attr_map[type->value - 1],
HISI_SELINUX_EBITMAP_RO;
#else #else
struct ebitmap *sattr = struct ebitmap *sattr =
flex_array_get(db->type_attr_map_array, type->value - 1); flex_array_get(db->type_attr_map_array, type->value - 1);
@@ -834,7 +913,7 @@ static void add_typeattribute_raw(struct policydb *db, struct type_datum *type,
struct hashtab_node *node; struct hashtab_node *node;
struct constraint_node *n; struct constraint_node *n;
struct constraint_expr *e; struct constraint_expr *e;
hashtab_for_each(db->p_classes.table, node) ksu_hashtab_for_each(db->p_classes.table, node)
{ {
struct class_datum *cls = (struct class_datum *)(node->datum); struct class_datum *cls = (struct class_datum *)(node->datum);
for (n = cls->constraints; n; n = n->next) { for (n = cls->constraints; n; n = n->next) {

View File

@@ -11,23 +11,40 @@ elif test -d "$GKI_ROOT/drivers"; then
DRIVER_DIR="$GKI_ROOT/drivers" DRIVER_DIR="$GKI_ROOT/drivers"
else else
echo '[ERROR] "drivers/" directory is not found.' echo '[ERROR] "drivers/" directory is not found.'
echo '[+] You should modify this scrpit by yourself.' echo '[+] You should modify this script by yourself.'
exit 127 exit 127
fi fi
test -d "$GKI_ROOT/KernelSU" || git clone https://github.com/tiann/KernelSU test -d "$GKI_ROOT/KernelSU" || git clone https://github.com/tiann/KernelSU
cd "$GKI_ROOT/KernelSU" cd "$GKI_ROOT/KernelSU"
git stash && git pull 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" cd "$GKI_ROOT"
echo "[+] GKI_ROOT: $GKI_ROOT" echo "[+] GKI_ROOT: $GKI_ROOT"
echo "[+] Copy kernel su driver to $DRIVER_DIR" echo "[+] Copy kernel su driver to $DRIVER_DIR"
test -e "$DRIVER_DIR/kernelsu" || ln -sf "$GKI_ROOT/KernelSU/kernel" "$DRIVER_DIR/kernelsu" 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' echo '[+] Add kernel su driver to Makefile'
DRIVER_MAKEFILE=$DRIVER_DIR/Makefile DRIVER_MAKEFILE=$DRIVER_DIR/Makefile
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-y += kernelsu/\n" >> "$DRIVER_MAKEFILE" DRIVER_KCONFIG=$DRIVER_DIR/Kconfig
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "obj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE"
grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG"
echo '[+] Done.' echo '[+] Done.'

View File

@@ -29,7 +29,7 @@ static bool is_uid_exist(uid_t uid, void *data)
bool exist = false; bool exist = false;
list_for_each_entry (np, list, list) { list_for_each_entry (np, list, list) {
if (np->uid == uid) { if (np->uid == uid % 100000) {
exist = true; exist = true;
break; break;
} }
@@ -97,7 +97,10 @@ static void do_update_uid(struct work_struct *work)
// first, check if manager_uid exist! // first, check if manager_uid exist!
bool manager_exist = false; bool manager_exist = false;
list_for_each_entry (np, &uid_list, list) { list_for_each_entry (np, &uid_list, list) {
if (np->uid == ksu_get_manager_uid()) { // 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; manager_exist = true;
break; break;
} }
@@ -133,4 +136,4 @@ int ksu_uid_observer_init()
int ksu_uid_observer_exit() int ksu_uid_observer_exit()
{ {
return 0; return 0;
} }

18
manager/.gitignore vendored
View File

@@ -1,17 +1,9 @@
*.iml *.iml
.gradle .gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties local.properties
sign.properties .idea
.DS_Store
build
captures
.cxx
key.jks key.jks

3
manager/.idea/.gitignore generated vendored
View File

@@ -1,3 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml

1
manager/.idea/.name generated
View File

@@ -1 +0,0 @@
KernelSU

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
</component>
</project>

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="Embedded JDK" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

View File

@@ -1,37 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
</profile>
</component>

10
manager/.idea/misc.xml generated
View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

6
manager/.idea/vcs.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

View File

@@ -1,37 +1,52 @@
import com.android.build.gradle.internal.api.BaseVariantOutputImpl import com.android.build.gradle.internal.api.BaseVariantOutputImpl
plugins { plugins {
id("com.android.application") alias(libs.plugins.agp.app)
id("com.google.devtools.ksp") alias(libs.plugins.kotlin)
kotlin("android") alias(libs.plugins.ksp)
alias(libs.plugins.lsplugin.apksign)
id("kotlin-parcelize")
}
val managerVersionCode: Int by rootProject.extra
val managerVersionName: String by rootProject.extra
apksign {
storeFileProperty = "KEYSTORE_FILE"
storePasswordProperty = "KEYSTORE_PASSWORD"
keyAliasProperty = "KEY_ALIAS"
keyPasswordProperty = "KEY_PASSWORD"
} }
android { android {
namespace = "me.weishu.kernelsu" namespace = "me.weishu.kernelsu"
ndkVersion = "25.1.8937393" buildTypes {
release {
defaultConfig { isMinifyEnabled = true
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" isShrinkResources = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
ndk {
abiFilters += listOf("arm64-v8a", "x86_64")
} }
} }
lint {
checkReleaseBuilds = false
}
buildFeatures { buildFeatures {
aidl = true
buildConfig = true
compose = true compose = true
} }
composeOptions { kotlinOptions {
kotlinCompilerExtensionVersion = "1.3.2" jvmTarget = "17"
} }
packagingOptions { composeOptions {
kotlinCompilerExtensionVersion = "1.4.3"
}
packaging {
jniLibs {
useLegacyPackaging = true
}
resources { resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}" excludes += "/META-INF/{AL2.0,LGPL2.1}"
} }
@@ -39,15 +54,14 @@ android {
externalNativeBuild { externalNativeBuild {
cmake { cmake {
path(file("src/main/cpp/CMakeLists.txt")) path("src/main/cpp/CMakeLists.txt")
version = "3.18.1"
} }
} }
applicationVariants.all { applicationVariants.all {
outputs.forEach { outputs.forEach {
val output = it as BaseVariantOutputImpl val output = it as BaseVariantOutputImpl
output.outputFileName = "KernelSU_$versionName-${buildType.name}.apk" output.outputFileName = "KernelSU_${managerVersionName}_${managerVersionCode}-$name.apk"
} }
kotlin.sourceSets { kotlin.sourceSets {
@@ -59,34 +73,41 @@ android {
} }
dependencies { dependencies {
val accompanistVersion = "0.28.0" implementation(libs.androidx.activity.compose)
val composeDestinationsVersion = "1.7.27-beta" implementation(libs.androidx.navigation.compose)
implementation(platform("androidx.compose:compose-bom:2022.12.00"))
debugImplementation("androidx.compose.ui:ui-test-manifest")
debugImplementation("androidx.compose.ui:ui-tooling")
implementation("androidx.activity:activity-compose:1.6.1")
implementation("androidx.compose.material:material:1.4.0-beta02")
implementation("androidx.compose.material:material-icons-extended")
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1")
implementation("androidx.navigation:navigation-compose:2.5.3")
implementation("com.google.accompanist:accompanist-drawablepainter:$accompanistVersion")
implementation("com.google.accompanist:accompanist-navigation-animation:$accompanistVersion")
implementation("com.google.accompanist:accompanist-systemuicontroller:$accompanistVersion")
implementation("io.github.raamcosta.compose-destinations:animations-core:$composeDestinationsVersion")
implementation("io.coil-kt:coil-compose:2.2.2") implementation(platform(libs.androidx.compose.bom))
implementation("me.zhanghai.android.appiconloader:appiconloader-coil:1.5.0") implementation(libs.androidx.compose.material.icons.extended)
implementation(libs.androidx.compose.material)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.tooling.preview)
implementation("com.github.topjohnwu.libsu:core:5.0.3") debugImplementation(libs.androidx.compose.ui.test.manifest)
implementation("com.github.alorma:compose-settings-ui-m3:0.22.0") debugImplementation(libs.androidx.compose.ui.tooling)
ksp("io.github.raamcosta.compose-destinations:ksp:$composeDestinationsVersion") implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.lifecycle.viewmodel.compose)
testImplementation("junit:junit:4.13.2") implementation(libs.com.google.accompanist.drawablepainter)
androidTestImplementation("androidx.test.ext:junit:1.1.4") implementation(libs.com.google.accompanist.navigation.animation)
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0") implementation(libs.com.google.accompanist.systemuicontroller)
implementation(libs.compose.destinations.animations.core)
ksp(libs.compose.destinations.ksp)
implementation(libs.com.github.topjohnwu.libsu.core)
implementation(libs.com.github.topjohnwu.libsu.service)
implementation(libs.dev.rikka.rikkax.parcelablelist)
implementation(libs.io.coil.kt.coil.compose)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.me.zhanghai.android.appiconloader.coil)
implementation(libs.sheet.compose.dialogs.core)
implementation(libs.sheet.compose.dialogs.list)
} }

View File

@@ -1,21 +1,9 @@
# Add project specific ProGuard rules here. -dontwarn org.bouncycastle.jsse.BCSSLParameters
# You can control the set of applied configuration files using the -dontwarn org.bouncycastle.jsse.BCSSLSocket
# proguardFiles setting in build.gradle. -dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
# -dontwarn org.conscrypt.Conscrypt$Version
# For more details, see -dontwarn org.conscrypt.Conscrypt
# http://developer.android.com/guide/developing/tools/proguard.html -dontwarn org.conscrypt.ConscryptHostnameVerifier
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
# If your project uses WebView with JS, uncomment the following -dontwarn org.openjsse.javax.net.ssl.SSLSocket
# and specify the fully qualified class name to the JavaScript interface -dontwarn org.openjsse.net.ssl.OpenJSSE
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -1,24 +0,0 @@
package me.weishu.kernelsu
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("me.weishu.kernelsu", appContext.packageName)
}
}

View File

@@ -2,22 +2,17 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<application <application
android:name=".KernelSUApplication" android:name=".KernelSUApplication"
android:allowBackup="true" android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:enableOnBackInvokedCallback="true" android:enableOnBackInvokedCallback="true"
android:extractNativeLibs="true" android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.KernelSU" android:theme="@style/Theme.KernelSU"
tools:targetApi="31"> tools:targetApi="33">
<activity <activity
android:name=".ui.MainActivity" android:name=".ui.MainActivity"
android:exported="true" android:exported="true"

View File

@@ -0,0 +1,9 @@
// IKsuInterface.aidl
package me.weishu.kernelsu;
import android.content.pm.PackageInfo;
import rikka.parcelablelist.ParcelableListSlice;
interface IKsuInterface {
ParcelableListSlice<PackageInfo> getPackages(int flags);
}

View File

@@ -3,15 +3,16 @@
#include <sys/prctl.h> #include <sys/prctl.h>
#include <android/log.h> #include <android/log.h>
#include <cstring>
#include "ksu.h" #include "ksu.h"
#define LOG_TAG "KernelSu" #define LOG_TAG "KernelSU"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
extern "C" extern "C"
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_me_weishu_kernelsu_Natives_becomeManager(JNIEnv *env, jclass clazz, jstring pkg) { Java_me_weishu_kernelsu_Natives_becomeManager(JNIEnv *env, jobject, jstring pkg) {
auto cpkg = env->GetStringUTFChars(pkg, nullptr); auto cpkg = env->GetStringUTFChars(pkg, nullptr);
auto result = become_manager(cpkg); auto result = become_manager(cpkg);
env->ReleaseStringUTFChars(pkg, cpkg); env->ReleaseStringUTFChars(pkg, cpkg);
@@ -20,13 +21,13 @@ Java_me_weishu_kernelsu_Natives_becomeManager(JNIEnv *env, jclass clazz, jstring
extern "C" extern "C"
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_me_weishu_kernelsu_Natives_getVersion(JNIEnv *env, jclass clazz) { Java_me_weishu_kernelsu_Natives_getVersion(JNIEnv *env, jobject) {
return get_version(); return get_version();
} }
extern "C" extern "C"
JNIEXPORT jintArray JNICALL JNIEXPORT jintArray JNICALL
Java_me_weishu_kernelsu_Natives_getAllowList(JNIEnv *env, jclass clazz) { Java_me_weishu_kernelsu_Natives_getAllowList(JNIEnv *env, jobject) {
int uids[1024]; int uids[1024];
int size = 0; int size = 0;
bool result = get_allow_list(uids, &size); bool result = get_allow_list(uids, &size);
@@ -39,31 +40,260 @@ Java_me_weishu_kernelsu_Natives_getAllowList(JNIEnv *env, jclass clazz) {
return env->NewIntArray(0); return env->NewIntArray(0);
} }
extern "C"
JNIEXPORT jintArray JNICALL
Java_me_weishu_kernelsu_Natives_getDenyList(JNIEnv *env, jclass clazz) {
int uids[1024];
int size = 0;
bool result = get_deny_list(uids, &size);
if (result) {
// success!
auto array = env->NewIntArray(size);
env->SetIntArrayRegion(array, 0, size, uids);
return array;
}
return env->NewIntArray(0);
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_me_weishu_kernelsu_Natives_allowRoot(JNIEnv *env, jclass clazz, jint uid, jboolean allow) {
return allow_su(uid, allow);
}
extern "C" extern "C"
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_me_weishu_kernelsu_Natives_isSafeMode(JNIEnv *env, jclass clazz) { Java_me_weishu_kernelsu_Natives_isSafeMode(JNIEnv *env, jclass clazz) {
return is_safe_mode(); return is_safe_mode();
}
static void fillIntArray(JNIEnv *env, jobject list, int *data, int count) {
auto cls = env->GetObjectClass(list);
auto add = env->GetMethodID(cls, "add", "(Ljava/lang/Object;)Z");
auto integerCls = env->FindClass("java/lang/Integer");
auto constructor = env->GetMethodID(integerCls, "<init>", "(I)V");
for (int i = 0; i < count; ++i) {
auto integer = env->NewObject(integerCls, constructor, data[i]);
env->CallBooleanMethod(list, add, integer);
}
}
static void addIntToList(JNIEnv *env, jobject list, int ele) {
auto cls = env->GetObjectClass(list);
auto add = env->GetMethodID(cls, "add", "(Ljava/lang/Object;)Z");
auto integerCls = env->FindClass("java/lang/Integer");
auto constructor = env->GetMethodID(integerCls, "<init>", "(I)V");
auto integer = env->NewObject(integerCls, constructor, ele);
env->CallBooleanMethod(list, add, integer);
}
static uint64_t capListToBits(JNIEnv *env, jobject list) {
auto cls = env->GetObjectClass(list);
auto get = env->GetMethodID(cls, "get", "(I)Ljava/lang/Object;");
auto size = env->GetMethodID(cls, "size", "()I");
auto listSize = env->CallIntMethod(list, size);
auto integerCls = env->FindClass("java/lang/Integer");
auto intValue = env->GetMethodID(integerCls, "intValue", "()I");
uint64_t result = 0;
for (int i = 0; i < listSize; ++i) {
auto integer = env->CallObjectMethod(list, get, i);
int data = env->CallIntMethod(integer, intValue);
if (cap_valid(data)) {
result |= (1ULL << data);
}
}
return result;
}
static int getListSize(JNIEnv *env, jobject list) {
auto cls = env->GetObjectClass(list);
auto size = env->GetMethodID(cls, "size", "()I");
return env->CallIntMethod(list, size);
}
static void fillArrayWithList(JNIEnv *env, jobject list, int *data, int count) {
auto cls = env->GetObjectClass(list);
auto get = env->GetMethodID(cls, "get", "(I)Ljava/lang/Object;");
auto integerCls = env->FindClass("java/lang/Integer");
auto intValue = env->GetMethodID(integerCls, "intValue", "()I");
for (int i = 0; i < count; ++i) {
auto integer = env->CallObjectMethod(list, get, i);
data[i] = env->CallIntMethod(integer, intValue);
}
}
extern "C"
JNIEXPORT jobject JNICALL
Java_me_weishu_kernelsu_Natives_getAppProfile(JNIEnv *env, jobject, jstring pkg, jint uid) {
if (env->GetStringLength(pkg) > KSU_MAX_PACKAGE_NAME) {
return nullptr;
}
p_key_t key = {};
auto cpkg = env->GetStringUTFChars(pkg, nullptr);
strcpy(key, cpkg);
env->ReleaseStringUTFChars(pkg, cpkg);
app_profile profile = {};
profile.version = KSU_APP_PROFILE_VER;
strcpy(profile.key, key);
profile.current_uid = uid;
bool useDefaultProfile = !get_app_profile(key, &profile);
auto cls = env->FindClass("me/weishu/kernelsu/Natives$Profile");
auto constructor = env->GetMethodID(cls, "<init>", "()V");
auto obj = env->NewObject(cls, constructor);
auto keyField = env->GetFieldID(cls, "name", "Ljava/lang/String;");
auto currentUidField = env->GetFieldID(cls, "currentUid", "I");
auto allowSuField = env->GetFieldID(cls, "allowSu", "Z");
auto rootUseDefaultField = env->GetFieldID(cls, "rootUseDefault", "Z");
auto rootTemplateField = env->GetFieldID(cls, "rootTemplate", "Ljava/lang/String;");
auto uidField = env->GetFieldID(cls, "uid", "I");
auto gidField = env->GetFieldID(cls, "gid", "I");
auto groupsField = env->GetFieldID(cls, "groups", "Ljava/util/List;");
auto capabilitiesField = env->GetFieldID(cls, "capabilities", "Ljava/util/List;");
auto domainField = env->GetFieldID(cls, "context", "Ljava/lang/String;");
auto namespacesField = env->GetFieldID(cls, "namespace", "I");
auto nonRootUseDefaultField = env->GetFieldID(cls, "nonRootUseDefault", "Z");
auto umountModulesField = env->GetFieldID(cls, "umountModules", "Z");
env->SetObjectField(obj, keyField, env->NewStringUTF(profile.key));
env->SetIntField(obj, currentUidField, profile.current_uid);
if (useDefaultProfile) {
// no profile found, so just use default profile:
// don't allow root and use default profile!
LOGD("use default profile for: %s, %d", key, uid);
// allow_su = false
// non root use default = true
env->SetBooleanField(obj, allowSuField, false);
env->SetBooleanField(obj, nonRootUseDefaultField, true);
jobject capList = env->GetObjectField(obj, capabilitiesField);
int DEFAULT_CAPS[] = {CAP_DAC_READ_SEARCH};
for (auto i: DEFAULT_CAPS) {
addIntToList(env, capList, i);
}
return obj;
}
auto allowSu = profile.allow_su;
if (allowSu) {
env->SetBooleanField(obj, rootUseDefaultField, (jboolean) profile.rp_config.use_default);
if (strlen(profile.rp_config.template_name) > 0) {
env->SetObjectField(obj, rootTemplateField,
env->NewStringUTF(profile.rp_config.template_name));
}
env->SetIntField(obj, uidField, profile.rp_config.profile.uid);
env->SetIntField(obj, gidField, profile.rp_config.profile.gid);
jobject groupList = env->GetObjectField(obj, groupsField);
int groupCount = profile.rp_config.profile.groups_count;
if (groupCount > KSU_MAX_GROUPS) {
LOGD("kernel group count too large: %d???", groupCount);
groupCount = KSU_MAX_GROUPS;
}
fillIntArray(env, groupList, profile.rp_config.profile.groups, groupCount);
jobject capList = env->GetObjectField(obj, capabilitiesField);
for (int i = 0; i <= CAP_LAST_CAP; i++) {
if (profile.rp_config.profile.capabilities.effective & (1ULL << i)) {
addIntToList(env, capList, i);
}
}
env->SetObjectField(obj, domainField,
env->NewStringUTF(profile.rp_config.profile.selinux_domain));
env->SetIntField(obj, namespacesField, profile.rp_config.profile.namespaces);
env->SetBooleanField(obj, allowSuField, profile.allow_su);
} else {
env->SetBooleanField(obj, nonRootUseDefaultField,
(jboolean) profile.nrp_config.use_default);
env->SetBooleanField(obj, umountModulesField, profile.nrp_config.profile.umount_modules);
}
return obj;
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_me_weishu_kernelsu_Natives_setAppProfile(JNIEnv *env, jobject clazz, jobject profile) {
auto cls = env->FindClass("me/weishu/kernelsu/Natives$Profile");
auto keyField = env->GetFieldID(cls, "name", "Ljava/lang/String;");
auto currentUidField = env->GetFieldID(cls, "currentUid", "I");
auto allowSuField = env->GetFieldID(cls, "allowSu", "Z");
auto rootUseDefaultField = env->GetFieldID(cls, "rootUseDefault", "Z");
auto rootTemplateField = env->GetFieldID(cls, "rootTemplate", "Ljava/lang/String;");
auto uidField = env->GetFieldID(cls, "uid", "I");
auto gidField = env->GetFieldID(cls, "gid", "I");
auto groupsField = env->GetFieldID(cls, "groups", "Ljava/util/List;");
auto capabilitiesField = env->GetFieldID(cls, "capabilities", "Ljava/util/List;");
auto domainField = env->GetFieldID(cls, "context", "Ljava/lang/String;");
auto namespacesField = env->GetFieldID(cls, "namespace", "I");
auto nonRootUseDefaultField = env->GetFieldID(cls, "nonRootUseDefault", "Z");
auto umountModulesField = env->GetFieldID(cls, "umountModules", "Z");
auto key = env->GetObjectField(profile, keyField);
if (!key) {
return false;
}
if (env->GetStringLength((jstring) key) > KSU_MAX_PACKAGE_NAME) {
return false;
}
auto cpkg = env->GetStringUTFChars((jstring) key, nullptr);
p_key_t p_key = {};
strcpy(p_key, cpkg);
env->ReleaseStringUTFChars((jstring) key, cpkg);
auto currentUid = env->GetIntField(profile, currentUidField);
auto uid = env->GetIntField(profile, uidField);
auto gid = env->GetIntField(profile, gidField);
auto groups = env->GetObjectField(profile, groupsField);
auto capabilities = env->GetObjectField(profile, capabilitiesField);
auto domain = env->GetObjectField(profile, domainField);
auto allowSu = env->GetBooleanField(profile, allowSuField);
auto umountModules = env->GetBooleanField(profile, umountModulesField);
app_profile p = {};
p.version = KSU_APP_PROFILE_VER;
strcpy(p.key, p_key);
p.allow_su = allowSu;
p.current_uid = currentUid;
if (allowSu) {
p.rp_config.use_default = env->GetBooleanField(profile, rootUseDefaultField);
auto templateName = env->GetObjectField(profile, rootTemplateField);
if (templateName) {
auto ctemplateName = env->GetStringUTFChars((jstring) templateName, nullptr);
strcpy(p.rp_config.template_name, ctemplateName);
env->ReleaseStringUTFChars((jstring) templateName, ctemplateName);
}
p.rp_config.profile.uid = uid;
p.rp_config.profile.gid = gid;
int groups_count = getListSize(env, groups);
if (groups_count > KSU_MAX_GROUPS) {
LOGD("groups count too large: %d", groups_count);
return false;
}
p.rp_config.profile.groups_count = groups_count;
fillArrayWithList(env, groups, p.rp_config.profile.groups, groups_count);
p.rp_config.profile.capabilities.effective = capListToBits(env, capabilities);
auto cdomain = env->GetStringUTFChars((jstring) domain, nullptr);
strcpy(p.rp_config.profile.selinux_domain, cdomain);
env->ReleaseStringUTFChars((jstring) domain, cdomain);
p.rp_config.profile.namespaces = env->GetIntField(profile, namespacesField);
} else {
p.nrp_config.use_default = env->GetBooleanField(profile, nonRootUseDefaultField);
p.nrp_config.profile.umount_modules = umountModules;
}
return set_app_profile(&p);
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_me_weishu_kernelsu_Natives_uidShouldUmount(JNIEnv *env, jobject thiz, jint uid) {
return uid_should_umount(uid);
} }

View File

@@ -6,6 +6,7 @@
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h>
#include "ksu.h" #include "ksu.h"
@@ -17,10 +18,16 @@
#define CMD_GET_VERSION 2 #define CMD_GET_VERSION 2
#define CMD_ALLOW_SU 3 #define CMD_ALLOW_SU 3
#define CMD_DENY_SU 4 #define CMD_DENY_SU 4
#define CMD_GET_ALLOW_LIST 5 #define CMD_GET_SU_LIST 5
#define CMD_GET_DENY_LIST 6 #define CMD_GET_DENY_LIST 6
#define CMD_CHECK_SAFEMODE 9 #define CMD_CHECK_SAFEMODE 9
#define CMD_GET_APP_PROFILE 10
#define CMD_SET_APP_PROFILE 11
#define CMD_IS_UID_GRANTED_ROOT 12
#define CMD_IS_UID_SHOULD_UMOUNT 13
static bool ksuctl(int cmd, void* arg1, void* arg2) { static bool ksuctl(int cmd, void* arg1, void* arg2) {
int32_t result = 0; int32_t result = 0;
prctl(KERNEL_SU_OPTION, cmd, arg1, arg2, &result); prctl(KERNEL_SU_OPTION, cmd, arg1, arg2, &result);
@@ -29,7 +36,14 @@ static bool ksuctl(int cmd, void* arg1, void* arg2) {
bool become_manager(const char* pkg) { bool become_manager(const char* pkg) {
char param[128]; char param[128];
sprintf(param, "/data/data/%s", pkg); uid_t uid = getuid();
uint32_t userId = uid / 100000;
if (userId == 0) {
sprintf(param, "/data/data/%s", pkg);
} else {
snprintf(param, sizeof(param), "/data/user/%d/%s", userId, pkg);
}
return ksuctl(CMD_BECOME_MANAGER, param, nullptr); return ksuctl(CMD_BECOME_MANAGER, param, nullptr);
} }
@@ -41,19 +55,23 @@ int get_version() {
return version; return version;
} }
bool allow_su(int uid, bool allow) {
int cmd = allow ? CMD_ALLOW_SU : CMD_DENY_SU;
return ksuctl(cmd, (void*) uid, nullptr);
}
bool get_allow_list(int *uids, int *size) { bool get_allow_list(int *uids, int *size) {
return ksuctl(CMD_GET_ALLOW_LIST, uids, size); return ksuctl(CMD_GET_SU_LIST, uids, size);
}
bool get_deny_list(int *uids, int *size) {
return ksuctl(CMD_GET_DENY_LIST, uids, size);
} }
bool is_safe_mode() { bool is_safe_mode() {
return ksuctl(CMD_CHECK_SAFEMODE, nullptr, nullptr); return ksuctl(CMD_CHECK_SAFEMODE, nullptr, nullptr);
} }
bool uid_should_umount(int uid) {
bool should;
return ksuctl(CMD_IS_UID_SHOULD_UMOUNT, reinterpret_cast<void*>(uid), &should) && should;
}
bool set_app_profile(const app_profile *profile) {
return ksuctl(CMD_SET_APP_PROFILE, (void*) profile, nullptr);
}
bool get_app_profile(p_key_t key, app_profile *profile) {
return ksuctl(CMD_GET_APP_PROFILE, (void*) profile, nullptr);
}

View File

@@ -5,16 +5,76 @@
#ifndef KERNELSU_KSU_H #ifndef KERNELSU_KSU_H
#define KERNELSU_KSU_H #define KERNELSU_KSU_H
bool become_manager(const char*); #include <linux/capability.h>
bool become_manager(const char *);
int get_version(); int get_version();
bool allow_su(int uid, bool allow);
bool get_allow_list(int *uids, int *size); bool get_allow_list(int *uids, int *size);
bool get_deny_list(int *uids, int *size); bool uid_should_umount(int uid);
bool is_safe_mode(); bool is_safe_mode();
#define KSU_APP_PROFILE_VER 2
#define KSU_MAX_PACKAGE_NAME 256
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
#define KSU_MAX_GROUPS 32
#define KSU_SELINUX_DOMAIN 64
using p_key_t = char[KSU_MAX_PACKAGE_NAME];
struct root_profile {
int32_t uid;
int32_t gid;
int32_t groups_count;
int32_t groups[KSU_MAX_GROUPS];
// kernel_cap_t is u32[2] for capabilities v3
struct {
uint64_t effective;
uint64_t permitted;
uint64_t inheritable;
} capabilities;
char selinux_domain[KSU_SELINUX_DOMAIN];
int32_t namespaces;
};
struct non_root_profile {
bool umount_modules;
};
struct app_profile {
// It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this.
uint32_t version;
// this is usually the package of the app, but can be other value for special apps
char key[KSU_MAX_PACKAGE_NAME];
int32_t current_uid;
bool allow_su;
union {
struct {
bool use_default;
char template_name[KSU_MAX_PACKAGE_NAME];
struct root_profile profile;
} rp_config;
struct {
bool use_default;
struct non_root_profile profile;
} nrp_config;
};
};
bool set_app_profile(const app_profile *profile);
bool get_app_profile(p_key_t key, app_profile *profile);
#endif //KERNELSU_KSU_H #endif //KERNELSU_KSU_H

View File

@@ -1,26 +0,0 @@
package me.weishu.kernelsu;
/**
* @author weishu
* @date 2022/12/8.
*/
public final class Natives {
static {
System.loadLibrary("kernelsu");
}
// become root manager, return true if success.
public static native boolean becomeManager(String pkg);
public static native int getVersion();
// get the uid list of allowed su processes.
public static native int[] getAllowList();
public static native int[] getDenyList();
public static native boolean allowRoot(int uid, boolean allow);
public static native boolean isSafeMode();
}

View File

@@ -0,0 +1,105 @@
package me.weishu.kernelsu
import android.os.Parcelable
import androidx.annotation.Keep
import androidx.compose.runtime.Immutable
import kotlinx.parcelize.Parcelize
/**
* @author weishu
* @date 2022/12/8.
*/
object Natives {
// minimal supported kernel version
// 10915: allowlist breaking change, add app profile
// 10931: app profile struct add 'version' field
// 10946: add capabilities
// 10977: change groups_count and groups to avoid overflow write
const val MINIMAL_SUPPORTED_KERNEL = 10977
init {
System.loadLibrary("kernelsu")
}
// become root manager, return true if success.
external fun becomeManager(pkg: String?): Boolean
val version: Int
external get
// get the uid list of allowed su processes.
val allowList: IntArray
external get
val isSafeMode: Boolean
external get
external fun uidShouldUmount(uid: Int): Boolean
/**
* Get the profile of the given package.
* @param key usually the package name
* @return return null if failed.
*/
external fun getAppProfile(key: String?, uid: Int): Profile
external fun setAppProfile(profile: Profile?): Boolean
private const val NON_ROOT_DEFAULT_PROFILE_KEY = "$"
private const val ROOT_DEFAULT_PROFILE_KEY = "#"
private const val NOBODY_UID = 9999
fun setDefaultUmountModules(umountModules: Boolean): Boolean {
Profile(
NON_ROOT_DEFAULT_PROFILE_KEY,
NOBODY_UID,
false,
umountModules = umountModules
).let {
return setAppProfile(it)
}
}
fun isDefaultUmountModules(): Boolean {
getAppProfile(NON_ROOT_DEFAULT_PROFILE_KEY, NOBODY_UID).let {
return it.umountModules
}
}
fun requireNewKernel(): Boolean {
return version < MINIMAL_SUPPORTED_KERNEL
}
@Immutable
@Parcelize
@Keep
data class Profile(
// and there is a default profile for root and non-root
val name: String,
// current uid for the package, this is convivent for kernel to check
// if the package name doesn't match uid, then it should be invalidated.
val currentUid: Int = 0,
// if this is true, kernel will grant root permission to this package
val allowSu: Boolean = false,
// these are used for root profile
val rootUseDefault: Boolean = true,
val rootTemplate: String? = null,
val uid: Int = 0,
val gid: Int = 0,
val groups: List<Int> = mutableListOf(),
val capabilities: List<Int> = mutableListOf(),
val context: String = "u:r:su:s0",
val namespace: Int = Namespace.Inherited.ordinal,
val nonRootUseDefault: Boolean = true,
val umountModules: Boolean = true,
) : Parcelable {
enum class Namespace {
Inherited,
Global,
Individual,
}
constructor() : this("")
}
}

View File

@@ -0,0 +1,49 @@
package me.weishu.kernelsu.profile
/**
* @author weishu
* @date 2023/6/3.
*/
enum class Capabilities(val cap: Int, val display: String, val desc: String) {
CAP_CHOWN(0, "CHOWN", "Make arbitrary changes to file UIDs and GIDs (see chown(2))"),
CAP_DAC_OVERRIDE(1, "DAC_OVERRIDE", "Bypass file read, write, and execute permission checks"),
CAP_DAC_READ_SEARCH(2, "DAC_READ_SEARCH", "Bypass file read permission checks and directory read and execute permission checks"),
CAP_FOWNER(3, "FOWNER", "Bypass permission checks on operations that normally require the filesystem UID of the process to match the UID of the file (e.g., chmod(2), utime(2)), excluding those operations covered by CAP_DAC_OVERRIDE and CAP_DAC_READ_SEARCH"),
CAP_FSETID(4, "FSETID", "Dont clear set-user-ID and set-group-ID permission bits when a file is modified; set the set-group-ID bit for a file whose GID does not match the filesystem or any of the supplementary GIDs of the calling process"),
CAP_KILL(5, "KILL", "Bypass permission checks for sending signals (see kill(2))."),
CAP_SETGID(6, "SETGID", "Make arbitrary manipulations of process GIDs and supplementary GID list; allow setgid(2) manipulation of the callers effective and real group IDs"),
CAP_SETUID(7, "SETUID", "Make arbitrary manipulations of process UIDs (setuid(2), setreuid(2), setresuid(2), setfsuid(2)); allow changing the current process user IDs; allow changing of the current process group ID to any value in the systems range of legal group IDs"),
CAP_SETPCAP(8, "SETPCAP", "If file capabilities are supported: grant or remove any capability in the callers permitted capability set to or from any other process. (This property supersedes the obsolete notion of giving a process all capabilities by granting all capabilities in its permitted set, and of removing all capabilities from a process by granting no capabilities in its permitted set. It does not permit any actions that were not permitted before.)"),
CAP_LINUX_IMMUTABLE(9, "LINUX_IMMUTABLE", "Set the FS_APPEND_FL and FS_IMMUTABLE_FL inode flags (see chattr(1))."),
CAP_NET_BIND_SERVICE(10, "NET_BIND_SERVICE", "Bind a socket to Internet domain"),
CAP_NET_BROADCAST(11, "NET_BROADCAST", "Make socket broadcasts, and listen to multicasts"),
CAP_NET_ADMIN(12, "NET_ADMIN", "Perform various network-related operations: interface configuration, administration of IP firewall, masquerading, and accounting, modify routing tables, bind to any address for transparent proxying, set type-of-service (TOS), clear driver statistics, set promiscuous mode, enabling multicasting, use setsockopt(2) to set the following socket options: SO_DEBUG, SO_MARK, SO_PRIORITY (for a priority outside the range 0 to 6), SO_RCVBUFFORCE, and SO_SNDBUFFORCE"),
CAP_NET_RAW(13, "NET_RAW", "Use RAW and PACKET sockets"),
CAP_IPC_LOCK(14, "IPC_LOCK", "Lock memory (mlock(2), mlockall(2), mmap(2), shmctl(2))"),
CAP_IPC_OWNER(15, "IPC_OWNER", "Bypass permission checks for operations on System V IPC objects"),
CAP_SYS_MODULE(16, "SYS_MODULE", "Load and unload kernel modules (see init_module(2) and delete_module(2)); in kernels before 2.6.25, this also granted rights for various other operations related to kernel modules"),
CAP_SYS_RAWIO(17, "SYS_RAWIO", "Perform I/O port operations (iopl(2) and ioperm(2)); access /proc/kcore"),
CAP_SYS_CHROOT(18, "SYS_CHROOT", "Use chroot(2)"),
CAP_SYS_PTRACE(19, "SYS_PTRACE", "Trace arbitrary processes using ptrace(2)"),
CAP_SYS_PACCT(20, "SYS_PACCT", "Use acct(2)"),
CAP_SYS_ADMIN(21, "SYS_ADMIN", "Perform a range of system administration operations including: quotactl(2), mount(2), umount(2), swapon(2), swapoff(2), sethostname(2), and setdomainname(2); set and modify process resource limits (setrlimit(2)); perform various network-related operations (e.g., setting privileged socket options, enabling multicasting, interface configuration); perform various IPC operations (e.g., SysV semaphores, POSIX message queues, System V shared memory); allow reboot and kexec_load(2); override /proc/sys kernel tunables; perform ptrace(2) PTRACE_SECCOMP_GET_FILTER operation; perform some tracing and debugging operations (see ptrace(2)); administer the lifetime of kernel tracepoints (tracefs(5)); perform the KEYCTL_CHOWN and KEYCTL_SETPERM keyctl(2) operations; perform the following keyctl(2) operations: KEYCTL_CAPABILITIES, KEYCTL_CAPSQUASH, and KEYCTL_PKEY_ OPERATIONS; set state for the Extensible Authentication Protocol (EAP) kernel module; and override the RLIMIT_NPROC resource limit; allow ioperm/iopl access to I/O ports"),
CAP_SYS_BOOT(22, "SYS_BOOT", "Use reboot(2) and kexec_load(2), reboot and load a new kernel for later execution"),
CAP_SYS_NICE(23, "SYS_NICE", "Raise process nice value (nice(2), setpriority(2)) and change the nice value for arbitrary processes; set real-time scheduling policies for calling process, and set scheduling policies and priorities for arbitrary processes (sched_setscheduler(2), sched_setparam(2)"),
CAP_SYS_RESOURCE(24, "SYS_RESOURCE", "Override resource Limits. Set resource limits (setrlimit(2), prlimit(2)), override quota limits (quota(2), quotactl(2)), override reserved space on ext2 filesystem (ext2_ioctl(2)), override size restrictions on IPC message queues (msg(2)) and system V shared memory segments (shmget(2)), and override the /proc/sys/fs/pipe-size-max limit"),
CAP_SYS_TIME(25, "SYS_TIME", "Set system clock (settimeofday(2), stime(2), adjtimex(2)); set real-time (hardware) clock"),
CAP_SYS_TTY_CONFIG(26, "SYS_TTY_CONFIG", "Use vhangup(2); employ various privileged ioctl(2) operations on virtual terminals"),
CAP_MKNOD(27, "MKNOD", "Create special files using mknod(2)"),
CAP_LEASE(28, "LEASE", "Establish leases on arbitrary files (see fcntl(2))"),
CAP_AUDIT_WRITE(29, "AUDIT_WRITE", "Write records to kernel auditing log"),
CAP_AUDIT_CONTROL(30, "AUDIT_CONTROL", "Enable and disable kernel auditing; change auditing filter rules; retrieve auditing status and filtering rules"),
CAP_SETFCAP(31, "SETFCAP", "If file capabilities are supported: grant or remove any capability in any capability set to any file"),
CAP_MAC_OVERRIDE(32, "MAC_OVERRIDE", "Override Mandatory Access Control (MAC). Implemented for the Smack Linux Security Module (LSM)"),
CAP_MAC_ADMIN(33, "MAC_ADMIN", "Allow MAC configuration or state changes. Implemented for the Smack LSM"),
CAP_SYSLOG(34, "SYSLOG", "Perform privileged syslog(2) operations. See syslog(2) for information on which operations require privilege"),
CAP_WAKE_ALARM(35, "WAKE_ALARM", "Trigger something that will wake up the system"),
CAP_BLOCK_SUSPEND(36, "BLOCK_SUSPEND", "Employ features that can block system suspend"),
CAP_AUDIT_READ(37, "AUDIT_READ", "Allow reading the audit log via a multicast netlink socket"),
CAP_PERFMON(38, "PERFMON", "Allow performance monitoring via perf_event_open(2)"),
CAP_BPF(39, "BPF", "Allow BPF operations via bpf(2)"),
CAP_CHECKPOINT_RESTORE(40, "CHECKPOINT_RESTORE", "Allow processes to be checkpointed via checkpoint/restore in user namespace(2)"),
}

View File

@@ -0,0 +1,87 @@
package me.weishu.kernelsu.profile
/**
* @author weishu
* @date 2023/6/3.
*/
enum class Groups(val gid: Int, val display: String, val desc: String) {
ROOT(0, "root", "traditional unix root user"),
DAEMON(1, "daemon", "Traditional unix daemon owner."),
BIN(2, "bin", "Traditional unix binaries owner."),
SYS(3, "sys", "A group with the same gid on Linux/macOS/Android."),
SYSTEM(1000, "system", "system server"),
RADIO(1001, "radio", "telephony subsystem, RIL"),
BLUETOOTH(1002, "bluetooth", "bluetooth subsystem"),
GRAPHICS(1003, "graphics", "graphics devices"),
INPUT(1004, "input", "input devices"),
AUDIO(1005, "audio", "audio devices"),
CAMERA(1006, "camera", "camera devices"),
LOG(1007, "log", "log devices"),
COMPASS(1008, "compass", "compass device"),
MOUNT(1009, "mount", "mountd socket"),
WIFI(1010, "wifi", "wifi subsystem"),
ADB(1011, "adb", "android debug bridge (adbd)"),
INSTALL(1012, "install", "group for installing packages"),
MEDIA(1013, "media", "mediaserver process"),
DHCP(1014, "dhcp", "dhcp client"),
SDCARD_RW(1015, "sdcard_rw", "external storage write access"),
VPN(1016, "vpn", "vpn system"),
KEYSTORE(1017, "keystore", "keystore subsystem"),
USB(1018, "usb", "USB devices"),
DRM(1019, "drm", "DRM server"),
MDNSR(1020, "mdnsr", "MulticastDNSResponder (service discovery)"),
GPS(1021, "gps", "GPS daemon"),
UNUSED1(1022, "unused1", "deprecated, DO NOT USE"),
MEDIA_RW(1023, "media_rw", "internal media storage write access"),
MTP(1024, "mtp", "MTP USB driver access"),
UNUSED2(1025, "unused2", "deprecated, DO NOT USE"),
DRMRPC(1026, "drmrpc", "group for drm rpc"),
NFC(1027, "nfc", "nfc subsystem"),
SDCARD_R(1028, "sdcard_r", "external storage read access"),
CLAT(1029, "clat", "clat part of nat464"),
LOOP_RADIO(1030, "loop_radio", "loop radio devices"),
MEDIA_DRM(1031, "media_drm", "MediaDrm plugins"),
PACKAGE_INFO(1032, "package_info", "access to installed package details"),
SDCARD_PICS(1033, "sdcard_pics", "external storage photos access"),
SDCARD_AV(1034, "sdcard_av", "external storage audio/video access"),
SDCARD_ALL(1035, "sdcard_all", "access all users external storage"),
LOGD(1036, "logd", "log daemon"),
SHARED_RELRO(1037, "shared_relro", "creator of shared GNU RELRO files"),
DBUS(1038, "dbus", "dbus-daemon IPC broker process"),
TLSDATE(1039, "tlsdate", "tlsdate unprivileged user"),
MEDIA_EX(1040, "media_ex", "mediaextractor process"),
AUDIOSERVER(1041, "audioserver", "audioserver process"),
METRICS_COLL(1042, "metrics_coll", "metrics_collector process"),
METRICSD(1043, "metricsd", "metricsd process"),
WEBSERV(1044, "webserv", "webservd process"),
DEBUGGERD(1045, "debuggerd", "debuggerd unprivileged user"),
MEDIA_CODEC(1046, "media_codec", "media_codec process"),
CAMERASERVER(1047, "cameraserver", "cameraserver process"),
FIREWALL(1048, "firewall", "firewall process"),
TRUNKS(1049, "trunks", "trunksd process"),
NVRAM(1050, "nvram", "nvram daemon"),
DNS_TETHER(1051, "dns_tether", "dns_tether device"),
DNS_TETHER_RESERVED(1052, "dns_tether_reserved", "Reserved range for dns_tether"),
WEBVIEW_ZYGOTE(1053, "webview_zygote", "zygote process"),
WEBVIEW_USER(1054, "webview_user", "webview chromium user"),
ETHERNET(1055, "ethernet", "Ethernet"),
TOMBSTONED(1056, "tombstoned", "tombstoned process"),
GRAPHICS_RW(1057, "graphics_rw", "graphics devices"),
SHELL(2000, "shell", "adb and debug shell user"),
CACHE(2001, "cache", "cache access"),
DIAG(2002, "diag", "diagnostics"),
NET_BT_ADMIN(3001, "net_bt_admin", "bluetooth: create any socket"),
NET_BT(3002, "net_bt", "bluetooth: create sco, rfcomm or l2cap sockets"),
INET(3003, "inet", "can create AF_INET and AF_INET6 sockets"),
NET_RAW(3004, "net_raw", "can create raw INET sockets"),
NET_ADMIN(3005, "net_admin", "can configure interfaces and routing tables."),
NET_BW_STATS(3006, "net_bw_stats", "read bandwidth statistics"),
NET_BW_ACCT(3007, "net_bw_acct", "change bandwidth statistics accounting"),
NET_BT_STACK(3008, "net_bt_stack", "access to various bluetooth management functions"),
QCOM_DIAG(3009, "qcom_diag", "allow msm specific diag commands"),
EVERYBODY(9997, "everybody", "Shared external storage read/write"),
MISC(9998, "misc", "Access to misc storage"),
NOBODY(9999, "nobody", "Reserved"),
APP(10000, "app", "Access to app data"),
}

View File

@@ -0,0 +1,77 @@
package me.weishu.kernelsu.ui;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.IBinder;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import androidx.annotation.NonNull;
import com.topjohnwu.superuser.ipc.RootService;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import me.weishu.kernelsu.IKsuInterface;
import rikka.parcelablelist.ParcelableListSlice;
/**
* @author weishu
* @date 2023/4/18.
*/
public class KsuService extends RootService {
private static final String TAG = "KsuService";
class Stub extends IKsuInterface.Stub {
@Override
public ParcelableListSlice<PackageInfo> getPackages(int flags) {
List<PackageInfo> list = getInstalledPackagesAll(flags);
Log.i(TAG, "getPackages: " + list.size());
return new ParcelableListSlice<>(list);
}
}
@Override
public IBinder onBind(@NonNull Intent intent) {
return new Stub();
}
List<Integer> getUserIds() {
List<Integer> result = new ArrayList<>();
UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
List<UserHandle> userProfiles = um.getUserProfiles();
for (UserHandle userProfile : userProfiles) {
int userId = userProfile.hashCode();
result.add(userProfile.hashCode());
}
return result;
}
ArrayList<PackageInfo> getInstalledPackagesAll(int flags) {
ArrayList<PackageInfo> packages = new ArrayList<>();
for (Integer userId : getUserIds()) {
Log.i(TAG, "getInstalledPackagesAll: " + userId);
packages.addAll(getInstalledPackagesAsUser(flags, userId));
}
return packages;
}
List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
try {
PackageManager pm = getPackageManager();
Method getInstalledPackagesAsUser = pm.getClass().getDeclaredMethod("getInstalledPackagesAsUser", int.class, int.class);
return (List<PackageInfo>) getInstalledPackagesAsUser.invoke(pm, flags, userId);
} catch (Throwable e) {
Log.e(TAG, "err", e);
}
return new ArrayList<>();
}
}

View File

@@ -5,30 +5,37 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.* import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier 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.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import com.google.accompanist.navigation.animation.rememberAnimatedNavController 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.utils.isRouteOnBackStackAsState
import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.ksuApp
import me.weishu.kernelsu.ui.component.rememberDialogHostState 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.screen.appCurrentDestinationAsState
import me.weishu.kernelsu.ui.screen.destinations.Destination
import me.weishu.kernelsu.ui.screen.startAppDestination
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.LocalDialogHost
import me.weishu.kernelsu.ui.util.LocalSnackbarHost import me.weishu.kernelsu.ui.util.LocalSnackbarHost
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
@OptIn(ExperimentalAnimationApi::class, ExperimentalMaterial3Api::class) @OptIn(ExperimentalAnimationApi::class)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -58,31 +65,29 @@ class MainActivity : ComponentActivity() {
@Composable @Composable
private fun BottomBar(navController: NavHostController) { private fun BottomBar(navController: NavHostController) {
val topDestination: Destination = navController.appCurrentDestinationAsState().value val isManager = Natives.becomeManager(ksuApp.packageName)
?: NavGraphs.root.startAppDestination val fullFeatured = isManager && !Natives.requireNewKernel()
val bottomBarRoutes = remember {
BottomBarDestination.values().map { it.direction.route }
}
NavigationBar(tonalElevation = 8.dp) { NavigationBar(tonalElevation = 8.dp) {
BottomBarDestination.values().forEach { destination -> BottomBarDestination.values().forEach { destination ->
if (!fullFeatured && destination.rootRequired) return@forEach
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
NavigationBarItem( NavigationBarItem(
selected = topDestination.route == destination.direction.route, selected = isCurrentDestOnBackStack,
onClick = { onClick = {
val firstRoute = navController.backQueue.reversed().first { if (isCurrentDestOnBackStack) {
it.destination.route in bottomBarRoutes navController.popBackStack(destination.direction, false)
}.destination.route }
navController.navigate(destination.direction.route) { navController.navigate(destination.direction.route) {
popUpTo(navController.graph.findStartDestination().id) { popUpTo(NavGraphs.root.route) {
saveState = firstRoute != destination.direction.route saveState = true
} }
launchSingleTop = true launchSingleTop = true
restoreState = true restoreState = true
} }
}, },
icon = { icon = {
if (topDestination.route == destination.direction.route) { if (isCurrentDestOnBackStack) {
Icon(destination.iconSelected, stringResource(destination.label)) Icon(destination.iconSelected, stringResource(destination.label))
} else { } else {
Icon(destination.iconNotSelected, stringResource(destination.label)) Icon(destination.iconNotSelected, stringResource(destination.label))

View File

@@ -0,0 +1,126 @@
package me.weishu.kernelsu.ui.component
import android.text.method.LinkMovementMethod
import android.widget.TextView
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.window.Dialog
import androidx.core.content.res.ResourcesCompat
import androidx.core.text.HtmlCompat
import com.google.accompanist.drawablepainter.rememberDrawablePainter
import me.weishu.kernelsu.BuildConfig
import me.weishu.kernelsu.R
@Preview
@Composable
fun AboutCard() {
ElevatedCard(
modifier = Modifier
.fillMaxWidth(),
shape = RoundedCornerShape(8.dp),
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(24.dp)
) {
AboutCardContent()
}
}
}
@Composable
fun AboutDialog(showAboutDialog: MutableState<Boolean>) {
if (showAboutDialog.value) {
Dialog(onDismissRequest = { showAboutDialog.value = false }) {
AboutCard()
}
}
}
@Composable
private fun AboutCardContent() {
Column(
modifier = Modifier
.fillMaxWidth()
) {
val drawable = ResourcesCompat.getDrawable(
LocalContext.current.resources,
R.mipmap.ic_launcher,
LocalContext.current.theme
)
Row {
Image(
painter = rememberDrawablePainter(drawable),
contentDescription = "icon",
modifier = Modifier.size(40.dp)
)
Spacer(modifier = Modifier.width(12.dp))
Column {
Text(
stringResource(id = R.string.app_name),
style = MaterialTheme.typography.titleSmall,
fontSize = 18.sp
)
Text(
BuildConfig.VERSION_NAME,
style = MaterialTheme.typography.bodySmall,
fontSize = 14.sp
)
Spacer(modifier = Modifier.height(8.dp))
HtmlText(
html = stringResource(
id = R.string.about_source_code,
"<b><a href=\"https://github.com/tiann/KernelSU\">GitHub</a></b>",
"<b><a href=\"https://t.me/KernelSU\">Telegram</a></b>"
)
)
}
}
}
}
@Composable
fun HtmlText(html: String, modifier: Modifier = Modifier) {
val contentColor = LocalContentColor.current
AndroidView(
modifier = modifier,
factory = { context ->
TextView(context).also {
it.movementMethod = LinkMovementMethod.getInstance()
}
},
update = {
it.text = HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_COMPACT)
it.setTextColor(contentColor.toArgb())
}
)
}

View File

@@ -1,56 +1,113 @@
package me.weishu.kernelsu.ui.component package me.weishu.kernelsu.ui.component
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import kotlinx.coroutines.CancellableContinuation import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import me.weishu.kernelsu.ui.util.LocalDialogHost import me.weishu.kernelsu.ui.util.LocalDialogHost
import kotlin.coroutines.resume import kotlin.coroutines.resume
sealed interface DialogResult { interface DialogVisuals
object Confirmed : DialogResult
object Dismissed : DialogResult
}
interface DialogVisuals { interface LoadingDialogVisuals : DialogVisuals
interface PromptDialogVisuals : DialogVisuals {
val title: String val title: String
val content: String val content: String
}
interface ConfirmDialogVisuals : PromptDialogVisuals {
val confirm: String? val confirm: String?
val dismiss: String? val dismiss: String?
} }
interface DialogData {
sealed interface DialogData {
val visuals: DialogVisuals val visuals: DialogVisuals
}
fun confirm() interface LoadingDialogData : DialogData {
override val visuals: LoadingDialogVisuals
fun dismiss() fun dismiss()
} }
interface PromptDialogData : DialogData {
override val visuals: PromptDialogVisuals
fun dismiss()
}
interface ConfirmDialogData : PromptDialogData {
override val visuals: ConfirmDialogVisuals
fun confirm()
}
sealed interface ConfirmResult {
object Confirmed : ConfirmResult
object Canceled : ConfirmResult
}
class DialogHostState { class DialogHostState {
private data class DialogVisualsImpl( private object LoadingDialogVisualsImpl : LoadingDialogVisuals
private data class PromptDialogVisualsImpl(
override val title: String,
override val content: String
) : PromptDialogVisuals
private data class ConfirmDialogVisualsImpl(
override val title: String, override val title: String,
override val content: String, override val content: String,
override val confirm: String?, override val confirm: String?,
override val dismiss: String? override val dismiss: String?
) : DialogVisuals ) : ConfirmDialogVisuals
private data class DialogDataImpl( private data class LoadingDialogDataImpl(
override val visuals: DialogVisuals, override val visuals: LoadingDialogVisuals,
val continuation: CancellableContinuation<DialogResult> private val continuation: CancellableContinuation<Unit>,
) : DialogData { ) : 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() { override fun confirm() {
if (continuation.isActive) continuation.resume(DialogResult.Confirmed) if (continuation.isActive) continuation.resume(ConfirmResult.Confirmed)
} }
override fun dismiss() { override fun dismiss() {
if (continuation.isActive) continuation.resume(DialogResult.Dismissed) if (continuation.isActive) continuation.resume(ConfirmResult.Canceled)
} }
} }
@@ -59,16 +116,58 @@ class DialogHostState {
var currentDialogData by mutableStateOf<DialogData?>(null) var currentDialogData by mutableStateOf<DialogData?>(null)
private set private set
suspend fun showDialog( 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,
confirm: String? = null, confirm: String? = null,
dismiss: String? = null dismiss: String? = null
): DialogResult = mutex.withLock { ): ConfirmResult = mutex.withLock {
try { try {
return@withLock suspendCancellableCoroutine { continuation -> return@withLock suspendCancellableCoroutine { continuation ->
currentDialogData = DialogDataImpl( currentDialogData = ConfirmDialogDataImpl(
visuals = DialogVisualsImpl(title, content, confirm, dismiss), visuals = ConfirmDialogVisualsImpl(title, content, confirm, dismiss),
continuation = continuation continuation = continuation
) )
} }
@@ -85,82 +184,85 @@ fun rememberDialogHostState(): DialogHostState {
} }
} }
@Composable private inline fun <reified T : DialogData> DialogData?.tryInto(): T? {
fun BaseDialog( return when (this) {
state: DialogHostState = LocalDialogHost.current, is T -> this
title: @Composable (String) -> Unit, else -> null
confirmButton: @Composable (String?, () -> Unit) -> Unit, }
dismissButton: @Composable (String?, () -> Unit) -> Unit,
content: @Composable (String) -> Unit = { Text(text = it) },
) {
val currentDialogData = state.currentDialogData ?: return
val visuals = currentDialogData.visuals
AlertDialog(
onDismissRequest = {
currentDialogData.dismiss()
},
title = {
title(visuals.title)
},
text = {
content(visuals.content)
},
confirmButton = {
confirmButton(visuals.confirm, currentDialogData::confirm)
},
dismissButton = {
dismissButton(visuals.dismiss, currentDialogData::dismiss)
}
)
} }
@Composable @Composable
fun SimpleDialog( fun LoadingDialog(
state: DialogHostState = LocalDialogHost.current, state: DialogHostState = LocalDialogHost.current,
content: @Composable (String) -> Unit
) { ) {
BaseDialog( state.currentDialogData.tryInto<LoadingDialogData>() ?: return
state = state, val dialogProperties = remember {
DialogProperties(dismissOnClickOutside = false, dismissOnBackPress = false)
}
Dialog(onDismissRequest = {}, properties = dialogProperties) {
Surface(
modifier = Modifier
.size(100.dp),
shape = RoundedCornerShape(8.dp)
) {
Box(
contentAlignment = Alignment.Center,
) {
CircularProgressIndicator()
}
}
}
}
@Composable
fun PromptDialog(
state: DialogHostState = LocalDialogHost.current,
) {
val promptDialogData = state.currentDialogData.tryInto<PromptDialogData>() ?: return
val visuals = promptDialogData.visuals
AlertDialog(
onDismissRequest = {
promptDialogData.dismiss()
},
title = { title = {
Text(text = it) Text(text = visuals.title)
}, },
confirmButton = { text, confirm -> text = {
text?.let { Text(text = visuals.content)
TextButton(onClick = confirm) { },
Text(text = it) confirmButton = {
} TextButton(onClick = { promptDialogData.dismiss() }) {
Text(text = stringResource(id = android.R.string.ok))
} }
}, },
dismissButton = { text, dismiss -> dismissButton = null,
text?.let {
TextButton(onClick = dismiss) {
Text(text = it)
}
}
},
content = content
) )
} }
@Composable @Composable
fun ConfirmDialog(state: DialogHostState = LocalDialogHost.current) { fun ConfirmDialog(state: DialogHostState = LocalDialogHost.current) {
BaseDialog( val confirmDialogData = state.currentDialogData.tryInto<ConfirmDialogData>() ?: return
state = state,
title = { val visuals = confirmDialogData.visuals
Text(text = it) AlertDialog(
onDismissRequest = {
confirmDialogData.dismiss()
}, },
confirmButton = { text, confirm -> title = {
text?.let { Text(text = visuals.title)
TextButton(onClick = confirm) { },
Text(text = it) text = {
} Text(text = visuals.content)
},
confirmButton = {
TextButton(onClick = { confirmDialogData.confirm() }) {
Text(text = visuals.confirm ?: stringResource(id = android.R.string.ok))
} }
}, },
dismissButton = { text, dismiss -> dismissButton = {
text?.let { TextButton(onClick = { confirmDialogData.dismiss() }) {
TextButton(onClick = dismiss) { Text(text = visuals.dismiss ?: stringResource(id = android.R.string.cancel))
Text(text = it)
}
} }
}, },
) )

View File

@@ -10,10 +10,22 @@ 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.filled.* import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.outlined.ArrowBack import androidx.compose.material.icons.outlined.ArrowBack
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.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@@ -21,11 +33,9 @@ import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
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 me.weishu.kernelsu.R
private const val TAG = "SearchBar" private const val TAG = "SearchBar"

View File

@@ -0,0 +1,52 @@
package me.weishu.kernelsu.ui.component
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.ImageVector
@Composable
fun SwitchItem(
icon: ImageVector? = null,
title: String,
summary: String? = null,
checked: Boolean,
enabled: Boolean = true,
onCheckedChange: (Boolean) -> Unit
) {
ListItem(
headlineContent = {
Text(title)
},
leadingContent = icon?.let {
{ Icon(icon, title) }
},
trailingContent = {
Switch(checked = checked, enabled = enabled, onCheckedChange = onCheckedChange)
},
supportingContent = {
if (summary != null) {
Text(summary)
}
}
)
}
@Composable
fun RadioItem(
title: String,
selected: Boolean,
onClick: () -> Unit,
) {
ListItem(
headlineContent = {
Text(title)
},
leadingContent = {
RadioButton(selected = selected, onClick = onClick)
},
)
}

View File

@@ -0,0 +1,63 @@
package me.weishu.kernelsu.ui.component.profile
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.component.SwitchItem
@Composable
fun AppProfileConfig(
modifier: Modifier = Modifier,
fixedName: Boolean,
enabled: Boolean,
profile: Natives.Profile,
onProfileChange: (Natives.Profile) -> Unit,
) {
Column(modifier = modifier) {
if (!fixedName) {
OutlinedTextField(
label = { Text(stringResource(R.string.profile_name)) },
value = profile.name,
onValueChange = { onProfileChange(profile.copy(name = it)) }
)
}
SwitchItem(
title = stringResource(R.string.profile_umount_modules),
summary = stringResource(R.string.profile_umount_modules_summary),
checked = if (enabled) {
profile.umountModules
} else {
Natives.isDefaultUmountModules()
},
enabled = enabled,
onCheckedChange = {
onProfileChange(
profile.copy(
umountModules = it,
nonRootUseDefault = false
)
)
}
)
}
}
@Preview
@Composable
private fun AppProfileConfigPreview() {
var profile by remember { mutableStateOf(Natives.Profile("")) }
AppProfileConfig(fixedName = true, enabled = false, profile = profile) {
profile = it
}
}

View File

@@ -0,0 +1,359 @@
@file:OptIn(ExperimentalMaterial3Api::class)
package me.weishu.kernelsu.ui.component.profile
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.ArrowDropUp
import androidx.compose.material3.AssistChip
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.text.isDigitsOnly
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
import com.maxkeppeler.sheets.list.ListDialog
import com.maxkeppeler.sheets.list.models.ListOption
import com.maxkeppeler.sheets.list.models.ListSelection
import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.R
import me.weishu.kernelsu.profile.Capabilities
import me.weishu.kernelsu.profile.Groups
@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class)
@Composable
fun RootProfileConfig(
modifier: Modifier = Modifier,
fixedName: Boolean,
profile: Natives.Profile,
onProfileChange: (Natives.Profile) -> Unit,
) {
Column(modifier = modifier) {
if (!fixedName) {
OutlinedTextField(
label = { Text(stringResource(R.string.profile_name)) },
value = profile.name,
onValueChange = { onProfileChange(profile.copy(name = it)) }
)
}
var expanded by remember { mutableStateOf(false) }
val currentNamespace = when (profile.namespace) {
Natives.Profile.Namespace.Inherited.ordinal -> stringResource(R.string.profile_namespace_inherited)
Natives.Profile.Namespace.Global.ordinal -> stringResource(R.string.profile_namespace_global)
Natives.Profile.Namespace.Individual.ordinal -> stringResource(R.string.profile_namespace_individual)
else -> stringResource(R.string.profile_namespace_inherited)
}
ListItem(headlineContent = {
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = !expanded }
) {
OutlinedTextField(
modifier = Modifier
.menuAnchor()
.fillMaxWidth(),
readOnly = true,
label = { Text(stringResource(R.string.profile_namespace)) },
value = currentNamespace,
onValueChange = {},
trailingIcon = {
if (expanded) Icon(Icons.Filled.ArrowDropUp, null)
else Icon(Icons.Filled.ArrowDropDown, null)
},
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false }
) {
DropdownMenuItem(
text = { Text(stringResource(R.string.profile_namespace_inherited)) },
onClick = {
onProfileChange(profile.copy(namespace = Natives.Profile.Namespace.Inherited.ordinal))
expanded = false
},
)
DropdownMenuItem(
text = { Text(stringResource(R.string.profile_namespace_global)) },
onClick = {
onProfileChange(profile.copy(namespace = Natives.Profile.Namespace.Global.ordinal))
expanded = false
},
)
DropdownMenuItem(
text = { Text(stringResource(R.string.profile_namespace_individual)) },
onClick = {
onProfileChange(profile.copy(namespace = Natives.Profile.Namespace.Individual.ordinal))
expanded = false
},
)
}
}
})
UidPanel(uid = profile.uid, label = "uid", onUidChange = {
onProfileChange(
profile.copy(
uid = it,
rootUseDefault = false
)
)
})
UidPanel(uid = profile.gid, label = "gid", onUidChange = {
onProfileChange(
profile.copy(
gid = it,
rootUseDefault = false
)
)
})
val selectedGroups = profile.groups.ifEmpty { listOf(0) }.let { e ->
e.mapNotNull { g ->
Groups.values().find { it.gid == g }
}
}
GroupsPanel(selectedGroups) {
onProfileChange(
profile.copy(
groups = it.map { group -> group.gid }.ifEmpty { listOf(0) },
rootUseDefault = false
)
)
}
val selectedCaps = profile.capabilities.mapNotNull { e ->
Capabilities.values().find { it.cap == e }
}
CapsPanel(selectedCaps) {
onProfileChange(
profile.copy(
capabilities = it.map { cap -> cap.cap },
rootUseDefault = false
)
)
}
ListItem(headlineContent = {
val keyboardController = LocalSoftwareKeyboardController.current
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
label = { Text("SELinux context") },
value = profile.context,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Ascii,
imeAction = ImeAction.Done,
),
keyboardActions = KeyboardActions(onDone = {
keyboardController?.hide()
}),
onValueChange = {
onProfileChange(profile.copy(context = it, rootUseDefault = false))
}
)
})
}
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun GroupsPanel(selected: List<Groups>, closeSelection: (selection: Set<Groups>) -> Unit) {
var showDialog by remember { mutableStateOf(false) }
if (showDialog) {
val groups = Groups.values()
val options = groups.map { value ->
ListOption(
titleText = value.display,
selected = selected.contains(value),
)
}
val selection = HashSet(selected)
ListDialog(
state = rememberUseCaseState(visible = true, onFinishedRequest = {
closeSelection(selection)
}, onCloseRequest = {
showDialog = false
}),
selection = ListSelection.Multiple(
showCheckBoxes = true,
options = options,
maxChoices = 32, // Kernel only supports 32 groups at most
) { indecies, _ ->
// Handle selection
selection.clear()
indecies.forEach { index ->
val group = groups[index]
selection.add(group)
}
}
)
}
OutlinedCard(modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.clickable {
showDialog = true
}) {
Column(modifier = Modifier.padding(16.dp)) {
Text("groups")
FlowRow {
selected.forEach { group ->
AssistChip(
modifier = Modifier.padding(3.dp),
onClick = { /*TODO*/ },
label = { Text(group.display) })
}
}
}
}
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun CapsPanel(
selected: Collection<Capabilities>,
closeSelection: (selection: Set<Capabilities>) -> Unit
) {
var showDialog by remember { mutableStateOf(false) }
if (showDialog) {
val caps = Capabilities.values()
val options = caps.map { value ->
ListOption(
titleText = value.display,
selected = selected.contains(value),
)
}
val selection = HashSet(selected)
ListDialog(
state = rememberUseCaseState(visible = true, onFinishedRequest = {
closeSelection(selection)
}, onCloseRequest = {
showDialog = false
}),
selection = ListSelection.Multiple(
showCheckBoxes = true,
options = options
) { indecies, _ ->
// Handle selection
selection.clear()
indecies.forEach { index ->
val group = caps[index]
selection.add(group)
}
}
)
}
OutlinedCard(modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.clickable {
showDialog = true
}) {
Column(modifier = Modifier.padding(16.dp)) {
Text("Capabilities")
FlowRow {
selected.forEach { group ->
AssistChip(
modifier = Modifier.padding(3.dp),
onClick = { /*TODO*/ },
label = { Text(group.display) })
}
}
}
}
}
@OptIn(ExperimentalComposeUiApi::class)
@Composable
private fun UidPanel(uid: Int, label: String, onUidChange: (Int) -> Unit) {
ListItem(headlineContent = {
var isError by remember {
mutableStateOf(false)
}
var lastValidUid by remember {
mutableStateOf(uid)
}
val keyboardController = LocalSoftwareKeyboardController.current
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
label = { Text(label) },
value = uid.toString(),
isError = isError,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = {
keyboardController?.hide()
}),
onValueChange = {
val valid = isTextValidUid(it)
val targetUid = if (valid) it.toInt() else lastValidUid
if (valid) {
lastValidUid = it.toInt()
}
onUidChange(targetUid)
isError = !valid
}
)
})
}
@Preview
@Composable
private fun RootProfileConfigPreview() {
var profile by remember { mutableStateOf(Natives.Profile("")) }
RootProfileConfig(fixedName = true, profile = profile) {
profile = it
}
}
private fun isTextValidUid(text: String): Boolean {
return text.isNotEmpty() && text.isDigitsOnly() && text.toInt() >= 0 && text.toInt() <= Int.MAX_VALUE
}

View File

@@ -0,0 +1,308 @@
package me.weishu.kernelsu.ui.screen
import android.util.Log
import androidx.annotation.StringRes
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountCircle
import androidx.compose.material.icons.filled.Android
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.ArrowDropUp
import androidx.compose.material.icons.filled.Security
import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.FilterChip
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import coil.request.ImageRequest
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.launch
import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.component.SwitchItem
import me.weishu.kernelsu.ui.component.profile.AppProfileConfig
import me.weishu.kernelsu.ui.component.profile.RootProfileConfig
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel
/**
* @author weishu
* @date 2023/5/16.
*/
@Destination
@Composable
fun AppProfileScreen(
navigator: DestinationsNavigator,
appInfo: SuperUserViewModel.AppInfo,
) {
val context = LocalContext.current
val snackbarHost = LocalSnackbarHost.current
val scope = rememberCoroutineScope()
val failToUpdateAppProfile =
stringResource(R.string.failed_to_update_app_profile).format(appInfo.label)
val packageName = appInfo.packageName
var profile by rememberSaveable {
mutableStateOf(Natives.getAppProfile(packageName, appInfo.uid))
}
Log.i("mylog", "profile: $profile")
Scaffold(
topBar = { TopBar { navigator.popBackStack() } }
) { paddingValues ->
AppProfileInner(
modifier = Modifier
.padding(paddingValues)
.verticalScroll(rememberScrollState()),
packageName = appInfo.packageName,
appLabel = appInfo.label,
appIcon = {
AsyncImage(
model = ImageRequest.Builder(context)
.data(appInfo.packageInfo)
.crossfade(true)
.build(),
contentDescription = appInfo.label,
modifier = Modifier
.padding(4.dp)
.width(48.dp)
.height(48.dp)
)
},
profile = profile,
onProfileChange = {
scope.launch {
if (!Natives.setAppProfile(it)) {
snackbarHost.showSnackbar(failToUpdateAppProfile.format(appInfo.uid))
} else {
profile = it
}
}
},
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun AppProfileInner(
modifier: Modifier = Modifier,
packageName: String,
appLabel: String,
appIcon: @Composable () -> Unit,
profile: Natives.Profile,
onProfileChange: (Natives.Profile) -> Unit,
) {
val isRootGranted = profile.allowSu
Column(modifier = modifier) {
ListItem(
headlineContent = { Text(appLabel) },
supportingContent = { Text(packageName) },
leadingContent = appIcon,
)
SwitchItem(
icon = Icons.Filled.Security,
title = stringResource(id = R.string.superuser),
checked = isRootGranted,
onCheckedChange = { onProfileChange(profile.copy(allowSu = it)) },
)
Crossfade(targetState = isRootGranted, label = "") { current ->
Column {
if (current) {
val initialMode = if (profile.rootUseDefault) {
Mode.Default
} else if (profile.rootTemplate != null) {
Mode.Template
} else {
Mode.Custom
}
var mode by remember {
mutableStateOf(initialMode)
}
ProfileBox(mode, false) {
// template mode shouldn't change profile here!
if (it == Mode.Default || it == Mode.Custom) {
onProfileChange(profile.copy(rootUseDefault = it == Mode.Default))
}
mode = it
}
Crossfade(targetState = mode, label = "") { currentMode ->
if (currentMode == Mode.Template) {
var expanded by remember { mutableStateOf(false) }
val templateNone = "None"
var template by rememberSaveable {
mutableStateOf(
profile.rootTemplate
?: templateNone
)
}
ListItem(headlineContent = {
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = it },
) {
OutlinedTextField(
modifier = Modifier.menuAnchor(),
readOnly = true,
label = { Text(stringResource(R.string.profile_template)) },
value = template,
onValueChange = {
if (template != templateNone) {
onProfileChange(
profile.copy(
rootTemplate = it,
rootUseDefault = false
)
)
template = it
}
},
trailingIcon = {
if (expanded) Icon(Icons.Filled.ArrowDropUp, null)
else Icon(Icons.Filled.ArrowDropDown, null)
},
)
// TODO: Template
}
})
} else if (mode == Mode.Custom) {
RootProfileConfig(
fixedName = true,
profile = profile,
onProfileChange = onProfileChange
)
}
}
} else {
val mode = if (profile.nonRootUseDefault) Mode.Default else Mode.Custom
ProfileBox(mode, false) {
onProfileChange(profile.copy(nonRootUseDefault = (it == Mode.Default)))
}
Crossfade(targetState = mode, label = "") { currentMode ->
val modifyEnabled = currentMode == Mode.Custom
AppProfileConfig(
fixedName = true,
profile = profile,
enabled = modifyEnabled,
onProfileChange = onProfileChange
)
}
}
}
}
}
}
private enum class Mode(@StringRes private val res: Int) {
Default(R.string.profile_default),
Template(R.string.profile_template),
Custom(R.string.profile_custom);
val text: String
@Composable get() = stringResource(res)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun TopBar(onBack: () -> Unit) {
TopAppBar(
title = {
Text(stringResource(R.string.profile))
},
navigationIcon = {
IconButton(
onClick = onBack
) { Icon(Icons.Filled.ArrowBack, contentDescription = null) }
},
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun ProfileBox(
mode: Mode,
hasTemplate: Boolean,
onModeChange: (Mode) -> Unit,
) {
ListItem(
headlineContent = { Text(stringResource(R.string.profile)) },
supportingContent = { Text(mode.text) },
leadingContent = { Icon(Icons.Filled.AccountCircle, null) },
)
Divider(thickness = Dp.Hairline)
ListItem(headlineContent = {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
FilterChip(
selected = mode == Mode.Default,
label = { Text(stringResource(R.string.profile_default)) },
onClick = { onModeChange(Mode.Default) },
)
if (hasTemplate) {
FilterChip(
selected = mode == Mode.Template,
label = { Text(stringResource(R.string.profile_template)) },
onClick = { onModeChange(Mode.Template) },
)
}
FilterChip(
selected = mode == Mode.Custom,
label = { Text(stringResource(R.string.profile_custom)) },
onClick = { onModeChange(Mode.Custom) },
)
}
})
}
@Preview
@Composable
private fun AppProfilePreview() {
var profile by remember { mutableStateOf(Natives.Profile("")) }
AppProfileInner(
packageName = "icu.nullptr.test",
appLabel = "Test",
appIcon = { Icon(Icons.Filled.Android, null) },
profile = profile,
onProfileChange = {
profile = it
},
)
}

View File

@@ -15,9 +15,10 @@ enum class BottomBarDestination(
val direction: DirectionDestinationSpec, val direction: DirectionDestinationSpec,
@StringRes val label: Int, @StringRes val label: Int,
val iconSelected: ImageVector, val iconSelected: ImageVector,
val iconNotSelected: ImageVector val iconNotSelected: ImageVector,
val rootRequired: Boolean,
) { ) {
Home(HomeScreenDestination, R.string.home, Icons.Filled.Home, Icons.Outlined.Home), Home(HomeScreenDestination, R.string.home, Icons.Filled.Home, Icons.Outlined.Home, false),
SuperUser(SuperUserScreenDestination, R.string.superuser, Icons.Filled.Security, Icons.Outlined.Security), SuperUser(SuperUserScreenDestination, R.string.superuser, Icons.Filled.Security, Icons.Outlined.Security, true),
Module(ModuleScreenDestination, R.string.module, Icons.Filled.Apps, Icons.Outlined.Apps) Module(ModuleScreenDestination, R.string.module, Icons.Filled.Apps, Icons.Outlined.Apps, true)
} }

View File

@@ -1,7 +1,5 @@
package me.weishu.kernelsu.ui.screen package me.weishu.kernelsu.ui.screen
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.os.Build import android.os.Build
import android.os.PowerManager import android.os.PowerManager
@@ -30,16 +28,11 @@ import androidx.compose.ui.unit.dp
import com.ramcosta.composedestinations.annotation.Destination 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.launch
import me.weishu.kernelsu.* import me.weishu.kernelsu.*
import me.weishu.kernelsu.R import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.screen.destinations.SettingScreenDestination import me.weishu.kernelsu.ui.screen.destinations.SettingScreenDestination
import me.weishu.kernelsu.ui.util.LocalSnackbarHost import me.weishu.kernelsu.ui.util.*
import me.weishu.kernelsu.ui.util.reboot
import me.weishu.kernelsu.ui.util.getSELinuxStatus
import me.weishu.kernelsu.ui.util.install
@OptIn(ExperimentalMaterial3Api::class)
@RootNavGraph(start = true) @RootNavGraph(start = true)
@Destination @Destination
@Composable @Composable
@@ -63,10 +56,20 @@ fun HomeScreen(navigator: DestinationsNavigator) {
SideEffect { SideEffect {
if (isManager) install() if (isManager) install()
} }
val ksuVersion = if (isManager) Natives.getVersion() else null val ksuVersion = if (isManager) Natives.version else null
StatusCard(kernelVersion, ksuVersion) StatusCard(kernelVersion, ksuVersion)
if (isManager && Natives.requireNewKernel()) {
WarningCard(
stringResource(id = R.string.require_kernel_version).format(
ksuVersion,
Natives.MINIMAL_SUPPORTED_KERNEL
)
)
}
InfoCard() InfoCard()
DonateCard()
LearnMoreCard()
Spacer(Modifier) Spacer(Modifier)
} }
} }
@@ -102,7 +105,8 @@ private fun TopBar(onSettingsClick: () -> Unit) {
RebootDropdownItem(id = R.string.reboot) RebootDropdownItem(id = R.string.reboot)
val pm = LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager? val pm =
LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && pm?.isRebootingUserspaceSupported == true) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && pm?.isRebootingUserspaceSupported == true) {
RebootDropdownItem(id = R.string.reboot_userspace, reason = "userspace") RebootDropdownItem(id = R.string.reboot_userspace, reason = "userspace")
} }
@@ -145,7 +149,7 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
) { ) {
when { when {
ksuVersion != null -> { ksuVersion != null -> {
val appendText = if (Natives.isSafeMode()) { val appendText = if (Natives.isSafeMode) {
" [${stringResource(id = R.string.safe_mode)}]" " [${stringResource(id = R.string.safe_mode)}]"
} else { } else {
"" ""
@@ -161,8 +165,22 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
text = stringResource(R.string.home_working_version, ksuVersion), text = stringResource(R.string.home_working_version, ksuVersion),
style = MaterialTheme.typography.bodyMedium style = MaterialTheme.typography.bodyMedium
) )
Spacer(Modifier.height(4.dp))
Text(
text = stringResource(
R.string.home_superuser_count,
getSuperuserCount()
),
style = MaterialTheme.typography.bodyMedium
)
Spacer(Modifier.height(4.dp))
Text(
text = stringResource(R.string.home_module_count, getModuleCount()),
style = MaterialTheme.typography.bodyMedium
)
} }
} }
kernelVersion.isGKI() -> { kernelVersion.isGKI() -> {
Icon(Icons.Outlined.Warning, stringResource(R.string.home_not_installed)) Icon(Icons.Outlined.Warning, stringResource(R.string.home_not_installed))
Column(Modifier.padding(start = 20.dp)) { Column(Modifier.padding(start = 20.dp)) {
@@ -177,6 +195,7 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
) )
} }
} }
else -> { else -> {
Icon(Icons.Outlined.Block, stringResource(R.string.home_unsupported)) Icon(Icons.Outlined.Block, stringResource(R.string.home_unsupported))
Column(Modifier.padding(start = 20.dp)) { Column(Modifier.padding(start = 20.dp)) {
@@ -197,11 +216,93 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
} }
} }
@Composable
fun WarningCard(message: String) {
ElevatedCard(
colors = CardDefaults.elevatedCardColors(
containerColor = MaterialTheme.colorScheme.error
)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(24.dp),
verticalAlignment = Alignment.CenterVertically
) {
Column() {
Text(
text = message,
style = MaterialTheme.typography.bodyMedium
)
}
}
}
}
@Composable
fun LearnMoreCard() {
val uriHandler = LocalUriHandler.current
val url = stringResource(R.string.home_learn_kernelsu_url)
ElevatedCard {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable {
uriHandler.openUri(url)
}
.padding(24.dp),
verticalAlignment = Alignment.CenterVertically
) {
Column() {
Text(
text = stringResource(R.string.home_learn_kernelsu),
style = MaterialTheme.typography.titleSmall
)
Spacer(Modifier.height(4.dp))
Text(
text = stringResource(R.string.home_click_to_learn_kernelsu),
style = MaterialTheme.typography.bodyMedium
)
}
}
}
}
@Composable
fun DonateCard() {
val uriHandler = LocalUriHandler.current
ElevatedCard {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable {
uriHandler.openUri("https://patreon.com/weishu")
}
.padding(24.dp),
verticalAlignment = Alignment.CenterVertically
) {
Column() {
Text(
text = stringResource(R.string.home_support_title),
style = MaterialTheme.typography.titleSmall
)
Spacer(Modifier.height(4.dp))
Text(
text = stringResource(R.string.home_support_content),
style = MaterialTheme.typography.bodyMedium
)
}
}
}
}
@Composable @Composable
private fun InfoCard() { private fun InfoCard() {
val context = LocalContext.current val context = LocalContext.current
val snackbarHost = LocalSnackbarHost.current
val scope = rememberCoroutineScope()
ElevatedCard { ElevatedCard {
Column( Column(
@@ -221,39 +322,19 @@ private fun InfoCard() {
InfoCardItem(stringResource(R.string.home_kernel), uname.release) InfoCardItem(stringResource(R.string.home_kernel), uname.release)
Spacer(Modifier.height(24.dp)) Spacer(Modifier.height(16.dp))
InfoCardItem(stringResource(R.string.home_manager_version), getManagerVersion(context)) InfoCardItem(stringResource(R.string.home_manager_version), getManagerVersion(context))
Spacer(Modifier.height(24.dp)) Spacer(Modifier.height(16.dp))
InfoCardItem(stringResource(R.string.home_api), Build.VERSION.SDK_INT.toString())
Spacer(Modifier.height(24.dp))
InfoCardItem(stringResource(R.string.home_abi), Build.SUPPORTED_ABIS.joinToString(", "))
Spacer(Modifier.height(24.dp))
InfoCardItem(stringResource(R.string.home_fingerprint), Build.FINGERPRINT) InfoCardItem(stringResource(R.string.home_fingerprint), Build.FINGERPRINT)
Spacer(Modifier.height(24.dp)) Spacer(Modifier.height(16.dp))
InfoCardItem(stringResource(R.string.home_securitypatch), Build.VERSION.SECURITY_PATCH)
Spacer(Modifier.height(24.dp))
InfoCardItem(stringResource(R.string.home_selinux_status), getSELinuxStatus()) InfoCardItem(stringResource(R.string.home_selinux_status), getSELinuxStatus())
val copiedMessage = stringResource(R.string.home_copied_to_clipboard)
TextButton(
modifier = Modifier.align(Alignment.End),
onClick = {
val cm = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
cm.setPrimaryClip(ClipData.newPlainText("KernelSU", contents.toString()))
scope.launch { snackbarHost.showSnackbar(copiedMessage) }
},
content = { Text(stringResource(android.R.string.copy)) }
)
} }
} }
} }
fun getManagerVersion(context: Context) : String { fun getManagerVersion(context: Context): String {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0) val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
return "${packageInfo.versionName} (${packageInfo.versionCode})" return "${packageInfo.versionName} (${packageInfo.versionCode})"
} }

View File

@@ -35,7 +35,6 @@ import java.util.*
* @author weishu * @author weishu
* @date 2023/1/1. * @date 2023/1/1.
*/ */
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@Destination @Destination
fun InstallScreen(navigator: DestinationsNavigator, uri: Uri) { fun InstallScreen(navigator: DestinationsNavigator, uri: Uri) {

View File

@@ -34,12 +34,11 @@ 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.ConfirmDialog
import me.weishu.kernelsu.ui.component.DialogResult import me.weishu.kernelsu.ui.component.ConfirmResult
import me.weishu.kernelsu.ui.screen.destinations.InstallScreenDestination import me.weishu.kernelsu.ui.screen.destinations.InstallScreenDestination
import me.weishu.kernelsu.ui.util.* import me.weishu.kernelsu.ui.util.*
import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Destination @Destination
@Composable @Composable
fun ModuleScreen(navigator: DestinationsNavigator) { fun ModuleScreen(navigator: DestinationsNavigator) {
@@ -51,11 +50,10 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
} }
} }
val isSafeMode = Natives.isSafeMode() val isSafeMode = Natives.isSafeMode
val isKSUVersionInvalid = Natives.getVersion() < 0
val hasMagisk = hasMagisk() val hasMagisk = hasMagisk()
val hideInstallButton = isSafeMode || isKSUVersionInvalid || hasMagisk val hideInstallButton = isSafeMode || hasMagisk
Scaffold(topBar = { Scaffold(topBar = {
TopBar() TopBar()
@@ -94,11 +92,6 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
ConfirmDialog() ConfirmDialog()
when { when {
isKSUVersionInvalid -> {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(stringResource(R.string.require_kernel_version_8))
}
}
hasMagisk -> { hasMagisk -> {
Box( Box(
modifier = Modifier modifier = Modifier
@@ -143,13 +136,13 @@ private fun ModuleList(viewModel: ModuleViewModel, modifier: Modifier = Modifier
val snackBarHost = LocalSnackbarHost.current val snackBarHost = LocalSnackbarHost.current
suspend fun onModuleUninstall(module: ModuleViewModel.ModuleInfo) { suspend fun onModuleUninstall(module: ModuleViewModel.ModuleInfo) {
val dialogResult = dialogHost.showDialog( val confirmResult = dialogHost.showConfirm(
moduleStr, moduleStr,
content = moduleUninstallConfirm.format(module.name), content = moduleUninstallConfirm.format(module.name),
confirm = uninstall, confirm = uninstall,
dismiss = cancel dismiss = cancel
) )
if (dialogResult != DialogResult.Confirmed) { if (confirmResult != ConfirmResult.Confirmed) {
return return
} }

View File

@@ -2,33 +2,38 @@ package me.weishu.kernelsu.ui.screen
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.BugReport
import androidx.compose.material.icons.filled.ContactPage
import androidx.compose.material.icons.filled.RemoveModerator
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
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.tooling.preview.Preview
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import com.alorma.compose.settings.ui.*
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 kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.weishu.kernelsu.BuildConfig import me.weishu.kernelsu.BuildConfig
import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.R import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.component.SimpleDialog import me.weishu.kernelsu.ui.component.AboutDialog
import me.weishu.kernelsu.ui.util.LinkifyText import me.weishu.kernelsu.ui.component.LoadingDialog
import me.weishu.kernelsu.ui.component.SwitchItem
import me.weishu.kernelsu.ui.util.LocalDialogHost import me.weishu.kernelsu.ui.util.LocalDialogHost
import me.weishu.kernelsu.ui.util.getBugreportFile import me.weishu.kernelsu.ui.util.getBugreportFile
/** /**
* @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) {
@@ -40,46 +45,70 @@ fun SettingScreen(navigator: DestinationsNavigator) {
}) })
} }
) { paddingValues -> ) { paddingValues ->
LoadingDialog()
SimpleDialog { val showAboutDialog = remember { mutableStateOf(false) }
SupportCard() AboutDialog(showAboutDialog)
}
Column(modifier = Modifier.padding(paddingValues)) { Column(modifier = Modifier.padding(paddingValues)) {
val context = LocalContext.current val context = LocalContext.current
SettingsMenuLink(title = { val scope = rememberCoroutineScope()
Text(stringResource(id = R.string.send_log)) val dialogHost = LocalDialogHost.current
},
onClick = {
val bugreport = getBugreportFile(context)
val uri: Uri = FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.fileprovider", bugreport)
val shareIntent = Intent(Intent.ACTION_SEND) var umountChecked by rememberSaveable {
shareIntent.putExtra(Intent.EXTRA_STREAM, uri) mutableStateOf(Natives.isDefaultUmountModules())
shareIntent.setDataAndType(uri, "application/zip") }
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) SwitchItem(
icon = Icons.Filled.RemoveModerator,
title = stringResource(id = R.string.settings_umount_modules_default),
summary = stringResource(id = R.string.settings_umount_modules_default_summary),
checked = umountChecked
) {
if (Natives.setDefaultUmountModules(it)) {
umountChecked = it
}
}
context.startActivity( ListItem(
Intent.createChooser( leadingContent = { Icon(Icons.Filled.BugReport, stringResource(id = R.string.send_log)) },
shareIntent, headlineContent = { Text(stringResource(id = R.string.send_log)) },
context.getString(R.string.send_log) modifier = Modifier.clickable {
scope.launch {
val bugreport = dialogHost.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)
)
) )
) }
} }
) )
val about = stringResource(id = R.string.about) val about = stringResource(id = R.string.about)
val ok = stringResource(id = android.R.string.ok) ListItem(
val scope = rememberCoroutineScope() leadingContent = { Icon(Icons.Filled.ContactPage, stringResource(id = R.string.about)) },
val dialogHost = LocalDialogHost.current headlineContent = { Text(about) },
SettingsMenuLink(title = { modifier = Modifier.clickable {
Text(about) showAboutDialog.value = true
},
onClick = {
scope.launch {
dialogHost.showDialog(about, content = "unused", confirm = ok)
}
} }
) )
} }
@@ -98,19 +127,3 @@ private fun TopBar(onBack: () -> Unit = {}) {
}, },
) )
} }
@Preview
@Composable
private fun SupportCard() {
Column(
modifier = Modifier
.fillMaxWidth()
) {
CompositionLocalProvider(LocalTextStyle provides MaterialTheme.typography.bodyMedium) {
LinkifyText("Author: weishu")
LinkifyText("Github: https://github.com/tiann/KernelSU")
LinkifyText("Telegram: https://t.me/KernelSU")
LinkifyText("QQ: https://pd.qq.com/s/8lipl1brp")
}
}
}

View File

@@ -1,8 +1,11 @@
package me.weishu.kernelsu.ui.screen package me.weishu.kernelsu.ui.screen
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
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.material.ExperimentalMaterialApi import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.MoreVert
@@ -11,30 +14,32 @@ 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.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
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.Color
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.TextStyle
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.AsyncImage import coil.compose.AsyncImage
import coil.request.ImageRequest import coil.request.ImageRequest
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
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.util.LocalSnackbarHost import me.weishu.kernelsu.ui.screen.destinations.AppProfileScreenDestination
import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel
import java.util.*
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class)
@Destination @Destination
@Composable @Composable
fun SuperUserScreen() { fun SuperUserScreen(navigator: DestinationsNavigator) {
val viewModel = viewModel<SuperUserViewModel>() val viewModel = viewModel<SuperUserViewModel>()
val snackbarHost = LocalSnackbarHost.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
@@ -91,6 +96,8 @@ fun SuperUserScreen() {
} }
) { 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() } },
@@ -100,19 +107,12 @@ fun SuperUserScreen() {
.padding(innerPadding) .padding(innerPadding)
.pullRefresh(refreshState) .pullRefresh(refreshState)
) { ) {
val failMessage = stringResource(R.string.superuser_failed_to_grant_root)
LazyColumn(Modifier.fillMaxSize()) { LazyColumn(Modifier.fillMaxSize()) {
items(viewModel.appList, key = { it.packageName }) { app -> items(viewModel.appList, key = { it.packageName + it.uid }) { app ->
var isChecked by rememberSaveable(app) { mutableStateOf(app.onAllowList) } AppItem(app) {
AppItem(app, isChecked) { checked -> navigator.navigate(AppProfileScreenDestination(app))
val success = Natives.allowRoot(app.uid, checked)
if (success) {
isChecked = checked
} else scope.launch {
snackbarHost.showSnackbar(failMessage.format(app.uid))
}
} }
} }
} }
@@ -125,20 +125,36 @@ fun SuperUserScreen() {
} }
} }
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalLayoutApi::class)
@Composable @Composable
private fun AppItem( private fun AppItem(
app: SuperUserViewModel.AppInfo, app: SuperUserViewModel.AppInfo,
isChecked: Boolean, onClickListener: () -> Unit,
onCheckedChange: (Boolean) -> Unit
) { ) {
ListItem( ListItem(
headlineText = { Text(app.label) }, modifier = Modifier.clickable(onClick = onClickListener),
supportingText = { Text(app.packageName) }, headlineContent = { Text(app.label) },
supportingContent = {
Column {
Text(app.packageName)
FlowRow {
if (app.allowSu) {
LabelText(label = "ROOT")
} else {
if (Natives.uidShouldUmount(app.uid)) {
LabelText(label = "UMOUNT")
}
}
if (app.hasCustomProfile) {
LabelText(label = "CUSTOM")
}
}
}
},
leadingContent = { leadingContent = {
AsyncImage( AsyncImage(
model = ImageRequest.Builder(LocalContext.current) model = ImageRequest.Builder(LocalContext.current)
.data(app.icon) .data(app.packageInfo)
.crossfade(true) .crossfade(true)
.build(), .build(),
contentDescription = app.label, contentDescription = app.label,
@@ -148,12 +164,26 @@ private fun AppItem(
.height(48.dp) .height(48.dp)
) )
}, },
trailingContent = {
Switch(
checked = isChecked,
onCheckedChange = onCheckedChange,
modifier = Modifier.padding(4.dp)
)
}
) )
} }
@Composable
fun LabelText(label: String) {
Box(
modifier = Modifier
.padding(top = 4.dp, end = 4.dp)
.background(
Color.Black,
shape = RoundedCornerShape(4.dp)
)
) {
Text(
text = label,
modifier = Modifier.padding(vertical = 2.dp, horizontal = 5.dp),
style = TextStyle(
fontSize = 8.sp,
color = Color.White,
)
)
}
}

View File

@@ -7,7 +7,10 @@ import com.topjohnwu.superuser.CallbackList
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.ShellUtils import com.topjohnwu.superuser.ShellUtils
import me.weishu.kernelsu.BuildConfig import me.weishu.kernelsu.BuildConfig
import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.ksuApp import me.weishu.kernelsu.ksuApp
import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel
import org.json.JSONArray
import java.io.File import java.io.File
@@ -21,6 +24,14 @@ private fun getKsuDaemonPath(): String {
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libksud.so" return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libksud.so"
} }
object KsuCli {
val SHELL: Shell = createRootShell()
}
fun getRootShell(): Shell {
return KsuCli.SHELL
}
fun createRootShell(): Shell { fun createRootShell(): Shell {
Shell.enableVerboseLogging = BuildConfig.DEBUG Shell.enableVerboseLogging = BuildConfig.DEBUG
val builder = Shell.Builder.create() val builder = Shell.Builder.create()
@@ -33,7 +44,7 @@ fun createRootShell(): Shell {
} }
fun execKsud(args: String): Boolean { fun execKsud(args: String): Boolean {
val shell = createRootShell() val shell = getRootShell()
return ShellUtils.fastCmdResult(shell, "${getKsuDaemonPath()} $args") return ShellUtils.fastCmdResult(shell, "${getKsuDaemonPath()} $args")
} }
@@ -44,12 +55,25 @@ fun install() {
} }
fun listModules(): String { fun listModules(): String {
val shell = createRootShell() val shell = getRootShell()
val out = shell.newJob().add("${getKsuDaemonPath()} module list").to(ArrayList(), null).exec().out val out =
shell.newJob().add("${getKsuDaemonPath()} module list").to(ArrayList(), null).exec().out
return out.joinToString("\n").ifBlank { "[]" } return out.joinToString("\n").ifBlank { "[]" }
} }
fun getModuleCount(): Int {
val result = listModules()
runCatching {
val array = JSONArray(result)
return array.length()
}.getOrElse { return 0 }
}
fun getSuperuserCount(): Int {
return Natives.allowList.size
}
fun toggleModule(id: String, enable: Boolean): Boolean { fun toggleModule(id: String, enable: Boolean): Boolean {
val cmd = if (enable) { val cmd = if (enable) {
"module enable $id" "module enable $id"
@@ -61,14 +85,14 @@ fun toggleModule(id: String, enable: Boolean): Boolean {
return result return result
} }
fun uninstallModule(id: String) : Boolean { fun uninstallModule(id: String): Boolean {
val cmd = "module uninstall $id" val cmd = "module uninstall $id"
val result = execKsud(cmd) val result = execKsud(cmd)
Log.i(TAG, "uninstall module $id result: $result") Log.i(TAG, "uninstall module $id result: $result")
return result return result
} }
fun installModule(uri: Uri, onFinish: (Boolean)->Unit, onOutput: (String) -> Unit) : Boolean { fun installModule(uri: Uri, onFinish: (Boolean) -> Unit, onOutput: (String) -> Unit): Boolean {
val resolver = ksuApp.contentResolver val resolver = ksuApp.contentResolver
with(resolver.openInputStream(uri)) { with(resolver.openInputStream(uri)) {
val file = File(ksuApp.cacheDir, "module.zip") val file = File(ksuApp.cacheDir, "module.zip")
@@ -77,7 +101,7 @@ fun installModule(uri: Uri, onFinish: (Boolean)->Unit, onOutput: (String) -> Uni
} }
val cmd = "module install ${file.absolutePath}" val cmd = "module install ${file.absolutePath}"
val shell = createRootShell() val shell = getRootShell()
val callbackList: CallbackList<String?> = object : CallbackList<String?>() { val callbackList: CallbackList<String?> = object : CallbackList<String?>() {
override fun onAddElement(s: String?) { override fun onAddElement(s: String?) {
@@ -85,7 +109,8 @@ fun installModule(uri: Uri, onFinish: (Boolean)->Unit, onOutput: (String) -> Uni
} }
} }
val result = shell.newJob().add("${getKsuDaemonPath()} $cmd").to(callbackList, callbackList).exec() val result =
shell.newJob().add("${getKsuDaemonPath()} $cmd").to(callbackList, callbackList).exec()
Log.i("KernelSU", "install module $uri result: $result") Log.i("KernelSU", "install module $uri result: $result")
file.delete() file.delete()
@@ -96,7 +121,7 @@ fun installModule(uri: Uri, onFinish: (Boolean)->Unit, onOutput: (String) -> Uni
} }
fun reboot(reason: String = "") { fun reboot(reason: String = "") {
val shell = createRootShell() val shell = getRootShell()
if (reason == "recovery") { if (reason == "recovery") {
// KEYCODE_POWER = 26, hide incorrect "Factory data reset" message // KEYCODE_POWER = 26, hide incorrect "Factory data reset" message
ShellUtils.fastCmd(shell, "/system/bin/input keyevent 26") ShellUtils.fastCmd(shell, "/system/bin/input keyevent 26")
@@ -105,13 +130,13 @@ fun reboot(reason: String = "") {
} }
fun overlayFsAvailable(): Boolean { fun overlayFsAvailable(): Boolean {
val shell = createRootShell() val shell = getRootShell()
// check /proc/filesystems // check /proc/filesystems
return ShellUtils.fastCmdResult(shell, "cat /proc/filesystems | grep overlay") return ShellUtils.fastCmdResult(shell, "cat /proc/filesystems | grep overlay")
} }
fun hasMagisk(): Boolean { fun hasMagisk(): Boolean {
val shell = createRootShell() val shell = getRootShell()
val result = shell.newJob().add("nsenter --mount=/proc/1/ns/mnt which magisk").exec() val result = shell.newJob().add("nsenter --mount=/proc/1/ns/mnt which magisk").exec()
Log.i(TAG, "has magisk: ${result.isSuccess}") Log.i(TAG, "has magisk: ${result.isSuccess}")
return result.isSuccess return result.isSuccess

View File

@@ -3,6 +3,7 @@ package me.weishu.kernelsu.ui.util
import android.content.Context import android.content.Context
import android.os.Build import android.os.Build
import android.system.Os import android.system.Os
import com.topjohnwu.superuser.ShellUtils
import me.weishu.kernelsu.Natives import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.ui.screen.getManagerVersion import me.weishu.kernelsu.ui.screen.getManagerVersion
import java.io.File import java.io.File
@@ -22,26 +23,32 @@ fun getBugreportFile(context: Context): File {
val dropboxFile = File(bugreportDir, "dropbox.tar.gz") val dropboxFile = File(bugreportDir, "dropbox.tar.gz")
val pstoreFile = File(bugreportDir, "pstore.tar.gz") val pstoreFile = File(bugreportDir, "pstore.tar.gz")
val diagFile = File(bugreportDir, "diag.tar.gz") val diagFile = File(bugreportDir, "diag.tar.gz")
val bootlogFile = File(bugreportDir, "bootlog.tar.gz")
val mountsFile = File(bugreportDir, "mounts.txt") val mountsFile = File(bugreportDir, "mounts.txt")
val fileSystemsFile = File(bugreportDir, "filesystems.txt") val fileSystemsFile = File(bugreportDir, "filesystems.txt")
val ksuFileTree = File(bugreportDir, "ksu_tree.txt") val ksuFileTree = File(bugreportDir, "ksu_tree.txt")
val appListFile = File(bugreportDir, "app_list.txt") val appListFile = File(bugreportDir, "packages.txt")
val propFile = File(bugreportDir, "props.txt") val propFile = File(bugreportDir, "props.txt")
val allowListFile = File(bugreportDir, "allowlist.bin")
val shell = createRootShell() val shell = getRootShell()
shell.newJob().add("dmesg > ${dmesgFile.absolutePath}").exec() shell.newJob().add("dmesg > ${dmesgFile.absolutePath}").exec()
shell.newJob().add("logcat -d > ${logcatFile.absolutePath}").exec() shell.newJob().add("logcat -d > ${logcatFile.absolutePath}").exec()
shell.newJob().add("tar -czf ${tombstonesFile.absolutePath} /data/tombstones").exec() shell.newJob().add("tar -czf ${tombstonesFile.absolutePath} -C /data/tombstones .").exec()
shell.newJob().add("tar -czf ${dropboxFile.absolutePath} /data/system/dropbox").exec() shell.newJob().add("tar -czf ${dropboxFile.absolutePath} -C /data/system/dropbox .").exec()
shell.newJob().add("tar -czf ${pstoreFile.absolutePath} /sys/fs/pstore").exec() shell.newJob().add("tar -czf ${pstoreFile.absolutePath} -C /sys/fs/pstore .").exec()
shell.newJob().add("tar -czf ${diagFile.absolutePath} /data/vendor/diag").exec() shell.newJob().add("tar -czf ${diagFile.absolutePath} -C /data/vendor/diag .").exec()
shell.newJob().add("tar -czf ${bootlogFile.absolutePath} -C /data/adb/ksu/log .").exec()
shell.newJob().add("cat /proc/mounts > ${mountsFile.absolutePath}").exec() shell.newJob().add("cat /proc/1/mountinfo > ${mountsFile.absolutePath}").exec()
shell.newJob().add("cat /proc/filesystems > ${fileSystemsFile.absolutePath}").exec() shell.newJob().add("cat /proc/filesystems > ${fileSystemsFile.absolutePath}").exec()
shell.newJob().add("ls -alRZ /data/adb > ${ksuFileTree.absolutePath}").exec() shell.newJob().add("ls -alRZ /data/adb > ${ksuFileTree.absolutePath}").exec()
shell.newJob().add("cat /data/system/packages.list > ${appListFile.absolutePath}").exec() shell.newJob().add("cp /data/system/packages.list ${appListFile.absolutePath}").exec()
shell.newJob().add("getprop > ${propFile.absolutePath}").exec() shell.newJob().add("getprop > ${propFile.absolutePath}").exec()
shell.newJob().add("cp /data/adb/ksu/.allowlist ${allowListFile.absolutePath}").exec()
val selinux = ShellUtils.fastCmd(shell, "getenforce");
// basic information // basic information
val buildInfo = File(bugreportDir, "basic.txt") val buildInfo = File(bugreportDir, "basic.txt")
@@ -56,6 +63,7 @@ fun getBugreportFile(context: Context): File {
pw.println("FINGERPRINT: " + Build.FINGERPRINT) pw.println("FINGERPRINT: " + Build.FINGERPRINT)
pw.println("DEVICE: " + Build.DEVICE) pw.println("DEVICE: " + Build.DEVICE)
pw.println("Manager: " + getManagerVersion(context)) pw.println("Manager: " + getManagerVersion(context))
pw.println("SELinux: $selinux")
val uname = Os.uname() val uname = Os.uname()
pw.println("KernelRelease: ${uname.release}") pw.println("KernelRelease: ${uname.release}")
@@ -64,9 +72,9 @@ fun getBugreportFile(context: Context): File {
pw.println("Nodename: ${uname.nodename}") pw.println("Nodename: ${uname.nodename}")
pw.println("Sysname: ${uname.sysname}") pw.println("Sysname: ${uname.sysname}")
val ksuKernel = Natives.getVersion() val ksuKernel = Natives.version
pw.println("KernelSU: $ksuKernel") pw.println("KernelSU: $ksuKernel")
val safeMode = Natives.isSafeMode() val safeMode = Natives.isSafeMode
pw.println("SafeMode: $safeMode") pw.println("SafeMode: $safeMode")
} }
@@ -84,4 +92,4 @@ fun getBugreportFile(context: Context): File {
shell.newJob().add("chmod 0644 ${targetFile.absolutePath}").exec() shell.newJob().add("chmod 0644 ${targetFile.absolutePath}").exec()
return targetFile return targetFile
} }

View File

@@ -1,8 +1,12 @@
package me.weishu.kernelsu.ui.viewmodel package me.weishu.kernelsu.ui.viewmodel
import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.content.pm.ApplicationInfo import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo import android.content.pm.PackageInfo
import android.graphics.drawable.Drawable import android.os.IBinder
import android.os.Parcelable
import android.os.SystemClock import android.os.SystemClock
import android.util.Log import android.util.Log
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
@@ -10,13 +14,20 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
import me.weishu.kernelsu.IKsuInterface
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.KsuService
import me.weishu.kernelsu.ui.util.HanziToPinyin import me.weishu.kernelsu.ui.util.HanziToPinyin
import me.weishu.kernelsu.ui.util.KsuCli
import java.text.Collator import java.text.Collator
import java.util.* import java.util.*
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
class SuperUserViewModel : ViewModel() { class SuperUserViewModel : ViewModel() {
@@ -25,14 +36,32 @@ class SuperUserViewModel : ViewModel() {
private var apps by mutableStateOf<List<AppInfo>>(emptyList()) private var apps by mutableStateOf<List<AppInfo>>(emptyList())
} }
class AppInfo( @Parcelize
data class AppInfo(
val label: String, val label: String,
val packageName: String, val packageInfo: PackageInfo,
val icon: PackageInfo, val profile: Natives.Profile?,
val uid: Int, ) : Parcelable {
val onAllowList: Boolean, val packageName: String
val onDenyList: Boolean get() = packageInfo.packageName
) val uid: Int
get() = packageInfo.applicationInfo.uid
val allowSu: Boolean
get() = profile != null && profile.allowSu
val hasCustomProfile: Boolean
get() {
if (profile == null) {
return false
}
return if (profile.allowSu) {
!profile.rootUseDefault
} else {
!profile.nonRootUseDefault
}
}
}
var search by mutableStateOf("") var search by mutableStateOf("")
var showSystemApps by mutableStateOf(false) var showSystemApps by mutableStateOf(false)
@@ -42,8 +71,8 @@ class SuperUserViewModel : ViewModel() {
private val sortedList by derivedStateOf { private val sortedList by derivedStateOf {
val comparator = compareBy<AppInfo> { val comparator = compareBy<AppInfo> {
when { when {
it.onAllowList -> 0 it.allowSu -> 0
it.onDenyList -> 1 it.hasCustomProfile -> 1
else -> 2 else -> 2
} }
}.then(compareBy(Collator.getInstance(Locale.getDefault()), AppInfo::label)) }.then(compareBy(Collator.getInstance(Locale.getDefault()), AppInfo::label))
@@ -58,29 +87,68 @@ class SuperUserViewModel : ViewModel() {
.toPinyinString(it.label).contains(search) .toPinyinString(it.label).contains(search)
}.filter { }.filter {
it.uid == 2000 // Always show shell it.uid == 2000 // Always show shell
|| showSystemApps || it.icon.applicationInfo.flags.and(ApplicationInfo.FLAG_SYSTEM) == 0 || showSystemApps || it.packageInfo.applicationInfo.flags.and(ApplicationInfo.FLAG_SYSTEM) == 0
} }
} }
private suspend inline fun connectKsuService(
crossinline onDisconnect: () -> Unit = {}
): Pair<IBinder, ServiceConnection> = suspendCoroutine {
val connection = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
onDisconnect()
}
override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
it.resume(binder as IBinder to this)
}
}
val intent = Intent(ksuApp, KsuService::class.java);
val task = KsuService.bindOrTask(
intent,
Shell.EXECUTOR,
connection,
)
val shell = KsuCli.SHELL
task?.let { it1 -> shell.execTask(it1) }
}
private fun stopKsuService() {
val intent = Intent(ksuApp, KsuService::class.java);
KsuService.stop(intent)
}
suspend fun fetchAppList() { suspend fun fetchAppList() {
isRefreshing = true
val result = connectKsuService {
Log.w(TAG, "KsuService disconnected")
}
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
isRefreshing = true
val pm = ksuApp.packageManager val pm = ksuApp.packageManager
val allowList = Natives.getAllowList().toSet()
val denyList = Natives.getDenyList().toSet()
Log.i(TAG, "allowList: $allowList")
Log.i(TAG, "denyList: $denyList")
val start = SystemClock.elapsedRealtime() val start = SystemClock.elapsedRealtime()
apps = pm.getInstalledPackages(0).map {
val binder = result.first
val allPackages = IKsuInterface.Stub.asInterface(binder).getPackages(0)
withContext(Dispatchers.Main) {
stopKsuService()
}
val packages = allPackages.list
apps = packages.map {
val appInfo = it.applicationInfo val appInfo = it.applicationInfo
val uid = appInfo.uid val uid = appInfo.uid
val profile = Natives.getAppProfile(it.packageName, uid)
AppInfo( AppInfo(
label = appInfo.loadLabel(pm).toString(), label = appInfo.loadLabel(pm).toString(),
packageName = it.packageName, packageInfo = it,
icon = it, profile = profile,
uid = uid,
onAllowList = uid in allowList,
onDenyList = uid in denyList
) )
}.filter { it.packageName != ksuApp.packageName } }.filter { it.packageName != ksuApp.packageName }
Log.i(TAG, "load cost: ${SystemClock.elapsedRealtime() - start}") Log.i(TAG, "load cost: ${SystemClock.elapsedRealtime() - start}")

View File

@@ -0,0 +1,61 @@
<resources>
<string name="app_name" translatable="false">KernelSU</string>
<string name="home">الرئيسية</string>
<string name="home_not_installed">غير مثبت</string>
<string name="home_click_to_install">إضغط للتثبيت</string>
<string name="home_working">يعمل</string>
<string name="home_working_version">الإصدار: %d</string>
<string name="home_superuser_count">مستخدمين الجذر: %d</string>
<string name="home_module_count">الوحدات: %d</string>
<string name="home_unsupported">غير مدعوم</string>
<string name="home_unsupported_reason">KernelSU يدعم GKI kernels فقط</string>
<string name="home_kernel">إصدار النواة</string>
<string name="home_manager_version">إصدار المدير</string>
<string name="home_fingerprint">البصمة</string>
<string name="home_selinux_status">وضع SELinux</string>
<string name="selinux_status_disabled">غير مفعل</string>
<string name="selinux_status_enforcing">مفروض</string>
<string name="selinux_status_permissive">متساهل</string>
<string name="selinux_status_unknown">مجهول</string>
<string name="superuser">مستخدم خارق</string>
<string name="module_failed_to_enable">فشل في تمكين الوحدة: %s</string>
<string name="module_failed_to_disable">فشل تعطيل الوحدة : %s</string>
<string name="module_empty">لا توجد وحدة مثبتة</string>
<string name="module">وحدة</string>
<string name="uninstall">إلغاء التثبيت</string>
<string name="module_install">تثبيت الوحدة</string>
<string name="install">تثبيت</string>
<string name="reboot">إعادة تشغيل</string>
<string name="settings">الإعدادات</string>
<string name="reboot_userspace">إعادة تشغيل سريعة</string>
<string name="reboot_recovery">إعادة تشغيل إلى وضع Recovery</string>
<string name="reboot_bootloader">إعادة تشغيل إلى وضع Bootloader</string>
<string name="reboot_download">إعادة تشغيل إلى وضع Download</string>
<string name="reboot_edl">إعادة تشغيل إلى وضع EDL</string>
<string name="about">من نحن</string>
<string name="module_uninstall_confirm">هل أنت متأكد أنك تريد إلغاء تثبيت الوحدة %s ?</string>
<string name="module_uninstall_success">تم إلغاء التثبيت %s</string>
<string name="module_uninstall_failed">فشل إلغاء التثبيت: %s</string>
<string name="module_version">الإصدار</string>
<string name="module_author">المطور</string>
<string name="module_overlay_fs_not_available">التراكبات غير متوفرة ، لا يمكن للوحدة أن تعمل!</string>
<string name="refresh">إنعاش</string>
<string name="show_system_apps">إظهار تطبيقات النظام</string>
<string name="hide_system_apps">إخفاء تطبيقات النظام</string>
<string name="send_log">إرسال السجلات</string>
<string name="safe_mode">الوضع الآمن</string>
<string name="reboot_to_apply">إعادة التشغيل لتطبيق التغييرات</string>
<string name="module_magisk_conflict">تم تعطيل الوحدة لأنها تتعارض مع وحدات Magisk</string>
<string name="home_learn_kernelsu">تعلم KernelSU</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">تعرف على كيفية تثبيت KernelSU واستخدام الوحدات</string>
<string name="home_support_title">إدعمنا</string>
<string name="home_support_content">KernelSU سيظل دائماً مجانياً ومفتوح المصدر. مع ذلك، يمكنك أن تظهر لنا أنك تهتم بالتبرع.</string>
<string name="about_source_code"><![CDATA[أنظر إلى مصدر البرمجة في %1$s<br/>إنضم إلى قناتنا في %2$s ]]></string>
</resources>

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">কার্নেলএসইউ</string>
<string name="home">হোম</string>
<string name="home_not_installed">ইনস্টল করা হয়নি</string>
<string name="home_click_to_install">ইনস্টল করার জন্য ক্লিক করুন</string>
<string name="home_working"> ওয়ার্কিং</string>
<string name="home_working_version">ওয়ার্কিং সংস্করণ: %d</string>
<string name="home_superuser_count">সুপার ইউজার: %d</string>
<string name="home_module_count">মডিউল: %d</string>
<string name="home_unsupported">অসমর্থিত</string>
<string name="home_unsupported_reason">কার্নেলএসইউ শুধুমাত্র জিকেআই কার্নেল সমর্থন করে</string>
<string name="home_kernel">কার্নেল</string>
<string name="home_manager_version">ম্যানেজার সংস্করণ</string>
<string name="home_fingerprint">ফিঙ্গারপ্রিন্ট</string>
<string name="home_selinux_status">সেলিনাক্স স্ট্যাটাস</string>
<string name="selinux_status_disabled">ডিজেবল</string>
<string name="selinux_status_enforcing">এনফোর্সিং</string>
<string name="selinux_status_permissive">অনুমতিমূলক</string>
<string name="selinux_status_unknown">অপরিচিত</string>
<string name="superuser">সুপার ইউজার</string>
<string name="module_failed_to_enable">মডিউল সক্ষম করতে ব্যর্থ হয়েছে: %s</string>
<string name="module_failed_to_disable">মডিউল নিষ্ক্রিয় করতে ব্যর্থ হয়েছে: %s</string>
<string name="module_empty">কোন মডিউল ইনস্টল করা নেই</string>
<string name="module">মডিউল</string>
<string name="uninstall">আনইন্সটল</string>
<string name="module_install">মডিউল ইনস্টল</string>
<string name="install">ইনস্টল</string>
<string name="reboot">রিবুট</string>
<string name="settings">সেটিংস</string>
<string name="reboot_userspace">সফট রিবুট</string>
<string name="reboot_recovery">রিবুট রিকোভারি</string>
<string name="reboot_bootloader">রিবুট বুটলোডার</string>
<string name="reboot_download">রিবুট ডাউনলোড</string>
<string name="reboot_edl">রিবুট ইডিএল</string>
<string name="about">এবাউট</string>
<string name="module_uninstall_confirm">মডিউল আনইনস্টল নিশ্চিত করুন %s?</string>
<string name="module_uninstall_success">%s আনইনস্টল সফল</string>
<string name="module_uninstall_failed">আনইন্সটল ব্যর্থ: %s</string>
<string name="module_version">ভার্সন</string>
<string name="module_author">লেখক</string>
<string name="module_overlay_fs_not_available">ওভারলেএফএস উপলব্ধ নয়, মডিউল কাজ করতে পারে না!</string>
<string name="refresh">রিফ্রেশ</string>
<string name="show_system_apps">শো সিস্টেম অ্যাপস</string>
<string name="hide_system_apps">হাইড সিস্টেম অ্যাপস</string>
<string name="send_log">সেন্ড লগ</string>
<string name="safe_mode">সেইফ মোড</string>
<string name="reboot_to_apply">রিবুট এপ্লাই</string>
<string name="module_magisk_conflict">মডিউলগুলি অক্ষম কারণ তারা ম্যাজিস্কের সাথে বিরোধিতা করে!</string>
<string name="home_learn_kernelsu">লার্ন কার্নেলএসইউ</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">কিভাবে কার্নেলএসইউ ইনস্টল করতে হয় এবং মডিউল ব্যবহার করতে হয় তা শিখুন</string>
<string name="home_support_title">সাপোর্ট টাইটেল</string>
<string name="home_support_content">কার্নেলএসইউ বিনামূল্যে এবং ওপেন সোর্স, এবং সবসময় থাকবে। আপনি সবসময় একটি অনুদান দিয়ে আপনার কৃতজ্ঞতা প্রদর্শন করতে পারেন.</string>
<string name="about_source_code"><![CDATA[Bekijk source code op %1$s<br/>আমাদের %2$s চ্যানেল মার্জ করুন]]></string>
</resources>

View File

@@ -0,0 +1,67 @@
<resources>
<string name="app_name" translatable="false">KernelSU</string>
<string name="home">خانه</string>
<string name="home_not_installed">نصب نشده است</string>
<string name="home_click_to_install">برای نصب ضربه بزنید</string>
<string name="home_working">به درستی کار می‌کند</string>
<string name="home_working_version">نسخه: %d</string>
<string name="home_superuser_count">برنامه های با دسترسی روت: %d</string>
<string name="home_module_count">ماژول‌ها: %d</string>
<string name="home_unsupported">پشتیبانی نشده</string>
<string name="home_unsupported_reason">کرنل اس یو فقط هسته های gki را پشتیبانی میکند</string>
<string name="home_kernel">هسته</string>
<string name="home_manager_version">نسخه برنامه</string>
<string name="home_fingerprint">اثرانگشت</string>
<string name="home_selinux_status">وضعیت SELinux</string>
<string name="selinux_status_disabled">غیرفعال</string>
<string name="selinux_status_enforcing">قانونمند</string>
<string name="selinux_status_permissive">آزاد</string>
<string name="selinux_status_unknown">ناشناخته</string>
<string name="superuser">دسترسی روت</string>
<string name="module_failed_to_enable">فعال کردن ماژول ناموفق بود: %s</string>
<string name="module_failed_to_disable">غیرفعال کردن ماژول ناموفق بود: %s</string>
<string name="module_empty">هیچ ماژولی نصب نشده است</string>
<string name="module">ماژول</string>
<string name="uninstall">لغو نصب</string>
<string name="module_install">نصب</string>
<string name="install">نصب</string>
<string name="reboot">راه اندازی دوباره</string>
<string name="settings">تنظیمات</string>
<string name="reboot_userspace">راه اندازی نرم</string>
<string name="reboot_recovery">راه اندازی به ریکاوری </string>
<string name="reboot_bootloader">راه اندازی به بوتلودر</string>
<string name="reboot_download">راه اندازی به حالت دانلود</string>
<string name="reboot_edl">راه اندازی به EDL</string>
<string name="about">درباره</string>
<string name="module_uninstall_confirm">آیا مطمئنید که میخواهید ماژول %s را پاک کنید؟</string>
<string name="module_uninstall_success">%s پاک شد</string>
<string name="module_uninstall_failed">پاک کردن ناموفق بود: %s</string>
<string name="module_version">نسخه</string>
<string name="module_author">سازنده</string>
<string name="module_overlay_fs_not_available">overlayfs موجود نیست. مازول کار نمیکند!!</string>
<string name="refresh">تازه‌سازی</string>
<string name="show_system_apps">نمایش برنامه های سیستمی</string>
<string name="hide_system_apps">مخفی کردن برنامه های سیستمی</string>
<string name="send_log">ارسال وقایع</string>
<string name="safe_mode">حالت امن</string>
<string name="reboot_to_apply">راه‌اندازی مجدد برای تاثیرگذاری</string>
<string name="module_magisk_conflict">مازول به دلیل تعارض با مجیسک غیرفعال شده اند\'s!</string>
<string name="home_learn_kernelsu">یادگیری کرنل اس یو</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">یاد بگیرید چگونه از کرنل اس یو و ماژول ها استفاده کنید</string>
<string name="home_support_title">از ما حمایت کنید</string>
<string name="home_support_content">KernelSU رایگان است و همیشه خواهد بود و منبع باز است. با این حال، می توانید با اهدای کمک مالی به ما نشان دهید که برایتان مهم است.</string>
<string name="about_source_code">
<![CDATA[ View source code at %1$s<br/>Join our %2$s channel ]]>
</string>
<string name="profile">پروفایل برنامه</string>
<string name="profile_default">پیش‌فرض</string>
<string name="profile_template">قالب</string>
<string name="profile_custom">شخصی سازی شده</string>
<string name="profile_name">اسم پروفایل</string>
<string name="profile_namespace">Mount namespace</string>
<string name="profile_namespace_inherited">اثر گرفته</string>
<string name="profile_namespace_global">گلوبال</string>
<string name="profile_namespace_individual">تکی</string>
<string name="profile_umount_modules">جداکردن ماژول ها</string>
</resources>

View File

@@ -1,55 +1,69 @@
<?xml version="1.0" encoding="utf-8"?> <resources>
<resources> <string name="app_name" translatable="false">KernelSU</string>
<string name="home">Beranda</string>
<string name="home_not_installed">Tidak terinstall</string> <string name="home">Beranda</string>
<string name="home_click_to_install">Klik untuk menginstall</string> <string name="home_not_installed">Tidak terpasang</string>
<string name="home_working">Bekerja</string> <string name="home_click_to_install">Klik untuk memasang</string>
<string name="home_working_version">Versi: %d</string> <string name="home_working">Bekerja</string>
<string name="home_unsupported">Tidak didukung</string> <string name="home_working_version">Versi: %d</string>
<string name="home_unsupported_reason">Saat ini kernelSu hanya mendukung GKI kernel</string> <string name="home_superuser_count">Superusers: %d</string>
<string name="home_copied_to_clipboard">Salin ke clipboard</string> <string name="home_module_count">Modul: %d</string>
<string name="home_support">Dukungan</string> <string name="home_unsupported">Tidak didukung</string>
<string name="home_kernel">Kernel</string> <string name="home_unsupported_reason">KernelSU hanya mendukung kernel GKI saat ini</string>
<string name="home_arch">Arch</string>
<string name="home_manager_version">Versi manager</string> <string name="home_kernel">Kernel</string>
<string name="home_api">API Level</string> <string name="home_manager_version">Versi Manager</string>
<string name="home_abi">ABI</string> <string name="home_fingerprint">Sidik jari</string>
<string name="home_fingerprint">Fingerprint</string>
<string name="home_securitypatch">Patch keamanan</string> <string name="home_selinux_status">status SELinux</string>
<string name="home_selinux_status">Status SElinux</string> <string name="selinux_status_disabled">Dinonaktifkan</string>
<string name="selinux_status_disabled">Cacat</string> <string name="selinux_status_enforcing">Enforcing</string>
<string name="selinux_status_enforcing">Enforcing</string> <string name="selinux_status_permissive">Permissive</string>
<string name="selinux_status_permissive">Permissive</string> <string name="selinux_status_unknown">Tidak dikenal</string>
<string name="selinux_status_unknown">Tidak tersedia</string> <string name="superuser">Superuser</string>
<string name="superuser">Superuser</string> <string name="module_failed_to_enable">Gagal mengaktifkan modul: %s</string>
<string name="superuser_failed_to_grant_root">Gagal mengizinkan root untuk %d</string> <string name="module_failed_to_disable">Gagal menonaktifkan modul: %s</string>
<string name="module_failed_to_enable">Gagal mengaktifkan module: %s</string> <string name="module_empty">Tidak ada modul terpasang</string>
<string name="module_failed_to_disable">Gagal menonaktifkan module: %s</string>
<string name="module_empty">Tidak ada module terpasang</string> <string name="module">Modul</string>
<string name="module">Module</string> <string name="uninstall">Copot</string>
<string name="uninstall">Hapus</string> <string name="module_install">Pasang</string>
<string name="module_install">Memasang module</string> <string name="install">Pasang</string>
<string name="install">Pasang</string> <string name="reboot">Reboot</string>
<string name="reboot">Reboot perangkat</string> <string name="settings">Pengaturan</string>
<string name="settings">Pengaturan</string> <string name="reboot_userspace">Reboot Lembut</string>
<string name="reboot_userspace">Soft Reboot</string> <string name="reboot_recovery">Reboot ke Recovery</string>
<string name="reboot_recovery">Reboot ke Recovery</string> <string name="reboot_bootloader">Reboot ke Bootloader</string>
<string name="reboot_bootloader">Reboot ke Bootloader</string> <string name="reboot_download">Reboot ke Download</string>
<string name="reboot_download">Reboot ke Download</string> <string name="reboot_edl">Reboot ke EDL</string>
<string name="reboot_edl">Reboot ke EDL</string> <string name="about">Tentang</string>
<string name="about">Tentang</string> <string name="module_uninstall_confirm">Apakah Anda yakin ingin mencopot modul %s?</string>
<string name="require_kernel_version_8">Membutuhkan KernelSU Versi 8+</string> <string name="module_uninstall_success">%s Tercopot</string>
<string name="module_uninstall_confirm">Apakah anda yakin ingin menghapus module ini %s?</string> <string name="module_uninstall_failed">Gagal untuk mencopot: %s</string>
<string name="module_uninstall_success">%s terhapus</string> <string name="module_version">Versi</string>
<string name="module_uninstall_failed">Gagal untuk menghapus: %s</string> <string name="module_author">Pembuat</string>
<string name="module_version">Versi</string> <string name="module_overlay_fs_not_available">overlayfs tidak tersedia, modul tidak dapat bekerja!</string>
<string name="module_author">Pembuat</string> <string name="refresh">Segarkan</string>
<string name="module_overlay_fs_not_available">overlayfs tidak tersedia, module tidak bekerja!</string> <string name="show_system_apps">Tampilkan apl sistem</string>
<string name="refresh">Segarkan</string> <string name="hide_system_apps">Sembunyikan apl sistem</string>
<string name="show_system_apps">Tampilkan system apps</string> <string name="send_log">Kirim Log</string>
<string name="hide_system_apps">Sembunyikan system apps</string> <string name="safe_mode">Mode aman</string>
<string name="send_log">Kirim logs</string> <string name="reboot_to_apply">Reboot untuk menerapkan</string>
<string name="safe_mode">Mode aman</string> <string name="module_magisk_conflict">Modul dinonaktifkan karena bertentangan dengan Magisk!</string>
<string name="reboot_to_apply">Restart untuk menerapkan</string> <string name="home_learn_kernelsu">Pelajari KernelSU</string>
<string name="module_magisk_conflict">Module akan di nonaktifkan karna konflik dengan magisk\'s!</string> <string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
</resources> <string name="home_click_to_learn_kernelsu">Pelajari cara memasang KernelSU dan menggunakan modul</string>
<string name="home_support_title">Dukung Kami</string>
<string name="home_support_content">KernelSU gratis dan bersumber terbuka, dan akan selalu seperti itu. Bagaimanapun juga Anda dapat menunjukan kepedulian Anda kepada kami dengan mengirimkan sedikit donasi.</string>
<string name="about_source_code"><![CDATA[Lihat sumber code di %1$s<br/>Gabung kanal %2$s kami]]></string>
<string name="profile">Profil Apl</string>
<string name="profile_default">Bawaan</string>
<string name="profile_template">Templat</string>
<string name="profile_custom">Khusus</string>
<string name="profile_name">Nama profil</string>
<string name="profile_namespace">Ikat ruang-nama</string>
<string name="profile_namespace_inherited">Diwariskan</string>
<string name="profile_namespace_global">Universal</string>
<string name="profile_namespace_individual">Personal</string>
<string name="profile_umount_modules">Lepas modul</string>
</resources>

View File

@@ -0,0 +1,57 @@
<resources>
<string name="home">Home</string>
<string name="home_not_installed">Non installato</string>
<string name="home_click_to_install">Clicca per installare</string>
<string name="home_working">In esecuzione</string>
<string name="home_working_version">Versione: %d</string>
<string name="home_superuser_count">Superuser: %d</string>
<string name="home_module_count">Moduli: %d</string>
<string name="home_unsupported">Non supportato</string>
<string name="home_unsupported_reason">KernelSU ora supporta solo i kernel GKI</string>
<string name="home_kernel">Kernel</string>
<string name="home_manager_version">Versione del manager</string>
<string name="home_fingerprint">Fingerprint</string>
<string name="home_selinux_status">Stato SELinux</string>
<string name="selinux_status_disabled">Disabilitato</string>
<string name="selinux_status_enforcing">Enforcing</string>
<string name="selinux_status_permissive">Permissive</string>
<string name="selinux_status_unknown">Sconosciuto</string>
<string name="superuser">Accesso root</string>
<string name="module_failed_to_enable">Impossibile abilitare il modulo: %s</string>
<string name="module_failed_to_disable">Impossibile disabilitare il modulo: %s</string>
<string name="module_empty">Nessun modulo installato</string>
<string name="module">Moduli</string>
<string name="uninstall">Disinstalla</string>
<string name="module_install">Installa</string>
<string name="install">Installa</string>
<string name="reboot">Riavvia</string>
<string name="settings">Impostazioni</string>
<string name="reboot_userspace">Riavvio veloce</string>
<string name="reboot_recovery">Riavvia in Recovery</string>
<string name="reboot_bootloader">Riavvia in modalità Bootloader</string>
<string name="reboot_download">Riavvia in modalità Download</string>
<string name="reboot_edl">Riavvia in modalità EDL</string>
<string name="about">Informazioni</string>
<string name="module_uninstall_confirm">Sei sicuro di voler disinstallare il modulo %s?</string>
<string name="module_uninstall_success">%s disinstallato</string>
<string name="module_uninstall_failed">Impossibile disinstallare: %s</string>
<string name="module_version">Versione</string>
<string name="module_author">Autore</string>
<string name="module_overlay_fs_not_available">overlayfs non è disponibile, il modulo non può funzionare!</string>
<string name="refresh">Aggiorna</string>
<string name="show_system_apps">Mostra app di sistema</string>
<string name="hide_system_apps">Nascondi app di sistema</string>
<string name="send_log">Invia log</string>
<string name="safe_mode">Modalità provvisoria</string>
<string name="reboot_to_apply">Riavvia per applicare la modifica</string>
<string name="module_magisk_conflict">I moduli sono disabilitati perché in conflitto con quelli di Magisk!</string>
<string name="home_learn_kernelsu">Scopri KernelSU</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">Scopri come installare KernelSU e utilizzare i moduli</string>
<string name="home_support_title">Supportaci</string>
<string name="home_support_content">KernelSU è, e sempre sarà, gratuito e open source. Puoi comunque mostrarci il tuo apprezzamento facendo una donazione.</string>
<string name="about_source_code"><![CDATA[Visualizza il codice sorgente su %1$s<br/>Unisciti al nostro canale %2$s]]></string>
</resources>

View File

@@ -1,54 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name" translatable="false">KernelSU</string>
<string name="home">ホーム</string> <string name="home">ホーム</string>
<string name="home_not_installed">未インストール</string> <string name="home_not_installed">未インストール</string>
<string name="home_click_to_install">タップでインストール</string> <string name="home_click_to_install">タップでインストール</string>
<string name="home_working">動作中</string> <string name="home_working">動作中</string>
<string name="home_working_version">バージョン: %d</string> <string name="home_working_version">バージョン: %d</string>
<string name="home_unsupported">非対応</string> <string name="home_superuser_count">スーパーユーザー: %d</string>
<string name="home_unsupported_reason">KernelSUは現在、GKIカーネルのみサポートをしています</string> <string name="home_module_count">モジュール: %d</string>
<string name="home_copied_to_clipboard">クリップボードにコピーしました</string> <string name="home_unsupported">非対応</string>
<string name="home_support">対応</string> <string name="home_unsupported_reason">KernelSU は現在、GKI カーネルにのみ対応しています</string>
<string name="home_kernel">カーネル</string>
<string name="home_arch">アーキテクチャ</string> <string name="home_kernel">カーネル</string>
<string name="home_manager_version">バージョン</string> <string name="home_manager_version">バージョン</string>
<string name="home_api">APIレベル</string> <string name="home_fingerprint">フィンガープリント</string>
<string name="home_abi">ABI</string>
<string name="home_fingerprint">Fingerprint</string> <string name="home_selinux_status">SELinux の状態</string>
<string name="home_securitypatch">セキュリティパッチ</string> <string name="selinux_status_disabled">無効</string>
<string name="home_selinux_status">SELinuxの状態</string> <string name="selinux_status_enforcing">Enforcing</string>
<string name="selinux_status_disabled">無効</string> <string name="selinux_status_permissive">Permissive</string>
<string name="selinux_status_enforcing">Enforcing</string> <string name="selinux_status_unknown">不明</string>
<string name="selinux_status_permissive">Permissive</string> <string name="superuser">スーパーユーザー</string>
<string name="selinux_status_unknown">不明</string> <string name="module_failed_to_enable">モジュールの有効化に失敗: %s</string>
<string name="superuser">スーパーユーザー</string> <string name="module_failed_to_disable">モジュールの無効化に失敗: %s</string>
<string name="superuser_failed_to_grant_root">%dの権限の付与に失敗しました</string> <string name="module_empty">モジュールをインストールしていません</string>
<string name="module_failed_to_enable">モジュールの有効化に失敗: %s</string>
<string name="module_failed_to_disable">モジュールの無効化に失敗: %s</string> <string name="module">モジュール</string>
<string name="module_empty">モジュールはインストールされていません</string> <string name="uninstall">アンインストール</string>
<string name="module">モジュール</string> <string name="module_install">インストール</string>
<string name="uninstall">アンインストール</string> <string name="install">インストール</string>
<string name="module_install">インストール</string> <string name="reboot">再起動</string>
<string name="install">インストール</string> <string name="settings">設定</string>
<string name="reboot">再起動</string> <string name="reboot_userspace">ソフトリブート</string>
<string name="settings">設定</string> <string name="reboot_recovery">リカバリーへ再起動</string>
<string name="reboot_userspace">ソフトリブート</string> <string name="reboot_bootloader">Bootloader へ再起動</string>
<string name="reboot_recovery">リカバリーで再起動</string> <string name="reboot_download">ダウンロードモードへ再起動</string>
<string name="reboot_bootloader">Bootloaderで再起動</string> <string name="reboot_edl">EDLへ再起動</string>
<string name="reboot_download">ダウンロードモードで再起動</string> <string name="about">アプリについて</string>
<string name="reboot_edl">EDLで再起動</string> <string name="module_uninstall_confirm">モジュール %s をアンインストールしますか?</string>
<string name="about">アプリについて</string> <string name="module_uninstall_success">%sをアンインストールしました</string>
<string name="require_kernel_version_8">KernelSU バージョン8以降が必要です</string> <string name="module_uninstall_failed">アンインストールに失敗: %s</string>
<string name="module_uninstall_success">%sをアンインストールしました</string> <string name="module_version">バージョン</string>
<string name="module_uninstall_failed">アンインストールに失敗: %s</string> <string name="module_author">制作者</string>
<string name="module_version">バージョン</string> <string name="module_overlay_fs_not_available">OverlayFS が有効でないためモジュールは動作しません</string>
<string name="module_author">作者</string> <string name="refresh">更新</string>
<string name="module_overlay_fs_not_available">OverlayFSが有効でないためモジュールは動作しません</string> <string name="show_system_apps">システムアプリを表示</string>
<string name="refresh">更新</string> <string name="hide_system_apps">システムアプリを非表示</string>
<string name="show_system_apps">システムアプリを表示</string> <string name="send_log">ログを送信</string>
<string name="hide_system_apps">システムアプリを非表示</string> <string name="safe_mode">セーフモード</string>
<string name="send_log">ログを送信</string> <string name="reboot_to_apply">再起動すると有効化されます</string>
<string name="safe_mode">セーフモード</string> <string name="module_magisk_conflict">Magisk と競合しているためモジュールは無効になっています!</string>
<string name="reboot_to_apply">再起動をして有効化する</string> <string name="home_learn_kernelsu">KernelSU の詳細</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/ja_JP/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">KernelSU のインストール方法やモジュールの使い方はこちら</string>
<string name="home_support_title">支援する</string>
<string name="home_support_content">KernelSU は無料かつオープンソースです。寄付していただくことで開発を支援できます。</string>
<string name="about_source_code"><![CDATA[%1$s でソースコードを表示<br/>%2$s チャンネルに参加]]></string>
<string name="profile">アプリのプロファイル</string>
<string name="profile_default">デフォルト</string>
<string name="profile_template">テンプレート</string>
<string name="profile_custom">カスタム</string>
<string name="profile_name">プロファイル名</string>
<string name="profile_namespace">マウント名前空間</string>
<string name="profile_namespace_inherited">継承</string>
<string name="profile_namespace_global">グローバル</string>
<string name="profile_namespace_individual">分離</string>
<string name="profile_umount_modules">モジュールのアンマウント</string>
</resources> </resources>

View File

@@ -0,0 +1,56 @@
<resources>
<string name="home"></string>
<string name="home_not_installed">설치되지 않음</string>
<string name="home_click_to_install">이 곳을 눌러 설치하기</string>
<string name="home_working">정상 작동 중</string>
<string name="home_working_version">버전: %d</string>
<string name="home_superuser_count">루트 권한: %d개</string>
<string name="home_module_count">설치된 모듈: %d개</string>
<string name="home_unsupported">지원되지 않음</string>
<string name="home_unsupported_reason">KernelSU는 현재 GKI 커널만 지원합니다</string>
<string name="home_kernel">커널</string>
<string name="home_manager_version">매니저 버전</string>
<string name="home_fingerprint">빌드 정보</string>
<string name="home_selinux_status">SELinux 상태</string>
<string name="selinux_status_disabled">비활성화됨</string>
<string name="selinux_status_enforcing">적용</string>
<string name="selinux_status_permissive">허용</string>
<string name="selinux_status_unknown">알 수 없음</string>
<string name="superuser">슈퍼유저</string>
<string name="module_failed_to_enable">모듈 활성화 실패: %s</string>
<string name="module_failed_to_disable">모듈 비활성화 실패: %s</string>
<string name="module_empty">설치된 모듈 없음</string>
<string name="module">모듈</string>
<string name="uninstall">삭제</string>
<string name="module_install">설치</string>
<string name="install">설치</string>
<string name="reboot">다시 시작</string>
<string name="settings">설정</string>
<string name="reboot_userspace">빠른 다시 시작</string>
<string name="reboot_recovery">복구 모드로 다시 시작</string>
<string name="reboot_bootloader">부트로더로 다시 시작</string>
<string name="reboot_download">다운로드 모드로 다시 시작</string>
<string name="reboot_edl">EDL 모드로 다시 시작</string>
<string name="about">정보</string>
<string name="module_uninstall_confirm">%s 모듈을 삭제할까요?</string>
<string name="module_uninstall_success">%s 모듈 삭제됨</string>
<string name="module_uninstall_failed">모듈 삭제 실패: %s</string>
<string name="module_version">버전</string>
<string name="module_author">제작자</string>
<string name="module_overlay_fs_not_available">overlayfs 사용 불가, 모듈을 사용할 수 없습니다!</string>
<string name="refresh">새로고침</string>
<string name="show_system_apps">시스템 앱 보이기</string>
<string name="hide_system_apps">시스템 앱 숨기기</string>
<string name="send_log">로그 보내기</string>
<string name="safe_mode">안전 모드</string>
<string name="reboot_to_apply">다시 시작하여 변경 사항 적용</string>
<string name="module_magisk_conflict">Magisk와의 충돌로 인해 모듈을 사용할 수 없습니다!</string>
<string name="home_learn_kernelsu">KernelSU 알아보기</string>
<string name="home_click_to_learn_kernelsu">KernelSU 설치 방법과 모듈 사용 방법을 확인합니다.</string>
<string name="home_support_title">지원이 필요해요!</string>
<string name="home_support_content">KernelSU는 지금도, 앞으로도 항상 무료이며 오픈 소스로 유지됩니다. 기부를 통해 여러분의 관심을 보여주세요.</string>
<string name="about_source_code"><![CDATA[%1$s에서 소스 코드 보기<br/>%2$s 채널 참가하기]]></string>
</resources>

View File

@@ -0,0 +1,74 @@
<resources>
<string name="app_name" translatable="false">KernelSU</string>
<string name="home">Home</string>
<string name="home_not_installed">Niet geïnstalleerd</string>
<string name="home_click_to_install">Klik om te installeren</string>
<string name="home_working">Werkend</string>
<string name="home_working_version">Versie: %d</string>
<string name="home_superuser_count">Superusers: %d</string>
<string name="home_module_count">Modules: %d</string>
<string name="home_unsupported">Niet ondersteund</string>
<string name="home_unsupported_reason">KernelSU ondersteunt alleen GKI kernels</string>
<string name="home_kernel">Kernel</string>
<string name="home_manager_version">Manager Versie</string>
<string name="home_fingerprint">Fingerprint</string>
<string name="home_selinux_status">SELinux status</string>
<string name="selinux_status_disabled">Uitgeschakeld</string>
<string name="selinux_status_enforcing">Afgedwongen</string>
<string name="selinux_status_permissive">Permissief</string>
<string name="selinux_status_unknown">Niet gekend</string>
<string name="superuser">Superuser</string>
<string name="module_failed_to_enable">Mislukt om module in te schakelen: %s</string>
<string name="module_failed_to_disable">Mislukt om module uit te schakelen: %s</string>
<string name="module_empty">Geen module geïnstalleerd</string>
<string name="module">Module</string>
<string name="uninstall">Verwijderen</string>
<string name="module_install">Installeren</string>
<string name="install">Installeren</string>
<string name="reboot">Herstart</string>
<string name="settings">Instellingen</string>
<string name="reboot_userspace">Soft herstart</string>
<string name="reboot_recovery">Herstart naar Recovery</string>
<string name="reboot_bootloader">Herstart naar Bootloader</string>
<string name="reboot_download">Herstart om te downloaden</string>
<string name="reboot_edl">Herstart naar EDL</string>
<string name="about">Over</string>
<string name="module_uninstall_confirm">Zeker van het verwijderen van module %s?</string>
<string name="module_uninstall_success">%s verwijderd</string>
<string name="module_uninstall_failed">Mislukt om te verwijderen: %s</string>
<string name="module_version">Versie</string>
<string name="module_author">Auteur</string>
<string name="module_overlay_fs_not_available">overlayfs is niet beschikbaar, module kan niet werken!</string>
<string name="refresh">Vernieuwen</string>
<string name="show_system_apps">Toon system apps</string>
<string name="hide_system_apps">Verberg system apps</string>
<string name="send_log">Stuur Log</string>
<string name="safe_mode">Safe mode</string>
<string name="reboot_to_apply">Herstart om effect te hebben</string>
<string name="module_magisk_conflict">Modules zijn uitgeschakeld omdat ze in conflict zijn met magisk!</string>
<string name="home_learn_kernelsu">Leer KernelSU</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">Leer hoe KernelSU te installeren en modules te gebruiken</string>
<string name="home_support_title">Ondersteun ons</string>
<string name="home_support_content">KernelSU is, en zal altijd, vrij en open source zijn. Je kan altijd je appreciatie tonen met een donatie.</string>
<string name="about_source_code"><![CDATA[Bekijk source code op %1$s<br/>Vervoeg ons %2$s kanaal]]></string>
<string name="profile" translatable="false">App profiel</string>
<string name="profile_default">Standaard</string>
<string name="profile_template">Sjabloon</string>
<string name="profile_custom">Aangepast</string>
<string name="profile_name">Profiel naam</string>
<string name="profile_namespace">Koppel naamruimte</string>
<string name="profile_namespace_inherited">Overgenomen</string>
<string name="profile_namespace_global">Globaal</string>
<string name="profile_namespace_individual">Individuëel</string>
<string name="profile_umount_modules">Ontkoppel modules</string>
<string name="failed_to_update_app_profile">Mislukt om App Profiel te updaten voor %s</string>
<string name="require_kernel_version">De bestaande kernel versie %d is te laag voor de manager om goed te werken. Upgrade best tot versie %d of hoger!</string>
<string name="settings_umount_modules_default">Ontkoppel standaard de modules</string>
<string name="settings_umount_modules_default_summary">De globale standaard waarde voor \"Ontkoppel modules\" in App Profielen. Indien geactiveerd, zal het alle module wijzigingen tot het systeeem verwijderen voor applicaties die geen Profiel ingesteld hebben.</string>
<string name="profile_umount_modules_summary">Deze optie ingeschakeld zal KernelSU toelaten om alle gewijzigde bestanden door de modules voor deze applicatie te herstellen.</string>
</resources>

View File

@@ -0,0 +1,74 @@
<resources>
<string name="app_name" translatable="false">KernelSU</string>
<string name="home">Início</string>
<string name="home_not_installed">Não instalado</string>
<string name="home_click_to_install">Clique para instalar</string>
<string name="home_working">Trabalhando</string>
<string name="home_working_version">Versão: %d</string>
<string name="home_superuser_count">Superusuários: %d</string>
<string name="home_module_count">Módulos: %d</string>
<string name="home_unsupported">Sem suporte</string>
<string name="home_unsupported_reason">Por enquanto, KernelSU suporta apenas kernels GKI</string>
<string name="home_kernel">Kernel</string>
<string name="home_manager_version">Versão do gerenciador</string>
<string name="home_fingerprint">Impressão digital</string>
<string name="home_selinux_status">Status do SELinux</string>
<string name="selinux_status_disabled">Desabilitado</string>
<string name="selinux_status_enforcing">Impondo</string>
<string name="selinux_status_permissive">Permissivo</string>
<string name="selinux_status_unknown">Desconhecido</string>
<string name="superuser">Superusuário</string>
<string name="module_failed_to_enable">Falha ao ativar o módulo: %s</string>
<string name="module_failed_to_disable">Falha ao desativar o módulo: %s</string>
<string name="module_empty">Nenhum módulo instalado</string>
<string name="module">Módulo</string>
<string name="uninstall">Desinstalar</string>
<string name="module_install">Instalar</string>
<string name="install">Instalar</string>
<string name="reboot">Reiniciar</string>
<string name="settings">Configurações</string>
<string name="reboot_userspace">Reinicialização suave</string>
<string name="reboot_recovery">Reiniciar para recuperação</string>
<string name="reboot_bootloader">Reinicializar para bootloader</string>
<string name="reboot_download">Reinicializar para download</string>
<string name="reboot_edl">Reinicializar para EDL</string>
<string name="about">Sobre</string>
<string name="module_uninstall_confirm">Tem certeza de que deseja desinstalar o módulo %s?</string>
<string name="module_uninstall_success">%s desinstalado</string>
<string name="module_uninstall_failed">Falha ao desinstalar: %s</string>
<string name="module_version">Versão</string>
<string name="module_author">Autor</string>
<string name="module_overlay_fs_not_available">overlayfs não está disponível, o módulo não funcioná!</string>
<string name="refresh">Atualizar</string>
<string name="show_system_apps">Mostrar aplicativos do sistema</string>
<string name="hide_system_apps">Ocultar aplicativos do sistema</string>
<string name="send_log">Enviar log</string>
<string name="safe_mode">Modo de segurança</string>
<string name="reboot_to_apply">Reinicie para entrar em vigor</string>
<string name="module_magisk_conflict">Os módulos estão desativados porque estão em conflito com o Magisk\'s!</string>
<string name="home_learn_kernelsu">Leia mais sobre KernelSU</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">Saiba como instalar o KernelSU e usar os módulos</string>
<string name="home_support_title">Apoie-nos</string>
<string name="home_support_content">O KernelSU é, e sempre será, gratuito e de código aberto. No entanto, você pode nos mostrar que se importa fazendo uma doação.</string>
<string name="about_source_code"><![CDATA[Veja o código-fonte em %1$s<br/>Junte-se ao nosso %2$s canal]]></string>
<string name="profile" translatable="false">Perfil do aplicativo</string>
<string name="profile_default">Padrão</string>
<string name="profile_template">Modelo</string>
<string name="profile_custom">Personalizado</string>
<string name="profile_name">Nome do perfil</string>
<string name="profile_namespace">Montar namespace</string>
<string name="profile_namespace_inherited">incluído</string>
<string name="profile_namespace_global">Global</string>
<string name="profile_namespace_individual">Individual</string>
<string name="profile_umount_modules">Módulos não montados</string>
<string name="failed_to_update_app_profile">Falha ao atualizar o perfil do aplicativo para %s</string>
<string name="require_kernel_version">A versão atual do kernel %d é muito baixo para o gerenciador funcionar corretamente. Atualize para a versão %d ou superior!</string>
<string name="settings_umount_modules_default">Não montar módulos por padrão</string>
<string name="settings_umount_modules_default_summary">O valor padrão global para \"Módulos não montados\" em perfis de aplicativos. Se ativado, removerá todas as modificações do módulo do sistema para aplicativos que não possuem um perfil definido.</string>
<string name="profile_umount_modules_summary">Ativar esta opção permitirá que o KernelSU restaure quaisquer arquivos modificados pelos módulos para este aplicativo.</string>
</resources>

View File

@@ -5,18 +5,14 @@
<string name="home_click_to_install">Click pentru a instala</string> <string name="home_click_to_install">Click pentru a instala</string>
<string name="home_working">Funcționează</string> <string name="home_working">Funcționează</string>
<string name="home_working_version">Versiune: %d</string> <string name="home_working_version">Versiune: %d</string>
<string name="home_superuser_count">Superuseri: %d</string>
<string name="home_module_count">Module: %d</string>
<string name="home_unsupported">Necompatibil</string> <string name="home_unsupported">Necompatibil</string>
<string name="home_unsupported_reason">KernelSU suportă doar nuclee GKI acum</string> <string name="home_unsupported_reason">KernelSU suportă doar nuclee GKI acum</string>
<string name="home_copied_to_clipboard">Copiat în clipboard</string>
<string name="home_support">Asistență</string>
<string name="home_kernel">Nucleu</string> <string name="home_kernel">Nucleu</string>
<string name="home_arch">Arhitectură</string>
<string name="home_manager_version">Versiune Manager</string> <string name="home_manager_version">Versiune Manager</string>
<string name="home_api">Nivel API</string>
<string name="home_abi">ABI</string>
<string name="home_fingerprint">Amprentă</string> <string name="home_fingerprint">Amprentă</string>
<string name="home_securitypatch">Corecție de securitate</string>
<string name="home_selinux_status">Stare SELinux</string> <string name="home_selinux_status">Stare SELinux</string>
<string name="selinux_status_disabled">Dezactivat</string> <string name="selinux_status_disabled">Dezactivat</string>
@@ -24,12 +20,11 @@
<string name="selinux_status_permissive">Permisiv</string> <string name="selinux_status_permissive">Permisiv</string>
<string name="selinux_status_unknown">Necunoscut</string> <string name="selinux_status_unknown">Necunoscut</string>
<string name="superuser">Superutilizator</string> <string name="superuser">Superutilizator</string>
<string name="superuser_failed_to_grant_root">Nu s-a putut acorda acces root pentru %d</string>
<string name="module_failed_to_enable">Activarea modulului %s a eșuat</string> <string name="module_failed_to_enable">Activarea modulului %s a eșuat</string>
<string name="module_failed_to_disable">Dezactivarea modulului %s a eșuat</string> <string name="module_failed_to_disable">Dezactivarea modulului %s a eșuat</string>
<string name="module_empty">Niciun modul instalat</string> <string name="module_empty">Niciun modul instalat</string>
<string name="module">Module</string> <string name="module">Modul</string>
<string name="uninstall">Dezinstalează</string> <string name="uninstall">Dezinstalează</string>
<string name="module_install">Instalează</string> <string name="module_install">Instalează</string>
<string name="install">Instalează</string> <string name="install">Instalează</string>
@@ -41,7 +36,6 @@
<string name="reboot_download">Repornire în Download</string> <string name="reboot_download">Repornire în Download</string>
<string name="reboot_edl">Repornire în EDL</string> <string name="reboot_edl">Repornire în EDL</string>
<string name="about">Despre</string> <string name="about">Despre</string>
<string name="require_kernel_version_8">Necesită KernelSU versiunea 8+</string>
<string name="module_uninstall_confirm">Sigur dorești să dezinstalezi modulul %s?</string> <string name="module_uninstall_confirm">Sigur dorești să dezinstalezi modulul %s?</string>
<string name="module_uninstall_success">%s dezinstalat</string> <string name="module_uninstall_success">%s dezinstalat</string>
<string name="module_uninstall_failed">Dezinstalare eșuată: %s</string> <string name="module_uninstall_failed">Dezinstalare eșuată: %s</string>
@@ -55,4 +49,24 @@
<string name="safe_mode">Mod sigur</string> <string name="safe_mode">Mod sigur</string>
<string name="reboot_to_apply">Repornește pentru ca modificările să intre în vigoare</string> <string name="reboot_to_apply">Repornește pentru ca modificările să intre în vigoare</string>
<string name="module_magisk_conflict">Modulele sunt dezactivate deoarece sunt în conflict cu cele ale Magisk-ului!</string> <string name="module_magisk_conflict">Modulele sunt dezactivate deoarece sunt în conflict cu cele ale Magisk-ului!</string>
<string name="home_learn_kernelsu">Află mai multe despre KernelSU</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">Află cum să instalezi KernelSU și să utilizezi module</string>
<string name="home_support_title">Suport</string>
<string name="home_support_content">KernelSU este, și va fi întotdeauna, gratuit și cu codul sursă deschis. Cu toate acestea, ne poți arăta că îți pasă făcând o donație.</string>
<string name="about_source_code"><![CDATA[Vezi codul sursă la %1$s<br/>Alătură-te canalului nostru %2$s]]></string>
<string name="profile_default">Implicit</string>
<string name="profile_template">Șablon</string>
<string name="profile_custom">Personalizat</string>
<string name="profile_name">Nume profil</string>
<string name="profile_namespace">Montare spațiu de nume</string>
<string name="profile_namespace_inherited">Moștenit</string>
<string name="profile_namespace_global">Global</string>
<string name="profile_namespace_individual">Individual</string>
<string name="profile_umount_modules">Module u-montate</string>
<string name="failed_to_update_app_profile">Nu s-a putut actualiza profilul aplicației pentru %s</string>
<string name="require_kernel_version">Versiunea actuală a nucleului %d este prea mică pentru ca managerul să funcționeze corect. Actualizează la versiunea %d sau o versiune superioară!</string>
<string name="settings_umount_modules_default">U-montează modulele în mod implicit</string>
<string name="settings_umount_modules_default_summary">Valoarea implicită globală pentru „Module u-montate” în Profilurile aplicațiilor. Dacă este activat, va elimina toate modificările modulelor aduse sistemului pentru aplicațiile care nu au un profil setat.</string>
<string name="profile_umount_modules_summary">Activarea acestei opțiuni va permite KernelSU să restaureze orice fișiere modificate de către modulele pentru această aplicație.</string>
</resources> </resources>

View File

@@ -4,18 +4,14 @@
<string name="home_click_to_install">Нажмите чтобы установить</string> <string name="home_click_to_install">Нажмите чтобы установить</string>
<string name="home_working">Работает</string> <string name="home_working">Работает</string>
<string name="home_working_version">Версия: %d</string> <string name="home_working_version">Версия: %d</string>
<string name="home_superuser_count">Superusers: %d</string>
<string name="home_module_count">Модули: %d</string>
<string name="home_unsupported">Не поддерживается</string> <string name="home_unsupported">Не поддерживается</string>
<string name="home_unsupported_reason">KernelSU поддерживает только GKI ядра</string> <string name="home_unsupported_reason">KernelSU поддерживает только GKI ядра</string>
<string name="home_copied_to_clipboard">Скопировано в буфер обмена</string>
<string name="home_support">Поддержка</string>
<string name="home_kernel">Ядро</string> <string name="home_kernel">Ядро</string>
<string name="home_arch">Архитектура</string>
<string name="home_manager_version">Версия</string> <string name="home_manager_version">Версия</string>
<string name="home_api">Уровень API</string>
<string name="home_abi">ABI</string>
<string name="home_fingerprint">Подпись</string> <string name="home_fingerprint">Подпись</string>
<string name="home_securitypatch">Патч Безопасности</string>
<string name="home_selinux_status">Состояние SELinux</string> <string name="home_selinux_status">Состояние SELinux</string>
<string name="selinux_status_disabled">Выключен</string> <string name="selinux_status_disabled">Выключен</string>
@@ -23,7 +19,6 @@
<string name="selinux_status_permissive">Разрешающий</string> <string name="selinux_status_permissive">Разрешающий</string>
<string name="selinux_status_unknown">Неизвестно</string> <string name="selinux_status_unknown">Неизвестно</string>
<string name="superuser">Superuser</string> <string name="superuser">Superuser</string>
<string name="superuser_failed_to_grant_root">Failed to grant root for %d</string>
<string name="module_failed_to_enable">Не удалось включить модуль: %s</string> <string name="module_failed_to_enable">Не удалось включить модуль: %s</string>
<string name="module_failed_to_disable">Не удалось отключить модуль: %s</string> <string name="module_failed_to_disable">Не удалось отключить модуль: %s</string>
<string name="module_empty">Нет установленных модулей</string> <string name="module_empty">Нет установленных модулей</string>
@@ -40,7 +35,7 @@
<string name="reboot_download">Reboot to Download</string> <string name="reboot_download">Reboot to Download</string>
<string name="reboot_edl">Reboot to EDL</string> <string name="reboot_edl">Reboot to EDL</string>
<string name="about">О KernelSU</string> <string name="about">О KernelSU</string>
<string name="require_kernel_version_8">Требуется KernelSU версии 8 и выше</string> <string name="module_uninstall_confirm">Вы уверены, что хотите удалить модуль %s?</string>
<string name="module_uninstall_success">%s удален</string> <string name="module_uninstall_success">%s удален</string>
<string name="module_uninstall_failed">Не удалось удалить: %s</string> <string name="module_uninstall_failed">Не удалось удалить: %s</string>
<string name="module_version">Версия</string> <string name="module_version">Версия</string>
@@ -52,5 +47,11 @@
<string name="send_log">Отправить лог</string> <string name="send_log">Отправить лог</string>
<string name="safe_mode">Безопасный режим</string> <string name="safe_mode">Безопасный режим</string>
<string name="reboot_to_apply">Перезагрузите, чтобы вступить в силу</string> <string name="reboot_to_apply">Перезагрузите, чтобы вступить в силу</string>
<string name="module_magisk_conflict">Модули отключены, потому что они конфликтуют с модулями Magisk!</string>
<string name="home_learn_kernelsu">Узнайте о KernelSU</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">Узнайте, как установить KernelSU и использовать модули</string>
<string name="home_support_title">Поддержите нас</string>
<string name="home_support_content">KernelSU был и всегда будет бесплатным и открытым проектом. Однако Вы всегда можете поддержать нас, отправив небольшое пожертвование.</string>
<string name="about_source_code"><![CDATA[Посмотреть исходники на %1$s<br/>Присоединяйтесь к каналу %2$s]]></string>
</resources> </resources>

View File

@@ -0,0 +1,67 @@
<resources>
<string name="home">Ana menü</string>
<string name="home_not_installed">Yüklü değil</string>
<string name="home_click_to_install">Yüklemek için dokun</string>
<string name="home_working">Çalışıyor</string>
<string name="home_working_version">Sürüm: %d</string>
<string name="home_superuser_count">Süper kullanıcılar: %d</string>
<string name="home_module_count">Modüller: %d</string>
<string name="home_unsupported">Desteklenmiyor</string>
<string name="home_unsupported_reason">KernelSU artık yalnızca GKI çekirdeklerini destekliyor</string>
<string name="home_kernel">Kernel</string>
<string name="home_manager_version">Yönetici Sürümü</string>
<string name="home_fingerprint">Parmak İzi</string>
<string name="home_selinux_status">SELinux Durumu</string>
<string name="selinux_status_disabled">Devre dışı</string>
<string name="selinux_status_enforcing">Zorunlu</string>
<string name="selinux_status_permissive">Serbest</string>
<string name="selinux_status_unknown">Bilinmiyor</string>
<string name="superuser">Süper kullanıcı</string>
<string name="module_failed_to_enable">Modül etkinleştirilemedi: %s</string>
<string name="module_failed_to_disable">Modül devre dışı bırakılamadı: %s</string>
<string name="module_empty">Yüklü modül yok</string>
<string name="module">Modül</string>
<string name="uninstall">Kaldır</string>
<string name="module_install">Yükle</string>
<string name="install">Yükle</string>
<string name="reboot">Yeniden başlat</string>
<string name="settings">Ayarlar</string>
<string name="reboot_userspace">Hızlı yeniden Başlat</string>
<string name="reboot_recovery">Kurtarma modunda Başlat</string>
<string name="reboot_bootloader">Bootloader modunda Başlat</string>
<string name="reboot_download">İndirme modunda Başlat</string>
<string name="reboot_edl">EDL modunda Başlat</string>
<string name="about">Hakkında</string>
<string name="module_uninstall_confirm">%s modülünü kaldırmak istediğinizden emin misiniz?</string>
<string name="module_uninstall_success">%s kaldırıldı</string>
<string name="module_uninstall_failed">Kaldırılamadı: %s</string>
<string name="module_version">Sürüm</string>
<string name="module_author">Yazar</string>
<string name="module_overlay_fs_not_available">overlayfs mevcut değil, modül çalışamıyor!</string>
<string name="refresh">Yenile</string>
<string name="show_system_apps">Sistem uygulamalarını göster</string>
<string name="hide_system_apps">Sistem uygulamalarını gizle</string>
<string name="send_log">Log gönder</string>
<string name="safe_mode">Güvenli mod</string>
<string name="reboot_to_apply">Etkili olması için yeniden başlat</string>
<string name="module_magisk_conflict">Modüller Magisk ile çakıştığı için devre dışı bırakıldı!</string>
<string name="home_learn_kernelsu">KernelSU\'yu Öğrenin</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">KernelSU\'yu nasıl kuracağınızı ve modülleri nasıl kullanacağınızı öğrenin</string>
<string name="home_support_title">Bizi destekle</string>
<string name="home_support_content">KernelSU ücretsiz ve açık kaynaktır ve her zaman öyle kalacaktır. Ancak bağış yaparak bize değer verdiğinizi gösterebilirsiniz.</string>
<string name="about_source_code"><![CDATA[%1$s kaynak kodunu görüntüle<br/>%2$s kanalımıza katılın]]></string>
<string name="profile">Uygulama profili</string>
<string name="profile_default">Varsayılan</string>
<string name="profile_template">Örnek</string>
<string name="profile_custom">Özel</string>
<string name="profile_name">Profil adı</string>
<string name="profile_namespace">Ad alanını bağla</string>
<string name="profile_namespace_inherited">Kalıtsal</string>
<string name="profile_namespace_global">Küresel</string>
<string name="profile_namespace_individual">Bireysel</string>
<string name="profile_umount_modules">Modüllerin bağlantısını kes</string>
</resources>

View File

@@ -0,0 +1,74 @@
<resources>
<string name="app_name" translatable="false">KernelSU</string>
<string name="home">Головна</string>
<string name="home_not_installed">Не встановлено</string>
<string name="home_click_to_install">Натисніть щоб встановити</string>
<string name="home_working">Працює</string>
<string name="home_working_version">Версія: %d</string>
<string name="home_superuser_count">Суперкористувачі: %d</string>
<string name="home_module_count">Модулі: %d</string>
<string name="home_unsupported">Не підтримується</string>
<string name="home_unsupported_reason">KernelSU підтримує лише ядра GKI</string>
<string name="home_kernel">Ядро</string>
<string name="home_manager_version">Версія менеджера</string>
<string name="home_fingerprint">Відбиток</string>
<string name="home_selinux_status">Статус SELinux</string>
<string name="selinux_status_disabled">Вимкнено</string>
<string name="selinux_status_enforcing">Примусовий</string>
<string name="selinux_status_permissive">Дозвільний</string>
<string name="selinux_status_unknown">Невідомо</string>
<string name="superuser">Суперкористувачі</string>
<string name="module_failed_to_enable">Не вдалося ввімкнути модуль: %s</string>
<string name="module_failed_to_disable">Не вдалося вимкнути модуль: %s</string>
<string name="module_empty">Немає встановлених модулів</string>
<string name="module">Модулі</string>
<string name="uninstall">Видалити</string>
<string name="module_install">Встановити</string>
<string name="install">Встановити</string>
<string name="reboot">Перезавантажити</string>
<string name="settings">Налаштування</string>
<string name="reboot_userspace">М\'яке перезавантаження</string>
<string name="reboot_recovery">Перезавантажити до Recovery</string>
<string name="reboot_bootloader">Перезавантажити до Bootloader</string>
<string name="reboot_download">Перезавантажити до Download</string>
<string name="reboot_edl">Перезавантажити до EDL</string>
<string name="about">Про додаток</string>
<string name="module_uninstall_confirm">Ви впевнені, що хочете видалити модуль %s?</string>
<string name="module_uninstall_success">%s видалено</string>
<string name="module_uninstall_failed">Не вдалося видалити: %s</string>
<string name="module_version">Версія</string>
<string name="module_author">Автор</string>
<string name="module_overlay_fs_not_available">overlayfs не доступний, модуль не може працювати!</string>
<string name="refresh">Оновити</string>
<string name="show_system_apps">Показати системні додатки</string>
<string name="hide_system_apps">Сховати системні додатки</string>
<string name="send_log">Надіслати лог</string>
<string name="safe_mode">Безпечний режим</string>
<string name="reboot_to_apply">Перезавантажте, щоб застосувати</string>
<string name="module_magisk_conflict">Модулі вимкнено, оскільки вони конфліктують із модулями Magisk!</string>
<string name="home_learn_kernelsu">Дізнайтеся про KernelSU</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">Дізнайтеся, як інсталювати KernelSU і використовувати модулі</string>
<string name="home_support_title">Підтримати нас</string>
<string name="home_support_content">KernelSU є, і завжди буде безкоштовним та з відкритим кодом. Однак, якщо вам не байдуже, можете зробити невеличке пожертвування.</string>
<string name="about_source_code"><![CDATA[Переглянути вихідний код на %1$s<br/>Приєднуйтесь до нашого каналу %2$s]]></string>
<string name="profile">Профіль додатка</string>
<string name="profile_default">Типовий</string>
<string name="profile_template">Шаблон</string>
<string name="profile_custom">Власний</string>
<string name="profile_name">Назва профілю</string>
<string name="profile_namespace">Монтування простору імен</string>
<string name="profile_namespace_inherited">Наслідуваний</string>
<string name="profile_namespace_global">Глобальний</string>
<string name="profile_namespace_individual">Індивідуальний</string>
<string name="profile_umount_modules">Розмонтувати модулі</string>
<string name="failed_to_update_app_profile">Не вдалося оновити профіль додатка для %s</string>
<string name="require_kernel_version">Поточна версія ядра %d занизька для належної роботи менджера. Будь ласка, оновіть до версії %d або вище!</string>
<string name="settings_umount_modules_default">Розмонтувати модулі за замовчуванням</string>
<string name="settings_umount_modules_default_summary">Загальне значення за замовчуванням для \"Розмонтувати модулі\" у профілях додатків. Якщо ввімкнено, буде видалено всі модифікації модулів у системі для додатків, які не мають встановленого профілю.</string>
<string name="profile_umount_modules_summary">Увімкнення даної опції дозволить KernelSU відновити для цього додатка будь-які файли, змінені модулями.</string>
</resources>

View File

@@ -1,4 +1,12 @@
<resources> <resources>
<string name="home_learn_kernelsu">Tìm hiểu về KernelSU</string>
<string name="home_click_to_learn_kernelsu">Cách cài đặt KernelSU và sử dụng mô-đun</string>
<string name="home_support_title">Hỗ trợ chúng tôi</string>
<string name="home_support_content">KernelSU sẽ luôn luôn miễn phi và mã nguồn mở. Tuy nhiên, bạn có thể cho chúng tôi thấy rằng bạn quan tâm bằng cách gửi một khoản đóng góp nhỏ.</string>
<string name="about_source_code"><![CDATA[Xem mã nguồn tại %1$s<br/>Tham gia kênh %2$s của chúng tôi]]></string>
<string name="module_magisk_conflict">"Các mô-đun bị vô hiệu hóa vì nó xung đột với Magisk!"</string>
<string name="module_uninstall_confirm">Bạn có chắc chắn muốn gỡ cài đặt mô-đun %s?</string>
<string name="send_log">Gửi nhật ký</string>
<string name="home">Trang chủ</string> <string name="home">Trang chủ</string>
<string name="home_not_installed">Chưa được cài đặt</string> <string name="home_not_installed">Chưa được cài đặt</string>
<string name="home_click_to_install">Nhấn đề cài dặt</string> <string name="home_click_to_install">Nhấn đề cài dặt</string>
@@ -6,22 +14,15 @@
<string name="home_working_version">Phiên bản: %d</string> <string name="home_working_version">Phiên bản: %d</string>
<string name="home_unsupported">Không hỗ trợ</string> <string name="home_unsupported">Không hỗ trợ</string>
<string name="home_unsupported_reason">KernelSU hiện tại chỉ hỗ trợ kernel GKI</string> <string name="home_unsupported_reason">KernelSU hiện tại chỉ hỗ trợ kernel GKI</string>
<string name="home_copied_to_clipboard">Sao chép vào clipboard</string>
<string name="home_support">Ủng hộ</string>
<string name="home_kernel">Kernel</string> <string name="home_kernel">Kernel</string>
<string name="home_arch">Kiến trúc</string>
<string name="home_manager_version">Phiên bản</string> <string name="home_manager_version">Phiên bản</string>
<string name="home_api">Cấp độ API</string>
<string name="home_abi">ABI</string>
<string name="home_fingerprint">Fingerprint</string> <string name="home_fingerprint">Fingerprint</string>
<string name="home_securitypatch">Bản vá bảo mật</string>
<string name="home_selinux_status">Trạng thái SELinux</string> <string name="home_selinux_status">Trạng thái SELinux</string>
<string name="selinux_status_disabled">Vô hiệu hóa</string> <string name="selinux_status_disabled">Vô hiệu hóa</string>
<string name="selinux_status_enforcing">Thực thi</string> <string name="selinux_status_enforcing">Thực thi</string>
<string name="selinux_status_permissive">Cho phép</string> <string name="selinux_status_permissive">Cho phép</string>
<string name="selinux_status_unknown">Không rõ</string> <string name="selinux_status_unknown">Không rõ</string>
<string name="superuser">Superuser</string> <string name="superuser">Superuser</string>
<string name="superuser_failed_to_grant_root">Không thể cấp quyền root cho %d</string>
<string name="module_failed_to_enable">Không thể kích hoạt mô-đun: %s</string> <string name="module_failed_to_enable">Không thể kích hoạt mô-đun: %s</string>
<string name="module_failed_to_disable">Không thể vô hiệu hóa mô-đun: %s</string> <string name="module_failed_to_disable">Không thể vô hiệu hóa mô-đun: %s</string>
<string name="module_empty">Chưa có mô-đun nào được cài đặt</string> <string name="module_empty">Chưa có mô-đun nào được cài đặt</string>
@@ -37,7 +38,6 @@
<string name="reboot_download">Khởi động lại vào Download Mode</string> <string name="reboot_download">Khởi động lại vào Download Mode</string>
<string name="reboot_edl">Khởi động vào EDL</string> <string name="reboot_edl">Khởi động vào EDL</string>
<string name="about">Thông tin</string> <string name="about">Thông tin</string>
<string name="require_kernel_version_8">Yêu cầu KernelSU phiên bản 8+</string>
<string name="module_uninstall_success">%s đã được gỡ cài đặt</string> <string name="module_uninstall_success">%s đã được gỡ cài đặt</string>
<string name="module_uninstall_failed">Lỗi khi gỡ cài đặt: %s</string> <string name="module_uninstall_failed">Lỗi khi gỡ cài đặt: %s</string>
<string name="module_version">Phiên bản</string> <string name="module_version">Phiên bản</string>
@@ -48,4 +48,5 @@
<string name="hide_system_apps">Ẩn ứng dụng hệ thống</string> <string name="hide_system_apps">Ẩn ứng dụng hệ thống</string>
<string name="safe_mode">Chế độ an toàn</string> <string name="safe_mode">Chế độ an toàn</string>
<string name="reboot_to_apply">Khởi động lại để có hiệu lực</string> <string name="reboot_to_apply">Khởi động lại để có hiệu lực</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/vi_VN/guide/what-is-kernelsu.html</string>
</resources> </resources>

View File

@@ -5,24 +5,18 @@
<string name="home_click_to_install">点击安装</string> <string name="home_click_to_install">点击安装</string>
<string name="home_working">工作中</string> <string name="home_working">工作中</string>
<string name="home_working_version">版本: %d</string> <string name="home_working_version">版本: %d</string>
<string name="home_superuser_count">超级用户数:%d</string>
<string name="home_unsupported">不支持</string> <string name="home_unsupported">不支持</string>
<string name="home_unsupported_reason">KernelSU 现在只支持 GKI 内核</string> <string name="home_unsupported_reason">KernelSU 现在只支持 GKI 内核</string>
<string name="home_copied_to_clipboard">已复制到剪贴板</string>
<string name="home_support">支持</string>
<string name="home_kernel">内核版本</string> <string name="home_kernel">内核版本</string>
<string name="home_arch">设备架构</string>
<string name="home_manager_version">管理器版本</string> <string name="home_manager_version">管理器版本</string>
<string name="home_api">API 版本</string>
<string name="home_abi">ABI 支持</string>
<string name="home_fingerprint">系统指纹</string> <string name="home_fingerprint">系统指纹</string>
<string name="home_securitypatch">安全补丁</string>
<string name="home_selinux_status">SELinux 状态</string> <string name="home_selinux_status">SELinux 状态</string>
<string name="selinux_status_disabled">被禁用</string> <string name="selinux_status_disabled">被禁用</string>
<string name="selinux_status_enforcing">强制执行</string> <string name="selinux_status_enforcing">强制执行</string>
<string name="selinux_status_permissive">宽容模式</string> <string name="selinux_status_permissive">宽容模式</string>
<string name="selinux_status_unknown">未知</string> <string name="selinux_status_unknown">未知</string>
<string name="superuser">超级用户</string> <string name="superuser">超级用户</string>
<string name="superuser_failed_to_grant_root">无法为 %d 授予 Root</string>
<string name="module_failed_to_enable">无法启用模块: %s</string> <string name="module_failed_to_enable">无法启用模块: %s</string>
<string name="module_failed_to_disable">无法禁用模块: %s</string> <string name="module_failed_to_disable">无法禁用模块: %s</string>
<string name="module_empty">没有安装模块</string> <string name="module_empty">没有安装模块</string>
@@ -38,7 +32,6 @@
<string name="reboot_download">重启到 Download</string> <string name="reboot_download">重启到 Download</string>
<string name="reboot_edl">重启到 EDL</string> <string name="reboot_edl">重启到 EDL</string>
<string name="about">关于</string> <string name="about">关于</string>
<string name="require_kernel_version_8">需要 KernelSU 版本 8+</string>
<string name="module_uninstall_confirm">确定要卸载模块 %s 吗?</string> <string name="module_uninstall_confirm">确定要卸载模块 %s 吗?</string>
<string name="module_uninstall_success">%s 已卸载</string> <string name="module_uninstall_success">%s 已卸载</string>
<string name="module_uninstall_failed">卸载失败: %s</string> <string name="module_uninstall_failed">卸载失败: %s</string>
@@ -52,4 +45,17 @@
<string name="safe_mode">安全模式</string> <string name="safe_mode">安全模式</string>
<string name="reboot_to_apply">重启生效</string> <string name="reboot_to_apply">重启生效</string>
<string name="module_magisk_conflict">所有模块已被禁用,因为它与 Magisk 的模块系统有冲突!</string> <string name="module_magisk_conflict">所有模块已被禁用,因为它与 Magisk 的模块系统有冲突!</string>
<string name="home_module_count">模块数:%d</string>
<string name="home_learn_kernelsu">了解 KernelSU</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/zh_CN/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">了解如何安装 KernelSU 以及如何开发模块</string>
<string name="home_support_title">支持开发</string>
<string name="home_support_content">KernelSU 将保持免费和开源,向开发者捐赠以表示支持。</string>
<string name="about_source_code"><![CDATA[在 %1$s 查看源码<br/>加入我们的 %2$s 频道<br/>加入我们的 <b><a href="https://pd.qq.com/s/8lipl1brp">QQ 频道</a></b>]]></string>
<string name="profile_umount_modules">卸载模块</string>
<string name="failed_to_update_app_profile">为 %s 更新 App Profile 失败</string>
<string name="require_kernel_version">当前内核版本 %d 过低,管理器无法正常工作,请升级内核版本至 %d 或以上!</string>
<string name="settings_umount_modules_default">默认卸载模块</string>
<string name="settings_umount_modules_default_summary">App Profile 中\"卸载模块\"的全局默认值,如果启用,将会为没有设置 Profile 的应用移除所有模块针对系统的修改</string>
<string name="profile_umount_modules_summary">启用后将允许 KernelSU 为本应用还原被模块修改过的文件</string>
</resources> </resources>

View File

@@ -5,18 +5,14 @@
<string name="home_click_to_install">按一下以安裝</string> <string name="home_click_to_install">按一下以安裝</string>
<string name="home_working">正在處理</string> <string name="home_working">正在處理</string>
<string name="home_working_version">版本:%d</string> <string name="home_working_version">版本:%d</string>
<string name="home_superuser_count">超級使用者:%d 個</string>
<string name="home_module_count">模組:%d 個</string>
<string name="home_unsupported">不支援</string> <string name="home_unsupported">不支援</string>
<string name="home_unsupported_reason">KernelSU 現在僅支援 GKI 核心</string> <string name="home_unsupported_reason">KernelSU 現在僅支援 GKI 核心</string>
<string name="home_copied_to_clipboard">已複製到剪貼簿</string>
<string name="home_support">支援</string>
<string name="home_kernel">核心</string> <string name="home_kernel">核心</string>
<string name="home_arch">架構</string>
<string name="home_manager_version">管理員版本</string> <string name="home_manager_version">管理員版本</string>
<string name="home_api">API 層級</string>
<string name="home_abi">ABI 支援</string>
<string name="home_fingerprint">指紋</string> <string name="home_fingerprint">指紋</string>
<string name="home_securitypatch">安全性修補程式</string>
<string name="home_selinux_status">SELinux 狀態</string> <string name="home_selinux_status">SELinux 狀態</string>
<string name="selinux_status_disabled">已停用</string> <string name="selinux_status_disabled">已停用</string>
@@ -24,7 +20,6 @@
<string name="selinux_status_permissive">寬鬆</string> <string name="selinux_status_permissive">寬鬆</string>
<string name="selinux_status_unknown">未知</string> <string name="selinux_status_unknown">未知</string>
<string name="superuser">超級使用者</string> <string name="superuser">超級使用者</string>
<string name="superuser_failed_to_grant_root">無法為 %d 授予 Root 存取權</string>
<string name="module_failed_to_enable">無法啟用模組:%s</string> <string name="module_failed_to_enable">無法啟用模組:%s</string>
<string name="module_failed_to_disable">無法停用模組:%s</string> <string name="module_failed_to_disable">無法停用模組:%s</string>
<string name="module_empty">尚未安裝模組</string> <string name="module_empty">尚未安裝模組</string>
@@ -41,9 +36,8 @@
<string name="reboot_download">重新啟動至 Download</string> <string name="reboot_download">重新啟動至 Download</string>
<string name="reboot_edl">重新啟動至 EDL</string> <string name="reboot_edl">重新啟動至 EDL</string>
<string name="about">關於</string> <string name="about">關於</string>
<string name="require_kernel_version_8">需要 KernelSU 8+ 版本</string> <string name="module_uninstall_confirm">您確定要解除安裝模組「%s」嗎</string>
<string name="module_uninstall_confirm">您確定要解除安裝模組 %s 嗎?</string> <string name="module_uninstall_success">「%s」已解除安裝</string>
<string name="module_uninstall_success">%s 已解除安裝</string>
<string name="module_uninstall_failed">無法解除安裝:%s</string> <string name="module_uninstall_failed">無法解除安裝:%s</string>
<string name="module_version">版本</string> <string name="module_version">版本</string>
<string name="module_author">作者</string> <string name="module_author">作者</string>
@@ -55,4 +49,10 @@
<string name="safe_mode">安全模式</string> <string name="safe_mode">安全模式</string>
<string name="reboot_to_apply">重新啟動以生效</string> <string name="reboot_to_apply">重新啟動以生效</string>
<string name="module_magisk_conflict">模組已停用,因其與 Magisk 的模組存在衝突!</string> <string name="module_magisk_conflict">模組已停用,因其與 Magisk 的模組存在衝突!</string>
<string name="home_learn_kernelsu">深入瞭解 KernelSU</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/zh_TW/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">瞭解如何安裝 KernelSU 和使用模組</string>
<string name="home_support_title">支持我們</string>
<string name="home_support_content">KernelSU 是免費且開源的,您可以透過捐贈來向我們展示您對我們的關心。</string>
<string name="about_source_code"><![CDATA[在 %1$s 中檢視原始碼<br/>加入我們的 %2$s 頻道]]></string>
</resources> </resources>

View File

@@ -4,27 +4,21 @@
<string name="home_not_installed">未安裝</string> <string name="home_not_installed">未安裝</string>
<string name="home_click_to_install">按一下以安裝</string> <string name="home_click_to_install">按一下以安裝</string>
<string name="home_working">正在處理</string> <string name="home_working">正在處理</string>
<string name="home_working_version">版本:%d</string> <string name="home_working_version">KernelSU 版本:%d</string>
<string name="home_superuser_count">已授權 Root%d 個</string>
<string name="home_unsupported">不支援</string> <string name="home_unsupported">不支援</string>
<string name="home_unsupported_reason">KernelSU 現在僅支援 GKI 核心</string> <string name="home_unsupported_reason">KernelSU 現在僅支援 GKI 核心</string>
<string name="home_copied_to_clipboard">已複製到剪貼簿</string>
<string name="home_support">支援</string>
<string name="home_kernel">核心</string> <string name="home_kernel">核心</string>
<string name="home_arch">架構</string> <string name="home_manager_version">管理器版本</string>
<string name="home_manager_version">管理員版本</string> <string name="home_fingerprint">設備指紋</string>
<string name="home_api">API 層級</string>
<string name="home_abi">ABI 支援</string>
<string name="home_fingerprint">指紋</string>
<string name="home_securitypatch">安全性修補程式</string>
<string name="home_selinux_status">SELinux 狀態</string> <string name="home_selinux_status">SELinux 狀態</string>
<string name="selinux_status_disabled">已停用</string> <string name="selinux_status_disabled">已停用</string>
<string name="selinux_status_enforcing">強制</string> <string name="selinux_status_enforcing">強制</string>
<string name="selinux_status_permissive">寬鬆</string> <string name="selinux_status_permissive">寬鬆</string>
<string name="selinux_status_unknown">未知</string> <string name="selinux_status_unknown">未知</string>
<string name="superuser">超級使用者</string> <string name="superuser">Root 授權</string>
<string name="superuser_failed_to_grant_root">無法為 %d 授予 Root 存取權</string>
<string name="module_failed_to_enable">無法啟用模組:%s</string> <string name="module_failed_to_enable">無法啟用模組:%s</string>
<string name="module_failed_to_disable">無法停用模組:%s</string> <string name="module_failed_to_disable">無法停用模組:%s</string>
<string name="module_empty">尚未安裝模組</string> <string name="module_empty">尚未安裝模組</string>
@@ -41,9 +35,8 @@
<string name="reboot_download">重新啟動至 Download</string> <string name="reboot_download">重新啟動至 Download</string>
<string name="reboot_edl">重新啟動至 EDL</string> <string name="reboot_edl">重新啟動至 EDL</string>
<string name="about">關於</string> <string name="about">關於</string>
<string name="require_kernel_version_8">需要 KernelSU 8+ 版本</string> <string name="module_uninstall_confirm">您確定要解除安裝模組「%s」嗎</string>
<string name="module_uninstall_confirm">您確定要解除安裝模組 %s 嗎?</string> <string name="module_uninstall_success">「%s」已解除安裝</string>
<string name="module_uninstall_success">%s 已解除安裝</string>
<string name="module_uninstall_failed">無法解除安裝:%s</string> <string name="module_uninstall_failed">無法解除安裝:%s</string>
<string name="module_version">版本</string> <string name="module_version">版本</string>
<string name="module_author">作者</string> <string name="module_author">作者</string>
@@ -55,4 +48,17 @@
<string name="safe_mode">安全模式</string> <string name="safe_mode">安全模式</string>
<string name="reboot_to_apply">重新啟動以生效</string> <string name="reboot_to_apply">重新啟動以生效</string>
<string name="module_magisk_conflict">模組已停用,因其與 Magisk 的模組存在衝突!</string> <string name="module_magisk_conflict">模組已停用,因其與 Magisk 的模組存在衝突!</string>
<string name="home_module_count">已安裝模組:%d 個</string>
<string name="home_learn_kernelsu">深入瞭解 KernelSU</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/zh_TW/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">瞭解如何安裝 KernelSU 以及如何開發模組</string>
<string name="home_support_title">支持開發</string>
<string name="home_support_content">KernelSU 將保持免費和開源,您可以考慮向開發人員捐贈以表示支持。</string>
<string name="about_source_code"><![CDATA[在 %1$s 中檢視原始碼<br/>加入我們的 %2$s 頻道]]></string>
<string name="profile_umount_modules">解除安裝模組</string>
<string name="failed_to_update_app_profile">為 %s 更新 App Profile 失敗</string>
<string name="require_kernel_version">目前安裝的核心版本 %d 過低,管理器無法正常工作,請升級核心版本至 %d 或以上!</string>
<string name="settings_umount_modules_default">預設解除安裝模組</string>
<string name="settings_umount_modules_default_summary">App Profile 中\"解除安裝模組\"的全域性預設值,如果啟用,將會為沒有設定 Profile 的應用移除所有模組針對系統的修改</string>
<string name="profile_umount_modules_summary">啟用後將允許 KernelSU 為本應用還原被模組修改過的檔案</string>
</resources> </resources>

View File

@@ -6,18 +6,14 @@
<string name="home_click_to_install">Click to install</string> <string name="home_click_to_install">Click to install</string>
<string name="home_working">Working</string> <string name="home_working">Working</string>
<string name="home_working_version">Version: %d</string> <string name="home_working_version">Version: %d</string>
<string name="home_superuser_count">Superusers: %d</string>
<string name="home_module_count">Modules: %d</string>
<string name="home_unsupported">Unsupported</string> <string name="home_unsupported">Unsupported</string>
<string name="home_unsupported_reason">KernelSU only supports GKI kernels now</string> <string name="home_unsupported_reason">KernelSU only supports GKI kernels now</string>
<string name="home_copied_to_clipboard">Copied to clipboard</string>
<string name="home_support">Support</string>
<string name="home_kernel">Kernel</string> <string name="home_kernel">Kernel</string>
<string name="home_arch">Arch</string>
<string name="home_manager_version">Manager Version</string> <string name="home_manager_version">Manager Version</string>
<string name="home_api">API Level</string>
<string name="home_abi">ABI</string>
<string name="home_fingerprint">Fingerprint</string> <string name="home_fingerprint">Fingerprint</string>
<string name="home_securitypatch">Security Patch</string>
<string name="home_selinux_status">SELinux status</string> <string name="home_selinux_status">SELinux status</string>
<string name="selinux_status_disabled">Disabled</string> <string name="selinux_status_disabled">Disabled</string>
@@ -25,7 +21,6 @@
<string name="selinux_status_permissive">Permissive</string> <string name="selinux_status_permissive">Permissive</string>
<string name="selinux_status_unknown">Unknown</string> <string name="selinux_status_unknown">Unknown</string>
<string name="superuser">Superuser</string> <string name="superuser">Superuser</string>
<string name="superuser_failed_to_grant_root">Failed to grant root for %d</string>
<string name="module_failed_to_enable">Failed to enable module: %s</string> <string name="module_failed_to_enable">Failed to enable module: %s</string>
<string name="module_failed_to_disable">Failed to disable module: %s</string> <string name="module_failed_to_disable">Failed to disable module: %s</string>
<string name="module_empty">No module installed</string> <string name="module_empty">No module installed</string>
@@ -42,7 +37,6 @@
<string name="reboot_download">Reboot to Download</string> <string name="reboot_download">Reboot to Download</string>
<string name="reboot_edl">Reboot to EDL</string> <string name="reboot_edl">Reboot to EDL</string>
<string name="about">About</string> <string name="about">About</string>
<string name="require_kernel_version_8">Require KernelSU version 8+</string>
<string name="module_uninstall_confirm">Are you sure you want to uninstall module %s?</string> <string name="module_uninstall_confirm">Are you sure you want to uninstall module %s?</string>
<string name="module_uninstall_success">%s uninstalled</string> <string name="module_uninstall_success">%s uninstalled</string>
<string name="module_uninstall_failed">Failed to uninstall: %s</string> <string name="module_uninstall_failed">Failed to uninstall: %s</string>
@@ -56,5 +50,25 @@
<string name="safe_mode">Safe mode</string> <string name="safe_mode">Safe mode</string>
<string name="reboot_to_apply">Reboot to take effect</string> <string name="reboot_to_apply">Reboot to take effect</string>
<string name="module_magisk_conflict">Modules are disabled because it is conflict with Magisk\'s!</string> <string name="module_magisk_conflict">Modules are disabled because it is conflict with Magisk\'s!</string>
<string name="home_learn_kernelsu">Learn KernelSU</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">Learn how to install KernelSU and use modules</string>
<string name="home_support_title">Support Us</string>
<string name="home_support_content">KernelSU is, and always will be, free, and open source. You can however show us that you care by making a donation.</string>
<string name="about_source_code"><![CDATA[View source code at %1$s<br/>Join our %2$s channel]]></string>
<string name="profile" translatable="false">App profile</string>
<string name="profile_default">Default</string>
<string name="profile_template">Template</string>
<string name="profile_custom">Custom</string>
<string name="profile_name">Profile name</string>
<string name="profile_namespace">Mount namespace</string>
<string name="profile_namespace_inherited">Inherited</string>
<string name="profile_namespace_global">Global</string>
<string name="profile_namespace_individual">Individual</string>
<string name="profile_umount_modules">Umoun modules</string>
<string name="failed_to_update_app_profile">Failed to update App Profile for %s</string>
<string name="require_kernel_version">The current kernel version %d is too low for the manager to function properly. Please upgrade to version %d or higher!</string>
<string name="settings_umount_modules_default">Umount modules by default</string>
<string name="settings_umount_modules_default_summary">The global default value for \"Umount modules\" in App Profiles. If enabled, it will remove all module modifications to the system for applications that do not have a Profile set.</string>
<string name="profile_umount_modules_summary">Enabling this option will allow KernelSU to restore any modified files by the modules for this application.</string>
</resources> </resources>

View File

@@ -1,17 +0,0 @@
package me.weishu.kernelsu
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

View File

@@ -1,33 +1,46 @@
import com.android.build.api.dsl.ApplicationExtension import com.android.build.api.dsl.ApplicationDefaultConfig
import com.android.build.gradle.BaseExtension import com.android.build.api.dsl.CommonExtension
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import com.android.build.gradle.api.AndroidBasePlugin
import org.jetbrains.kotlin.konan.properties.Properties
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
plugins { plugins {
id("com.android.application") apply false alias(libs.plugins.agp.app) apply false
id("com.android.library") apply false alias(libs.plugins.agp.lib) apply false
kotlin("android") apply false alias(libs.plugins.kotlin) apply false
alias(libs.plugins.lsplugin.cmaker)
} }
buildscript { cmaker {
repositories { default {
google() arguments.addAll(
mavenCentral() arrayOf(
"-DANDROID_STL=c++_static",
)
)
val flags = arrayOf(
"-Wno-gnu-string-literal-operator-template",
"-Wno-c++2b-extensions",
)
cFlags.addAll(flags)
cppFlags.addAll(flags)
abiFilters("arm64-v8a", "x86_64")
} }
dependencies { buildTypes {
classpath(kotlin("gradle-plugin", version = "1.7.20")) if (it.name == "release") {
arguments += "-DDEBUG_SYMBOLS_PATH=${buildDir.absolutePath}/symbols"
}
} }
} }
val androidMinSdk = 26 val androidMinSdkVersion = 26
val androidTargetSdk = 33 val androidTargetSdkVersion = 33
val androidCompileSdk = 33 val androidCompileSdkVersion = 33
val androidBuildToolsVersion = "33.0.1" val androidBuildToolsVersion = "33.0.2"
val androidSourceCompatibility = JavaVersion.VERSION_11 val androidCompileNdkVersion = "25.2.9519653"
val androidTargetCompatibility = JavaVersion.VERSION_11 val androidSourceCompatibility = JavaVersion.VERSION_17
val managerVersionCode = getVersionCode() val androidTargetCompatibility = JavaVersion.VERSION_17
val managerVersionName = getVersionName() val managerVersionCode by extra(getVersionCode())
val managerVersionName by extra(getVersionName())
tasks.register<Delete>("clean") { tasks.register<Delete>("clean") {
delete(rootProject.buildDir) delete(rootProject.buildDir)
@@ -61,71 +74,31 @@ fun getVersionName(): String {
return getGitDescribe() return getGitDescribe()
} }
fun Project.configureBaseExtension() { subprojects {
extensions.findByType<BaseExtension>()?.run { plugins.withType(AndroidBasePlugin::class.java) {
compileSdkVersion(androidCompileSdk) extensions.configure(CommonExtension::class.java) {
buildToolsVersion = androidBuildToolsVersion compileSdk = androidCompileSdkVersion
ndkVersion = androidCompileNdkVersion
buildToolsVersion = androidBuildToolsVersion
defaultConfig { defaultConfig {
minSdk = androidMinSdk minSdk = androidMinSdkVersion
targetSdk = androidTargetSdk if (this is ApplicationDefaultConfig) {
versionCode = managerVersionCode targetSdk = androidTargetSdkVersion
versionName = managerVersionName versionCode = managerVersionCode
versionName = managerVersionName
consumerProguardFiles("proguard-rules.pro")
}
val signFile = rootProject.file("sign.properties")
val config = if (signFile.canRead()) {
val prop = Properties()
prop.load(signFile.inputStream())
signingConfigs.create("config") {
storeFile = file(prop.getProperty("KEYSTORE_FILE"))
storePassword = prop.getProperty("KEYSTORE_PASSWORD")
keyAlias = prop.getProperty("KEY_ALIAS")
keyPassword = prop.getProperty("KEY_PASSWORD")
}
} else {
signingConfigs["debug"]
}
buildTypes {
all {
signingConfig = config
}
named("release") {
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
compileOptions {
sourceCompatibility = androidSourceCompatibility
targetCompatibility = androidTargetCompatibility
}
extensions.findByType<ApplicationExtension>()?.run {
buildTypes {
named("release") {
isShrinkResources = true
} }
} }
}
extensions.findByType<KotlinCompile>()?.run { lint {
kotlinOptions { abortOnError = true
jvmTarget = "11" checkReleaseBuilds = false
}
compileOptions {
sourceCompatibility = androidSourceCompatibility
targetCompatibility = androidTargetCompatibility
} }
} }
} }
} }
subprojects {
plugins.withId("com.android.application") {
configureBaseExtension()
}
plugins.withId("com.android.library") {
configureBaseExtension()
}
}

View File

@@ -1,23 +1,3 @@
# Project-wide Gradle settings. android.experimental.enableNewResourceShrinker.preciseShrinking=true
# IDE (e.g. Android Studio) users: android.enableAppCompileTimeRClass=true
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true android.useAndroidX=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true

View File

@@ -0,0 +1,57 @@
[versions]
agp = "8.0.1"
kotlin = "1.8.10"
ksp = "1.8.10-1.0.9"
compose-bom = "2023.05.01"
lifecycle = "2.6.1"
accompanist = "0.30.0"
navigation = "2.5.3"
compose-destination = "1.9.42-beta"
libsu = "5.0.5"
sheets-compose-dialogs = "1.1.1"
[plugins]
agp-app = { id = "com.android.application", version.ref = "agp" }
agp-lib = { id = "com.android.library", version.ref = "agp" }
kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
lsplugin-apksign = { id = "org.lsposed.lsplugin.apksign", version = "1.1" }
lsplugin-cmaker = { id = "org.lsposed.lsplugin.cmaker", version = "1.1" }
[libraries]
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version = "1.7.1" }
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
androidx-compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" }
androidx-compose-material = { group = "androidx.compose.material", name = "material" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle" }
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version = "lifecycle" }
com-google-accompanist-drawablepainter = { group = "com.google.accompanist", name = "accompanist-drawablepainter", version.ref = "accompanist" }
com-google-accompanist-navigation-animation = { group = "com.google.accompanist", name = "accompanist-navigation-animation", version.ref = "accompanist" }
com-google-accompanist-systemuicontroller = { group = "com.google.accompanist", name = "accompanist-systemuicontroller", version.ref = "accompanist" }
com-github-topjohnwu-libsu-core = { group = "com.github.topjohnwu.libsu", name = "core", version.ref = "libsu" }
com-github-topjohnwu-libsu-service = { group = "com.github.topjohnwu.libsu", name = "service", version.ref = "libsu" }
dev-rikka-rikkax-parcelablelist = { module = "dev.rikka.rikkax.parcelablelist:parcelablelist", version = "2.0.1" }
io-coil-kt-coil-compose = { group = "io.coil-kt", name = "coil-compose", version = "2.3.0" }
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version = "1.7.1" }
me-zhanghai-android-appiconloader-coil = { group = "me.zhanghai.android.appiconloader", name = "appiconloader-coil", version = "1.5.0" }
compose-destinations-animations-core = { group = "io.github.raamcosta.compose-destinations", name = "animations-core", version.ref = "compose-destination" }
compose-destinations-ksp = { group = "io.github.raamcosta.compose-destinations", name = "ksp", version.ref = "compose-destination" }
sheet-compose-dialogs-core = { group = "com.maxkeppeler.sheets-compose-dialogs", name = "core", version.ref = "sheets-compose-dialogs"}
sheet-compose-dialogs-list = { group = "com.maxkeppeler.sheets-compose-dialogs", name = "list", version.ref = "sheets-compose-dialogs"}

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

View File

@@ -6,14 +6,6 @@ pluginManagement {
google() google()
mavenCentral() mavenCentral()
} }
plugins {
val agp = "7.3.1"
val kotlin = "1.7.20"
id("com.android.application") version agp
id("com.android.library") version agp
id("com.google.devtools.ksp") version "$kotlin-1.0.8"
kotlin("android") version kotlin
}
} }
dependencyResolutionManagement { dependencyResolutionManagement {

View File

@@ -6,6 +6,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#define MAIN #define MAIN

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