Compare commits

...

317 Commits

Author SHA1 Message Date
raystef66
58ffaeb4e2 Update Flemish/Dutch translation (#677) 2023-06-23 09:31:58 +08:00
Ali Beyaz
76499ee629 Translated latest strings to Turkish (#676) 2023-06-23 09:31:23 +08:00
weishu
fedfa3e3fd manager: update card color 2023-06-23 00:35:35 +08:00
Igor Sorocean
2902e42f91 manager: update ro translation (#674) 2023-06-23 00:32:26 +08:00
weishu
37f4045499 manager: add a simple manager updater, close #627 2023-06-23 00:31:36 +08:00
weishu
12761ee167 manager: don't remember state when process died. 2023-06-22 23:24:35 +08:00
weishu
0d254237a4 manager: fix module install 2023-06-22 23:20:13 +08:00
weishu
f5bb24676e manager: fix download state 2023-06-22 19:46:26 +08:00
weishu
303a3a8482 manager: fix update button 2023-06-22 19:29:37 +08:00
weishu
07273b6971 manager: support module update online 2023-06-22 18:40:28 +08:00
weishu
c7c9e9c3ed ksud: respect the skip_mount flag of module 2023-06-22 17:00:02 +08:00
weishu
c3c990c50a ksud: increase reserved size to 256M 2023-06-22 16:48:13 +08:00
weishu
6942fe12b5 manager: set keyboard options for inputtext 2023-06-22 16:46:41 +08:00
weishu
f5cfb32882 kernel: fix incorrect umount for apps 2023-06-22 15:17:32 +08:00
weishu
e17f3eab96 Revert "kernel: use vfs_fstatat on kernel 5.10+, vfs_statx may have cfi."
This reverts commit cd3e292489.
2023-06-22 14:37:17 +08:00
weishu
08884da423 kernel: don't alloc groups for default groups 2023-06-22 13:42:28 +08:00
weishu
5f1d70dabb Revert "kernel: getname might sleep in kprobe handler (#670)"
This reverts commit 79bb9813ef.
2023-06-22 12:54:30 +08:00
weishu
79bb9813ef kernel: getname might sleep in kprobe handler (#670) 2023-06-22 10:54:50 +08:00
Ali Beyaz
1cda4ba245 Update latest strings to Turkish (#662) 2023-06-20 18:45:24 +08:00
raystef66
1cc678d20b Update Flemish/Dutch translation (#665) 2023-06-20 18:44:04 +08:00
weishu
cd3e292489 kernel: use vfs_fstatat on kernel 5.10+, vfs_statx may have cfi. 2023-06-20 18:42:41 +08:00
Howard Wu
40ea27a59c ci: Fix kernel version (#666) 2023-06-20 18:10:07 +08:00
Azeroth
e95ca9316c Fix typo (#667)
Fixed typo in this section
https://kernelsu.org/guide/installation.html#patch-boot-img-manully
2023-06-20 17:58:05 +08:00
Ikko Eltociear Ashimine
9b2f907a48 kernel: fix typo in allowlist.c (#663)
creat -> create
2023-06-20 11:03:09 +08:00
Coconut
90299ad0bb kernel:Fix the issue of incompatible __maybe_unused in the GCC compiler kernel used in versions 4.4. x to 4.9. x. (#660) 2023-06-20 10:35:07 +08:00
weishu
22d084f89b manager: Add selinux rules UI 2023-06-19 22:20:40 +08:00
Muhammad Fadlyas
99770a7362 Update Indonesian translation (#659) 2023-06-19 20:07:47 +08:00
weishu
bbc7ebe5b1 kernel: Enforcement of Manager Signature Verification 2023-06-19 17:57:19 +08:00
exer
d131b7561d [add device]: Sony Tama (XZ2/c/p, XZ3) (#656) 2023-06-18 17:06:41 +08:00
weishu
ff8c614737 kernel: allow uid 1000(system_uid) to grant root. close #645 2023-06-18 13:00:37 +08:00
weishu
c12ad9d1c7 kernel: fix compile err. close #647 2023-06-18 12:51:34 +08:00
weishu
1703c16948 ci: support deprecated kernel versions (#648) 2023-06-18 12:47:29 +08:00
github-actions[bot]
a48d7b112f [add device]: (#650)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/644

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-06-17 23:11:41 +08:00
Ylarod
f2d5e57f01 fix add-device (#649) 2023-06-17 22:07:39 +08:00
Gustavo Mendes
4111bbf720 Update Portuguese brazilian translation (#643)
Signed-off-by: Gustavo Mendes <gusttavo.me@outlook.com>
2023-06-17 21:14:36 +08:00
Ali Beyaz
cd32ad8751 Fixed some Turkish strings again (#646) 2023-06-17 21:14:09 +08:00
Rem01Gaming
fefb826177 Update Indonesian translation (#634) 2023-06-16 20:50:31 +08:00
Ali Beyaz
e27fc04b5b Fixed some Turkish strings (#640) 2023-06-16 20:50:13 +08:00
SupeChicken666
168f412324 Add CI workflow for ChromeOS ARCVM (#641)
All changes are tested on my fork (the Telegram error was fixed in
9b16150):
https://github.com/supechicken/KernelSU/actions/runs/5287864543

---------

Co-authored-by: weishu <twsxtd@gmail.com>
2023-06-16 20:49:51 +08:00
Juhyung Park
bd8434f4f4 Hook improvements (take 2) (#563)
Hi @tiann.

Thanks for the great project, I had great fun playing around with it.

This PR mainly tries to further minimize the possible delays caused by
KernelSU hooking.

There are 3 major changes:
- Processes with 0 < UID < 2000 are blocked straight-up before going
through the allow_list.
I don't see any need for such processes to be interested in root, and
this allows returning early before going through a more expensive
lookup.
If there's an expected breakage due to this change, I'll remove it. Let
me know.
- A page-sized (4K) bitmap is added.
This allows O(1) lookup for UID <= 32767.
This speeds up `ksu_is_allow_uid()` by about 4.8x by sacrificing a 4K
memory. IMHO, a good trade-off.
Most notably, this reduces the 99.999% result previously from worrying
milliseconds scale to microseconds scale.
For UID > 32767, another page-sized (4K) sequential array is used to
cache allow_list.

Compared to the previous PR #557, this new approach gives another nice
25% performance boost in average, 63-96% boost in worst cases.

Benchmark results are available at
https://docs.google.com/spreadsheets/d/1w_tO1zRLPNMFRer49pL1TQfL6ndEhilRrDU1XFIcWXY/edit?usp=sharing

Thanks!

---------

Signed-off-by: Juhyung Park <qkrwngud825@gmail.com>
2023-06-16 19:53:15 +08:00
weishu
c697398893 kernel: fix warning on x86_64, close #637 2023-06-16 19:32:48 +08:00
Ali Beyaz
1e0176242a Translated latest 3 strings to Turkish (#630) 2023-06-14 11:03:42 +08:00
Pegioner
0e5e12b331 Update Russian translation and typo fix (#631) 2023-06-14 11:03:27 +08:00
Igor Sorocean
a2b240121a Update ro translation (#629) 2023-06-13 19:23:48 +08:00
raystef66
246dadea05 Update Flemish/Dutch translation (#626) 2023-06-13 14:22:45 +08:00
Hikari-no-Tenshi
b5291077a9 manager: Make more strings translatable (#622)
Co-authored-by: weishu <twsxtd@gmail.com>
2023-06-13 12:51:03 +08:00
Ali Beyaz
ee548d513a Turkish translation (#618) 2023-06-13 12:11:12 +08:00
YuKongA
bdd269ea22 ci: upgrade kernel branches (#614)
* Add android12-5.10.177 & android13-5.10.177
2023-06-10 20:40:50 +08:00
weishu
e1d7bb91e8 ci: Fix incorrect patch level for a13 kernel. close #601 2023-06-10 20:12:15 +08:00
weishu
593bc06302 ci: fix clippy (#616) 2023-06-10 20:04:12 +08:00
weishu
8b6204a534 ksud: Fix su parameter parsing. close #530, close #305, close #330, close #305 2023-06-10 15:23:45 +08:00
weishu
01ea9c9dce manager: Add title to profile list dialog 2023-06-09 20:43:44 +08:00
weishu
12e664b0a4 manager: Add desc to profile list item 2023-06-09 20:04:23 +08:00
weishu
0ca92b9de9 manager: minor fixes for uid/gid input 2023-06-09 19:59:11 +08:00
Pegioner
2fc9e03d86 Update Russian translation (#611) 2023-06-09 11:06:50 +08:00
Fiqri Ardyansyah
4218aa488f Fix error build when using GCC 12.0.0 (#609)
Signed-off-by: Fiqri Ardyansyah <fiqri0927936@gmail.com>
2023-06-09 10:35:16 +08:00
Gustavo Mendes
d95ede1f76 Update Portuguese brazilian translation (#608)
Signed-off-by: Gustavo Mendes <gusttavo.me@outlook.com>
2023-06-08 10:48:02 +08:00
Rushi Ranpise
56f72e8702 Update English translation (#605)
Fixes https://github.com/tiann/KernelSU/issues/602
2023-06-07 16:37:08 +08:00
raystef66
964bf34325 Update Flemish/Dutch translation (#604) 2023-06-07 11:13:04 +08:00
WeeAris
3191165ba2 website: Update unofficial device maintainer information (#603)
RealKing kernel has included kernelsu officially, so I don't need to
keep maintaining this fork. With the consent of the author of the
RealKing kernel, the maintainer information here is updated to his.
2023-06-07 10:37:16 +08:00
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
tiann
f9df8d3351 Revert "ci: Remove tags with same KMI"
This reverts commit ddbc71b273.
2023-04-06 13:35:43 +08:00
tiann
8f4299ef62 ksud: keep pwd after switch mnt ns 2023-04-06 13:20:23 +08:00
zhcnUTF8
fabaa61279 kernel: check CONFIG_SECCOMP status (#351) 2023-04-05 19:09:47 +08:00
Howard Wu
f17de87952 Save install logs to public download folder (#350) 2023-04-05 15:54:55 +08:00
Ylarod
93a607083a ksud: exec uninstall.sh while uninstalling (#347) 2023-04-05 14:36:35 +08:00
Ylarod
7544558173 website: fix typo, add hidden-features (#349) 2023-04-05 14:36:22 +08:00
Ylarod
c96b0e8f8b ksud: support .ksurc (#345) 2023-04-05 13:30:21 +08:00
Igor Sorocean
b181147922 Fix ro translation (#344) 2023-04-05 12:16:02 +08:00
Ylarod
862d12a904 ksud: add /data/adb/ksu/bin to PATH (#343) 2023-04-05 11:47:35 +08:00
176 changed files with 8721 additions and 2041 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

@@ -11,7 +11,15 @@ build_from_image() {
echo "[+] patch level: $PATCH_LEVEL"
echo '[+] Download prebuilt ramdisk'
curl -Lo gki-kernel.zip https://dl.google.com/android/gki/gki-certified-boot-android12-5.10-"${PATCH_LEVEL}"_r1.zip
GKI_URL=https://dl.google.com/android/gki/gki-certified-boot-android12-5.10-"${PATCH_LEVEL}"_r1.zip
FALLBACK_URL=https://dl.google.com/android/gki/gki-certified-boot-android12-5.10-2023-01_r1.zip
status=$(curl -sL -w "%{http_code}" "$GKI_URL" -o /dev/null)
if [ "$status" = "200" ]; then
curl -Lo gki-kernel.zip "$GKI_URL"
else
echo "[+] $GKI_URL not found, using $FALLBACK_URL"
curl -Lo gki-kernel.zip "$FALLBACK_URL"
fi
unzip gki-kernel.zip && rm gki-kernel.zip
echo '[+] Unpack prebuilt boot.img'
@@ -37,7 +45,7 @@ build_from_image() {
echo '[+] Compress images'
for image in boot*.img; do
$GZIP -n -f -9 "$image"
mv "$image".gz ksu-"$VERSION"-"$1"-"$image".gz
mv "$image".gz "${1//Image-/}"-"$image".gz
done
echo "[+] Images to upload"

View File

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

View File

@@ -42,12 +42,12 @@ jobs:
run: |
echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"
- uses: ben-z/actions-comment-on-issue@1.0.2
- uses: Kernel-SU/actions-comment-on-issue@master
if: ${{ steps.cpr.outputs.pull-request-number }}
with:
message: "Automatically created pull request: ${{ steps.cpr.outputs.pull-request-url }}"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: ben-z/actions-comment-on-issue@1.0.2
- uses: Kernel-SU/actions-comment-on-issue@master
if: steps.handle-add-device.outputs.success != 'true'
with:
message: "Cannot create pull request. Please check the issue content. Or you can create a pull request manually."
@@ -56,4 +56,4 @@ jobs:
uses: peter-evans/close-issue@v1
with:
issue-number: ${{ github.event.issue.number }}
token: ${{ secrets.GITHUB_TOKEN }}
token: ${{ secrets.GITHUB_TOKEN }}

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.177
tag: android12-5.10-2023-06
os_patch_level: 2023-06
patch_path: "5.10"
debug: true
build-debug-kernel-a13:
strategy:
matrix:
include:
- version: "5.10"
sub_level: 177
os_patch_level: 2023-06
- version: "5.15"
sub_level: 94
os_patch_level: 2023-06
uses: ./.github/workflows/gki-kernel.yml
with:
version: android13-${{ matrix.version }}
version_name: android13-${{ matrix.version }}.${{ matrix.sub_level }}
tag: android13-${{ matrix.version }}-${{ matrix.os_patch_level }}
patch_path: ${{ matrix.version }}
debug: true

View File

@@ -23,8 +23,22 @@ jobs:
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
os_patch_level: 2022-11
- sub_level: 149
os_patch_level: 2023-01
- sub_level: 160
os_patch_level: 2023-02
os_patch_level: 2023-03
- sub_level: 168
os_patch_level: 2023-05
- sub_level: 177
os_patch_level: 2023-06
uses: ./.github/workflows/gki-kernel.yml
secrets: inherit
with:
@@ -105,7 +119,7 @@ jobs:
uses: ./.github/workflows/gki-kernel.yml
with:
version: android12-5.10
version_name: android12-5.10.160
tag: android12-5.10-2023-02
os_patch_level: 2023-02
version_name: android12-5.10.177
tag: android12-5.10-2023-06
os_patch_level: 2023-06
patch_path: "5.10"

View File

@@ -21,18 +21,43 @@ jobs:
strategy:
matrix:
include:
- version: "5.10"
sub_level: 107
os_patch_level: 2022-11
- version: "5.10"
sub_level: 149
os_patch_level: 2023-01
- version: "5.10"
sub_level: 157
os_patch_level: 2023-03
- version: "5.10"
sub_level: 168
os_patch_level: 2023-05
- version: "5.10"
sub_level: 177
os_patch_level: 2023-06
- version: "5.15"
sub_level: 41
os_patch_level: 2022-11
- version: "5.15"
sub_level: 74
os_patch_level: 2023-02
- version: "5.15"
sub_level: 78
os_patch_level: 2023-02
os_patch_level: 2023-03
- version: "5.15"
sub_level: 94
os_patch_level: 2023-05
- version: "5.15"
sub_level: 104
os_patch_level: 2023-06
uses: ./.github/workflows/gki-kernel.yml
secrets: inherit
with:
version: android13-${{ matrix.version }}
version_name: android13-${{ matrix.version }}.${{ matrix.sub_level }}
tag: android13-${{ matrix.version }}-${{ matrix.os_patch_level }}
os_patch_level: ${{ matrix.os_patch_level }}
patch_path: ${{ matrix.version }}
upload-artifacts:
needs: build-kernel
@@ -107,14 +132,14 @@ jobs:
matrix:
include:
- version: "5.10"
sub_level: 149
os_patch_level: 2022-11
sub_level: 177
os_patch_level: 2023-06
- version: "5.15"
sub_level: 74
os_patch_level: 2023-01
sub_level: 104
os_patch_level: 2023-06
uses: ./.github/workflows/gki-kernel.yml
with:
version: android13-${{ matrix.version }}
version_name: android13-${{ matrix.version }}.${{ matrix.sub_level }}
tag: android13-${{ matrix.version }}-${{ matrix.os_patch_level }}
patch_path: ${{ matrix.version }}
patch_path: ${{ matrix.version }}

137
.github/workflows/build-kernel-arcvm.yml vendored Normal file
View File

@@ -0,0 +1,137 @@
name: Build Kernel - ChromeOS ARCVM
on:
push:
branches: ["main"]
paths:
- ".github/workflows/build-kernel-arcvm.yml"
- "kernel/**"
pull_request:
branches: ["main"]
paths:
- ".github/workflows/build-kernel-arcvm.yml"
- "kernel/**"
workflow_call:
workflow_dispatch:
jobs:
build:
strategy:
matrix:
arch: [x86_64]
version: ["5.10.178"]
include:
- arch: x86_64
git_tag: chromeos-5.10-arcvm
file_name: "bzImage"
name: Build ChromeOS ARCVM kernel
runs-on: ubuntu-20.04
env:
LTO: thin
ROOT_DIR: /
KERNEL_DIR: ${{ github.workspace }}/kernel
steps:
- name: Install Build Tools
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends bc \
bison build-essential ca-certificates flex git gnupg \
libelf-dev libssl-dev lsb-release software-properties-common wget \
libncurses-dev binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu nuget gzip \
rsync python3 device-tree-compiler
sudo ln -s --force python3 /usr/bin/python
export LLVM_VERSION=12
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh $LLVM_VERSION
rm ./llvm.sh
sudo ln -s --force /usr/bin/clang-$LLVM_VERSION /usr/bin/clang
sudo ln -s --force /usr/bin/ld.lld-$LLVM_VERSION /usr/bin/ld.lld
sudo ln -s --force /usr/bin/llvm-objdump-$LLVM_VERSION /usr/bin/llvm-objdump
sudo ln -s --force /usr/bin/llvm-ar-$LLVM_VERSION /usr/bin/llvm-ar
sudo ln -s --force /usr/bin/llvm-nm-$LLVM_VERSION /usr/bin/llvm-nm
sudo ln -s --force /usr/bin/llvm-strip-$LLVM_VERSION /usr/bin/llvm-strip
sudo ln -s --force /usr/bin/llvm-objcopy-$LLVM_VERSION /usr/bin/llvm-objcopy
sudo ln -s --force /usr/bin/llvm-readelf-$LLVM_VERSION /usr/bin/llvm-readelf
sudo ln -s --force /usr/bin/clang++-$LLVM_VERSION /usr/bin/clang++
- name: Checkout KernelSU
uses: actions/checkout@v3
with:
path: KernelSU
ref: main
fetch-depth: 0
- name: Setup kernel source
run: git clone https://chromium.googlesource.com/chromiumos/third_party/kernel.git -b ${{ matrix.git_tag }} --depth=1
- name: Setup KernelSU
working-directory: kernel
run: |
echo "[+] KernelSU setup"
KERNEL_ROOT=$GITHUB_WORKSPACE/kernel
echo "[+] KERNEL_ROOT: $KERNEL_ROOT"
echo "[+] Copy KernelSU driver to $KERNEL_ROOT/drivers"
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $KERNEL_ROOT/drivers/kernelsu
echo "[+] Add KernelSU driver to Makefile"
DRIVER_MAKEFILE=$KERNEL_ROOT/drivers/Makefile
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
echo "[+] Apply KernelSU patches"
cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/5.10/*.patch
echo "[+] Patch script/setlocalversion"
sed -i 's/-dirty//g' $KERNEL_ROOT/scripts/setlocalversion
echo "[+] KernelSU setup done."
cd $GITHUB_WORKSPACE/KernelSU
VERSION=$(($(git rev-list --count HEAD) + 10200))
echo "VERSION: $VERSION"
echo "kernelsu_version=$VERSION" >> $GITHUB_ENV
- name: Build Kernel
working-directory: kernel
run: |
set -a && . build.config.gki.x86_64; set +a
export DEFCONFIG=x86_64_arcvm_defconfig
make LLVM=1 LLVM_IAS=1 DEPMOD=depmod DTC=dtc O=${PWD} mrproper
make LLVM=1 LLVM_IAS=1 DEPMOD=depmod DTC=dtc O=${PWD} ${DEFCONFIG} < /dev/null
scripts/config --file .config -e LTO_CLANG -d LTO_NONE -e LTO_CLANG_THIN -d LTO_CLANG_FULL -e THINLTO
make LLVM=1 LLVM_IAS=1 DEPMOD=depmod DTC=dtc O=${PWD} -j$(nproc) bzImage modules prepare-objtool
echo "file_path=${PWD}/arch/x86/boot/bzImage" >> $GITHUB_ENV
- name: Upload kernel-ARCVM-${{ matrix.arch }}-${{ matrix.version }}
uses: actions/upload-artifact@v3
with:
name: kernel-ARCVM-${{ matrix.arch }}-${{ matrix.version }}
path: "${{ env.file_path }}"
- name: Post to Telegram
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
env:
CHAT_ID: ${{ secrets.CHAT_ID }}
CACHE_CHAT_ID: ${{ secrets.CACHE_CHAT_ID }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
COMMIT_URL: ${{ github.event.head_commit.url }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
TITLE=kernel-ARCVM-${{ matrix.arch }}-${{ matrix.version }}
echo "[+] title: $TITLE"
export TITLE
export VERSION="${{ env.kernelsu_version }}"
echo "[+] Compress images"
gzip -n -f -9 "${{ env.file_path }}"
echo "[+] Image to upload"
ls -l "${{ env.file_path }}.gz"
if [ -n "${{ secrets.BOT_TOKEN }}" ]; then
pip3 install python-telegram-bot
python3 "$GITHUB_WORKSPACE/KernelSU/scripts/ksubot.py" "${{ env.file_path }}.gz"
fi

View File

@@ -1,13 +1,14 @@
name: Build WSA-5.10.117-Kernel
name: Build Kernel - WSA
on:
push:
branches: ["main"]
paths:
- ".github/workflows/build-WSA-5.10.117-kernel.yml"
- ".github/workflows/build-kernel-wsa.yml"
- "kernel/**"
pull_request:
branches: ["main"]
paths:
- ".github/workflows/build-kernel-wsa.yml"
- "kernel/**"
workflow_call:
workflow_dispatch:
@@ -17,29 +18,45 @@ jobs:
strategy:
matrix:
arch: [x86_64, arm64]
version: [5.10.117.2]
version: ["5.15.78.1", "5.15.94.4"]
include:
- file_name: "bzImage"
make_config: "config-wsa"
arch: x86_64
- file_name: "Image"
- arch: x86_64
file_name: "bzImage"
- arch: arm64
file_name: "Image"
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
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 }}
runs-on: ubuntu-20.04
env:
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
CCACHE_NOHASHDIR: "true"
CCACHE_MAXSIZE: "2G"
CCACHE_HARDLINK: "true"
steps:
- name: Install Build Tools
run: |
sudo apt 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 update
sudo apt-get install -y --no-install-recommends bc bison build-essential ca-certificates flex git gnupg libelf-dev libssl-dev lsb-release software-properties-common wget libncurses-dev binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu nuget gzip
export LLVM_VERSION=12
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
@@ -66,14 +83,15 @@ jobs:
uses: actions/checkout@v3
with:
repository: microsoft/WSA-Linux-Kernel
ref: android-lts/latte/${{ matrix.version }}
ref: android-lts/${{ matrix.device_code }}/${{ matrix.version }}
path: WSA-Linux-Kernel
- name: Setup Ccache
uses: hendrikmuhs/ccache-action@v1.2
with:
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
working-directory: WSA-Linux-Kernel
@@ -87,17 +105,47 @@ jobs:
DRIVER_MAKEFILE=$KERNEL_ROOT/drivers/Makefile
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
echo "[+] Apply KernelSU patches"
cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/5.10/*.patch
cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/${{ matrix.kernel_version }}/*.patch
echo "[+] KernelSU setup done."
cd $GITHUB_WORKSPACE/KernelSU
VERSION=$(($(git rev-list --count HEAD) + 10200))
echo "VERSION: $VERSION"
echo "kernelsu_version=$VERSION" >> $GITHUB_ENV
- name: Build Kernel
working-directory: WSA-Linux-Kernel
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"
echo "file_path=WSA-Linux-Kernel/arch/${{ matrix.arch }}/boot/${{ matrix.file_name }}" >> $GITHUB_ENV
- name: Upload kernel-${{ matrix.arch }}-${{ matrix.version }}
uses: actions/upload-artifact@v3
with:
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
on:
push:
branches: [ "main" ]
paths:
paths:
- '.github/workflows/build-manager.yml'
- 'manager/**'
- 'userspace/ksud/**'
pull_request:
branches: [ "main" ]
paths:
paths:
- 'manager/**'
workflow_call:
jobs:
build-ksud:
strategy:
@@ -21,87 +23,104 @@ jobs:
uses: ./.github/workflows/ksud.yml
with:
target: ${{ matrix.target }}
build-manager:
needs: build-ksud
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./manager
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup need_upload
id: need_upload
run: |
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
echo "UPLOAD=true" >> $GITHUB_OUTPUT
else
echo "UPLOAD=false" >> $GITHUB_OUTPUT
fi
- name: set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: gradle
- uses: nttld/setup-ndk@v1
with:
ndk-version: r25b
local-cache: true
- name: Extract keystore
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
run: |
if [ ! -z "${{ secrets.KEYSTORE }}" ]; then
echo KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}' >> sign.properties
echo KEY_ALIAS='${{ secrets.KEY_ALIAS }}' >> sign.properties
echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}' >> sign.properties
echo KEYSTORE_FILE='../key.jks' >> sign.properties
echo ${{ secrets.KEYSTORE }} | base64 --decode > key.jks
fi
- name: Download arm64 ksud
uses: actions/download-artifact@v3
with:
name: ksud-aarch64-linux-android
path: .
- name: Download x86_64 ksud
uses: actions/download-artifact@v3
with:
name: ksud-x86_64-linux-android
path: .
- name: Copy ksud to app jniLibs
run: |
mkdir -p app/src/main/jniLibs/arm64-v8a
mkdir -p app/src/main/jniLibs/x86_64
cp -f ../aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud.so
cp -f ../x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud.so
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew clean assembleRelease
- name: Upload build artifact
uses: actions/upload-artifact@v3
with:
name: manager
path: manager/app/build/outputs/apk/release/*.apk
- name: Setup mutex for uploading
uses: ben-z/gh-action-mutex@v1.0-alpha-7
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
- name: Upload to telegram
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
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
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup need_upload
id: need_upload
run: |
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
echo "UPLOAD=true" >> $GITHUB_OUTPUT
else
echo "UPLOAD=false" >> $GITHUB_OUTPUT
fi
- name: Write key
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
run: |
if [ ! -z "${{ secrets.KEYSTORE }}" ]; then
echo KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}' >> gradle.properties
echo KEY_ALIAS='${{ secrets.KEY_ALIAS }}' >> gradle.properties
echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}' >> gradle.properties
echo KEYSTORE_FILE='../key.jks' >> gradle.properties
echo ${{ secrets.KEYSTORE }} | base64 --decode > key.jks
fi
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: "temurin"
java-version: "17"
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
with:
gradle-home-cache-cleanup: true
- name: Download arm64 ksud
uses: actions/download-artifact@v3
with:
name: ksud-aarch64-linux-android
path: .
- name: Download x86_64 ksud
uses: actions/download-artifact@v3
with:
name: ksud-x86_64-linux-android
path: .
- name: Copy ksud to app jniLibs
run: |
mkdir -p app/src/main/jniLibs/arm64-v8a
mkdir -p app/src/main/jniLibs/x86_64
cp -f ../aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud.so
cp -f ../x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud.so
- name: Build with Gradle
run: |
echo 'org.gradle.parallel=true' >> gradle.properties
echo 'org.gradle.vfs.watch=true' >> gradle.properties
echo 'org.gradle.jvmargs=-Xmx2048m' >> gradle.properties
echo 'android.native.buildOutput=verbose' >> gradle.properties
sed -i 's/org.gradle.configuration-cache=true//g' gradle.properties
./gradlew clean assembleRelease
- name: Upload build artifact
uses: actions/upload-artifact@v3
with:
name: manager
path: manager/app/build/outputs/apk/release/*.apk
- name: Setup mutex for uploading
uses: ben-z/gh-action-mutex@v1.0-alpha-7
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
- name: Upload to telegram
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
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:
push:
branches:
- 'main'
- main
paths:
- '.github/workflows/clippy.yml'
- 'userspace/ksud/**'
pull_request:
branches:
- main
paths:
- '.github/workflows/clippy.yml'
- 'userspace/ksud/**'
@@ -16,7 +22,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# cross build failed after Rust 1.68, see https://github.com/cross-rs/cross/issues/1222
- run: rustup default 1.67.0
- uses: Swatinem/rust-cache@v2
with:
workspaces: userspace/ksud
@@ -26,5 +33,5 @@ jobs:
- name: Run clippy
run: |
cross clippy --manifest-path userspace/ksud/Cargo.toml --target aarch64-linux-android
cross clippy --manifest-path userspace/ksud/Cargo.toml --target x86_64-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 --release

View File

@@ -45,6 +45,10 @@ on:
description: >
Artifact name of prebuilt ksud to be embedded
for example: ksud-aarch64-linux-android
debug:
required: false
type: boolean
default: false
secrets:
BOOT_SIGN_KEY:
required: false
@@ -64,7 +68,6 @@ jobs:
env:
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
CCACHE_NOHASHDIR: "true"
CCACHE_MAXSIZE: "2G"
CCACHE_HARDLINK: "true"
steps:
- uses: actions/checkout@v3
@@ -72,13 +75,6 @@ jobs:
path: KernelSU
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
id: need_upload
run: |
@@ -94,11 +90,18 @@ jobs:
git clone https://gerrit.googlesource.com/git-repo
mkdir android-kernel && cd android-kernel
../git-repo/repo init --depth=1 --u https://android.googlesource.com/kernel/manifest -b common-${{ inputs.tag }}
REMOTE_BRANCH=$(git ls-remote https://android.googlesource.com/kernel/common ${{ inputs.tag }})
if grep -q deprecated <<< $REMOTE_BRANCH; then
echo "Found deprecated branch: ${{ inputs.tag }}"
sed -i 's/"${{ inputs.tag }}"/"deprecated\/${{ inputs.tag }}"/g' .repo/manifests/default.xml
cat .repo/manifests/default.xml
fi
../git-repo/repo sync -j$(nproc --all)
- name: Setup KernelSU
env:
PATCH_PATH: ${{ inputs.patch_path }}
IS_DEBUG_KERNEL: ${{ inputs.debug }}
run: |
cd $GITHUB_WORKSPACE/android-kernel
echo "[+] KernelSU setup"
@@ -111,6 +114,13 @@ jobs:
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
echo "[+] Apply KernelSU patches"
cd $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/$PATCH_PATH/*.patch
echo "[+] Patch script/setlocalversion"
sed -i 's/-dirty//g' $GKI_ROOT/common/scripts/setlocalversion
if [ "$IS_DEBUG_KERNEL" = "true" ]; then
echo "[+] Enable debug features for kernel"
echo "ccflags-y += -DCONFIG_KSU_DEBUG" >> $GITHUB_WORKSPACE/KernelSU/kernel/Makefile
fi
echo "[+] KernelSU setup done."
- name: Symbol magic
@@ -124,6 +134,14 @@ jobs:
echo "[+] Add KernelSU symbols"
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
working-directory: android-kernel
run: CCACHE="/usr/bin/ccache" LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
@@ -149,4 +167,4 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: AnyKernel3-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
path: ./AnyKernel3/*
path: ./AnyKernel3/*

View File

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

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.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) | [Türkçe](README_TR.md)
# KernelSU
@@ -11,7 +11,7 @@ A Kernel based root solution for Android devices.
## 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.

View File

@@ -1,4 +1,4 @@
[English](README.md) | **中文**
[English](README.md) | **简体中文** | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Portuguese-Brazil](README_PT-BR.md) | [Türkçe](README_TR.md)
# KernelSU
@@ -37,6 +37,6 @@ WSA 和运行在容器上的 Android 也可以与 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 技巧。
- [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) | [Türkçe](README_TR.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** | [Türkçe](README_TR.md)
# KernelSU
Uma solução raiz baseada em Kernel para dispositivos Android.
## Características
1. `su` baseado em kernel e gerenciamento de acesso root.
2. Sistema modular baseado em overlayfs.
## 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_TR.md Normal file
View File

@@ -0,0 +1,42 @@
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Portuguese-Brazil](README_PT-BR.md) | **Türkçe**
# KernelSU
Android cihazlar için kernel tabanlı bir root çözümü.
## Özellikler
1. Kernel-tabanlı `su` ve root erişimi yönetimi.
2. Overlayfs'ye dayalı modül sistemi.
## Uyumluluk Durumu
KernelSU resmi olarak Android GKI 2.0 cihazlarını ( 5.10+ kernelli) destekler, eski kernellerle de (4.14+) uyumludur, ancak kerneli kendinizin inşaa etmesi gerekir.
WSA ve konteyner tabanlı Android'in de, KernelSU ile entegre olarak da çalışması gerekmektedir.
Ve desteklenen mevcut ABI'ler : `arm64-v8a` ve `x86_64`
## Kullanım
[Yükleme](https://kernelsu.org/guide/installation.html)
## İnşaa
[Nasıl inşa edilir?](https://kernelsu.org/guide/how-to-build.html)
### Tartışma
- Telegram: [@KernelSU](https://t.me/KernelSU)
## Lisans
- `kernel` klasöründeki dosyalar [GPL-2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) lisansı altındadır.
- `kernel` klasörü dışındaki bütün diğer bölümler [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html) lisansı altındadır.
## Krediler
- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU fikri.
- [genuine](https://github.com/brevent/genuine/): apk v2 imza doğrulama.
- [Diamorphine](https://github.com/m0nad/Diamorphine): bazı rootkit becerileri.
- [Magisk](https://github.com/topjohnwu/Magisk): sepolicy uygulaması.

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) | [Türkçe](README_TR.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,4 +1,4 @@
Diagnostics:
UnusedIncludes: Strict
ClangTidy:
Remove: bugprone-sizeof-expression
Remove: bugprone-sizeof-expression

View File

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

View File

@@ -14,7 +14,7 @@ obj-y += kernel_compat.o
obj-y += selinux/
# .git is a text file while the module is imported by 'git submodule add'.
ifeq ($(shell test -e $(srctree)/$(src)/../.git; echo $$?),0)
KSU_GIT_VERSION := $(shell cd $(srctree)/$(src); /usr/bin/env PATH=$$PATH:/usr/bin:/usr/local/bin git rev-list --count HEAD)
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)
endif
@@ -29,4 +29,4 @@ endif
ccflags-y += -DEXPECTED_SIZE=$(EXPECTED_SIZE)
ccflags-y += -DEXPECTED_HASH=$(EXPECTED_HASH)
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
ccflags-y += -Wno-macro-redefined -Wno-declaration-after-statement
ccflags-y += -Wno-declaration-after-statement

View File

@@ -1,27 +1,91 @@
#include "linux/delay.h"
#include "ksu.h"
#include "linux/compiler.h"
#include "linux/fs.h"
#include "linux/gfp.h"
#include "linux/kernel.h"
#include "linux/list.h"
#include "linux/printk.h"
#include "linux/slab.h"
#include "linux/types.h"
#include "linux/version.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
#include "linux/compiler_types.h"
#endif
#include "klog.h" // IWYU pragma: keep
#include "selinux/selinux.h"
#include "kernel_compat.h"
#include "allowlist.h"
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
#define FILE_FORMAT_VERSION 1 // u32
#define FILE_FORMAT_VERSION 3 // u32
#define KSU_APP_PROFILE_PRESERVE_UID 9999 // NOBODY_UID
#define KSU_DEFAULT_SELINUX_DOMAIN "u:r:su:s0"
static DEFINE_MUTEX(allowlist_mutex);
// default profiles, these may be used frequently, so we cache it
static struct root_profile default_root_profile;
static struct non_root_profile default_non_root_profile;
static int allow_list_arr[PAGE_SIZE / sizeof(int)] __read_mostly __aligned(PAGE_SIZE);
static int allow_list_pointer __read_mostly = 0;
static void remove_uid_from_arr(uid_t uid)
{
int *temp_arr;
int i, j;
if (allow_list_pointer == 0)
return;
temp_arr = kmalloc(sizeof(allow_list_arr), GFP_KERNEL);
if (temp_arr == NULL) {
pr_err("%s: unable to allocate memory\n", __func__);
return;
}
for (i = j = 0; i < allow_list_pointer; i++) {
if (allow_list_arr[i] == uid)
continue;
temp_arr[j++] = allow_list_arr[i];
}
allow_list_pointer = j;
for (; j < ARRAY_SIZE(allow_list_arr); j++)
temp_arr[j] = -1;
memcpy(&allow_list_arr, temp_arr, PAGE_SIZE);
kfree(temp_arr);
}
static void init_default_profiles()
{
default_root_profile.uid = 0;
default_root_profile.gid = 0;
default_root_profile.groups_count = 1;
default_root_profile.groups[0] = 0;
memset(&default_root_profile.capabilities, 0xff,
sizeof(default_root_profile.capabilities));
default_root_profile.namespaces = 0;
strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
// This means that we will umount modules by default!
default_non_root_profile.umount_modules = true;
}
struct perm_data {
struct list_head list;
uid_t uid;
bool allow;
struct app_profile profile;
};
static struct list_head allow_list;
static uint8_t allow_list_bitmap[PAGE_SIZE] __read_mostly __aligned(PAGE_SIZE);
#define BITMAP_UID_MAX ((sizeof(allow_list_bitmap) * BITS_PER_BYTE) - 1)
#define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist"
static struct work_struct ksu_save_work;
@@ -36,65 +100,231 @@ void ksu_show_allow_list(void)
pr_info("ksu_show_allow_list");
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
pr_info("uid :%d, allow: %d\n", p->uid, p->allow);
pr_info("uid :%d, allow: %d\n", p->profile.current_uid,
p->profile.allow_su);
}
}
bool ksu_allow_uid(uid_t uid, bool allow, bool persist)
#ifdef CONFIG_KSU_DEBUG
static void ksu_grant_root_to_shell()
{
struct app_profile profile = {
.allow_su = true,
.current_uid = 2000,
};
strcpy(profile.key, "com.android.shell");
strcpy(profile.rp_config.profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
ksu_set_app_profile(&profile, false);
}
#endif
bool ksu_get_app_profile(struct app_profile *profile)
{
struct perm_data *p = NULL;
struct list_head *pos = NULL;
bool found = false;
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
bool uid_match = profile->current_uid == p->profile.current_uid;
if (uid_match) {
// found it, override it with ours
memcpy(profile, &p->profile, sizeof(*profile));
found = true;
goto exit;
}
}
exit:
return found;
}
static inline bool forbid_system_uid(uid_t uid) {
#define SHELL_UID 2000
#define SYSTEM_UID 1000
return uid < SHELL_UID && uid != SYSTEM_UID;
}
static bool profile_valid(struct app_profile *profile)
{
if (!profile) {
return false;
}
if (forbid_system_uid(profile->current_uid)) {
pr_err("uid lower than 2000 is unsupported: %d\n", profile->current_uid);
return false;
}
if (profile->version < KSU_APP_PROFILE_VER) {
pr_info("Unsupported profile version: %d\n", profile->version);
return false;
}
if (profile->allow_su) {
if (profile->rp_config.profile.groups_count > KSU_MAX_GROUPS) {
return false;
}
if (strlen(profile->rp_config.profile.selinux_domain) == 0) {
return false;
}
}
return true;
}
bool ksu_set_app_profile(struct app_profile *profile, bool persist)
{
// find the node first!
struct perm_data *p = NULL;
struct list_head *pos = NULL;
bool result = false;
if (!profile_valid(profile)) {
pr_err("Failed to set app profile: invalid profile!\n");
return false;
}
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
if (uid == p->uid) {
p->allow = allow;
// both uid and package must match, otherwise it will break multiple package with different user id
if (profile->current_uid == p->profile.current_uid &&
!strcmp(profile->key, p->profile.key)) {
// found it, just override it all!
memcpy(&p->profile, profile, sizeof(*profile));
result = true;
goto exit;
goto out;
}
}
// not found, alloc a new node!
p = (struct perm_data *)kmalloc(sizeof(struct perm_data), GFP_KERNEL);
if (!p) {
pr_err("alloc allow node failed.\n");
pr_err("ksu_set_app_profile alloc failed\n");
return false;
}
p->uid = uid;
p->allow = allow;
memcpy(&p->profile, profile, sizeof(*profile));
if (profile->allow_su) {
pr_info("set root profile, key: %s, uid: %d, gid: %d, context: %s\n",
profile->key, profile->current_uid,
profile->rp_config.profile.gid,
profile->rp_config.profile.selinux_domain);
} else {
pr_info("set app profile, key: %s, uid: %d, umount modules: %d\n",
profile->key, profile->current_uid,
profile->nrp_config.profile.umount_modules);
}
list_add_tail(&p->list, &allow_list);
out:
if (profile->current_uid <= BITMAP_UID_MAX) {
if (profile->allow_su)
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] |= 1 << (profile->current_uid % BITS_PER_BYTE);
else
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] &= ~(1 << (profile->current_uid % BITS_PER_BYTE));
} else {
if (profile->allow_su) {
/*
* 1024 apps with uid higher than BITMAP_UID_MAX
* registered to request superuser?
*/
if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) {
pr_err("too many apps registered\n");
WARN_ON(1);
return false;
}
allow_list_arr[allow_list_pointer++] = profile->current_uid;
} else {
remove_uid_from_arr(profile->current_uid);
}
}
result = true;
exit:
// check if the default profiles is changed, cache it to a single struct to accelerate access.
if (unlikely(!strcmp(profile->key, "$"))) {
// set default non root profile
memcpy(&default_non_root_profile, &profile->nrp_config.profile,
sizeof(default_non_root_profile));
}
if (unlikely(!strcmp(profile->key, "#"))) {
// set default root profile
memcpy(&default_root_profile, &profile->rp_config.profile,
sizeof(default_root_profile));
}
if (persist)
persistent_allow_list();
return result;
}
bool ksu_is_allow_uid(uid_t uid)
bool __ksu_is_allow_uid(uid_t uid)
{
struct perm_data *p = NULL;
struct list_head *pos = NULL;
int i;
if (uid == 0) {
if (unlikely(uid == 0)) {
// already root, but only allow our domain.
return is_ksu_domain();
}
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
// pr_info("is_allow_uid uid :%d, allow: %d\n", p->uid, p->allow);
if (uid == p->uid) {
return p->allow;
if (forbid_system_uid(uid)) {
// do not bother going through the list if it's system
return false;
}
if (likely(uid <= BITMAP_UID_MAX)) {
return !!(allow_list_bitmap[uid / BITS_PER_BYTE] & (1 << (uid % BITS_PER_BYTE)));
} else {
for (i = 0; i < allow_list_pointer; i++) {
if (allow_list_arr[i] == uid)
return true;
}
}
return false;
}
bool ksu_uid_should_umount(uid_t uid)
{
struct app_profile profile = { .current_uid = uid };
bool found = ksu_get_app_profile(&profile);
if (!found) {
// no app profile found, it must be non root app
return default_non_root_profile.umount_modules;
}
if (profile.allow_su) {
// if found and it is granted to su, we shouldn't umount for it
return false;
} else {
// found an app profile
if (profile.nrp_config.use_default) {
return default_non_root_profile.umount_modules;
} else {
return profile.nrp_config.profile.umount_modules;
}
}
}
struct root_profile *ksu_get_root_profile(uid_t uid)
{
struct perm_data *p = NULL;
struct list_head *pos = NULL;
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
if (uid == p->profile.current_uid && p->profile.allow_su) {
if (!p->profile.rp_config.use_default) {
return &p->profile.rp_config.profile;
}
}
}
// use default profile
return &default_root_profile;
}
bool ksu_get_allow_list(int *array, int *length, bool allow)
{
struct perm_data *p = NULL;
@@ -102,9 +332,9 @@ bool ksu_get_allow_list(int *array, int *length, bool allow)
int i = 0;
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow);
if (p->allow == allow) {
array[i++] = p->uid;
// pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow);
if (p->profile.allow_su == allow) {
array[i++] = p->profile.current_uid;
}
}
*length = i;
@@ -112,7 +342,7 @@ bool ksu_get_allow_list(int *array, int *length, bool allow)
return true;
}
void do_persistent_allow_list(struct work_struct *work)
void do_save_allow_list(struct work_struct *work)
{
u32 magic = FILE_MAGIC;
u32 version = FILE_FORMAT_VERSION;
@@ -124,12 +354,13 @@ void do_persistent_allow_list(struct work_struct *work)
filp_open(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT, 0644);
if (IS_ERR(fp)) {
pr_err("save_allow_list creat file failed: %d\n", PTR_ERR(fp));
pr_err("save_allow_list create file failed: %ld\n", PTR_ERR(fp));
return;
}
// store magic and version
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) !=
sizeof(magic)) {
pr_err("save_allow_list write magic failed.\n");
goto exit;
}
@@ -142,10 +373,12 @@ void do_persistent_allow_list(struct work_struct *work)
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
pr_info("save allow list uid :%d, allow: %d\n", p->uid,
p->allow);
ksu_kernel_write_compat(fp, &p->uid, sizeof(p->uid), &off);
ksu_kernel_write_compat(fp, &p->allow, sizeof(p->allow), &off);
pr_info("save allow list, name: %s uid :%d, allow: %d\n",
p->profile.key, p->profile.current_uid,
p->profile.allow_su);
ksu_kernel_write_compat(fp, &p->profile, sizeof(p->profile),
&off);
}
exit:
@@ -161,27 +394,21 @@ void do_load_allow_list(struct work_struct *work)
u32 version;
KWORKER_INSTALL_KEYRING();
#ifdef CONFIG_KSU_DEBUG
// always allow adb shell by default
ksu_grant_root_to_shell();
#endif
// load allowlist now!
fp = filp_open(KERNEL_SU_ALLOWLIST, O_RDONLY, 0);
if (IS_ERR(fp)) {
#ifdef CONFIG_KSU_DEBUG
int errno = PTR_ERR(fp);
if (errno == -ENOENT) {
ksu_allow_uid(2000, true,
true); // allow adb shell by default
} else {
pr_err("load_allow_list open file failed: %d\n",
PTR_ERR(fp));
}
#else
pr_err("load_allow_list open file failed: %d\n", PTR_ERR(fp));
#endif
pr_err("load_allow_list open file failed: %ld\n", PTR_ERR(fp));
return;
}
// verify magic
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic) ||
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) !=
sizeof(magic) ||
magic != FILE_MAGIC) {
pr_err("allowlist file invalid: %d!\n", magic);
goto exit;
@@ -196,18 +423,19 @@ void do_load_allow_list(struct work_struct *work)
pr_info("allowlist version: %d\n", version);
while (true) {
u32 uid;
bool allow = false;
ret = ksu_kernel_read_compat(fp, &uid, sizeof(uid), &off);
struct app_profile profile;
ret = ksu_kernel_read_compat(fp, &profile, sizeof(profile),
&off);
if (ret <= 0) {
pr_info("load_allow_list read err: %d\n", ret);
pr_info("load_allow_list read err: %zd\n", ret);
break;
}
ret = ksu_kernel_read_compat(fp, &allow, sizeof(allow), &off);
pr_info("load_allow_uid: %d, allow: %d\n", uid, allow);
ksu_allow_uid(uid, allow, false);
pr_info("load_allow_uid, name: %s, uid: %d, allow: %d\n",
profile.key, profile.current_uid, profile.allow_su);
ksu_set_app_profile(&profile, false);
}
exit:
@@ -224,11 +452,16 @@ void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, void *), void *data)
// TODO: use RCU!
mutex_lock(&allowlist_mutex);
list_for_each_entry_safe (np, n, &allow_list, list) {
uid_t uid = np->uid;
if (!is_uid_exist(uid, data)) {
uid_t uid = np->profile.current_uid;
// we use this uid for special cases, don't prune it!
bool is_preserved_uid = uid == KSU_APP_PROFILE_PRESERVE_UID;
if (!is_preserved_uid && !is_uid_exist(uid, data)) {
modified = true;
pr_info("prune uid: %d\n", uid);
list_del(&np->list);
allow_list_bitmap[uid / BITS_PER_BYTE] &= ~(1 << (uid % BITS_PER_BYTE));
remove_uid_from_arr(uid);
smp_mb();
kfree(np);
}
}
@@ -252,10 +485,20 @@ bool ksu_load_allow_list(void)
void ksu_allowlist_init(void)
{
int i;
BUILD_BUG_ON(sizeof(allow_list_bitmap) != PAGE_SIZE);
BUILD_BUG_ON(sizeof(allow_list_arr) != PAGE_SIZE);
for (i = 0; i < ARRAY_SIZE(allow_list_arr); i++)
allow_list_arr[i] = -1;
INIT_LIST_HEAD(&allow_list);
INIT_WORK(&ksu_save_work, do_persistent_allow_list);
INIT_WORK(&ksu_save_work, do_save_allow_list);
INIT_WORK(&ksu_load_work, do_load_allow_list);
init_default_profiles();
}
void ksu_allowlist_exit(void)
@@ -263,7 +506,7 @@ void ksu_allowlist_exit(void)
struct perm_data *np = NULL;
struct perm_data *n = NULL;
do_persistent_allow_list(NULL);
do_save_allow_list(NULL);
// free allowlist
mutex_lock(&allowlist_mutex);
@@ -272,4 +515,4 @@ void ksu_allowlist_exit(void)
kfree(np);
}
mutex_unlock(&allowlist_mutex);
}
}

View File

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

View File

@@ -4,4 +4,4 @@
// return 0 if signature match
int is_manager_apk(char *path);
#endif
#endif

View File

@@ -1,3 +1,4 @@
#include "linux/capability.h"
#include "linux/cred.h"
#include "linux/dcache.h"
#include "linux/err.h"
@@ -5,6 +6,7 @@
#include "linux/kernel.h"
#include "linux/kprobes.h"
#include "linux/lsm_hooks.h"
#include "linux/nsproxy.h"
#include "linux/path.h"
#include "linux/printk.h"
#include "linux/uaccess.h"
@@ -38,36 +40,99 @@ static inline bool is_allow_su()
return ksu_is_allow_uid(current_uid().val);
}
static inline bool is_isolated_uid(uid_t uid) {
#define FIRST_ISOLATED_UID 99000
#define LAST_ISOLATED_UID 99999
#define FIRST_APP_ZYGOTE_ISOLATED_UID 90000
#define LAST_APP_ZYGOTE_ISOLATED_UID 98999
uid_t appid = uid % 100000;
return (appid >= FIRST_ISOLATED_UID && appid <= LAST_ISOLATED_UID)
|| (appid >= FIRST_APP_ZYGOTE_ISOLATED_UID && appid <= LAST_APP_ZYGOTE_ISOLATED_UID);
static inline bool is_isolated_uid(uid_t uid)
{
#define FIRST_ISOLATED_UID 99000
#define LAST_ISOLATED_UID 99999
#define FIRST_APP_ZYGOTE_ISOLATED_UID 90000
#define LAST_APP_ZYGOTE_ISOLATED_UID 98999
uid_t appid = uid % 100000;
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;
}
if (profile->groups_count == 1 && profile->groups[0] == 0) {
// setgroup to root and return early.
if (cred->group_info)
put_group_info(cred->group_info);
cred->group_info = get_group_info(&root_groups);
return;
}
u32 ngroups = profile->groups_count;
struct group_info *group_info = groups_alloc(ngroups);
if (!group_info) {
pr_warn("Failed to setgroups, ENOMEM for: %d\n", profile->uid);
return;
}
int i;
for (i = 0; i < ngroups; i++) {
gid_t gid = profile->groups[i];
kgid_t kgid = make_kgid(current_user_ns(), gid);
if (!gid_valid(kgid)) {
pr_warn("Failed to setgroups, invalid gid: %d\n", gid);
put_group_info(group_info);
return;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
group_info->gid[i] = kgid;
#else
GROUP_AT(group_info, i) = kgid;
#endif
}
groups_sort(group_info);
set_groups(cred, group_info);
}
void escape_to_root(void)
{
struct cred *cred;
cred = (struct cred *)__task_cred(current);
memset(&cred->uid, 0, sizeof(cred->uid));
memset(&cred->gid, 0, sizeof(cred->gid));
memset(&cred->suid, 0, sizeof(cred->suid));
memset(&cred->euid, 0, sizeof(cred->euid));
memset(&cred->egid, 0, sizeof(cred->egid));
memset(&cred->fsuid, 0, sizeof(cred->fsuid));
memset(&cred->fsgid, 0, sizeof(cred->fsgid));
memset(&cred->cap_inheritable, 0xff, sizeof(cred->cap_inheritable));
memset(&cred->cap_permitted, 0xff, sizeof(cred->cap_permitted));
memset(&cred->cap_effective, 0xff, sizeof(cred->cap_effective));
memset(&cred->cap_bset, 0xff, sizeof(cred->cap_bset));
memset(&cred->cap_ambient, 0xff, sizeof(cred->cap_ambient));
if (cred->euid.val == 0) {
pr_warn("Already root, don't escape!\n");
return;
}
struct root_profile *profile = ksu_get_root_profile(cred->uid.val);
cred->uid.val = profile->uid;
cred->suid.val = profile->uid;
cred->euid.val = profile->uid;
cred->fsuid.val = profile->uid;
cred->gid.val = profile->gid;
cred->fsgid.val = profile->gid;
cred->sgid.val = profile->gid;
cred->egid.val = profile->gid;
BUILD_BUG_ON(sizeof(profile->capabilities.effective) !=
sizeof(kernel_cap_t));
// capabilities
memcpy(&cred->cap_effective, &profile->capabilities.effective,
sizeof(cred->cap_effective));
memcpy(&cred->cap_inheritable, &profile->capabilities.effective,
sizeof(cred->cap_inheritable));
memcpy(&cred->cap_permitted, &profile->capabilities.effective,
sizeof(cred->cap_permitted));
memcpy(&cred->cap_bset, &profile->capabilities.effective,
sizeof(cred->cap_bset));
memcpy(&cred->cap_ambient, &profile->capabilities.effective,
sizeof(cred->cap_ambient));
// disable seccomp
#if defined(CONFIG_GENERIC_ENTRY) && \
@@ -76,15 +141,16 @@ void escape_to_root(void)
#else
current_thread_info()->flags &= ~(TIF_SECCOMP | _TIF_SECCOMP);
#endif
#ifdef CONFIG_SECCOMP
current->seccomp.mode = 0;
current->seccomp.filter = NULL;
#else
#endif
// setgroup to root
if (cred->group_info)
put_group_info(cred->group_info);
cred->group_info = get_group_info(&root_groups);
setup_groups(profile, cred);
setup_selinux();
setup_selinux(profile->selinux_domain);
}
int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry)
@@ -164,14 +230,26 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
}
// someone wants to be root manager, just check it!
// arg3 should be `/data/data/<manager_package_name>`
// arg3 should be `/data/user/<userId>/<manager_package_name>`
char param[128];
const char *prefix = "/data/data/";
if (copy_from_user(param, arg3, sizeof(param))) {
pr_err("become_manager: copy param err\n");
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) {
pr_info("become_manager: invalid param: %s\n", param);
return 0;
@@ -209,10 +287,6 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("grant_root: prctl reply error\n");
}
} else {
pr_info("deny root for: %d\n", current_uid());
// add it to deny list!
ksu_allow_uid(current_uid().val, false, true);
}
return 0;
}
@@ -307,6 +381,30 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
return 0;
}
if (arg2 == CMD_UID_GRANTED_ROOT || arg2 == CMD_UID_SHOULD_UMOUNT) {
if (is_manager() || 0 == current_uid().val) {
uid_t target_uid = (uid_t)arg3;
bool allow = false;
if (arg2 == CMD_UID_GRANTED_ROOT) {
allow = ksu_is_allow_uid(target_uid);
} else if (arg2 == CMD_UID_SHOULD_UMOUNT) {
allow = ksu_uid_should_umount(target_uid);
} else {
pr_err("unknown cmd: %d\n", arg2);
}
if (!copy_to_user(arg4, &allow, sizeof(allow))) {
if (copy_to_user(result, &reply_ok,
sizeof(reply_ok))) {
pr_err("prctl reply error, cmd: %d\n",
arg2);
}
} else {
pr_err("prctl copy err, cmd: %d\n", arg2);
}
}
return 0;
}
// all other cmds are for 'root manager'
if (!is_manager()) {
last_failed_uid = current_uid().val;
@@ -314,45 +412,76 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
}
// we are already manager
if (arg2 == CMD_ALLOW_SU || arg2 == CMD_DENY_SU) {
bool allow = arg2 == CMD_ALLOW_SU;
bool success = false;
uid_t uid = (uid_t)arg3;
success = ksu_allow_uid(uid, allow, true);
if (arg2 == CMD_GET_APP_PROFILE) {
struct app_profile profile;
if (copy_from_user(&profile, arg3, sizeof(profile))) {
pr_err("copy profile failed\n");
return 0;
}
bool success = ksu_get_app_profile(&profile);
if (success) {
if (copy_to_user(arg3, &profile, sizeof(profile))) {
pr_err("copy profile failed\n");
return 0;
}
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("prctl reply error, cmd: %d\n", arg2);
}
}
return 0;
}
if (arg2 == CMD_SET_APP_PROFILE) {
struct app_profile profile;
if (copy_from_user(&profile, arg3, sizeof(profile))) {
pr_err("copy profile failed\n");
return 0;
}
// todo: validate the params
if (ksu_set_app_profile(&profile, true)) {
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("prctl reply error, cmd: %d\n", arg2);
}
}
ksu_show_allow_list();
return 0;
}
return 0;
}
static bool is_appuid(kuid_t uid) {
#define PER_USER_RANGE 100000
#define FIRST_APPLICATION_UID 10000
#define LAST_APPLICATION_UID 19999
static bool is_appuid(kuid_t uid)
{
#define PER_USER_RANGE 100000
#define FIRST_APPLICATION_UID 10000
#define LAST_APPLICATION_UID 19999
uid_t appid = uid.val % PER_USER_RANGE;
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) {
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) {
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;
}
return false;
}
static void try_umount(const char *mnt) {
static void try_umount(const char *mnt)
{
struct path path;
int err = kern_path(mnt, 0, &path);
if (err) {
@@ -372,7 +501,8 @@ static void try_umount(const char *mnt) {
#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) {
return 0;
}
@@ -397,6 +527,14 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old) {
return 0;
}
if (!ksu_uid_should_umount(new_uid.val)) {
return 0;
} else {
#ifdef CONFIG_KSU_DEBUG
pr_info("uid: %d should not umount!\n", current_uid().val);
#endif
}
// umount the target mnt
pr_info("handle umount for uid: %d\n", new_uid.val);
@@ -505,7 +643,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,
int flags) {
int flags)
{
return ksu_handle_setuid(new, old);
}
@@ -545,4 +684,4 @@ void ksu_core_exit(void)
pr_info("ksu_kprobe_exit\n");
ksu_kprobe_exit();
#endif
}
}

View File

@@ -2,4 +2,4 @@
// This file will be regenerated by CI
unsigned int ksud_size = 0;
const char ksud[0] = {};
const char ksud[0] = {};

View File

@@ -1,2 +1,2 @@
register_kprobe
unregister_kprobe
unregister_kprobe

View File

@@ -31,4 +31,4 @@ ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count, l
}
return result;
#endif
}
}

View File

@@ -39,4 +39,4 @@ static inline int install_session_keyring(struct key *keyring)
#define KWORKER_INSTALL_KEYRING()
#endif
#endif
#endif

View File

@@ -8,4 +8,4 @@
#define pr_fmt(fmt) "KernelSU: " fmt
#endif
#endif
#endif

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
#include "asm/current.h"
#include "linux/string.h"
#include "linux/compat.h"
#include "linux/cred.h"
#include "linux/dcache.h"
#include "linux/err.h"
@@ -23,6 +24,7 @@ static const char KERNEL_SU_RC[] =
"\n"
"on post-fs-data\n"
" start logd\n"
// We should wait for the post-fs-data finish
" exec u:r:su:s0 root -- " KSUD_PATH " post-fs-data\n"
"\n"
@@ -50,9 +52,9 @@ static struct work_struct stop_vfs_read_work;
static struct work_struct stop_execve_hook_work;
static struct work_struct stop_input_hook_work;
#else
static bool vfs_read_hook = true;
static bool execveat_hook = true;
static bool input_hook = true;
bool ksu_vfs_read_hook __read_mostly = true;
bool ksu_execveat_hook __read_mostly = true;
bool ksu_input_hook __read_mostly = true;
#endif
void on_post_fs_data(void)
@@ -69,11 +71,80 @@ void on_post_fs_data(void)
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.
*/
/*
* Make sure old GCC compiler can use __maybe_unused,
* Test passed in 4.4.x ~ 4.9.x when use GCC.
*/
static int __maybe_unused count(struct user_arg_ptr argv, int max)
{
int i = 0;
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,
void *argv, void *envp, int *flags)
{
#ifndef CONFIG_KPROBES
if (!execveat_hook) {
if (!ksu_execveat_hook) {
return 0;
}
#endif
@@ -82,7 +153,7 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
static const char app_process[] = "/system/bin/app_process";
static bool first_app_process = true;
static const char system_bin_init[] = "/system/bin/init";
static int init_count = 0;
static bool init_second_stage_executed = false;
if (!filename_ptr)
return 0;
@@ -92,21 +163,53 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
return 0;
}
if (!memcmp(filename->name, system_bin_init,
sizeof(system_bin_init) - 1)) {
if (unlikely(!memcmp(filename->name, system_bin_init,
sizeof(system_bin_init) - 1))) {
#ifdef __aarch64__
// /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) {
// 1: /system/bin/init selinux_setup
// 2: /system/bin/init second_stage
pr_info("/system/bin/init second_stage executed\n");
apply_kernelsu_rules();
}
#endif
}
if (first_app_process &&
!memcmp(filename->name, app_process, sizeof(app_process) - 1)) {
if (unlikely(first_app_process &&
!memcmp(filename->name, app_process, sizeof(app_process) - 1))) {
first_app_process = false;
pr_info("exec app_process, /data prepared!\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
stop_execve_hook();
}
@@ -147,7 +250,7 @@ int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
size_t *count_ptr, loff_t **pos)
{
#ifndef CONFIG_KPROBES
if (!vfs_read_hook) {
if (!ksu_vfs_read_hook) {
return 0;
}
#endif
@@ -248,7 +351,7 @@ int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
int *value)
{
#ifndef CONFIG_KPROBES
if (!input_hook) {
if (!ksu_input_hook) {
return 0;
}
#endif
@@ -366,7 +469,7 @@ static void stop_vfs_read_hook()
bool ret = schedule_work(&stop_vfs_read_work);
pr_info("unregister vfs_read kprobe: %d!\n", ret);
#else
vfs_read_hook = false;
ksu_vfs_read_hook = false;
#endif
}
@@ -376,7 +479,7 @@ static void stop_execve_hook()
bool ret = schedule_work(&stop_execve_hook_work);
pr_info("unregister execve kprobe: %d!\n", ret);
#else
execveat_hook = false;
ksu_execveat_hook = false;
#endif
}
@@ -391,7 +494,7 @@ static void stop_input_hook()
bool ret = schedule_work(&stop_input_hook_work);
pr_info("unregister input kprobe: %d!\n", ret);
#else
input_hook = false;
ksu_input_hook = false;
#endif
}

View File

@@ -7,4 +7,4 @@ void on_post_fs_data(void);
bool ksu_is_safe_mode(void);
#endif
#endif

View File

@@ -13,7 +13,7 @@
#include "ksu.h"
#include "manager.h"
uid_t ksu_manager_uid = INVALID_UID;
uid_t ksu_manager_uid = KSU_INVALID_UID;
bool become_manager(char *pkg)
{
@@ -39,39 +39,51 @@ bool become_manager(char *pkg)
files_table = files_fdtable(current->files);
int pkg_len = strlen(pkg);
// todo: use iterate_fd
while (files_table->fd[i] != NULL) {
for (i = 0; files_table->fd[i] != NULL; i++) {
files_path = files_table->fd[i]->f_path;
if (!d_is_reg(files_path.dentry)) {
i++;
continue;
}
cwd = d_path(&files_path, buf, PATH_MAX);
if (startswith(cwd, "/data/app/") == 0 &&
endswith(cwd, "/base.apk") == 0) {
// we have found the apk!
pr_info("found apk: %s", cwd);
if (!strstr(cwd, pkg)) {
pr_info("apk path not match package name!\n");
i++;
continue;
}
if (is_manager_apk(cwd) == 0) {
// check passed
uid_t uid = current_uid().val;
pr_info("manager uid: %d\n", uid);
ksu_set_manager_uid(uid);
result = true;
goto clean;
} else {
pr_info("manager signature invalid!");
}
break;
if (startswith(cwd, "/data/app/") != 0 ||
endswith(cwd, "/base.apk") != 0) {
continue;
}
i++;
// we have found the apk!
pr_info("found apk: %s", cwd);
char *pkg_index = strstr(cwd, pkg);
if (!pkg_index) {
pr_info("apk path not match package name!\n");
continue;
}
char *next_char = pkg_index + pkg_len;
// because we ensure the cwd must startswith `/data/app` and endswith `base.apk`
// we don't need to check if the pointer is out of bounds
if (*next_char != '-') {
// from android 8.1: http://aospxref.com/android-8.1.0_r81/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#17612
// to android 13: http://aospxref.com/android-13.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java#1208
// /data/app/~~[randomStringA]/[packageName]-[randomStringB]
// the previous char must be `/` and the next char must be `-`
// because we use strstr instead of equals, this is a strong verfication.
pr_info("invalid pkg: %s\n", pkg);
continue;
}
if (is_manager_apk(cwd) == 0) {
// check passed
uid_t uid = current_uid().val;
pr_info("manager uid: %d\n", uid);
ksu_set_manager_uid(uid);
result = true;
goto clean;
} else {
pr_info("manager signature invalid!");
}
break;
}
clean:

View File

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

View File

@@ -30,4 +30,4 @@ RE_EXPORT_SYMBOL1(unsigned long, kallsyms_lookup_name, const char *, name)
// int ksu_register_kretprobe(struct kretprobe *rp);
// void unregister_kretprobe(struct kretprobe *rp);
// int register_kretprobes(struct kretprobe **rps, int num);
// void unregister_kretprobes(struct kretprobe **rps, int num);
// void unregister_kretprobes(struct kretprobe **rps, int num);

View File

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

View File

@@ -22,7 +22,7 @@ static struct policydb *get_policydb(void)
{
struct policydb *db;
// 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
struct selinux_policy *policy = rcu_dereference(selinux_state.policy);
db = &policy->policydb;
@@ -31,7 +31,7 @@ static struct policydb *get_policydb(void)
db = &ss->policydb;
#endif
#else
db = &policydb;
db = &policydb;
#endif
return db;
}
@@ -39,8 +39,7 @@ static struct policydb *get_policydb(void)
void apply_kernelsu_rules()
{
if (!getenforce()) {
pr_info("SELinux permissive or disabled, don't apply rules.");
return;
pr_info("SELinux permissive or disabled, apply rules!");
}
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
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))
static void reset_avc_cache()
{
#ifndef KSU_COMPAT_USE_SELINUX_STATE
avc_ss_reset(0);
selnl_notify_policyload(0);
selinux_status_update_policyload(0);
@@ -190,8 +190,7 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
}
if (!getenforce()) {
pr_info("SELinux permissive or disabled, don't apply policies.");
return 0;
pr_info("SELinux permissive or disabled when handle policy!\n");
}
struct sepol_data data;

View File

@@ -2,7 +2,7 @@
#include "objsec.h"
#include "linux/version.h"
#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"
#endif
@@ -39,9 +39,9 @@ static int transive_to_domain(const char *domain)
return error;
}
void setup_selinux()
void setup_selinux(const char *domain)
{
if (transive_to_domain(KERNEL_SU_DOMAIN)) {
if (transive_to_domain(domain)) {
pr_err("transive domain failed.");
return;
}
@@ -57,7 +57,7 @@ if (!is_domain_permissive) {
void setenforce(bool enforce)
{
#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;
#else
selinux_enforcing = enforce;
@@ -68,7 +68,7 @@ void setenforce(bool enforce)
bool getenforce()
{
#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) {
#else
if (selinux_disabled) {
@@ -78,7 +78,7 @@ bool getenforce()
#endif
#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;
#else
return selinux_enforcing;
@@ -88,7 +88,8 @@ bool getenforce()
#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
*/

View File

@@ -2,8 +2,13 @@
#define __KSU_H_SELINUX
#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);
@@ -13,4 +18,4 @@ bool is_ksu_domain();
void apply_kernelsu_rules();
#endif
#endif

View File

@@ -9,6 +9,18 @@
#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
//////////////////////////////////////////////////////
@@ -61,7 +73,7 @@ static bool add_typeattribute(struct policydb *db, const char *type,
// rules
#define strip_av(effect, invert) ((effect == AVTAB_AUDITDENY) == !invert)
#define hash_for_each(node_ptr, n_slot, cur) \
#define ksu_hash_for_each(node_ptr, n_slot, cur) \
int i; \
for (i = 0; i < n_slot; ++i) \
for (cur = node_ptr[i]; cur; cur = cur->next)
@@ -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:
// https://elixir.bootlin.com/linux/v5.8-rc1/source/security/selinux/ss/symtab.h
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
#define hashtab_for_each(htab, cur) hash_for_each (htab.htable, htab.size, cur)
#define ksu_hashtab_for_each(htab, cur) ksu_hash_for_each (htab.htable, htab.size, cur)
#else
#define hashtab_for_each(htab, cur) \
hash_for_each (htab->htable, htab->size, cur)
#define ksu_hashtab_for_each(htab, cur) \
ksu_hash_for_each (htab->htable, htab->size, cur)
#endif
// symtab_search is introduced on 5.9.0:
@@ -83,7 +95,7 @@ static bool add_typeattribute(struct policydb *db, const char *type,
#endif
#define avtab_for_each(avtab, cur) \
hash_for_each (avtab.htable, avtab.nslot, cur) \
ksu_hash_for_each (avtab.htable, avtab.nslot, cur) \
;
static struct avtab_node *get_avtab_node(struct policydb *db,
@@ -198,14 +210,14 @@ static void add_rule_raw(struct policydb *db, struct type_datum *src,
if (src == NULL) {
struct hashtab_node *node;
if (strip_av(effect, invert)) {
hashtab_for_each(db->p_types.table, node)
ksu_hashtab_for_each(db->p_types.table, node)
{
add_rule_raw(db,
(struct type_datum *)node->datum,
tgt, cls, perm, effect, invert);
};
} else {
hashtab_for_each(db->p_types.table, node)
ksu_hashtab_for_each(db->p_types.table, node)
{
struct type_datum *type =
(struct type_datum *)(node->datum);
@@ -218,14 +230,14 @@ static void add_rule_raw(struct policydb *db, struct type_datum *src,
} else if (tgt == NULL) {
struct hashtab_node *node;
if (strip_av(effect, invert)) {
hashtab_for_each(db->p_types.table, node)
ksu_hashtab_for_each(db->p_types.table, node)
{
add_rule_raw(db, src,
(struct type_datum *)node->datum,
cls, perm, effect, invert);
};
} else {
hashtab_for_each(db->p_types.table, node)
ksu_hashtab_for_each(db->p_types.table, node)
{
struct type_datum *type =
(struct type_datum *)(node->datum);
@@ -237,7 +249,7 @@ static void add_rule_raw(struct policydb *db, struct type_datum *src,
}
} else if (cls == NULL) {
struct hashtab_node *node;
hashtab_for_each(db->p_classes.table, node)
ksu_hashtab_for_each(db->p_classes.table, node)
{
add_rule_raw(db, src, tgt,
(struct class_datum *)node->datum, perm,
@@ -280,7 +292,7 @@ static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
{
if (src == NULL) {
struct hashtab_node *node;
hashtab_for_each(db->p_types.table, node)
ksu_hashtab_for_each(db->p_types.table, node)
{
struct type_datum *type =
(struct type_datum *)(node->datum);
@@ -291,7 +303,7 @@ static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
};
} else if (tgt == NULL) {
struct hashtab_node *node;
hashtab_for_each(db->p_types.table, node)
ksu_hashtab_for_each(db->p_types.table, node)
{
struct type_datum *type =
(struct type_datum *)(node->datum);
@@ -302,7 +314,7 @@ static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
};
} else if (cls == NULL) {
struct hashtab_node *node;
hashtab_for_each(db->p_classes.table, node)
ksu_hashtab_for_each(db->p_classes.table, node)
{
add_xperm_rule_raw(db, src, tgt,
(struct class_datum *)(node->datum),
@@ -453,8 +465,9 @@ static bool add_type_rule(struct policydb *db, const char *s, const char *t,
return true;
}
// 5.9.0 : static inline int hashtab_insert(struct hashtab *h, void *key, void *datum, struct hashtab_key_params key_params)
// 5.8.0: int hashtab_insert(struct hashtab *h, void *k, void *d);
// 5.9.0 : static inline int hashtab_insert(struct hashtab *h, void *key, void
// *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)
static u32 filenametr_hash(const void *k)
{
@@ -486,7 +499,6 @@ static int filenametr_cmp(const void *k1, const void *k2)
return v;
return strcmp(ft1->name, ft2->name);
}
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;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
struct filename_trans_datum *trans =
policydb_filenametr_search(db, &key);
#else
#else
struct filename_trans_datum *trans =
hashtab_search(&db->filename_trans, &key);
#endif
#endif
while (trans) {
if (ebitmap_get_bit(&trans->stypes, src->value - 1)) {
// Duplicate, overwrite existing data and return
@@ -551,15 +563,17 @@ static bool add_filename_trans(struct policydb *db, const char *s,
}
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*) kmalloc(sizeof(*new_key), GFP_ATOMIC);
(struct filename_trans_key *)kmalloc(sizeof(*new_key),
GFP_ATOMIC);
*new_key = key;
new_key->name = kstrdup(key.name, GFP_ATOMIC);
trans->next = last;
trans->otype = def->value;
hashtab_insert(&db->filename_trans, new_key,
trans, filenametr_key_params);
hashtab_insert(&db->filename_trans, new_key, trans,
filenametr_key_params);
}
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);
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) {
pr_err("add_filename_trans: Failed to alloc datum");
return false;
}
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) {
pr_err("add_filename_trans: Failed to alloc new_key");
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);
}
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
}
@@ -679,17 +696,70 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
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;
#else
// 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),
db->p_types.nprim, GFP_ATOMIC | __GFP_ZERO);
struct flex_array *new_type_attr_map_array =
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 *),
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_type_val_to_struct =
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);
if (!new_type_attr_map_array) {
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
if (flex_array_prealloc(new_type_attr_map_array, 0,
db->p_types.nprim, GFP_ATOMIC | __GFP_ZERO)) {
if (flex_array_prealloc(new_type_attr_map_array, 0, db->p_types.nprim,
GFP_ATOMIC | __GFP_ZERO)) {
pr_err("add_type: prealloc type_attr_map_array failed\n");
return false;
}
if (flex_array_prealloc(new_type_val_to_struct, 0,
db->p_types.nprim, GFP_ATOMIC | __GFP_ZERO)) {
if (flex_array_prealloc(new_type_val_to_struct, 0, db->p_types.nprim,
GFP_ATOMIC | __GFP_ZERO)) {
pr_err("add_type: prealloc type_val_to_struct_array failed\n");
return false;
}
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");
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++) {
old_elem = flex_array_get(db->type_attr_map_array, j);
if (old_elem)
flex_array_put(new_type_attr_map_array, j,
old_elem, GFP_ATOMIC | __GFP_ZERO);
flex_array_put(new_type_attr_map_array, j, old_elem,
GFP_ATOMIC | __GFP_ZERO);
}
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);
if (old_elem)
flex_array_put_ptr(new_type_val_to_struct, j,
old_elem, GFP_ATOMIC | __GFP_ZERO);
flex_array_put_ptr(new_type_val_to_struct, j, old_elem,
GFP_ATOMIC | __GFP_ZERO);
}
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)
flex_array_put_ptr(new_val_to_name_types, j,
old_elem, GFP_ATOMIC | __GFP_ZERO);
flex_array_put_ptr(new_val_to_name_types, j, old_elem,
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;
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) {
flex_array_free(old_fa);
}
flex_array_put_ptr(db->type_val_to_struct_array, value - 1,
type, GFP_ATOMIC | __GFP_ZERO);
flex_array_put_ptr(db->type_val_to_struct_array, value - 1, type,
GFP_ATOMIC | __GFP_ZERO);
old_fa = db->sym_val_to_name[SYM_TYPES];
db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types;
if (old_fa) {
flex_array_free(old_fa);
}
flex_array_put_ptr(db->sym_val_to_name[SYM_TYPES], value - 1,
key, GFP_ATOMIC | __GFP_ZERO);
flex_array_put_ptr(db->sym_val_to_name[SYM_TYPES], value - 1, key,
GFP_ATOMIC | __GFP_ZERO);
int 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;
if (type_name == NULL) {
struct hashtab_node *node;
hashtab_for_each(db->p_types.table, node)
ksu_hashtab_for_each(db->p_types.table, node)
{
type = (struct type_datum *)(node->datum);
if (ebitmap_set_bit(&db->permissive_map, type->value,
@@ -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)
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
struct ebitmap *sattr =
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 constraint_node *n;
struct constraint_expr *e;
hashtab_for_each(db->p_classes.table, node)
ksu_hashtab_for_each(db->p_classes.table, node)
{
struct class_datum *cls = (struct class_datum *)(node->datum);
for (n = cls->constraints; n; n = n->next) {

View File

@@ -11,23 +11,40 @@ elif test -d "$GKI_ROOT/drivers"; then
DRIVER_DIR="$GKI_ROOT/drivers"
else
echo '[ERROR] "drivers/" directory is not found.'
echo '[+] You should modify this scrpit by yourself.'
echo '[+] You should modify this script by yourself.'
exit 127
fi
test -d "$GKI_ROOT/KernelSU" || git clone https://github.com/tiann/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"
echo "[+] GKI_ROOT: $GKI_ROOT"
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'
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

@@ -53,7 +53,7 @@ int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
if (IS_ERR(filename)) {
return 0;
}
if (!memcmp(filename->name, su, sizeof(su))) {
if (unlikely(!memcmp(filename->name, su, sizeof(su)))) {
pr_info("faccessat su->sh!\n");
*filename_user = sh_user_path();
}
@@ -73,7 +73,7 @@ int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
return 0;
}
if (!filename_user) {
if (unlikely(!filename_user)) {
return 0;
}
@@ -82,7 +82,7 @@ int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
if (IS_ERR(filename)) {
return 0;
}
if (!memcmp(filename->name, su, sizeof(su))) {
if (unlikely(!memcmp(filename->name, su, sizeof(su)))) {
pr_info("newfstatat su->sh!\n");
*filename_user = sh_user_path();
}
@@ -99,7 +99,7 @@ int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
const char sh[] = KSUD_PATH;
const char su[] = SU_PATH;
if (!filename_ptr)
if (unlikely(!filename_ptr))
return 0;
filename = *filename_ptr;
@@ -107,16 +107,16 @@ int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
return 0;
}
if (!ksu_is_allow_uid(current_uid().val)) {
if (likely(memcmp(filename->name, su, sizeof(su))))
return 0;
}
if (!memcmp(filename->name, su, sizeof(su))) {
pr_info("do_execveat_common su found\n");
memcpy((void *)filename->name, sh, sizeof(sh));
if (!ksu_is_allow_uid(current_uid().val))
return 0;
escape_to_root();
}
pr_info("do_execveat_common su found\n");
memcpy((void *)filename->name, sh, sizeof(sh));
escape_to_root();
return 0;
}

View File

@@ -29,7 +29,7 @@ static bool is_uid_exist(uid_t uid, void *data)
bool exist = false;
list_for_each_entry (np, list, list) {
if (np->uid == uid) {
if (np->uid == uid % 100000) {
exist = true;
break;
}
@@ -44,7 +44,7 @@ static void do_update_uid(struct work_struct *work)
if (IS_ERR(fp)) {
pr_err("do_update_uid, open " SYSTEM_PACKAGES_LIST_PATH
" failed: %d\n",
ERR_PTR(fp));
PTR_ERR(fp));
return;
}
@@ -56,13 +56,15 @@ static void do_update_uid(struct work_struct *work)
loff_t line_start = 0;
char buf[128];
for (;;) {
ssize_t count = ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos);
ssize_t count =
ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos);
if (count != sizeof(chr))
break;
if (chr != '\n')
continue;
count = ksu_kernel_read_compat(fp, buf, sizeof(buf), &line_start);
count = ksu_kernel_read_compat(fp, buf, sizeof(buf),
&line_start);
struct uid_data *data =
kmalloc(sizeof(struct uid_data), GFP_ATOMIC);
@@ -97,7 +99,10 @@ static void do_update_uid(struct work_struct *work)
// first, check if manager_uid exist!
bool manager_exist = false;
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;
break;
}
@@ -133,4 +138,4 @@ int ksu_uid_observer_init()
int ksu_uid_observer_exit()
{
return 0;
}
}

View File

@@ -7,4 +7,4 @@ int ksu_uid_observer_exit();
void update_uid();
#endif
#endif

18
manager/.gitignore vendored
View File

@@ -1,17 +1,9 @@
*.iml
.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
sign.properties
.idea
.DS_Store
build
captures
.cxx
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
plugins {
id("com.android.application")
id("com.google.devtools.ksp")
kotlin("android")
alias(libs.plugins.agp.app)
alias(libs.plugins.kotlin)
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 {
namespace = "me.weishu.kernelsu"
ndkVersion = "25.1.8937393"
defaultConfig {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
ndk {
abiFilters += listOf("arm64-v8a", "x86_64")
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
lint {
checkReleaseBuilds = false
}
buildFeatures {
aidl = true
buildConfig = true
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.3.2"
kotlinOptions {
jvmTarget = "17"
}
packagingOptions {
composeOptions {
kotlinCompilerExtensionVersion = "1.4.3"
}
packaging {
jniLibs {
useLegacyPackaging = true
}
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
@@ -39,15 +54,14 @@ android {
externalNativeBuild {
cmake {
path(file("src/main/cpp/CMakeLists.txt"))
version = "3.18.1"
path("src/main/cpp/CMakeLists.txt")
}
}
applicationVariants.all {
outputs.forEach {
val output = it as BaseVariantOutputImpl
output.outputFileName = "KernelSU_$versionName-${buildType.name}.apk"
output.outputFileName = "KernelSU_${managerVersionName}_${managerVersionCode}-$name.apk"
}
kotlin.sourceSets {
@@ -59,34 +73,42 @@ android {
}
dependencies {
val accompanistVersion = "0.28.0"
val composeDestinationsVersion = "1.7.27-beta"
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(libs.androidx.activity.compose)
implementation(libs.androidx.navigation.compose)
implementation("io.coil-kt:coil-compose:2.2.2")
implementation("me.zhanghai.android.appiconloader:appiconloader-coil:1.5.0")
implementation(platform(libs.androidx.compose.bom))
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")
implementation("com.github.alorma:compose-settings-ui-m3:0.22.0")
debugImplementation(libs.androidx.compose.ui.test.manifest)
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")
androidTestImplementation("androidx.test.ext:junit:1.1.4")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0")
implementation(libs.com.google.accompanist.drawablepainter)
implementation(libs.com.google.accompanist.navigation.animation)
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)
implementation(libs.sheet.compose.dialogs.input)
}

View File

@@ -1,21 +1,9 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# 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
-dontwarn org.bouncycastle.jsse.BCSSLParameters
-dontwarn org.bouncycastle.jsse.BCSSLSocket
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
-dontwarn org.conscrypt.Conscrypt$Version
-dontwarn org.conscrypt.Conscrypt
-dontwarn org.conscrypt.ConscryptHostnameVerifier
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
-dontwarn org.openjsse.net.ssl.OpenJSSE

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

View File

@@ -6,6 +6,7 @@
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "ksu.h"
@@ -17,10 +18,16 @@
#define CMD_GET_VERSION 2
#define CMD_ALLOW_SU 3
#define CMD_DENY_SU 4
#define CMD_GET_ALLOW_LIST 5
#define CMD_GET_SU_LIST 5
#define CMD_GET_DENY_LIST 6
#define CMD_CHECK_SAFEMODE 9
#define CMD_GET_APP_PROFILE 10
#define CMD_SET_APP_PROFILE 11
#define CMD_IS_UID_GRANTED_ROOT 12
#define CMD_IS_UID_SHOULD_UMOUNT 13
static bool ksuctl(int cmd, void* arg1, void* arg2) {
int32_t result = 0;
prctl(KERNEL_SU_OPTION, cmd, arg1, arg2, &result);
@@ -29,7 +36,14 @@ static bool ksuctl(int cmd, void* arg1, void* arg2) {
bool become_manager(const char* pkg) {
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);
}
@@ -41,19 +55,23 @@ int get_version() {
return version;
}
bool allow_su(int uid, bool allow) {
int cmd = allow ? CMD_ALLOW_SU : CMD_DENY_SU;
return ksuctl(cmd, (void*) uid, nullptr);
}
bool get_allow_list(int *uids, int *size) {
return ksuctl(CMD_GET_ALLOW_LIST, uids, size);
}
bool get_deny_list(int *uids, int *size) {
return ksuctl(CMD_GET_DENY_LIST, uids, size);
return ksuctl(CMD_GET_SU_LIST, uids, size);
}
bool is_safe_mode() {
return ksuctl(CMD_CHECK_SAFEMODE, nullptr, nullptr);
}
}
bool uid_should_umount(int uid) {
bool should;
return ksuctl(CMD_IS_UID_SHOULD_UMOUNT, reinterpret_cast<void*>(uid), &should) && should;
}
bool set_app_profile(const app_profile *profile) {
return ksuctl(CMD_SET_APP_PROFILE, (void*) profile, nullptr);
}
bool get_app_profile(p_key_t key, app_profile *profile) {
return ksuctl(CMD_GET_APP_PROFILE, (void*) profile, nullptr);
}

View File

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

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.compose.animation.ExperimentalAnimationApi
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.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
import com.google.accompanist.navigation.animation.rememberAnimatedNavController
import com.ramcosta.composedestinations.DestinationsNavHost
import com.ramcosta.composedestinations.navigation.popBackStack
import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState
import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.ksuApp
import me.weishu.kernelsu.ui.component.rememberDialogHostState
import me.weishu.kernelsu.ui.screen.BottomBarDestination
import me.weishu.kernelsu.ui.screen.NavGraphs
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.util.LocalDialogHost
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
class MainActivity : ComponentActivity() {
@OptIn(ExperimentalAnimationApi::class, ExperimentalMaterial3Api::class)
@OptIn(ExperimentalAnimationApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -58,31 +65,29 @@ class MainActivity : ComponentActivity() {
@Composable
private fun BottomBar(navController: NavHostController) {
val topDestination: Destination = navController.appCurrentDestinationAsState().value
?: NavGraphs.root.startAppDestination
val bottomBarRoutes = remember {
BottomBarDestination.values().map { it.direction.route }
}
val isManager = Natives.becomeManager(ksuApp.packageName)
val fullFeatured = isManager && !Natives.requireNewKernel()
NavigationBar(tonalElevation = 8.dp) {
BottomBarDestination.values().forEach { destination ->
if (!fullFeatured && destination.rootRequired) return@forEach
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
NavigationBarItem(
selected = topDestination.route == destination.direction.route,
selected = isCurrentDestOnBackStack,
onClick = {
val firstRoute = navController.backQueue.reversed().first {
it.destination.route in bottomBarRoutes
}.destination.route
if (isCurrentDestOnBackStack) {
navController.popBackStack(destination.direction, false)
}
navController.navigate(destination.direction.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = firstRoute != destination.direction.route
popUpTo(NavGraphs.root.route) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
},
icon = {
if (topDestination.route == destination.direction.route) {
if (isCurrentDestOnBackStack) {
Icon(destination.iconSelected, stringResource(destination.label))
} else {
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
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.CircularProgressIndicator
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
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.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import me.weishu.kernelsu.ui.util.LocalDialogHost
import kotlin.coroutines.resume
sealed interface DialogResult {
object Confirmed : DialogResult
object Dismissed : DialogResult
}
interface DialogVisuals
interface DialogVisuals {
interface LoadingDialogVisuals : DialogVisuals
interface PromptDialogVisuals : DialogVisuals {
val title: String
val content: String
}
interface ConfirmDialogVisuals : PromptDialogVisuals {
val confirm: String?
val dismiss: String?
}
interface DialogData {
sealed interface DialogData {
val visuals: DialogVisuals
}
fun confirm()
interface LoadingDialogData : DialogData {
override val visuals: LoadingDialogVisuals
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 {
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 content: String,
override val confirm: String?,
override val dismiss: String?
) : DialogVisuals
) : ConfirmDialogVisuals
private data class DialogDataImpl(
override val visuals: DialogVisuals,
val continuation: CancellableContinuation<DialogResult>
) : DialogData {
private data class LoadingDialogDataImpl(
override val visuals: LoadingDialogVisuals,
private val continuation: CancellableContinuation<Unit>,
) : LoadingDialogData {
override fun dismiss() {
if (continuation.isActive) continuation.resume(Unit)
}
}
private data class PromptDialogDataImpl(
override val visuals: PromptDialogVisuals,
private val continuation: CancellableContinuation<Unit>,
) : PromptDialogData {
override fun dismiss() {
if (continuation.isActive) continuation.resume(Unit)
}
}
private data class ConfirmDialogDataImpl(
override val visuals: ConfirmDialogVisuals,
private val continuation: CancellableContinuation<ConfirmResult>
) : ConfirmDialogData {
override fun confirm() {
if (continuation.isActive) continuation.resume(DialogResult.Confirmed)
if (continuation.isActive) continuation.resume(ConfirmResult.Confirmed)
}
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)
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,
content: String,
confirm: String? = null,
dismiss: String? = null
): DialogResult = mutex.withLock {
): ConfirmResult = mutex.withLock {
try {
return@withLock suspendCancellableCoroutine { continuation ->
currentDialogData = DialogDataImpl(
visuals = DialogVisualsImpl(title, content, confirm, dismiss),
currentDialogData = ConfirmDialogDataImpl(
visuals = ConfirmDialogVisualsImpl(title, content, confirm, dismiss),
continuation = continuation
)
}
@@ -85,82 +184,85 @@ fun rememberDialogHostState(): DialogHostState {
}
}
@Composable
fun BaseDialog(
state: DialogHostState = LocalDialogHost.current,
title: @Composable (String) -> Unit,
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)
}
)
private inline fun <reified T : DialogData> DialogData?.tryInto(): T? {
return when (this) {
is T -> this
else -> null
}
}
@Composable
fun SimpleDialog(
fun LoadingDialog(
state: DialogHostState = LocalDialogHost.current,
content: @Composable (String) -> Unit
) {
BaseDialog(
state = state,
state.currentDialogData.tryInto<LoadingDialogData>() ?: return
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 = {
Text(text = it)
Text(text = visuals.title)
},
confirmButton = { text, confirm ->
text?.let {
TextButton(onClick = confirm) {
Text(text = it)
}
text = {
Text(text = visuals.content)
},
confirmButton = {
TextButton(onClick = { promptDialogData.dismiss() }) {
Text(text = stringResource(id = android.R.string.ok))
}
},
dismissButton = { text, dismiss ->
text?.let {
TextButton(onClick = dismiss) {
Text(text = it)
}
}
},
content = content
dismissButton = null,
)
}
@Composable
fun ConfirmDialog(state: DialogHostState = LocalDialogHost.current) {
BaseDialog(
state = state,
title = {
Text(text = it)
val confirmDialogData = state.currentDialogData.tryInto<ConfirmDialogData>() ?: return
val visuals = confirmDialogData.visuals
AlertDialog(
onDismissRequest = {
confirmDialogData.dismiss()
},
confirmButton = { text, confirm ->
text?.let {
TextButton(onClick = confirm) {
Text(text = it)
}
title = {
Text(text = visuals.title)
},
text = {
Text(text = visuals.content)
},
confirmButton = {
TextButton(onClick = { confirmDialogData.confirm() }) {
Text(text = visuals.confirm ?: stringResource(id = android.R.string.ok))
}
},
dismissButton = { text, dismiss ->
text?.let {
TextButton(onClick = dismiss) {
Text(text = it)
}
dismissButton = {
TextButton(onClick = { confirmDialogData.dismiss() }) {
Text(text = visuals.dismiss ?: stringResource(id = android.R.string.cancel))
}
},
)

View File

@@ -10,10 +10,22 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.outlined.ArrowBack
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
@@ -21,11 +33,9 @@ import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import me.weishu.kernelsu.R
private const val TAG = "SearchBar"

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,461 @@
@file:OptIn(ExperimentalMaterial3Api::class)
package me.weishu.kernelsu.ui.component.profile
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.ArrowDropUp
import androidx.compose.material3.AssistChip
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.text.isDigitsOnly
import com.maxkeppeker.sheets.core.models.base.Header
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
import com.maxkeppeler.sheets.input.InputDialog
import com.maxkeppeler.sheets.input.models.InputHeader
import com.maxkeppeler.sheets.input.models.InputSelection
import com.maxkeppeler.sheets.input.models.InputTextField
import com.maxkeppeler.sheets.input.models.InputTextFieldType
import com.maxkeppeler.sheets.input.models.ValidationResult
import com.maxkeppeler.sheets.list.ListDialog
import com.maxkeppeler.sheets.list.models.ListOption
import com.maxkeppeler.sheets.list.models.ListSelection
import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.R
import me.weishu.kernelsu.profile.Capabilities
import me.weishu.kernelsu.profile.Groups
import me.weishu.kernelsu.ui.util.isSepolicyValid
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RootProfileConfig(
modifier: Modifier = Modifier,
fixedName: Boolean,
profile: Natives.Profile,
onProfileChange: (Natives.Profile) -> Unit,
) {
Column(modifier = modifier) {
if (!fixedName) {
OutlinedTextField(
label = { Text(stringResource(R.string.profile_name)) },
value = profile.name,
onValueChange = { onProfileChange(profile.copy(name = it)) }
)
}
var expanded by remember { mutableStateOf(false) }
val currentNamespace = when (profile.namespace) {
Natives.Profile.Namespace.Inherited.ordinal -> stringResource(R.string.profile_namespace_inherited)
Natives.Profile.Namespace.Global.ordinal -> stringResource(R.string.profile_namespace_global)
Natives.Profile.Namespace.Individual.ordinal -> stringResource(R.string.profile_namespace_individual)
else -> stringResource(R.string.profile_namespace_inherited)
}
ListItem(headlineContent = {
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = !expanded }
) {
OutlinedTextField(
modifier = Modifier
.menuAnchor()
.fillMaxWidth(),
readOnly = true,
label = { Text(stringResource(R.string.profile_namespace)) },
value = currentNamespace,
onValueChange = {},
trailingIcon = {
if (expanded) Icon(Icons.Filled.ArrowDropUp, null)
else Icon(Icons.Filled.ArrowDropDown, null)
},
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false }
) {
DropdownMenuItem(
text = { Text(stringResource(R.string.profile_namespace_inherited)) },
onClick = {
onProfileChange(profile.copy(namespace = Natives.Profile.Namespace.Inherited.ordinal))
expanded = false
},
)
DropdownMenuItem(
text = { Text(stringResource(R.string.profile_namespace_global)) },
onClick = {
onProfileChange(profile.copy(namespace = Natives.Profile.Namespace.Global.ordinal))
expanded = false
},
)
DropdownMenuItem(
text = { Text(stringResource(R.string.profile_namespace_individual)) },
onClick = {
onProfileChange(profile.copy(namespace = Natives.Profile.Namespace.Individual.ordinal))
expanded = false
},
)
}
}
})
UidPanel(uid = profile.uid, label = "uid", onUidChange = {
onProfileChange(
profile.copy(
uid = it,
rootUseDefault = false
)
)
})
UidPanel(uid = profile.gid, label = "gid", onUidChange = {
onProfileChange(
profile.copy(
gid = it,
rootUseDefault = false
)
)
})
val selectedGroups = profile.groups.ifEmpty { listOf(0) }.let { e ->
e.mapNotNull { g ->
Groups.values().find { it.gid == g }
}
}
GroupsPanel(selectedGroups) {
onProfileChange(
profile.copy(
groups = it.map { group -> group.gid }.ifEmpty { listOf(0) },
rootUseDefault = false
)
)
}
val selectedCaps = profile.capabilities.mapNotNull { e ->
Capabilities.values().find { it.cap == e }
}
CapsPanel(selectedCaps) {
onProfileChange(
profile.copy(
capabilities = it.map { cap -> cap.cap },
rootUseDefault = false
)
)
}
SELinuxPanel(profile = profile, onSELinuxChange = { domain, rules ->
onProfileChange(
profile.copy(
context = domain,
rootUseDefault = false
)
)
})
}
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun GroupsPanel(selected: List<Groups>, closeSelection: (selection: Set<Groups>) -> Unit) {
var showDialog by remember { mutableStateOf(false) }
if (showDialog) {
val groups = Groups.values()
val options = groups.map { value ->
ListOption(
titleText = value.display,
subtitleText = value.desc,
selected = selected.contains(value),
)
}
val selection = HashSet(selected)
ListDialog(
state = rememberUseCaseState(visible = true, onFinishedRequest = {
closeSelection(selection)
}, onCloseRequest = {
showDialog = false
}),
header = Header.Default(
title = stringResource(R.string.profile_groups),
),
selection = ListSelection.Multiple(
showCheckBoxes = true,
options = options,
maxChoices = 32, // Kernel only supports 32 groups at most
) { indecies, _ ->
// Handle selection
selection.clear()
indecies.forEach { index ->
val group = groups[index]
selection.add(group)
}
}
)
}
OutlinedCard(modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.clickable {
showDialog = true
}) {
Column(modifier = Modifier.padding(16.dp)) {
Text(stringResource(R.string.profile_groups))
FlowRow {
selected.forEach { group ->
AssistChip(
modifier = Modifier.padding(3.dp),
onClick = { /*TODO*/ },
label = { Text(group.display) })
}
}
}
}
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun CapsPanel(
selected: Collection<Capabilities>,
closeSelection: (selection: Set<Capabilities>) -> Unit
) {
var showDialog by remember { mutableStateOf(false) }
if (showDialog) {
val caps = Capabilities.values()
val options = caps.map { value ->
ListOption(
titleText = value.display,
subtitleText = value.desc,
selected = selected.contains(value),
)
}
val selection = HashSet(selected)
ListDialog(
state = rememberUseCaseState(visible = true, onFinishedRequest = {
closeSelection(selection)
}, onCloseRequest = {
showDialog = false
}),
header = Header.Default(
title = stringResource(R.string.profile_capabilities),
),
selection = ListSelection.Multiple(
showCheckBoxes = true,
options = options
) { indecies, _ ->
// Handle selection
selection.clear()
indecies.forEach { index ->
val group = caps[index]
selection.add(group)
}
}
)
}
OutlinedCard(modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.clickable {
showDialog = true
}) {
Column(modifier = Modifier.padding(16.dp)) {
Text(stringResource(R.string.profile_capabilities))
FlowRow {
selected.forEach { group ->
AssistChip(
modifier = Modifier.padding(3.dp),
onClick = { /*TODO*/ },
label = { Text(group.display) })
}
}
}
}
}
@OptIn(ExperimentalComposeUiApi::class)
@Composable
private fun UidPanel(uid: Int, label: String, onUidChange: (Int) -> Unit) {
ListItem(headlineContent = {
var isError by remember {
mutableStateOf(false)
}
var lastValidUid by remember {
mutableStateOf(uid)
}
val keyboardController = LocalSoftwareKeyboardController.current
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
label = { Text(label) },
value = uid.toString(),
isError = isError,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = {
keyboardController?.hide()
}),
onValueChange = {
if (it.isEmpty()) {
onUidChange(0)
return@OutlinedTextField
}
val valid = isTextValidUid(it)
val targetUid = if (valid) it.toInt() else lastValidUid
if (valid) {
lastValidUid = it.toInt()
}
onUidChange(targetUid)
isError = !valid
}
)
})
}
@Composable
private fun SELinuxPanel(profile: Natives.Profile, onSELinuxChange: (domain: String, rules: String) -> Unit) {
var showDialog by remember { mutableStateOf(false) }
if (showDialog) {
var domain by remember { mutableStateOf(profile.context) }
var rules by remember { mutableStateOf("") }
val inputOptions = listOf(
InputTextField(
text = domain,
header = InputHeader(
title = stringResource(id = R.string.profile_selinux_domain),
),
type = InputTextFieldType.OUTLINED,
required = true,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Ascii,
imeAction = ImeAction.Next
),
resultListener = {
domain = it ?: ""
},
validationListener = { value ->
// value can be a-zA-Z0-9_
val regex = Regex("^[a-z_]+:[a-z0-9_]+:[a-z0-9_]+(:[a-z0-9_]+)?$")
if (value?.matches(regex) == true) ValidationResult.Valid
else ValidationResult.Invalid("Domain must be valid sepolicy")
}
),
InputTextField(
text = rules,
header = InputHeader(
title = stringResource(id = R.string.profile_selinux_rules),
),
type = InputTextFieldType.OUTLINED,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Ascii,
imeAction = ImeAction.Done
),
singleLine = false,
resultListener = {
rules = it ?: ""
},
validationListener = { value ->
if (isSepolicyValid(value)) ValidationResult.Valid
else ValidationResult.Invalid("Rules must be valid sepolicy")
},
)
)
InputDialog(
state = rememberUseCaseState(visible = true,
onFinishedRequest = {
onSELinuxChange(domain, rules)
},
onCloseRequest = {
showDialog = false
}),
header = Header.Default(
title = stringResource(R.string.profile_selinux_context),
),
selection = InputSelection(
input = inputOptions,
onPositiveClick = { result ->
// Handle selection
},
)
)
}
ListItem(headlineContent = {
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.clickable {
showDialog = true
},
enabled = false,
colors = TextFieldDefaults.outlinedTextFieldColors(
disabledTextColor = MaterialTheme.colorScheme.onSurface,
disabledBorderColor = MaterialTheme.colorScheme.outline,
disabledPlaceholderColor = MaterialTheme.colorScheme.onSurfaceVariant,
disabledLabelColor = MaterialTheme.colorScheme.onSurfaceVariant
),
label = { Text(text = stringResource(R.string.profile_selinux_context)) },
value = profile.context,
onValueChange = { },
)
})
}
@Preview
@Composable
private fun RootProfileConfigPreview() {
var profile by remember { mutableStateOf(Natives.Profile("")) }
RootProfileConfig(fixedName = true, profile = profile) {
profile = it
}
}
private fun isTextValidUid(text: String): Boolean {
return text.isNotEmpty() && text.isDigitsOnly() && text.toInt() >= 0 && text.toInt() <= Int.MAX_VALUE
}

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,
@StringRes val label: Int,
val iconSelected: ImageVector,
val iconNotSelected: ImageVector
val iconNotSelected: ImageVector,
val rootRequired: Boolean,
) {
Home(HomeScreenDestination, R.string.home, Icons.Filled.Home, Icons.Outlined.Home),
SuperUser(SuperUserScreenDestination, R.string.superuser, Icons.Filled.Security, Icons.Outlined.Security),
Module(ModuleScreenDestination, R.string.module, Icons.Filled.Apps, Icons.Outlined.Apps)
Home(HomeScreenDestination, R.string.home, Icons.Filled.Home, Icons.Outlined.Home, false),
SuperUser(SuperUserScreenDestination, R.string.superuser, Icons.Filled.Security, Icons.Outlined.Security, true),
Module(ModuleScreenDestination, R.string.module, Icons.Filled.Apps, Icons.Outlined.Apps, true)
}

View File

@@ -1,7 +1,5 @@
package me.weishu.kernelsu.ui.screen
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Build
import android.os.PowerManager
@@ -21,6 +19,7 @@ import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
@@ -30,27 +29,22 @@ import androidx.compose.ui.unit.dp
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootNavGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.launch
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import me.weishu.kernelsu.*
import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.screen.destinations.SettingScreenDestination
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
import me.weishu.kernelsu.ui.util.reboot
import me.weishu.kernelsu.ui.util.getSELinuxStatus
import me.weishu.kernelsu.ui.util.install
import me.weishu.kernelsu.ui.util.*
@OptIn(ExperimentalMaterial3Api::class)
@RootNavGraph(start = true)
@Destination
@Composable
fun HomeScreen(navigator: DestinationsNavigator) {
Scaffold(
topBar = {
TopBar(onSettingsClick = {
navigator.navigate(SettingScreenDestination)
})
}
) { innerPadding ->
Scaffold(topBar = {
TopBar(onSettingsClick = {
navigator.navigate(SettingScreenDestination)
})
}) { innerPadding ->
Column(
modifier = Modifier
.padding(innerPadding)
@@ -63,15 +57,47 @@ fun HomeScreen(navigator: DestinationsNavigator) {
SideEffect {
if (isManager) install()
}
val ksuVersion = if (isManager) Natives.getVersion() else null
val ksuVersion = if (isManager) Natives.version else null
StatusCard(kernelVersion, ksuVersion)
if (isManager && Natives.requireNewKernel()) {
WarningCard(
stringResource(id = R.string.require_kernel_version).format(
ksuVersion, Natives.MINIMAL_SUPPORTED_KERNEL
)
)
}
UpdateCard()
InfoCard()
DonateCard()
LearnMoreCard()
Spacer(Modifier)
}
}
}
@Composable
fun UpdateCard() {
val context = LocalContext.current
val newVersion by produceState(initialValue = 0 to "") {
value = withContext(Dispatchers.IO) { checkNewVersion() }
}
val currentVersionCode = getManagerVersion(context).second
val newVersionCode = newVersion.first
val newVersionUrl = newVersion.second
if (newVersionCode <= currentVersionCode) {
return
}
val uriHandler = LocalUriHandler.current
WarningCard(
message = stringResource(id = R.string.new_version_available).format(newVersionCode),
MaterialTheme.colorScheme.outlineVariant
) {
uriHandler.openUri(newVersionUrl)
}
}
@Composable
fun RebootDropdownItem(@StringRes id: Int, reason: String = "") {
DropdownMenuItem(text = {
@@ -84,43 +110,41 @@ fun RebootDropdownItem(@StringRes id: Int, reason: String = "") {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun TopBar(onSettingsClick: () -> Unit) {
TopAppBar(
title = { Text(stringResource(R.string.app_name)) },
actions = {
var showDropdown by remember { mutableStateOf(false) }
IconButton(onClick = {
showDropdown = true
TopAppBar(title = { Text(stringResource(R.string.app_name)) }, actions = {
var showDropdown by remember { mutableStateOf(false) }
IconButton(onClick = {
showDropdown = true
}) {
Icon(
imageVector = Icons.Filled.Refresh,
contentDescription = stringResource(id = R.string.reboot)
)
DropdownMenu(expanded = showDropdown, onDismissRequest = {
showDropdown = false
}) {
Icon(
imageVector = Icons.Filled.Refresh,
contentDescription = stringResource(id = R.string.reboot)
)
DropdownMenu(expanded = showDropdown, onDismissRequest = {
showDropdown = false
}) {
RebootDropdownItem(id = R.string.reboot)
RebootDropdownItem(id = R.string.reboot)
val pm = LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && pm?.isRebootingUserspaceSupported == true) {
RebootDropdownItem(id = R.string.reboot_userspace, reason = "userspace")
}
RebootDropdownItem(id = R.string.reboot_recovery, reason = "recovery")
RebootDropdownItem(id = R.string.reboot_bootloader, reason = "bootloader")
RebootDropdownItem(id = R.string.reboot_download, reason = "download")
RebootDropdownItem(id = R.string.reboot_edl, reason = "edl")
val pm =
LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && pm?.isRebootingUserspaceSupported == true) {
RebootDropdownItem(id = R.string.reboot_userspace, reason = "userspace")
}
}
IconButton(onClick = onSettingsClick) {
Icon(
imageVector = Icons.Filled.Settings,
contentDescription = stringResource(id = R.string.settings)
)
RebootDropdownItem(id = R.string.reboot_recovery, reason = "recovery")
RebootDropdownItem(id = R.string.reboot_bootloader, reason = "bootloader")
RebootDropdownItem(id = R.string.reboot_download, reason = "download")
RebootDropdownItem(id = R.string.reboot_edl, reason = "edl")
}
}
)
IconButton(onClick = onSettingsClick) {
Icon(
imageVector = Icons.Filled.Settings,
contentDescription = stringResource(id = R.string.settings)
)
}
})
}
@Composable
@@ -132,20 +156,17 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
})
) {
val uriHandler = LocalUriHandler.current
Row(
modifier = Modifier
.fillMaxWidth()
.clickable {
if (kernelVersion.isGKI() && ksuVersion == null) {
uriHandler.openUri("https://kernelsu.org/guide/installation.html")
}
Row(modifier = Modifier
.fillMaxWidth()
.clickable {
if (kernelVersion.isGKI() && ksuVersion == null) {
uriHandler.openUri("https://kernelsu.org/guide/installation.html")
}
.padding(24.dp),
verticalAlignment = Alignment.CenterVertically
) {
}
.padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
when {
ksuVersion != null -> {
val appendText = if (Natives.isSafeMode()) {
val appendText = if (Natives.isSafeMode) {
" [${stringResource(id = R.string.safe_mode)}]"
} else {
""
@@ -161,8 +182,20 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
text = stringResource(R.string.home_working_version, ksuVersion),
style = MaterialTheme.typography.bodyMedium
)
Spacer(Modifier.height(4.dp))
Text(
text = stringResource(
R.string.home_superuser_count, getSuperuserCount()
), style = MaterialTheme.typography.bodyMedium
)
Spacer(Modifier.height(4.dp))
Text(
text = stringResource(R.string.home_module_count, getModuleCount()),
style = MaterialTheme.typography.bodyMedium
)
}
}
kernelVersion.isGKI() -> {
Icon(Icons.Outlined.Warning, stringResource(R.string.home_not_installed))
Column(Modifier.padding(start = 20.dp)) {
@@ -177,6 +210,7 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
)
}
}
else -> {
Icon(Icons.Outlined.Block, stringResource(R.string.home_unsupported))
Column(Modifier.padding(start = 20.dp)) {
@@ -197,11 +231,90 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
}
}
@Composable
fun WarningCard(
message: String, color: Color = MaterialTheme.colorScheme.error, onClick: () -> Unit = {}
) {
ElevatedCard(
colors = CardDefaults.elevatedCardColors(
containerColor = color
)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(24.dp)
.clickable {
onClick()
}, 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
private fun InfoCard() {
val context = LocalContext.current
val snackbarHost = LocalSnackbarHost.current
val scope = rememberCoroutineScope()
ElevatedCard {
Column(
@@ -221,41 +334,25 @@ private fun InfoCard() {
InfoCardItem(stringResource(R.string.home_kernel), uname.release)
Spacer(Modifier.height(24.dp))
InfoCardItem(stringResource(R.string.home_manager_version), getManagerVersion(context))
Spacer(Modifier.height(16.dp))
val managerVersion = getManagerVersion(context)
InfoCardItem(
stringResource(R.string.home_manager_version),
"${managerVersion.first} (${managerVersion.second})"
)
Spacer(Modifier.height(24.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))
Spacer(Modifier.height(16.dp))
InfoCardItem(stringResource(R.string.home_fingerprint), Build.FINGERPRINT)
Spacer(Modifier.height(24.dp))
InfoCardItem(stringResource(R.string.home_securitypatch), Build.VERSION.SECURITY_PATCH)
Spacer(Modifier.height(24.dp))
Spacer(Modifier.height(16.dp))
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): Pair<String, Int> {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
return "${packageInfo.versionName} (${packageInfo.versionCode})"
return Pair(packageInfo.versionName, packageInfo.versionCode)
}
@Preview

View File

@@ -13,7 +13,6 @@ import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Save
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
@@ -24,7 +23,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.weishu.kernelsu.R
import me.weishu.kernelsu.ksuApp
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
import me.weishu.kernelsu.ui.util.installModule
import me.weishu.kernelsu.ui.util.reboot
@@ -36,13 +34,12 @@ import java.util.*
* @author weishu
* @date 2023/1/1.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@Destination
fun InstallScreen(navigator: DestinationsNavigator, uri: Uri) {
var text by rememberSaveable { mutableStateOf("") }
var showFloatAction by rememberSaveable { mutableStateOf(false) }
var text by remember { mutableStateOf("") }
var showFloatAction by remember { mutableStateOf(false) }
val snackBarHost = LocalSnackbarHost.current
val scope = rememberCoroutineScope()
@@ -73,7 +70,7 @@ fun InstallScreen(navigator: DestinationsNavigator, uri: Uri) {
val format = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault())
val date = format.format(Date())
val file = File(
ksuApp.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
"KernelSU_install_log_${date}.log"
)
file.writeText(text)

View File

@@ -2,12 +2,15 @@ package me.weishu.kernelsu.ui.screen
import android.app.Activity.RESULT_OK
import android.content.Intent
import android.net.Uri
import android.util.Log
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
@@ -16,9 +19,9 @@ import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
@@ -34,12 +37,11 @@ import kotlinx.coroutines.launch
import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.component.ConfirmDialog
import me.weishu.kernelsu.ui.component.DialogResult
import me.weishu.kernelsu.ui.component.ConfirmResult
import me.weishu.kernelsu.ui.screen.destinations.InstallScreenDestination
import me.weishu.kernelsu.ui.util.*
import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Destination
@Composable
fun ModuleScreen(navigator: DestinationsNavigator) {
@@ -51,11 +53,10 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
}
}
val isSafeMode = Natives.isSafeMode()
val isKSUVersionInvalid = Natives.getVersion() < 0
val isSafeMode = Natives.isSafeMode
val hasMagisk = hasMagisk()
val hideInstallButton = isSafeMode || isKSUVersionInvalid || hasMagisk
val hideInstallButton = isSafeMode || hasMagisk
Scaffold(topBar = {
TopBar()
@@ -94,11 +95,6 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
ConfirmDialog()
when {
isKSUVersionInvalid -> {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(stringResource(R.string.require_kernel_version_8))
}
}
hasMagisk -> {
Box(
modifier = Modifier
@@ -112,13 +108,15 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
)
}
}
else -> {
ModuleList(
viewModel = viewModel,
modifier = Modifier
viewModel = viewModel, modifier = Modifier
.padding(innerPadding)
.fillMaxSize()
)
) {
navigator.navigate(InstallScreenDestination(it))
}
}
}
}
@@ -126,7 +124,9 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun ModuleList(viewModel: ModuleViewModel, modifier: Modifier = Modifier) {
private fun ModuleList(
viewModel: ModuleViewModel, modifier: Modifier = Modifier, onInstallModule: (Uri) -> Unit
) {
val failedEnable = stringResource(R.string.module_failed_to_enable)
val failedDisable = stringResource(R.string.module_failed_to_disable)
val failedUninstall = stringResource(R.string.module_uninstall_failed)
@@ -136,20 +136,19 @@ private fun ModuleList(viewModel: ModuleViewModel, modifier: Modifier = Modifier
val moduleStr = stringResource(id = R.string.module)
val uninstall = stringResource(id = R.string.uninstall)
val cancel = stringResource(id = android.R.string.cancel)
val moduleUninstallConfirm =
stringResource(id = R.string.module_uninstall_confirm)
val moduleUninstallConfirm = stringResource(id = R.string.module_uninstall_confirm)
val dialogHost = LocalDialogHost.current
val snackBarHost = LocalSnackbarHost.current
suspend fun onModuleUninstall(module: ModuleViewModel.ModuleInfo) {
val dialogResult = dialogHost.showDialog(
val confirmResult = dialogHost.showConfirm(
moduleStr,
content = moduleUninstallConfirm.format(module.name),
confirm = uninstall,
dismiss = cancel
)
if (dialogResult != DialogResult.Confirmed) {
if (confirmResult != ConfirmResult.Confirmed) {
return
}
@@ -173,12 +172,12 @@ private fun ModuleList(viewModel: ModuleViewModel, modifier: Modifier = Modifier
}
}
val refreshState = rememberPullRefreshState(
refreshing = viewModel.isRefreshing,
onRefresh = { viewModel.fetchModuleList() }
)
val refreshState = rememberPullRefreshState(refreshing = viewModel.isRefreshing,
onRefresh = { viewModel.fetchModuleList() })
Box(modifier.pullRefresh(refreshState)) {
if (viewModel.isOverlayAvailable) {
val context = LocalContext.current
LazyColumn(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(16.dp),
@@ -187,8 +186,7 @@ private fun ModuleList(viewModel: ModuleViewModel, modifier: Modifier = Modifier
start = 16.dp,
top = 16.dp,
end = 16.dp,
bottom = 16.dp
+ 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */
bottom = 16.dp + 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */
)
},
) {
@@ -196,17 +194,23 @@ private fun ModuleList(viewModel: ModuleViewModel, modifier: Modifier = Modifier
if (isEmpty) {
item {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center
) {
Text(stringResource(R.string.module_empty))
}
}
} else {
items(viewModel.moduleList) { module ->
var isChecked by rememberSaveable(module) { mutableStateOf(module.enabled) }
var isChecked by remember(module) { mutableStateOf(module.enabled) }
val scope = rememberCoroutineScope()
ModuleItem(module, isChecked, onUninstall = {
val updateUrl by produceState(initialValue = "") {
viewModel.checkUpdate(module) { value = it.orEmpty() }
}
val downloadingText = stringResource(R.string.module_downloading)
val startDownloadingText = stringResource(R.string.module_start_downloading)
ModuleItem(module, isChecked, updateUrl, onUninstall = {
scope.launch { onModuleUninstall(module) }
}, onCheckChanged = {
val success = toggleModule(module.id, !isChecked)
@@ -226,12 +230,37 @@ private fun ModuleList(viewModel: ModuleViewModel, modifier: Modifier = Modifier
val message = if (isChecked) failedDisable else failedEnable
snackBarHost.showSnackbar(message.format(module.name))
}
}, onUpdate = {
scope.launch {
Toast.makeText(
context,
startDownloadingText.format(module.name),
Toast.LENGTH_SHORT
).show()
}
val downloading = downloadingText.format(module.name)
download(
context,
updateUrl,
"${module.name}-${module.version}.zip",
downloading,
onDownloaded = onInstallModule,
onDownloading = {
Toast.makeText(context, downloading, Toast.LENGTH_SHORT).show()
}
)
})
// fix last item shadow incomplete in LazyColumn
Spacer(Modifier.height(1.dp))
}
}
}
DownloadListener(context, onInstallModule)
} else {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(stringResource(R.string.module_overlay_fs_not_available))
@@ -239,9 +268,7 @@ private fun ModuleList(viewModel: ModuleViewModel, modifier: Modifier = Modifier
}
PullRefreshIndicator(
refreshing = viewModel.isRefreshing,
state = refreshState,
modifier = Modifier.align(
refreshing = viewModel.isRefreshing, state = refreshState, modifier = Modifier.align(
Alignment.TopCenter
)
)
@@ -258,8 +285,10 @@ private fun TopBar() {
private fun ModuleItem(
module: ModuleViewModel.ModuleInfo,
isChecked: Boolean,
updateUrl: String,
onUninstall: (ModuleViewModel.ModuleInfo) -> Unit,
onCheckChanged: (Boolean) -> Unit
onCheckChanged: (Boolean) -> Unit,
onUpdate: (ModuleViewModel.ModuleInfo) -> Unit,
) {
ElevatedCard(
modifier = Modifier.fillMaxWidth(),
@@ -341,6 +370,23 @@ private fun ModuleItem(
) {
Spacer(modifier = Modifier.weight(1f, true))
if (updateUrl.isNotEmpty()) {
Button(
modifier = Modifier
.padding(0.dp)
.defaultMinSize(52.dp, 32.dp),
onClick = { onUpdate(module) },
shape = RoundedCornerShape(6.dp),
contentPadding = PaddingValues(0.dp)
) {
Text(
fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
fontSize = MaterialTheme.typography.labelMedium.fontSize,
text = stringResource(R.string.module_update),
)
}
}
TextButton(
enabled = !module.remove,
onClick = { onUninstall(module) },
@@ -369,6 +415,7 @@ fun ModuleItemPreview() {
enabled = true,
update = true,
remove = true,
updateJson = ""
)
ModuleItem(module, true, {}, {})
ModuleItem(module, true, "", {}, {}, {})
}

View File

@@ -2,33 +2,38 @@ package me.weishu.kernelsu.ui.screen
import android.content.Intent
import android.net.Uri
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.BugReport
import androidx.compose.material.icons.filled.ContactPage
import androidx.compose.material.icons.filled.RemoveModerator
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.core.content.FileProvider
import com.alorma.compose.settings.ui.*
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.weishu.kernelsu.BuildConfig
import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.component.SimpleDialog
import me.weishu.kernelsu.ui.util.LinkifyText
import me.weishu.kernelsu.ui.component.AboutDialog
import me.weishu.kernelsu.ui.component.LoadingDialog
import me.weishu.kernelsu.ui.component.SwitchItem
import me.weishu.kernelsu.ui.util.LocalDialogHost
import me.weishu.kernelsu.ui.util.getBugreportFile
/**
* @author weishu
* @date 2023/1/1.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Destination
@Composable
fun SettingScreen(navigator: DestinationsNavigator) {
@@ -40,46 +45,70 @@ fun SettingScreen(navigator: DestinationsNavigator) {
})
}
) { paddingValues ->
LoadingDialog()
SimpleDialog {
SupportCard()
}
val showAboutDialog = remember { mutableStateOf(false) }
AboutDialog(showAboutDialog)
Column(modifier = Modifier.padding(paddingValues)) {
val context = LocalContext.current
SettingsMenuLink(title = {
Text(stringResource(id = R.string.send_log))
},
onClick = {
val bugreport = getBugreportFile(context)
val uri: Uri = FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.fileprovider", bugreport)
val scope = rememberCoroutineScope()
val dialogHost = LocalDialogHost.current
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)
var umountChecked by rememberSaveable {
mutableStateOf(Natives.isDefaultUmountModules())
}
SwitchItem(
icon = Icons.Filled.RemoveModerator,
title = stringResource(id = R.string.settings_umount_modules_default),
summary = stringResource(id = R.string.settings_umount_modules_default_summary),
checked = umountChecked
) {
if (Natives.setDefaultUmountModules(it)) {
umountChecked = it
}
}
context.startActivity(
Intent.createChooser(
shareIntent,
context.getString(R.string.send_log)
ListItem(
leadingContent = { Icon(Icons.Filled.BugReport, stringResource(id = R.string.send_log)) },
headlineContent = { Text(stringResource(id = R.string.send_log)) },
modifier = Modifier.clickable {
scope.launch {
val bugreport = dialogHost.withLoading {
withContext(Dispatchers.IO) {
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 ok = stringResource(id = android.R.string.ok)
val scope = rememberCoroutineScope()
val dialogHost = LocalDialogHost.current
SettingsMenuLink(title = {
Text(about)
},
onClick = {
scope.launch {
dialogHost.showDialog(about, content = "unused", confirm = ok)
}
ListItem(
leadingContent = { Icon(Icons.Filled.ContactPage, stringResource(id = R.string.about)) },
headlineContent = { Text(about) },
modifier = Modifier.clickable {
showAboutDialog.value = true
}
)
}
@@ -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
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
@@ -11,30 +14,32 @@ import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.AsyncImage
import coil.request.ImageRequest
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.launch
import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.component.ConfirmDialog
import me.weishu.kernelsu.ui.component.SearchAppBar
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
import me.weishu.kernelsu.ui.screen.destinations.AppProfileScreenDestination
import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel
import java.util.*
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
@OptIn(ExperimentalMaterialApi::class)
@Destination
@Composable
fun SuperUserScreen() {
fun SuperUserScreen(navigator: DestinationsNavigator) {
val viewModel = viewModel<SuperUserViewModel>()
val snackbarHost = LocalSnackbarHost.current
val scope = rememberCoroutineScope()
LaunchedEffect(Unit) {
@@ -91,6 +96,8 @@ fun SuperUserScreen() {
}
) { innerPadding ->
ConfirmDialog()
val refreshState = rememberPullRefreshState(
refreshing = viewModel.isRefreshing,
onRefresh = { scope.launch { viewModel.fetchAppList() } },
@@ -100,19 +107,12 @@ fun SuperUserScreen() {
.padding(innerPadding)
.pullRefresh(refreshState)
) {
val failMessage = stringResource(R.string.superuser_failed_to_grant_root)
LazyColumn(Modifier.fillMaxSize()) {
items(viewModel.appList, key = { it.packageName }) { app ->
var isChecked by rememberSaveable(app) { mutableStateOf(app.onAllowList) }
AppItem(app, isChecked) { checked ->
val success = Natives.allowRoot(app.uid, checked)
if (success) {
isChecked = checked
} else scope.launch {
snackbarHost.showSnackbar(failMessage.format(app.uid))
}
items(viewModel.appList, key = { it.packageName + it.uid }) { app ->
AppItem(app) {
navigator.navigate(AppProfileScreenDestination(app))
}
}
}
@@ -125,20 +125,36 @@ fun SuperUserScreen() {
}
}
@OptIn(ExperimentalMaterial3Api::class)
@OptIn(ExperimentalLayoutApi::class)
@Composable
private fun AppItem(
app: SuperUserViewModel.AppInfo,
isChecked: Boolean,
onCheckedChange: (Boolean) -> Unit
onClickListener: () -> Unit,
) {
ListItem(
headlineText = { Text(app.label) },
supportingText = { Text(app.packageName) },
modifier = Modifier.clickable(onClick = onClickListener),
headlineContent = { Text(app.label) },
supportingContent = {
Column {
Text(app.packageName)
FlowRow {
if (app.allowSu) {
LabelText(label = "ROOT")
} else {
if (Natives.uidShouldUmount(app.uid)) {
LabelText(label = "UMOUNT")
}
}
if (app.hasCustomProfile) {
LabelText(label = "CUSTOM")
}
}
}
},
leadingContent = {
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(app.icon)
.data(app.packageInfo)
.crossfade(true)
.build(),
contentDescription = app.label,
@@ -148,12 +164,26 @@ private fun AppItem(
.height(48.dp)
)
},
trailingContent = {
Switch(
checked = isChecked,
onCheckedChange = onCheckedChange,
modifier = Modifier.padding(4.dp)
)
}
)
}
@Composable
fun LabelText(label: String) {
Box(
modifier = Modifier
.padding(top = 4.dp, end = 4.dp)
.background(
Color.Black,
shape = RoundedCornerShape(4.dp)
)
) {
Text(
text = label,
modifier = Modifier.padding(vertical = 2.dp, horizontal = 5.dp),
style = TextStyle(
fontSize = 8.sp,
color = Color.White,
)
)
}
}

View File

@@ -0,0 +1,133 @@
package me.weishu.kernelsu.ui.util
import android.annotation.SuppressLint
import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.Uri
import android.os.Environment
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
/**
* @author weishu
* @date 2023/6/22.
*/
@SuppressLint("Range")
fun download(
context: Context,
url: String,
fileName: String,
description: String,
onDownloaded: (Uri) -> Unit = {},
onDownloading: () -> Unit = {}
) {
val downloadManager =
context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
val query = DownloadManager.Query()
query.setFilterByStatus(DownloadManager.STATUS_RUNNING or DownloadManager.STATUS_PAUSED or DownloadManager.STATUS_PENDING)
downloadManager.query(query).use { cursor ->
while (cursor.moveToNext()) {
val uri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_URI))
val localUri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI))
val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
val columnTitle = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TITLE))
if (url == uri || fileName == columnTitle) {
if (status == DownloadManager.STATUS_RUNNING || status == DownloadManager.STATUS_PENDING) {
onDownloading()
return
} else if (status == DownloadManager.STATUS_SUCCESSFUL) {
onDownloaded(Uri.parse(localUri))
return
}
}
}
}
val request = DownloadManager.Request(Uri.parse(url))
.setDestinationInExternalPublicDir(
Environment.DIRECTORY_DOWNLOADS,
fileName
)
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
.setMimeType("application/zip")
.setTitle(fileName)
.setDescription(description)
downloadManager.enqueue(request)
}
fun checkNewVersion(): Pair<Int, String> {
val url = "https://api.github.com/repos/tiann/KernelSU/releases/latest"
val defaultValue = 0 to ""
runCatching {
okhttp3.OkHttpClient().newCall(okhttp3.Request.Builder().url(url).build()).execute()
.use { response ->
if (!response.isSuccessful) {
return defaultValue
}
val body = response.body?.string() ?: return defaultValue
val json = org.json.JSONObject(body)
val assets = json.getJSONArray("assets")
for (i in 0 until assets.length()) {
val asset = assets.getJSONObject(i)
val name = asset.getString("name")
if (!name.endsWith(".apk")) {
continue
}
val regex = Regex("v(.+?)_(\\d+)-")
val matchResult = regex.find(name) ?: continue
val versionName = matchResult.groupValues[1]
val versionCode = matchResult.groupValues[2].toInt()
val downloadUrl = asset.getString("browser_download_url")
return versionCode to downloadUrl
}
}
}
return defaultValue
}
@Composable
fun DownloadListener(context: Context, onDownloaded: (Uri) -> Unit) {
DisposableEffect(context) {
val receiver = object : BroadcastReceiver() {
@SuppressLint("Range")
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == DownloadManager.ACTION_DOWNLOAD_COMPLETE) {
val id = intent.getLongExtra(
DownloadManager.EXTRA_DOWNLOAD_ID, -1
)
val query = DownloadManager.Query().setFilterById(id)
val downloadManager =
context?.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
val cursor = downloadManager.query(query)
if (cursor.moveToFirst()) {
val status = cursor.getInt(
cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)
)
if (status == DownloadManager.STATUS_SUCCESSFUL) {
val uri = cursor.getString(
cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)
)
onDownloaded(Uri.parse(uri))
}
}
}
}
}
context.registerReceiver(
receiver,
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
)
onDispose {
context.unregisterReceiver(receiver)
}
}
}

View File

@@ -7,7 +7,10 @@ import com.topjohnwu.superuser.CallbackList
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.ShellUtils
import me.weishu.kernelsu.BuildConfig
import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.ksuApp
import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel
import org.json.JSONArray
import java.io.File
@@ -21,6 +24,14 @@ private fun getKsuDaemonPath(): String {
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libksud.so"
}
object KsuCli {
val SHELL: Shell = createRootShell()
}
fun getRootShell(): Shell {
return KsuCli.SHELL
}
fun createRootShell(): Shell {
Shell.enableVerboseLogging = BuildConfig.DEBUG
val builder = Shell.Builder.create()
@@ -33,7 +44,7 @@ fun createRootShell(): Shell {
}
fun execKsud(args: String): Boolean {
val shell = createRootShell()
val shell = getRootShell()
return ShellUtils.fastCmdResult(shell, "${getKsuDaemonPath()} $args")
}
@@ -44,12 +55,25 @@ fun install() {
}
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 { "[]" }
}
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 {
val cmd = if (enable) {
"module enable $id"
@@ -61,14 +85,14 @@ fun toggleModule(id: String, enable: Boolean): Boolean {
return result
}
fun uninstallModule(id: String) : Boolean {
fun uninstallModule(id: String): Boolean {
val cmd = "module uninstall $id"
val result = execKsud(cmd)
Log.i(TAG, "uninstall module $id result: $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
with(resolver.openInputStream(uri)) {
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 shell = createRootShell()
val shell = getRootShell()
val callbackList: CallbackList<String?> = object : CallbackList<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")
file.delete()
@@ -96,7 +121,7 @@ fun installModule(uri: Uri, onFinish: (Boolean)->Unit, onOutput: (String) -> Uni
}
fun reboot(reason: String = "") {
val shell = createRootShell()
val shell = getRootShell()
if (reason == "recovery") {
// KEYCODE_POWER = 26, hide incorrect "Factory data reset" message
ShellUtils.fastCmd(shell, "/system/bin/input keyevent 26")
@@ -105,14 +130,24 @@ fun reboot(reason: String = "") {
}
fun overlayFsAvailable(): Boolean {
val shell = createRootShell()
val shell = getRootShell()
// check /proc/filesystems
return ShellUtils.fastCmdResult(shell, "cat /proc/filesystems | grep overlay")
}
fun hasMagisk(): Boolean {
val shell = createRootShell()
val shell = getRootShell()
val result = shell.newJob().add("nsenter --mount=/proc/1/ns/mnt which magisk").exec()
Log.i(TAG, "has magisk: ${result.isSuccess}")
return result.isSuccess
}
fun isSepolicyValid(rules: String?): Boolean {
if (rules == null) {
return true
}
val shell = getRootShell()
val result =
shell.newJob().add("ksud sepolicy check '$rules'").to(ArrayList(), null).exec()
return result.isSuccess
}

View File

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

View File

@@ -1,5 +1,6 @@
package me.weishu.kernelsu.ui.viewmodel
import android.net.Uri
import android.os.SystemClock
import android.util.Log
import androidx.compose.runtime.derivedStateOf
@@ -13,6 +14,7 @@ import kotlinx.coroutines.launch
import me.weishu.kernelsu.ui.util.listModules
import me.weishu.kernelsu.ui.util.overlayFsAvailable
import org.json.JSONArray
import org.json.JSONObject
import java.text.Collator
import java.util.*
@@ -33,6 +35,14 @@ class ModuleViewModel : ViewModel() {
val enabled: Boolean,
val update: Boolean,
val remove: Boolean,
val updateJson: String,
)
data class ModuleUpdateInfo(
val version: String,
val versionCode: Int,
val zipUrl: String,
val changelog: String,
)
var isRefreshing by mutableStateOf(false)
@@ -78,6 +88,7 @@ class ModuleViewModel : ViewModel() {
obj.getBoolean("enabled"),
obj.getBoolean("update"),
obj.getBoolean("remove"),
obj.optString("updateJson", "")
)
}.toList()
}.onFailure { e ->
@@ -94,4 +105,57 @@ class ModuleViewModel : ViewModel() {
Log.i(TAG, "load cost: ${SystemClock.elapsedRealtime() - start}, modules: $modules")
}
}
fun checkUpdate(m: ModuleInfo, callback: (String?) -> Unit) {
if (m.updateJson.isEmpty()) {
callback(null)
return
}
viewModelScope.launch(Dispatchers.IO) {
// download updateJson
val result = kotlin.runCatching {
val url = m.updateJson
Log.i(TAG, "checkUpdate url: $url")
val response = okhttp3.OkHttpClient()
.newCall(
okhttp3.Request.Builder()
.url(url)
.build()
).execute()
Log.d(TAG, "checkUpdate code: ${response.code}")
if (response.isSuccessful) {
response.body?.string() ?: ""
} else {
""
}
}.getOrDefault("")
Log.i(TAG, "checkUpdate result: $result")
if (result.isEmpty()) {
callback(null)
return@launch
}
val updateJson = kotlin.runCatching {
JSONObject(result)
}.getOrNull()
if (updateJson == null) {
callback(null)
return@launch
}
val version = updateJson.optString("version", "")
val versionCode = updateJson.optInt("versionCode", 0)
val zipUrl = updateJson.optString("zipUrl", "")
val changelog = updateJson.optString("changelog", "")
if (versionCode <= m.versionCode || zipUrl.isEmpty()) {
callback(null)
return@launch
}
callback(zipUrl)
}
}
}

View File

@@ -1,8 +1,12 @@
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.PackageInfo
import android.graphics.drawable.Drawable
import android.os.IBinder
import android.os.Parcelable
import android.os.SystemClock
import android.util.Log
import androidx.compose.runtime.derivedStateOf
@@ -10,13 +14,20 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
import me.weishu.kernelsu.IKsuInterface
import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.ksuApp
import me.weishu.kernelsu.ui.KsuService
import me.weishu.kernelsu.ui.util.HanziToPinyin
import me.weishu.kernelsu.ui.util.KsuCli
import java.text.Collator
import java.util.*
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
class SuperUserViewModel : ViewModel() {
@@ -25,14 +36,32 @@ class SuperUserViewModel : ViewModel() {
private var apps by mutableStateOf<List<AppInfo>>(emptyList())
}
class AppInfo(
@Parcelize
data class AppInfo(
val label: String,
val packageName: String,
val icon: PackageInfo,
val uid: Int,
val onAllowList: Boolean,
val onDenyList: Boolean
)
val packageInfo: PackageInfo,
val profile: Natives.Profile?,
) : Parcelable {
val packageName: String
get() = packageInfo.packageName
val uid: Int
get() = packageInfo.applicationInfo.uid
val allowSu: Boolean
get() = profile != null && profile.allowSu
val hasCustomProfile: Boolean
get() {
if (profile == null) {
return false
}
return if (profile.allowSu) {
!profile.rootUseDefault
} else {
!profile.nonRootUseDefault
}
}
}
var search by mutableStateOf("")
var showSystemApps by mutableStateOf(false)
@@ -42,8 +71,8 @@ class SuperUserViewModel : ViewModel() {
private val sortedList by derivedStateOf {
val comparator = compareBy<AppInfo> {
when {
it.onAllowList -> 0
it.onDenyList -> 1
it.allowSu -> 0
it.hasCustomProfile -> 1
else -> 2
}
}.then(compareBy(Collator.getInstance(Locale.getDefault()), AppInfo::label))
@@ -58,29 +87,68 @@ class SuperUserViewModel : ViewModel() {
.toPinyinString(it.label).contains(search)
}.filter {
it.uid == 2000 // Always show shell
|| showSystemApps || it.icon.applicationInfo.flags.and(ApplicationInfo.FLAG_SYSTEM) == 0
|| showSystemApps || it.packageInfo.applicationInfo.flags.and(ApplicationInfo.FLAG_SYSTEM) == 0
}
}
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() {
isRefreshing = true
val result = connectKsuService {
Log.w(TAG, "KsuService disconnected")
}
withContext(Dispatchers.IO) {
isRefreshing = true
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()
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 uid = appInfo.uid
val profile = Natives.getAppProfile(it.packageName, uid)
AppInfo(
label = appInfo.loadLabel(pm).toString(),
packageName = it.packageName,
icon = it,
uid = uid,
onAllowList = uid in allowList,
onDenyList = uid in denyList
packageInfo = it,
profile = profile,
)
}.filter { it.packageName != ksuApp.packageName }
Log.i(TAG, "load cost: ${SystemClock.elapsedRealtime() - start}")

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,77 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="home">Beranda</string>
<string name="home_not_installed">Tidak terinstall</string>
<string name="home_click_to_install">Klik untuk menginstall</string>
<string name="home_working">Bekerja</string>
<string name="home_working_version">Versi: %d</string>
<string name="home_unsupported">Tidak didukung</string>
<string name="home_unsupported_reason">Saat ini kernelSu hanya mendukung GKI kernel</string>
<string name="home_copied_to_clipboard">Salin ke clipboard</string>
<string name="home_support">Dukungan</string>
<string name="home_kernel">Kernel</string>
<string name="home_arch">Arch</string>
<string name="home_manager_version">Versi manager</string>
<string name="home_api">API Level</string>
<string name="home_abi">ABI</string>
<string name="home_fingerprint">Fingerprint</string>
<string name="home_securitypatch">Patch keamanan</string>
<string name="home_selinux_status">Status SElinux</string>
<string name="selinux_status_disabled">Cacat</string>
<string name="selinux_status_enforcing">Enforcing</string>
<string name="selinux_status_permissive">Permissive</string>
<string name="selinux_status_unknown">Tidak tersedia</string>
<string name="superuser">Superuser</string>
<string name="superuser_failed_to_grant_root">Gagal mengizinkan root untuk %d</string>
<string name="module_failed_to_enable">Gagal mengaktifkan module: %s</string>
<string name="module_failed_to_disable">Gagal menonaktifkan module: %s</string>
<string name="module_empty">Tidak ada module terpasang</string>
<string name="module">Module</string>
<string name="uninstall">Hapus</string>
<string name="module_install">Memasang module</string>
<string name="install">Pasang</string>
<string name="reboot">Reboot perangkat</string>
<string name="settings">Pengaturan</string>
<string name="reboot_userspace">Soft Reboot</string>
<string name="reboot_recovery">Reboot ke Recovery</string>
<string name="reboot_bootloader">Reboot ke Bootloader</string>
<string name="reboot_download">Reboot ke Download</string>
<string name="reboot_edl">Reboot ke EDL</string>
<string name="about">Tentang</string>
<string name="require_kernel_version_8">Membutuhkan KernelSU Versi 8+</string>
<string name="module_uninstall_confirm">Apakah anda yakin ingin menghapus module ini %s?</string>
<string name="module_uninstall_success">%s terhapus</string>
<string name="module_uninstall_failed">Gagal untuk menghapus: %s</string>
<string name="module_version">Versi</string>
<string name="module_author">Pembuat</string>
<string name="module_overlay_fs_not_available">overlayfs tidak tersedia, module tidak bekerja!</string>
<string name="refresh">Segarkan</string>
<string name="show_system_apps">Tampilkan system apps</string>
<string name="hide_system_apps">Sembunyikan system apps</string>
<string name="send_log">Kirim logs</string>
<string name="safe_mode">Mode aman</string>
<string name="reboot_to_apply">Restart untuk menerapkan</string>
<string name="module_magisk_conflict">Module akan di nonaktifkan karna konflik dengan magisk\'s!</string>
</resources>
<resources>
<string name="app_name" translatable="false">KernelSU</string>
<string name="home">Beranda</string>
<string name="home_not_installed">Tidak terpasang</string>
<string name="home_click_to_install">Klik untuk memasang</string>
<string name="home_working">Bekerja</string>
<string name="home_working_version">Versi: %d</string>
<string name="home_superuser_count">Superusers: %d</string>
<string name="home_module_count">Modul: %d</string>
<string name="home_unsupported">Tidak didukung</string>
<string name="home_unsupported_reason">KernelSU hanya mendukung kernel GKI saat ini</string>
<string name="home_kernel">Kernel</string>
<string name="home_manager_version">Versi Manager</string>
<string name="home_fingerprint">Sidik jari</string>
<string name="home_selinux_status">status SELinux</string>
<string name="selinux_status_disabled">Dinonaktifkan</string>
<string name="selinux_status_enforcing">Enforcing</string>
<string name="selinux_status_permissive">Permissive</string>
<string name="selinux_status_unknown">Tidak dikenal</string>
<string name="superuser">Superuser</string>
<string name="module_failed_to_enable">Gagal mengaktifkan modul: %s</string>
<string name="module_failed_to_disable">Gagal menonaktifkan modul: %s</string>
<string name="module_empty">Tidak ada modul terpasang</string>
<string name="module">Modul</string>
<string name="uninstall">Copot</string>
<string name="module_install">Pasang</string>
<string name="install">Pasang</string>
<string name="reboot">Reboot</string>
<string name="settings">Pengaturan</string>
<string name="reboot_userspace">Reboot Lembut</string>
<string name="reboot_recovery">Reboot ke Recovery</string>
<string name="reboot_bootloader">Reboot ke Bootloader</string>
<string name="reboot_download">Reboot ke Download</string>
<string name="reboot_edl">Reboot ke EDL</string>
<string name="about">Tentang</string>
<string name="module_uninstall_confirm">Apakah Anda yakin ingin mencopot modul %s?</string>
<string name="module_uninstall_success">%s Tercopot</string>
<string name="module_uninstall_failed">Gagal untuk mencopot: %s</string>
<string name="module_version">Versi</string>
<string name="module_author">Pembuat</string>
<string name="module_overlay_fs_not_available">overlayfs tidak tersedia, modul tidak dapat bekerja!</string>
<string name="refresh">Segarkan</string>
<string name="show_system_apps">Tampilkan apl sistem</string>
<string name="hide_system_apps">Sembunyikan apl sistem</string>
<string name="send_log">Kirim Log</string>
<string name="safe_mode">Mode aman</string>
<string name="reboot_to_apply">Reboot untuk menerapkan</string>
<string name="module_magisk_conflict">Modul dinonaktifkan karena bertentangan dengan Magisk!</string>
<string name="home_learn_kernelsu">Pelajari KernelSU</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">Pelajari cara memasang KernelSU dan menggunakan modul</string>
<string name="home_support_title">Dukung Kami</string>
<string name="home_support_content">KernelSU gratis dan bersumber terbuka, dan akan selalu seperti itu. Bagaimanapun juga Anda dapat menunjukan kepedulian Anda kepada kami dengan mengirimkan sedikit donasi.</string>
<string name="about_source_code"><![CDATA[Lihat sumber code di %1$s<br/>Gabung kanal %2$s kami]]></string>
<string name="profile">Profil Apl</string>
<string name="profile_default">Bawaan</string>
<string name="profile_template">Templat</string>
<string name="profile_custom">Khusus</string>
<string name="profile_name">Nama profil</string>
<string name="profile_namespace">Ikat ruang-nama</string>
<string name="profile_namespace_inherited">Diwariskan</string>
<string name="profile_namespace_global">Universal</string>
<string name="profile_namespace_individual">Personal</string>
<string name="profile_groups">Kelompok</string>
<string name="profile_capabilities">Kemampuan</string>
<string name="profile_selinux_context">Konteks SELinux</string>
<string name="profile_umount_modules">Lepas modul</string>
<string name="failed_to_update_app_profile">Gagal memperbarui Profil Apl untuk %s</string>
<string name="require_kernel_version">Versi kernel saat ini %d terlalu rendah bagi manager untuk berfungsi dengan baik. Tolong tingkatkan ke versi %d atau lebih tinggi!</string>
<string name="settings_umount_modules_default">Lepas modul secara bawaan</string>
<string name="settings_umount_modules_default_summary">Nilai bawaan universal untuk \"Lepas modul\" di Profil-profil Apl. Jika diaktifkan, ini akan menghapus semua modifikasi modul pada sistem untuk aplikasi yang tidak memiliki set Profil.</string>
<string name="profile_umount_modules_summary">Mengaktifkan opsi ini akan mengizinkan KernelSU memulihkan file-file yang dimodifikasi oleh modul untuk aplikasi ini.</string>
</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>
<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_unsupported">非対応</string>
<string name="home_unsupported_reason">KernelSUは現在、GKIカーネルのみサポートをしています</string>
<string name="home_copied_to_clipboard">クリップボードにコピーしました</string>
<string name="home_support">対応</string>
<string name="home_kernel">カーネル</string>
<string name="home_arch">アーキテクチャ</string>
<string name="home_manager_version">バージョン</string>
<string name="home_api">APIレベル</string>
<string name="home_abi">ABI</string>
<string name="home_fingerprint">Fingerprint</string>
<string name="home_securitypatch">セキュリティパッチ</string>
<string name="home_selinux_status">SELinuxの状態</string>
<string name="selinux_status_disabled">無効</string>
<string name="selinux_status_enforcing">Enforcing</string>
<string name="selinux_status_permissive">Permissive</string>
<string name="selinux_status_unknown">不明</string>
<string name="superuser">スーパーユーザー</string>
<string name="superuser_failed_to_grant_root">%dの権限の付与に失敗しました</string>
<string name="module_failed_to_enable">モジュールの有効化に失敗: %s</string>
<string name="module_failed_to_disable">モジュールの無効化に失敗: %s</string>
<string name="module_empty">モジュールはインストールされていません</string>
<string name="module">モジュール</string>
<string name="uninstall">アンインストール</string>
<string name="module_install">インストール</string>
<string name="install">インストール</string>
<string name="reboot">再起動</string>
<string name="settings">設定</string>
<string name="reboot_userspace">ソフトリブート</string>
<string name="reboot_recovery">リカバリーで再起動</string>
<string name="reboot_bootloader">Bootloaderで再起動</string>
<string name="reboot_download">ダウンロードモードで再起動</string>
<string name="reboot_edl">EDLで再起動</string>
<string name="about">アプリについて</string>
<string name="require_kernel_version_8">KernelSU バージョン8以降が必要です</string>
<string name="module_uninstall_success">%sをアンインストールしました</string>
<string name="module_uninstall_failed">アンインストールに失敗: %s</string>
<string name="module_version">バージョン</string>
<string name="module_author">作者</string>
<string name="module_overlay_fs_not_available">OverlayFSが有効でないためモジュールは動作しません</string>
<string name="refresh">更新</string>
<string name="show_system_apps">システムアプリを表示</string>
<string name="hide_system_apps">システムアプリを非表示</string>
<string name="send_log">ログを送信</string>
<string name="safe_mode">セーフモード</string>
<string name="reboot_to_apply">再起動をして有効化する</string>
<string name="home">ホーム</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">Enforcing</string>
<string name="selinux_status_permissive">Permissive</string>
<string name="selinux_status_unknown">不明</string>
<string name="superuser">スーパーユーザー</string>
<string name="module_failed_to_enable">モジュールの有効化に失敗: %s</string>
<string name="module_failed_to_disable">モジュールの無効化に失敗: %s</string>
<string name="module_empty">モジュールをインストールしていません</string>
<string name="module">モジュール</string>
<string name="uninstall">アンインストール</string>
<string name="module_install">インストール</string>
<string name="install">インストール</string>
<string name="reboot">再起動</string>
<string name="settings">設定</string>
<string name="reboot_userspace">ソフトリブート</string>
<string name="reboot_recovery">リカバリーへ再起動</string>
<string name="reboot_bootloader">Bootloader へ再起動</string>
<string name="reboot_download">ダウンロードモードへ再起動</string>
<string name="reboot_edl">EDLへ再起動</string>
<string name="about">アプリについて</string>
<string name="module_uninstall_confirm">モジュール %s をアンインストールしますか?</string>
<string name="module_uninstall_success">%sをアンインストールしました</string>
<string name="module_uninstall_failed">アンインストールに失敗: %s</string>
<string name="module_version">バージョン</string>
<string name="module_author">制作者</string>
<string name="module_overlay_fs_not_available">OverlayFS が有効でないためモジュールは動作しません</string>
<string name="refresh">更新</string>
<string name="show_system_apps">システムアプリを表示</string>
<string name="hide_system_apps">システムアプリを非表示</string>
<string name="send_log">ログを送信</string>
<string name="safe_mode">セーフモード</string>
<string name="reboot_to_apply">再起動すると有効化されます</string>
<string name="module_magisk_conflict">Magisk と競合しているためモジュールは無効になっています!</string>
<string name="home_learn_kernelsu">KernelSU の詳細</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/ja_JP/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">KernelSU のインストール方法やモジュールの使い方はこちら</string>
<string name="home_support_title">支援する</string>
<string name="home_support_content">KernelSU は無料かつオープンソースです。寄付していただくことで開発を支援できます。</string>
<string name="about_source_code"><![CDATA[%1$s でソースコードを表示<br/>%2$s チャンネルに参加]]></string>
<string name="profile">アプリのプロファイル</string>
<string name="profile_default">デフォルト</string>
<string name="profile_template">テンプレート</string>
<string name="profile_custom">カスタム</string>
<string name="profile_name">プロファイル名</string>
<string name="profile_namespace">マウント名前空間</string>
<string name="profile_namespace_inherited">継承</string>
<string name="profile_namespace_global">グローバル</string>
<string name="profile_namespace_individual">分離</string>
<string name="profile_umount_modules">モジュールのアンマウント</string>
</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,82 @@
<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 systeem apps</string>
<string name="hide_system_apps">Verberg systeem apps</string>
<string name="send_log">Stuur Log</string>
<string name="safe_mode">Safe mode</string>
<string name="reboot_to_apply">Herstart om effect te hebben</string>
<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_groups">Groepen</string>
<string name="profile_capabilities">Mogelijkheden</string>
<string name="profile_selinux_context">SELinux context</string>
<string name="profile_umount_modules">Ontkoppel modules</string>
<string name="failed_to_update_app_profile">Mislukt om App Profiel te updaten voor %s</string>
<string name="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 systeem verwijderen voor applicaties die geen Profiel ingesteld hebben.</string>
<string name="profile_umount_modules_summary">Met deze optie ingeschakeld zal KernelSU toelaten om alle gewijzigde bestanden door de modules voor deze applicatie te herstellen.</string>
<string name="profile_selinux_domain">Domein</string>
<string name="profile_selinux_rules">Regels</string>
<string name="module_update">Update</string>
<string name="module_downloading">Downloaden van module: %s</string>
<string name="new_version_available">Nieuwe versie: %s is beschikbaar, klik om te downloaden</string>
</resources>

View File

@@ -0,0 +1,77 @@
<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">Em Execução</string>
<string name="home_working_version">Versão: %d</string>
<string name="home_superuser_count">Superusuários: %d</string>
<string name="home_module_count">Módulos: %d</string>
<string name="home_unsupported">Sem Suporte</string>
<string name="home_unsupported_reason">Por enquanto, KernelSU suporta apenas kernels GKI</string>
<string name="home_kernel">Kernel</string>
<string name="home_manager_version">Versão do gerenciador</string>
<string name="home_fingerprint">Impressão digital</string>
<string name="home_selinux_status">Status do SELinux</string>
<string name="selinux_status_disabled">Desabilitado</string>
<string name="selinux_status_enforcing">Enforcing</string>
<string name="selinux_status_permissive">Permissive</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">Reiniciar para bootloader</string>
<string name="reboot_download">Reiniciar para download</string>
<string name="reboot_edl">Reiniciar para EDL</string>
<string name="about">Sobre</string>
<string name="module_uninstall_confirm">Tem certeza de que deseja desinstalar o módulo %s?</string>
<string name="module_uninstall_success">%s desinstalado</string>
<string name="module_uninstall_failed">Falha ao desinstalar: %s</string>
<string name="module_version">Versão</string>
<string name="module_author">Autor</string>
<string name="module_overlay_fs_not_available">overlayfs não está disponível, o módulo não funcionará!</string>
<string name="refresh">Atualizar</string>
<string name="show_system_apps">Mostrar aplicativos do sistema</string>
<string name="hide_system_apps">Ocultar aplicativos do sistema</string>
<string name="send_log">Enviar log</string>
<string name="safe_mode">Modo de segurança</string>
<string name="reboot_to_apply">Reinicie para entrar em vigor</string>
<string name="module_magisk_conflict">Os módulos estão desativados porque entraram em conflito com o Magisk\'s!</string>
<string name="home_learn_kernelsu">Leia mais sobre KernelSU</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">Saiba como instalar o KernelSU e usar os módulos</string>
<string name="home_support_title">Apoie-nos</string>
<string name="home_support_content">O KernelSU é, e sempre será, gratuito e de código aberto. No entanto, você pode mostrar seu apoio fazendo uma doação.</string>
<string name="about_source_code"><![CDATA[Veja o código-fonte no %1$s<br/>Junte-se ao nosso canal do %2$s]]></string>
<string name="profile" translatable="false">Perfil do Aplicativo</string>
<string name="profile_default">Padrão</string>
<string name="profile_template">Modelo</string>
<string name="profile_custom">Personalizado</string>
<string name="profile_name">Nome do perfil</string>
<string name="profile_namespace">Montar namespace</string>
<string name="profile_namespace_inherited">Padrão</string>
<string name="profile_namespace_global">Global</string>
<string name="profile_namespace_individual">Individual</string>
<string name="profile_groups">Grupos</string>
<string name="profile_capabilities">Permissões</string>
<string name="profile_selinux_context">Contexto do SELinux</string>
<string name="profile_umount_modules">Módulos não montados</string>
<string name="failed_to_update_app_profile">Falha ao atualizar o perfil do aplicativo para %s</string>
<string name="require_kernel_version">A versão atual do kernel %d é muito baixa 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>

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