1
mirror of https://github.com/topjohnwu/Magisk synced 2025-10-22 22:22:14 +02:00

Compare commits

...

189 Commits

Author SHA1 Message Date
topjohnwu
4ee2235961 Update dependencies 2025-10-20 10:30:09 -07:00
topjohnwu
536e50c6e0 Support optional trailing positional arguments 2025-10-19 17:15:30 -07:00
topjohnwu
57d9fc6099 Support short only options and switches 2025-10-19 17:15:30 -07:00
topjohnwu
52d8910bdd Cleanup code for EarlyExit during help triggers 2025-10-19 17:15:30 -07:00
topjohnwu
c94bd49a89 Update default help triggers 2025-10-19 17:15:30 -07:00
topjohnwu
b72ba6759e Vendor argh sources
Further customization will come in future commits
2025-10-19 17:15:30 -07:00
topjohnwu
5bcb55b7fc Format Rust imports with rustfmt 2025-10-19 17:15:30 -07:00
topjohnwu
0dc8231585 Make all dependencies workspace = true 2025-10-19 17:15:30 -07:00
Wang Han
470acc93c9 Remove clickable attribute from item_module_md2.xml 2025-10-19 14:02:02 -07:00
Wang Han
0edb80b10f Set module card as non clickable
It's so easy to mis-click card.
2025-10-19 14:02:02 -07:00
topjohnwu
bcc6296d94 Build debug without debug-info 2025-10-03 00:16:17 -07:00
topjohnwu
c3db2e368d Release Magisk v30.4
[skip ci]
2025-10-02 04:30:47 -07:00
topjohnwu
d37da5ca66 Cleanup code 2025-10-02 04:18:20 -07:00
topjohnwu
aac52176ed Support API level as floating point 2025-10-02 04:10:22 -07:00
topjohnwu
78e2fc37e5 Add easy knobs to disable security checks 2025-10-02 04:09:46 -07:00
Wang Han
ca2e40593f Make fetchUpdate safe 2025-10-02 04:03:44 -07:00
LoveSy
c07fdc87e3 Handle second splice() failure gracefully 2025-10-02 04:03:27 -07:00
topjohnwu
7270f5e413 Several minor fixes/improvements 2025-10-02 04:03:08 -07:00
topjohnwu
07cc85ccb1 Default initialize before swap in move constructor
Fix #9373, fix #9384, fix #9400, fix #9404
2025-10-02 04:03:08 -07:00
topjohnwu
d6f17c42d5 Fix logging implementation error 2025-10-02 04:03:08 -07:00
Wang Han
d60806f429 Only reset NB prop when zygisk is enabled 2025-10-02 03:19:32 -07:00
Mohammad Hasan Keramat J
8836a09c8c core: Update Persian translation 2025-09-30 00:21:44 -07:00
topjohnwu
f16e93c7db Release Magisk v30.3
[skip ci]
2025-09-29 21:35:45 -07:00
Thonsi
1b0ddec66e Remove unused code 2025-09-29 02:33:02 -07:00
topjohnwu
cd8820f563 Refactor code for more readability 2025-09-29 01:41:55 -07:00
topjohnwu
b70192ca3e Sync libsepol with upstream AOSP 2025-09-29 01:18:52 -07:00
LoveSy
d42ec5da9a Fix pattern matching for CANARY version 2025-09-29 01:18:52 -07:00
topjohnwu
742913ebcb Support installing Magisk on vendor_boot
Close #9238, fix #8835
2025-09-28 01:10:11 -07:00
topjohnwu
ed206c6480 Upgrade cargo dependencies 2025-09-26 23:37:45 -07:00
topjohnwu
f9a8052583 Improve build.py
Close #8988
2025-09-26 17:00:58 -07:00
topjohnwu
f4fdd516f9 Upgrade gradle dependencies 2025-09-24 03:18:35 -07:00
topjohnwu
5925a71f94 Upgrade cargo dependencies 2025-09-24 03:05:18 -07:00
topjohnwu
3cda9beb93 Cleanup unused bindings 2025-09-24 02:38:18 -07:00
topjohnwu
8b7d1ffcdd Migrate magisk_main to Rust 2025-09-18 03:22:44 -07:00
topjohnwu
8d02d0632e Fix comments 2025-09-18 03:22:44 -07:00
topjohnwu
dd743f6f7e Improve Encodable/Decodable impls 2025-09-18 01:17:28 -07:00
topjohnwu
cf483ad4d2 Migrate connect_daemon to Rust 2025-09-15 14:25:18 -07:00
topjohnwu
4aed644e08 Directly accept RequestCode for connect_daemon 2025-09-15 14:25:18 -07:00
topjohnwu
0acc39cec0 Use bitflags to implement BootState 2025-09-15 14:25:18 -07:00
topjohnwu
8b3a44344f Move bootstages into its own module 2025-09-15 14:25:18 -07:00
topjohnwu
8b49eda85a Migrate daemon_entry to Rust 2025-09-15 14:25:18 -07:00
topjohnwu
7057d4c7f1 Migrate setup_magisk_env to Rust 2025-09-15 14:25:18 -07:00
Radoš Milićev
aab8344058 Update Serbian 2025-09-14 18:42:22 -07:00
topjohnwu
7cccf83b37 Remove unused poll_ctrl implementation 2025-09-14 01:59:04 -07:00
topjohnwu
f10ad93c4e Move more code of daemon_entry into Rust 2025-09-13 01:21:33 -07:00
topjohnwu
f143b5df15 Do not mount directories as mirror
Mounting real directories into worker will cause init to start tracking
the mount point through dev.mnt. This causes issues, so we are forced
to recursively reconstruct the mirror directory structure from scratch.

Fix #9316
2025-09-12 22:01:08 -07:00
topjohnwu
71213cc6f4 Fix path tracking in module.rs 2025-09-12 22:01:08 -07:00
topjohnwu
e2a1774e5b Make logging.rs use nix 2025-09-11 01:17:34 -07:00
topjohnwu
0222527a1e Use bitflags macro 2025-09-11 01:17:34 -07:00
topjohnwu
312bfe1bab Do not leak base::ffi to external crates 2025-09-11 01:17:34 -07:00
topjohnwu
48c62a1dae Disable exit on error for cmdline_logging 2025-09-11 01:17:34 -07:00
rikka
cfc2bcb665 Fix zygisk native bridge library name concatenation order 2025-09-11 01:16:54 -07:00
topjohnwu
94b1ff674f Allow calling remove_all on non-existence file 2025-09-10 03:44:39 -07:00
topjohnwu
111136733a Migrate away from unsafe set_len of Utf8CStr 2025-09-09 22:19:05 -07:00
topjohnwu
c8caaa98f5 Enable mount for nix 2025-09-09 20:17:09 -07:00
topjohnwu
8d28f10a3f Enable zerocopy for nix 2025-09-09 12:04:46 -07:00
topjohnwu
177a456d8b Enable term for nix 2025-09-09 12:04:31 -07:00
topjohnwu
ef4e230258 Use nix for libc functions 2025-09-08 23:59:29 -07:00
topjohnwu
17082af438 Simplify OsError 2025-09-08 11:25:20 -07:00
topjohnwu
1df5b34175 Stop differentiate Error vs ErrorCxx 2025-09-08 11:25:18 -07:00
topjohnwu
ea5fe7525d Simplify LibcReturn 2025-09-08 10:55:57 -07:00
topjohnwu
a75c335261 Update cargo dependencies 2025-09-08 02:24:01 -07:00
topjohnwu
3903f42cf6 Support specify ABI for clippy 2025-09-08 02:23:49 -07:00
topjohnwu
fb0c4ea838 Fallback to userspace copy if splice failed
Fix #9032
2025-09-03 16:10:18 -07:00
topjohnwu
bc89c60977 Run cargo fmt 2025-09-02 22:06:08 -07:00
topjohnwu
bd657c354c Reduce FFI across C++/Rust 2025-09-02 22:06:08 -07:00
MONA
675b5f9565 feat(i18n): Add Hinglish translation 2025-09-02 01:27:58 -07:00
MONA
1b2c43268e feat(i18n): Add Hinglish translation 2025-09-02 01:27:58 -07:00
topjohnwu
653730d75e Make cxx binding generate less code 2025-08-29 01:44:06 -07:00
topjohnwu
d472e9c36e Update cargo dependencies 2025-08-28 22:01:35 -07:00
topjohnwu
484d53ef7e Update to ONDK r29.2 2025-08-28 16:15:59 -07:00
topjohnwu
c4e2985677 Migrate resetprop to Rust 2025-08-27 22:48:48 -07:00
topjohnwu
42d9f87bc9 Cleanup resetprop code 2025-08-27 22:48:48 -07:00
topjohnwu
2e4fa6864c Make Utf8CStr a first class citizen in C++ codebase
Utf8CStr is in many cases a better string view class than
std::string_view, because it provides "view" access to a string buffer
that is guaranteed to be null terminated. It also has the additional
benefit of being UTF-8 verified and can seemlessly cross FFI boundaries.

We would want to start use more Utf8CStr in our existing C++ codebase.
2025-08-27 22:48:48 -07:00
topjohnwu
e2abb648ac Update system_properties 2025-08-27 10:12:51 -07:00
topjohnwu
3599dcedfb Make argh directly parse into Utf8CString 2025-08-27 01:26:41 -07:00
topjohnwu
ea72666df8 Only specify ADB port for tests 2025-08-25 15:34:04 -07:00
topjohnwu
bd2a47ba18 Merge libbase cpp files 2025-08-25 01:31:47 -07:00
topjohnwu
b861671391 Cleanup libbase 2025-08-25 01:31:47 -07:00
topjohnwu
e91fc75d86 Consolidate for_each implementation into Rust 2025-08-25 01:31:47 -07:00
LoveSy
78f5cd55c7 Use lzma-rust2 for xz and lzma compression and decompression 2025-08-24 00:23:55 -07:00
topjohnwu
9787a69528 Make all decoders Read instead of Write
Most libraries only implement Read for decoders
2025-08-24 00:23:55 -07:00
topjohnwu
87b8fe374d Fix magiskboot cli parsing 2025-08-23 20:31:15 -07:00
topjohnwu
7b706bb0cb Cleanup and fix compress/decompress command 2025-08-23 20:31:15 -07:00
topjohnwu
c1491b8d2b Fix LoggedResult implementation error 2025-08-23 15:25:52 -07:00
LoveSy
5cbaf2ae11 Use super let to simplify code 2025-08-22 12:05:44 -07:00
topjohnwu
8ebc6207b4 Merge headers 2025-08-22 12:03:47 -07:00
topjohnwu
7848ee616b Cleanup magiskboot main function 2025-08-22 12:03:47 -07:00
topjohnwu
fd193c3cae Simplify ResultExt implementation
Also introduce OptionExt
2025-08-22 12:03:47 -07:00
topjohnwu
36d33c7a85 Make log_err directly return LoggedResult 2025-08-22 12:03:47 -07:00
topjohnwu
5caf28d27c Hide harmless error reporting 2025-08-22 12:03:47 -07:00
topjohnwu
2c39d0234d Fix compression format detection 2025-08-21 12:21:22 -07:00
topjohnwu
c313812129 Simplify magiskboot FFI 2025-08-21 12:21:22 -07:00
topjohnwu
af51880a81 Introduce CmdArgs for argument parsing in Rust 2025-08-21 12:21:22 -07:00
LoveSy
db8d832707 Move magiskboot cli to argh 2025-08-20 21:40:34 -07:00
Wang Han
8dc23d0ead Avoid triggering magisk --zygote-restart twice
We have already used on restart keyword to inject zygote restart, so
triggering it here on prop is not needed.
2025-08-20 12:34:39 -07:00
topjohnwu
b4287700d5 Increase timeout to 15 minutes 2025-08-20 11:23:18 -07:00
topjohnwu
8d10ab89f2 Set zygisk properties in Rust 2025-08-20 11:23:18 -07:00
topjohnwu
49fdc1addb Prevent setting zygisk prop twice 2025-08-20 11:23:18 -07:00
topjohnwu
1333d3b986 Fix canary emulator 2025-08-18 11:25:47 -07:00
残页
335146a6a2 Update supported API levels 2025-08-17 23:58:43 -07:00
topjohnwu
eaf9527971 Use AOSP ATD for API 36
[skip ci]
2025-08-15 17:25:41 -07:00
LoveSy
da937a88c8 if !restore { set_zygisk_prop(); } 2025-08-15 16:45:01 -07:00
topjohnwu
9476e7282d More borrowing, less copying 2025-08-08 21:06:41 -07:00
topjohnwu
251c3c3e0e Remove old ffi data structure 2025-08-08 21:06:41 -07:00
topjohnwu
cd0eca20b0 Migrate connect.cpp to Rust 2025-08-08 21:06:41 -07:00
topjohnwu
6839cb9ab2 Keep /system/xbin/su on emulators 2025-08-08 21:06:41 -07:00
topjohnwu
d11a3397d8 Reduce verbose logging in Zygisk 2025-08-08 21:06:41 -07:00
topjohnwu
975120d6a6 Release Magisk v30.2
[skip ci]
2025-08-06 03:32:32 -07:00
topjohnwu
e489b3b6dd Migrate load_modules to Rust 2025-08-05 11:24:55 -07:00
topjohnwu
589a270b8d Migrate disable/remove modules to Rust 2025-08-05 11:24:55 -07:00
topjohnwu
7961be5cfa Migrate prepare_modules to Rust 2025-08-05 11:24:55 -07:00
topjohnwu
959430e030 Fix systemless hosts installation 2025-08-05 09:44:51 -07:00
topjohnwu
2923c8ccd1 Add module upgrade test 2025-08-05 09:44:51 -07:00
topjohnwu
7df4a9d74f Add uninstaller.sh test 2025-08-05 09:44:51 -07:00
topjohnwu
bf4ed295da Update cargo dependencies 2025-08-02 13:43:27 -07:00
topjohnwu
a5fca960dc Update gradle and dependencies 2025-08-02 02:29:14 -07:00
topjohnwu
f99912b9db Update libsystem_properties 2025-07-21 13:47:30 -07:00
5ec1cff
a54bdb54e4 Skip avb 1,0 verify if tail contains avb 2.0 header
This way, magiskboot will not print "unexpected ASN.1 DER tag: expected SEQUENCE, got APPLICATION [1] (primitive)".
2025-07-21 00:51:14 -07:00
topjohnwu
cd9851a1fe Add regression test for #9179 2025-07-18 17:58:29 -07:00
Wang Han
9ca469898c Use worker for replace feature
This fixes https://github.com/topjohnwu/Magisk/issues/9179.
2025-07-18 16:57:20 -07:00
𝗛𝗼𝗹𝗶
0665549473 Update Turkish
Missing parts were filled in and made better
2025-07-14 10:46:05 -07:00
topjohnwu
9d7a14b335 Remove unnecessary return 2025-07-14 10:03:22 -07:00
Wang Han
62e29fee74 Treat bind mount failure same as C++ implementation
This fixes #9139 and #9174.
2025-07-14 00:27:24 -07:00
igor
e472db552b Update portuguese/english translations 2025-07-11 10:52:21 -07:00
topjohnwu
466e4bd4e1 Update cargo dependencies 2025-07-11 02:04:33 -07:00
topjohnwu
4cf525c588 Add Android canary builds into CI tests 2025-07-11 00:16:39 -07:00
topjohnwu
c8aec2510d Restrict sccache cache size 2025-07-11 00:16:39 -07:00
topjohnwu
ccbfe0e66e Update gradle dependencies 2025-07-10 15:55:14 -07:00
南宫雪珊
23ea28de6f scripts: fix modules_update dir context 2025-07-10 10:59:39 -07:00
topjohnwu
55c3ee3a6f Move Zygisk code out of module.cpp 2025-07-07 13:43:11 -07:00
vvb2060
2a42ca2b8f app: fix time i18n 2025-07-07 11:04:18 -07:00
topjohnwu
a897e82fa4 Remove release notes
They are embedded into GitHub releases
2025-07-07 10:37:45 -07:00
topjohnwu
ffa15831d3 Add release dates 2025-07-07 10:35:22 -07:00
topjohnwu
a344ebf28c Add v30.1 changelog 2025-07-03 18:02:27 -07:00
topjohnwu
78f7fa348e Release Magisk v30.1
[skip ci]
2025-07-03 03:10:10 -07:00
pndwal
d8c448b99d Update faq.md
Add information on restoring Magisk App functionality when stub and full apps conflict.
2025-07-03 02:51:01 -07:00
topjohnwu
d4b83b6a44 Fix app compilation 2025-07-03 02:42:08 -07:00
vvb2060
e5d36d1d24 app: support config restrict policy 2025-07-03 02:42:08 -07:00
vvb2060
ff18cb8e70 su: support drop capabilities 2025-07-03 02:42:08 -07:00
topjohnwu
37a9724a54 Apply clippy fix 2025-07-02 21:20:14 -07:00
topjohnwu
d660401063 Treat magisk symlinks differently 2025-07-02 21:20:14 -07:00
topjohnwu
88541d6f49 Fix file attribute copy in module mounting logic
Due to various reasons, we cannot directly mount module files in /data
into the real paths. Instead we bind mount the module root directory and
remount this mirror with specific mount-point flags. Relevant to this
bug, the module mount is mounted as read-only, which means the file
attribute copy operation could fail in certain configurations.

The fix here is to always copy file attributes into writable locations,
so either in the tmpfs worker directory, or in the module directory
under /data.

A new test case is added to make sure this regression will no longer
happen again in the future.

Fix #9139
2025-07-02 19:23:46 -07:00
topjohnwu
ecd6129fe5 Add systemless hosts test 2025-07-02 19:23:46 -07:00
topjohnwu
6dfe9df9e2 Run cargo fmt 2025-07-02 19:23:46 -07:00
topjohnwu
e81de7ec36 Release Magisk v30.0
[skip ci]
2025-07-01 10:14:43 -07:00
topjohnwu
c78da1ce24 Update v30.0 changelog 2025-07-01 10:00:38 -07:00
Wang Han
7b2d40987c Refactor magisk bins injection logic
Magisk binary mounting logic is not very clear now. In this commit, it
is rewritten in a more robust way. Now it has following cases in mind:
1) Device has a su binary, magisk need to overlay it
2) Choose the PATH with least files to reduce bind mount
3) Filter path which is not suitable
2025-07-01 02:16:16 -07:00
Wang Han
3a37e8c9c5 Don't clone attributes for magisk symlinks
This avoids use existing attributes for su, which will obviously break
magisk functions.
2025-07-01 02:16:16 -07:00
igor
58b405bce1 Update portuguese/english translation 2025-07-01 01:32:36 -07:00
Wang Han
810174ef73 Ignore set_context() error if magisktmp is /sbin
recreate_sbin() will bind mount original files in /sbin to tmpfs /sbin,
so we have no choice but just log here to let the loop continue.
2025-06-30 17:39:54 -07:00
topjohnwu
690a5ac033 Update to ONDK r28.5 2025-06-30 12:15:43 -07:00
topjohnwu
89aad31f7e Update gradle dependencies 2025-06-26 09:35:18 -07:00
topjohnwu
7124db98e3 Stop involving JSON in release script 2025-06-20 00:59:38 -07:00
topjohnwu
0860e859f7 Stop updating README for each release 2025-06-20 00:32:11 -07:00
topjohnwu
04008949b8 Deprecate canary channel 2025-06-20 00:22:17 -07:00
Wang Han
39f2940bd1 Skip symlink in restore_tmpcon()
If magisktmp is /sbin, there may exist files which is symlink to files in
root dir. As root is RO, setcontext will fail and break iterating loop.
2025-06-16 11:09:33 -07:00
topjohnwu
1460317ebd Cleanup C++ headers 2025-06-16 02:25:38 -07:00
topjohnwu
12340c9bd5 Update gradle version 2025-06-16 02:17:29 -07:00
topjohnwu
c4590fe2ba Reorganize buildSrc 2025-06-10 16:51:31 -07:00
topjohnwu
b36066bbcd Run clippy fix 2025-06-10 16:34:38 -07:00
topjohnwu
65d1c5827c Update dependencies 2025-06-10 16:33:06 -07:00
AshiVered
1d675c8b2e Update strings.xml
Improve Hebrew translations
2025-06-06 17:08:51 -07:00
Wang Han
0b494ed7df Avoid throwing error if chmod a symlink
It is possible that we want to clone attributes of a regular file to a
symlink. In this case, we don't need to error out if chmod fails. Just
skip it.
2025-06-06 17:08:34 -07:00
topjohnwu
48d9fc24eb Update release.sh 2025-06-05 11:00:27 -07:00
topjohnwu
83426f7f36 Update check update logic 2025-06-04 12:50:19 -07:00
Wang Han
0e86d4dbcb Fix file pointer leak on error path 2025-06-03 00:34:52 -07:00
Wang Han
5e050d7456 Check binary existence before injecting zygisk bins 2025-06-03 00:33:40 -07:00
topjohnwu
898580bf90 Update dependencies 2025-06-02 19:51:12 -07:00
topjohnwu
86621a4c46 Fix gradle cache 2025-05-30 11:11:53 -07:00
topjohnwu
a834e72b71 Use sccache for clippy 2025-05-30 11:11:45 -07:00
topjohnwu
d8cf42af16 Reduce unstable feature usage 2025-05-30 11:11:36 -07:00
topjohnwu
8c79d66b7b Update ONDK r28.4 2025-05-30 01:53:26 -07:00
Wang Han
fada8b148a Ensure manager can always bypass su access policy
This fixes https://github.com/topjohnwu/Magisk/issues/9050.
2025-05-21 16:14:46 -07:00
topjohnwu
dc0acea47c Remove C++ I/O streams 2025-05-20 03:26:00 -07:00
topjohnwu
78d1200608 Migrate all compression code to Rust 2025-05-20 03:26:00 -07:00
topjohnwu
527bbc0368 Migrate module mounting to Rust 2025-05-20 03:24:43 -07:00
topjohnwu
4c89c7e2b3 Directly use MagiskJson 2025-05-19 10:40:58 -07:00
topjohnwu
adbea7e313 Improve update check code 2025-05-19 10:23:15 -07:00
vvb2060
76962f965e app: use github api to check updates 2025-05-19 10:23:15 -07:00
topjohnwu
a4b8c5e46b Update app dependencies 2025-05-18 22:24:44 -07:00
The Ali Dev
83c707439c Add Urdu Translation 2025-05-18 22:21:05 -07:00
Radoš Milićev
25dd6121f4 Add author for Serbian translations to .xml files 2025-05-18 21:54:24 -07:00
Radoš Milićev
67f35ad027 Add Serbian latin (values-b+sr+Latn) 2025-05-18 21:54:24 -07:00
topjohnwu
0c4b8afbc5 Simplify JNI hooking code 2025-05-17 02:05:21 -07:00
topjohnwu
34b30d7ce1 Promite Magisk v29.0 to stable 2025-05-16 12:17:53 -07:00
topjohnwu
2215088973 Remove unused files in native directory 2025-05-15 13:48:12 -07:00
topjohnwu
8b7fb6cdde Improve scripts 2025-05-15 02:56:36 -07:00
topjohnwu
94c7dbedf2 Fix signing config
[skip ci]
2025-05-14 15:14:34 -07:00
259 changed files with 13891 additions and 10586 deletions

View File

@@ -45,7 +45,7 @@ runs:
env:
SCCACHE_DIRECT: false
SCCACHE_DIR: ${{ github.workspace }}/.sccache
SCCACHE_CACHE_SIZE: 2G
SCCACHE_CACHE_SIZE: ${{ inputs.is-asset-build == 'true' && '2G' || '300M' }}
SCCACHE_IDLE_TIMEOUT: 0
run: |
bash $GITHUB_ACTION_PATH/sccache.sh
@@ -70,7 +70,7 @@ runs:
.gradle/caches
.gradle/wrapper
!.gradle/caches/build-cache-*
key: gradle-cache-${{ hashFiles('gradle/**') }}
key: gradle-cache-${{ hashFiles('app/gradle/**') }}
restore-keys: gradle-cache-
- name: Restore Gradle dependencies

View File

@@ -82,12 +82,10 @@ jobs:
strategy:
fail-fast: false
matrix:
version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, "CANARY"]
type: [""]
include:
- version: 36
type: "google_apis"
- version: 36
- version: "CANARY"
type: "google_apis_ps16k"
steps:
@@ -107,10 +105,10 @@ jobs:
sudo udevadm trigger --name-match=kvm
- name: Run AVD test
timeout-minutes: 10
timeout-minutes: 15
env:
AVD_TEST_LOG: 1
run: scripts/avd_test.sh ${{ matrix.version }} ${{ matrix.type }}
run: scripts/avd.sh test ${{ matrix.version }} ${{ matrix.type }}
- name: Upload logs on error
if: ${{ failure() }}
@@ -148,11 +146,11 @@ jobs:
sudo udevadm trigger --name-match=kvm
- name: Run AVD test
timeout-minutes: 10
timeout-minutes: 15
env:
FORCE_32_BIT: 1
AVD_TEST_LOG: 1
run: scripts/avd_test.sh ${{ matrix.version }}
run: scripts/avd.sh test ${{ matrix.version }}
- name: Upload logs on error
if: ${{ failure() }}
@@ -193,7 +191,7 @@ jobs:
scripts/cuttlefish.sh download ${{ matrix.branch }} ${{ matrix.device }}
- name: Run Cuttlefish test
timeout-minutes: 10
timeout-minutes: 15
run: sudo -E -u $USER scripts/cuttlefish.sh test
- name: Upload logs on error

6
.gitmodules vendored
View File

@@ -4,15 +4,9 @@
[submodule "lz4"]
path = native/src/external/lz4
url = https://github.com/lz4/lz4.git
[submodule "xz"]
path = native/src/external/xz
url = https://github.com/xz-mirror/xz.git
[submodule "libcxx"]
path = native/src/external/libcxx
url = https://github.com/topjohnwu/libcxx.git
[submodule "zopfli"]
path = native/src/external/zopfli
url = https://github.com/google/zopfli.git
[submodule "cxx-rs"]
path = native/src/external/cxx-rs
url = https://github.com/topjohnwu/cxx.git

View File

@@ -16,13 +16,7 @@ Some highlight features:
## Downloads
[Github](https://github.com/topjohnwu/Magisk/) is the only source where you can get official Magisk information and downloads.
Click the icon below to download Magisk apk.
[![](https://img.shields.io/badge/Magisk-v28.1-blue)](https://github.com/topjohnwu/Magisk/releases/tag/v28.1)
[![](https://img.shields.io/badge/Magisk%20Beta-v29.0-blue)](https://github.com/topjohnwu/Magisk/releases/tag/v29.0)
[![](https://img.shields.io/badge/Magisk-Canary-red)](https://github.com/topjohnwu/Magisk/releases/tag/canary-29001)
[Github](https://github.com/topjohnwu/Magisk/releases) is the only source where you can get official Magisk information and downloads.
## Useful Links

View File

@@ -24,6 +24,7 @@ import androidx.core.widget.ImageViewCompat
import androidx.databinding.BindingAdapter
import androidx.databinding.InverseBindingAdapter
import androidx.databinding.InverseBindingListener
import androidx.databinding.InverseMethod
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.GridLayoutManager
@@ -33,9 +34,11 @@ import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView
import com.google.android.material.chip.Chip
import com.google.android.material.slider.Slider
import com.google.android.material.textfield.TextInputLayout
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.di.ServiceLocator
import com.topjohnwu.magisk.core.model.su.SuPolicy
import com.topjohnwu.magisk.utils.TextHolder
import com.topjohnwu.superuser.internal.UiThreadHandler
import com.topjohnwu.widget.IndeterminateCheckBox
@@ -306,3 +309,38 @@ fun TextView.setText(text: TextHolder) {
fun Spinner.setAdapter(items: Array<Any>, layoutRes: Int) {
adapter = ArrayAdapter(context, layoutRes, items)
}
@BindingAdapter("labelFormatter")
fun Slider.setLabelFormatter(formatter: (Float) -> Int) {
setLabelFormatter { value -> resources.getString(formatter(value)) }
}
@InverseBindingAdapter(attribute = "android:value")
fun Slider.getValueBinding() = value
@BindingAdapter("android:valueAttrChanged")
fun Slider.setListener(attrChange: InverseBindingListener) {
addOnSliderTouchListener(object : Slider.OnSliderTouchListener {
override fun onStartTrackingTouch(slider: Slider) = Unit
override fun onStopTrackingTouch(slider: Slider) = attrChange.onChange()
})
}
@InverseMethod("sliderValueToPolicy")
fun policyToSliderValue(policy: Int): Float {
return when (policy) {
SuPolicy.DENY -> 1f
SuPolicy.RESTRICT -> 2f
SuPolicy.ALLOW -> 3f
else -> 1f
}
}
fun sliderValueToPolicy(value: Float): Int {
return when (value) {
1f -> SuPolicy.DENY
2f -> SuPolicy.RESTRICT
3f -> SuPolicy.ALLOW
else -> SuPolicy.DENY
}
}

View File

@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.dialog
import com.topjohnwu.magisk.core.AppContext
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.R
import com.topjohnwu.magisk.core.di.ServiceLocator
import com.topjohnwu.magisk.core.download.DownloadEngine
import com.topjohnwu.magisk.core.download.Subject
import com.topjohnwu.magisk.view.MagiskDialog
@@ -11,15 +10,10 @@ import java.io.File
class ManagerInstallDialog : MarkDownDialog() {
private val svc get() = ServiceLocator.networkService
override suspend fun getMarkdownText(): String {
val text = svc.fetchString(Info.remote.magisk.note)
val text = Info.update.note
// Cache the changelog
AppContext.cacheDir.listFiles { _, name -> name.endsWith(".md") }.orEmpty().forEach {
it.delete()
}
File(AppContext.cacheDir, "${Info.remote.magisk.versionCode}.md").writeText(text)
File(AppContext.cacheDir, "${Info.update.versionCode}.md").writeText(text)
return text
}

View File

@@ -91,16 +91,15 @@ class HomeViewModel(
override suspend fun doLoadWork() {
appState = State.LOADING
Info.getRemote(svc)?.apply {
Info.fetchUpdate(svc)?.apply {
appState = when {
BuildConfig.APP_VERSION_CODE < magisk.versionCode -> State.OUTDATED
BuildConfig.APP_VERSION_CODE < versionCode -> State.OUTDATED
else -> State.UP_TO_DATE
}
val isDebug = Config.updateChannel == Config.Value.DEBUG_CHANNEL
managerRemoteVersion =
("${magisk.version} (${magisk.versionCode})" +
if (isDebug) " (D)" else "").asText()
("$version (${versionCode})" + if (isDebug) " (D)" else "").asText()
} ?: run {
appState = State.INVALID
managerRemoteVersion = CoreR.string.not_available.asText()

View File

@@ -14,9 +14,8 @@ import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseViewModel
import com.topjohnwu.magisk.core.AppContext
import com.topjohnwu.magisk.core.BuildConfig
import com.topjohnwu.magisk.core.BuildConfig.APP_VERSION_CODE
import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.base.ContentResultCallback
import com.topjohnwu.magisk.core.ktx.toast
@@ -70,17 +69,17 @@ class InstallViewModel(svc: NetworkService, markwon: Markwon) : BaseViewModel()
init {
viewModelScope.launch(Dispatchers.IO) {
try {
val file = File(AppContext.cacheDir, "${BuildConfig.APP_VERSION_CODE}.md")
val text = when {
file.exists() -> file.readText()
Const.Url.CHANGELOG_URL.isEmpty() -> ""
val noteFile = File(AppContext.cacheDir, "${APP_VERSION_CODE}.md")
val noteText = when {
noteFile.exists() -> noteFile.readText()
else -> {
val str = svc.fetchString(Const.Url.CHANGELOG_URL)
file.writeText(str)
str
val note = svc.fetchUpdate(APP_VERSION_CODE)?.note.orEmpty()
if (note.isEmpty()) return@launch
noteFile.writeText(note)
note
}
}
val spanned = markwon.toMarkdown(text)
val spanned = markwon.toMarkdown(noteText)
withContext(Dispatchers.Main) {
notes = spanned
}
@@ -100,13 +99,15 @@ class InstallViewModel(svc: NetworkService, markwon: Markwon) : BaseViewModel()
}
override fun onSaveState(state: Bundle) {
state.putParcelable(INSTALL_STATE_KEY, InstallState(
methodId,
step,
Config.keepVerity,
Config.keepEnc,
Config.recovery
))
state.putParcelable(
INSTALL_STATE_KEY, InstallState(
methodId,
step,
Config.keepVerity,
Config.keepEnc,
Config.recovery
)
)
}
override fun onRestoreState(state: Bundle) {
@@ -124,6 +125,7 @@ class InstallViewModel(svc: NetworkService, markwon: Markwon) : BaseViewModel()
override fun onActivityLaunch() {
AppContext.toast(CoreR.string.patch_file_msg, Toast.LENGTH_LONG)
}
override fun onActivityResult(result: Uri) {
uri.value = result
}

View File

@@ -147,19 +147,11 @@ object UpdateChannel : BaseSettingsItem.Selector() {
get() = Config.updateChannel
set(value) {
Config.updateChannel = value
Info.remote = Info.EMPTY_REMOTE
Info.resetUpdate()
}
override val title = CoreR.string.settings_update_channel_title.asText()
override val entryRes = CoreR.array.update_channel
override fun entries(res: Resources): Array<String> {
return super.entries(res).let {
if (!Const.APP_IS_CANARY && !BuildConfig.DEBUG)
it.copyOfRange(0, Config.Value.CANARY_CHANNEL)
else it
}
}
}
object UpdateChannelUrl : BaseSettingsItem.Input() {
@@ -169,7 +161,7 @@ object UpdateChannelUrl : BaseSettingsItem.Input() {
get() = Config.customChannelUrl
set(value) {
Config.customChannelUrl = value
Info.remote = Info.EMPTY_REMOTE
Info.resetUpdate()
notifyPropertyChanged(BR.description)
}
@@ -330,6 +322,12 @@ object Reauthenticate : BaseSettingsItem.Toggle() {
override var value by Config::suReAuth
override fun refresh() {
isEnabled = Build.VERSION.SDK_INT < Build.VERSION_CODES.O && Info.showSuperUser
isEnabled = Build.VERSION.SDK_INT < Build.VERSION_CODES.O
}
}
object Restrict : BaseSettingsItem.Toggle() {
override val title = CoreR.string.settings_su_restrict_title.asText()
override val description = CoreR.string.settings_su_restrict_summary.asText()
override var value by Config::suRestrict
}

View File

@@ -22,11 +22,11 @@ import com.topjohnwu.magisk.core.ktx.activity
import com.topjohnwu.magisk.core.ktx.toast
import com.topjohnwu.magisk.core.tasks.AppMigration
import com.topjohnwu.magisk.core.utils.LocaleSetting
import com.topjohnwu.magisk.core.utils.RootUtils
import com.topjohnwu.magisk.databinding.bindExtra
import com.topjohnwu.magisk.events.AddHomeIconEvent
import com.topjohnwu.magisk.events.AuthEvent
import com.topjohnwu.magisk.events.SnackbarEvent
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.launch
class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
@@ -83,6 +83,9 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
// Can hide overlay windows on 12.0+
list.remove(Tapjack)
}
if (Const.Version.atLeast_30_1()) {
list.add(Restrict)
}
}
return list
@@ -127,7 +130,8 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
}
private fun createHosts() {
Shell.cmd("add_hosts_module").submit {
viewModelScope.launch {
RootUtils.addSystemlessHosts()
AppContext.toast(R.string.settings_hosts_toast, Toast.LENGTH_SHORT)
}
}

View File

@@ -4,11 +4,13 @@ import android.graphics.drawable.Drawable
import androidx.databinding.Bindable
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.model.su.SuPolicy
import com.topjohnwu.magisk.databinding.DiffItem
import com.topjohnwu.magisk.databinding.ItemWrapper
import com.topjohnwu.magisk.databinding.ObservableRvItem
import com.topjohnwu.magisk.databinding.set
import com.topjohnwu.magisk.core.R as CoreR
class PolicyRvItem(
private val viewModel: SuperuserViewModel,
@@ -33,14 +35,34 @@ class PolicyRvItem(
var isExpanded = false
set(value) = set(value, field, { field = it }, BR.expanded)
val showSlider = Config.suRestrict || item.policy == SuPolicy.RESTRICT
@get:Bindable
var isEnabled
get() = item.policy == SuPolicy.ALLOW
get() = item.policy >= SuPolicy.ALLOW
set(value) = setImpl(value, isEnabled) {
notifyPropertyChanged(BR.enabled)
viewModel.togglePolicy(this, value)
viewModel.updatePolicy(this, if (it) SuPolicy.ALLOW else SuPolicy.DENY)
}
@get:Bindable
var sliderValue
get() = item.policy
set(value) = setImpl(value, sliderValue) {
notifyPropertyChanged(BR.sliderValue)
notifyPropertyChanged(BR.enabled)
viewModel.updatePolicy(this, it)
}
val sliderValueToPolicyString: (Float) -> Int = { value ->
when (value.toInt()) {
1 -> CoreR.string.deny
2 -> CoreR.string.restrict
3 -> CoreR.string.grant
else -> CoreR.string.deny
}
}
@get:Bindable
var shouldNotify
get() = item.notification

View File

@@ -156,15 +156,16 @@ class SuperuserViewModel(
}
}
fun togglePolicy(item: PolicyRvItem, enable: Boolean) {
fun updatePolicy(item: PolicyRvItem, policy: Int) {
val items = itemsPolicies.filter { it.item.uid == item.item.uid }
fun updateState() {
viewModelScope.launch {
val res = if (enable) R.string.su_snack_grant else R.string.su_snack_deny
item.item.policy = if (enable) SuPolicy.ALLOW else SuPolicy.DENY
val res = if (policy >= SuPolicy.ALLOW) R.string.su_snack_grant else R.string.su_snack_deny
item.item.policy = policy
db.update(item.item)
items.forEach {
it.notifyPropertyChanged(BR.enabled)
it.notifyPropertyChanged(BR.sliderValue)
}
SnackbarEvent(res.asText(item.appName)).publish()
}

View File

@@ -43,10 +43,6 @@ enum class Theme(
val isSelected get() = Config.themeOrdinal == ordinal
fun select() {
Config.themeOrdinal = ordinal
}
companion object {
val selected get() = values().getOrNull(Config.themeOrdinal) ?: Piplup
}

View File

@@ -25,7 +25,7 @@
<include
android:id="@+id/log_track_container"
bullet="@{item.log.action == 2 ? R.drawable.ic_check_md2 : R.drawable.ic_close_md2}"
bullet="@{item.log.action >= 2 ? R.drawable.ic_check_md2 : R.drawable.ic_close_md2}"
isBottom="@{item.isBottom}"
isSelected="@{item.log.action != 2}"
isTop="@{item.isTop}"

View File

@@ -27,10 +27,8 @@
isEnabled="@{!item.removed &amp;&amp; item.enabled &amp;&amp; !item.showNotice}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="@{!item.removed &amp;&amp; item.enabled &amp;&amp; !item.showNotice}"
android:focusable="@{!item.removed &amp;&amp; item.enabled &amp;&amp; !item.showNotice}"
android:nextFocusRight="@id/module_indicator"
android:onClick="@{() -> item.setEnabled(!item.enabled)}"
app:cardBackgroundColor="@color/color_card_background_color_selector"
tools:isEnabled="false"
tools:layout_gravity="center"

View File

@@ -5,6 +5,8 @@
<data>
<import type="com.topjohnwu.magisk.databinding.DataBindingAdaptersKt" />
<variable
name="item"
type="com.topjohnwu.magisk.ui.superuser.PolicyRvItem" />
@@ -85,16 +87,32 @@
app:layout_constraintVertical_bias="0"
tools:text="com.topjohnwu.magisk" />
<com.google.android.material.switchmaterial.SwitchMaterial
<FrameLayout
android:id="@+id/policy_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/l1"
android:checked="@={item.enabled}"
android:nextFocusLeft="@id/policy"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.switchmaterial.SwitchMaterial
gone="@{item.showSlider}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="@={item.enabled}" />
<com.google.android.material.slider.Slider
goneUnless="@{item.showSlider}"
labelFormatter="@{item.sliderValueToPolicyString}"
android:layout_width="96dp"
android:layout_height="wrap_content"
android:stepSize="1"
android:value="@={DataBindingAdaptersKt.policyToSliderValue(item.sliderValue)}"
android:valueFrom="1"
android:valueTo="3" />
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -18,17 +18,12 @@ gradlePlugin {
}
}
kotlin {
compilerOptions {
languageVersion = KotlinVersion.KOTLIN_2_0
}
}
dependencies {
implementation(kotlin("gradle-plugin", libs.versions.kotlin.get()))
implementation(libs.android.gradle.plugin)
implementation(libs.ksp.plugin)
implementation(libs.navigation.safe.args.plugin)
implementation(libs.lsparanoid.plugin)
implementation(libs.moshi.plugin)
implementation(libs.jgit)
}

View File

@@ -0,0 +1,77 @@
import com.android.build.api.artifact.ArtifactTransformationRequest
import com.android.build.api.dsl.ApkSigningConfig
import com.android.builder.internal.packaging.IncrementalPackager
import com.android.tools.build.apkzlib.sign.SigningExtension
import com.android.tools.build.apkzlib.sign.SigningOptions
import com.android.tools.build.apkzlib.zfile.ZFiles
import com.android.tools.build.apkzlib.zip.ZFileOptions
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import java.io.File
import java.security.KeyStore
import java.security.cert.X509Certificate
import java.util.jar.JarFile
abstract class AddCommentTask: DefaultTask() {
@get:Input
abstract val comment: Property<String>
@get:Input
abstract val signingConfig: Property<ApkSigningConfig>
@get:InputFiles
abstract val apkFolder: DirectoryProperty
@get:OutputDirectory
abstract val outFolder: DirectoryProperty
@get:Internal
abstract val transformationRequest: Property<ArtifactTransformationRequest<AddCommentTask>>
@TaskAction
fun taskAction() = transformationRequest.get().submit(this) { artifact ->
val inFile = File(artifact.outputFile)
val outFile = outFolder.file(inFile.name).get().asFile
val privateKey = signingConfig.get().getPrivateKey()
val signingOptions = SigningOptions.builder()
.setMinSdkVersion(0)
.setV1SigningEnabled(true)
.setV2SigningEnabled(true)
.setKey(privateKey.privateKey)
.setCertificates(privateKey.certificate as X509Certificate)
.setValidation(SigningOptions.Validation.ASSUME_INVALID)
.build()
val options = ZFileOptions().apply {
noTimestamps = true
autoSortFiles = true
}
outFile.parentFile?.mkdirs()
inFile.copyTo(outFile, overwrite = true)
ZFiles.apk(outFile, options).use {
SigningExtension(signingOptions).register(it)
it.eocdComment = comment.get().toByteArray()
it.get(IncrementalPackager.APP_METADATA_ENTRY_PATH)?.delete()
it.get(IncrementalPackager.VERSION_CONTROL_INFO_ENTRY_PATH)?.delete()
it.get(JarFile.MANIFEST_NAME)?.delete()
}
outFile
}
private fun ApkSigningConfig.getPrivateKey(): KeyStore.PrivateKeyEntry {
val keyStore = KeyStore.getInstance(storeType ?: KeyStore.getDefaultType())
storeFile!!.inputStream().use {
keyStore.load(it, storePassword!!.toCharArray())
}
val keyPwdArray = keyPassword!!.toCharArray()
val entry = keyStore.getEntry(keyAlias!!, KeyStore.PasswordProtection(keyPwdArray))
return entry as KeyStore.PrivateKeyEntry
}
}

View File

@@ -5,6 +5,12 @@ import org.gradle.api.Project
import org.gradle.kotlin.dsl.provideDelegate
import java.io.File
import java.util.Properties
import java.util.Random
// Set non-zero value here to fix the random seed for reproducible builds
// CI builds are always reproducible
val RAND_SEED = if (System.getenv("CI") != null) 42 else 0
lateinit var RANDOM: Random
private val props = Properties()
private var commitHash = ""
@@ -28,7 +34,11 @@ object Config {
}
}
val Project.baseDir: File get() = rootProject.file("..")
fun Project.rootFile(path: String): File {
val file = File(path)
return if (file.isAbsolute) file
else File(rootProject.file(".."), path)
}
class MagiskPlugin : Plugin<Project> {
override fun apply(project: Project) = project.applyPlugin()
@@ -38,11 +48,11 @@ class MagiskPlugin : Plugin<Project> {
props.clear()
rootProject.file("gradle.properties").inputStream().use { props.load(it) }
val configPath: String? by this
val config = configPath?.let { File(it) } ?: File(baseDir, "config.prop")
val config = rootFile(configPath ?: "config.prop")
if (config.exists())
config.inputStream().use { props.load(it) }
val repo = FileRepository(File(baseDir, ".git"))
val repo = FileRepository(rootFile(".git"))
val refId = repo.refDatabase.exactRef("HEAD").objectId
commitHash = repo.newObjectReader().abbreviate(refId, 8).name()
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,11 @@
import com.android.build.api.artifact.SingleArtifact
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Delete
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
@@ -10,22 +13,25 @@ import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.TaskAction
import org.gradle.kotlin.dsl.assign
import org.gradle.kotlin.dsl.named
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.PrintStream
import java.security.SecureRandom
import java.util.Random
import java.util.zip.Deflater
import java.util.zip.DeflaterOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipOutputStream
import javax.crypto.Cipher
import javax.crypto.CipherOutputStream
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
import kotlin.random.asKotlinRandom
// Set non-zero value here to fix the random seed for reproducible builds
// CI builds are always reproducible
val RAND_SEED = if (System.getenv("CI") != null) 42 else 0
private lateinit var RANDOM: Random
private val kRANDOM get() = RANDOM.asKotlinRandom()
private val c1 = mutableListOf<String>()
@@ -72,7 +78,7 @@ private fun PrintStream.byteField(name: String, bytes: ByteArray) {
}
@CacheableTask
abstract class ManifestUpdater: DefaultTask() {
private abstract class ManifestUpdater: DefaultTask() {
@get:Input
abstract val applicationId: Property<String>
@@ -182,9 +188,7 @@ abstract class ManifestUpdater: DefaultTask() {
}
fun genStubClasses(factoryOutDir: File, appOutDir: File) {
fun String.ind(level: Int) = replaceIndentByMargin(" ".repeat(level))
private fun genStubClasses(factoryOutDir: File, appOutDir: File) {
val classNameGenerator = sequence {
fun notJavaKeyword(name: String) = when (name) {
"do", "if", "for", "int", "new", "try" -> false
@@ -228,7 +232,7 @@ fun genStubClasses(factoryOutDir: File, appOutDir: File) {
genClass("StubApplication", appOutDir)
}
fun genEncryptedResources(res: ByteArray, outDir: File) {
private fun genEncryptedResources(res: ByteArray, outDir: File) {
val mainPkgDir = File(outDir, "com/topjohnwu/magisk")
mainPkgDir.mkdirs()
@@ -259,3 +263,86 @@ fun genEncryptedResources(res: ByteArray, outDir: File) {
it.println("}")
}
}
fun Project.setupStubApk() {
setupAppCommon()
androidComponents.onVariants { variant ->
val variantName = variant.name
val variantCapped = variantName.replaceFirstChar { it.uppercase() }
val manifestUpdater =
project.tasks.register("${variantName}ManifestProducer", ManifestUpdater::class.java) {
dependsOn("generate${variantCapped}ObfuscatedClass")
applicationId = variant.applicationId
appClassDir.set(layout.buildDirectory.dir("generated/source/app/$variantName"))
factoryClassDir.set(layout.buildDirectory.dir("generated/source/factory/$variantName"))
}
variant.artifacts.use(manifestUpdater)
.wiredWithFiles(
ManifestUpdater::mergedManifest,
ManifestUpdater::outputManifest)
.toTransform(SingleArtifact.MERGED_MANIFEST)
}
androidApp.applicationVariants.all {
val variantCapped = name.replaceFirstChar { it.uppercase() }
val variantLowered = name.lowercase()
val outFactoryClassDir = layout.buildDirectory.file("generated/source/factory/${variantLowered}").get().asFile
val outAppClassDir = layout.buildDirectory.file("generated/source/app/${variantLowered}").get().asFile
val outResDir = layout.buildDirectory.dir("generated/source/res/${variantLowered}").get().asFile
val aapt = File(androidApp.sdkDirectory, "build-tools/${androidApp.buildToolsVersion}/aapt2")
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
"${variantLowered}/process${variantCapped}Resources/linked-resources-binary-format-${variantLowered}.ap_").get().asFile
val genManifestTask = tasks.register("generate${variantCapped}ObfuscatedClass") {
inputs.property("seed", RAND_SEED)
outputs.dirs(outFactoryClassDir, outAppClassDir)
doLast {
outFactoryClassDir.mkdirs()
outAppClassDir.mkdirs()
genStubClasses(outFactoryClassDir, outAppClassDir)
}
}
registerJavaGeneratingTask(genManifestTask, outFactoryClassDir, outAppClassDir)
val processResourcesTask = tasks.named("process${variantCapped}Resources") {
outputs.dir(outResDir)
doLast {
val apkTmp = File("${apk}.tmp")
providers.exec {
commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
}.result.get()
val bos = ByteArrayOutputStream()
ZipFile(apkTmp).use { src ->
ZipOutputStream(apk.outputStream()).use {
it.setLevel(Deflater.BEST_COMPRESSION)
it.putNextEntry(ZipEntry("AndroidManifest.xml"))
src.getInputStream(src.getEntry("AndroidManifest.xml")).transferTo(it)
it.closeEntry()
}
DeflaterOutputStream(bos, Deflater(Deflater.BEST_COMPRESSION)).use {
src.getInputStream(src.getEntry("resources.arsc")).transferTo(it)
}
}
apkTmp.delete()
genEncryptedResources(bos.toByteArray(), outResDir)
}
}
registerJavaGeneratingTask(processResourcesTask, outResDir)
}
// Override optimizeReleaseResources task
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
"release/processReleaseResources/linked-resources-binary-format-release.ap_").get().asFile
val optRes = layout.buildDirectory.file("intermediates/optimized_processed_res/" +
"release/optimizeReleaseResources/resources-release-optimize.ap_").get().asFile
afterEvaluate {
tasks.named("optimizeReleaseResources") {
doLast { apk.copyTo(optRes, true) }
}
}
tasks.named<Delete>("clean") {
delete.addAll(listOf("src/debug/AndroidManifest.xml", "src/release/AndroidManifest.xml"))
}
}

5
app/core/.gitignore vendored
View File

@@ -1,4 +1,3 @@
/build
src/*/assets
src/*/jniLibs
src/*/resources
src/debug
src/release

View File

@@ -2,6 +2,7 @@ plugins {
id("com.android.library")
kotlin("android")
kotlin("plugin.parcelize")
id("dev.zacsweers.moshix")
id("com.google.devtools.ksp")
}
@@ -26,10 +27,15 @@ android {
aidl = true
buildConfig = true
}
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
}
dependencies {
api(project(":shared"))
coreLibraryDesugaring(libs.jdk.libs)
api(libs.timber)
api(libs.markwon.core)
@@ -48,9 +54,6 @@ dependencies {
implementation(libs.okhttp.logging)
implementation(libs.okhttp.dnsoverhttps)
implementation(libs.moshi)
ksp(libs.moshi.codegen)
implementation(libs.room.runtime)
implementation(libs.room.ktx)
ksp(libs.room.compiler)

View File

@@ -38,3 +38,4 @@
-allowaccessmodification
-dontwarn org.junit.**
-dontwarn org.apache.**

View File

@@ -6,4 +6,5 @@ package com.topjohnwu.magisk.core.utils;
interface IRootUtils {
android.app.ActivityManager.RunningAppProcessInfo getAppProcess(int pid);
IBinder getFileSystem();
boolean addSystemlessHosts();
}

View File

@@ -32,8 +32,9 @@ object Config : PreferenceConfig, DBConfig {
const val SU_NOTIFICATION = "su_notification"
const val SU_REAUTH = "su_reauth"
const val SU_TAPJACK = "su_tapjack"
const val SU_RESTRICT = "su_restrict"
const val CHECK_UPDATES = "check_update"
const val UPDATE_CHANNEL = "update_channel"
const val RELEASE_CHANNEL = "release_channel"
const val CUSTOM_CHANNEL = "custom_channel"
const val LOCALE = "locale"
const val DARK_THEME = "dark_theme_extended"
@@ -48,7 +49,7 @@ object Config : PreferenceConfig, DBConfig {
SU_AUTO_RESPONSE, SU_REAUTH, SU_TAPJACK)
}
object Value {
object OldValue {
// Update channels
const val DEFAULT_CHANNEL = -1
const val STABLE_CHANNEL = 0
@@ -56,6 +57,15 @@ object Config : PreferenceConfig, DBConfig {
const val CUSTOM_CHANNEL = 2
const val CANARY_CHANNEL = 3
const val DEBUG_CHANNEL = 4
}
object Value {
// Update channels
const val DEFAULT_CHANNEL = -1
const val STABLE_CHANNEL = 0
const val BETA_CHANNEL = 1
const val DEBUG_CHANNEL = 2
const val CUSTOM_CHANNEL = 3
// root access mode
const val ROOT_ACCESS_DISABLED = 0
@@ -86,14 +96,6 @@ object Config : PreferenceConfig, DBConfig {
val TIMEOUT_LIST = longArrayOf(0, -1, 10, 20, 30, 60)
}
private val defaultChannel =
if (BuildConfig.DEBUG)
Value.DEBUG_CHANNEL
else if (Const.APP_IS_CANARY)
Value.CANARY_CHANNEL
else
Value.DEFAULT_CHANNEL
@JvmField var keepVerity = false
@JvmField var keepEnc = false
@JvmField var recovery = false
@@ -109,7 +111,7 @@ object Config : PreferenceConfig, DBConfig {
private var checkUpdatePrefs by preference(Key.CHECK_UPDATES, true)
private var localePrefs by preference(Key.LOCALE, "")
var doh by preference(Key.DOH, false)
var updateChannel by preferenceStrInt(Key.UPDATE_CHANNEL, defaultChannel)
var updateChannel by preference(Key.RELEASE_CHANNEL, Value.DEFAULT_CHANNEL)
var customChannelUrl by preference(Key.CUSTOM_CHANNEL, "")
var downloadDir by preference(Key.DOWNLOAD_DIR, "")
var randName by preference(Key.RAND_NAME, true)
@@ -146,8 +148,10 @@ object Config : PreferenceConfig, DBConfig {
}
var suReAuth by preference(Key.SU_REAUTH, false)
var suTapjack by preference(Key.SU_TAPJACK, true)
var suRestrict by preference(Key.SU_RESTRICT, false)
private const val SU_FINGERPRINT = "su_fingerprint"
private const val UPDATE_CHANNEL = "update_channel"
fun toBundle(): Bundle {
val map = prefs.all - Key.NO_MIGRATION
@@ -183,17 +187,23 @@ object Config : PreferenceConfig, DBConfig {
}
prefs.edit {
// Settings migration
// Migrate su_fingerprint
if (prefs.getBoolean(SU_FINGERPRINT, false))
suBiometric = true
remove(SU_FINGERPRINT)
prefs.getString(Key.UPDATE_CHANNEL, null).also {
if (it == null ||
it.toInt() > Value.DEBUG_CHANNEL ||
it.toInt() < Value.DEFAULT_CHANNEL) {
putString(Key.UPDATE_CHANNEL, defaultChannel.toString())
// Migrate update_channel
prefs.getString(UPDATE_CHANNEL, null)?.let {
val channel = when (it.toInt()) {
OldValue.STABLE_CHANNEL -> Value.STABLE_CHANNEL
OldValue.CANARY_CHANNEL, OldValue.BETA_CHANNEL -> Value.BETA_CHANNEL
OldValue.DEBUG_CHANNEL -> Value.DEBUG_CHANNEL
OldValue.CUSTOM_CHANNEL -> Value.CUSTOM_CHANNEL
else -> Value.DEFAULT_CHANNEL
}
putInt(Key.RELEASE_CHANNEL, channel)
}
remove(UPDATE_CHANNEL)
}
}
}

View File

@@ -2,6 +2,7 @@ package com.topjohnwu.magisk.core
import android.os.Build
import android.os.Process
import com.topjohnwu.magisk.core.BuildConfig.APP_VERSION_CODE
@Suppress("DEPRECATION")
object Const {
@@ -20,18 +21,16 @@ object Const {
// Misc
val USER_ID = Process.myUid() / 100000
val APP_IS_CANARY get() = Version.isCanary(BuildConfig.APP_VERSION_CODE)
object Version {
const val MIN_VERSION = "v22.0"
const val MIN_VERCODE = 22000
private fun isCanary() = (Info.env.versionCode % 100) != 0
fun atLeast_24_0() = Info.env.versionCode >= 24000 || isCanary()
fun atLeast_25_0() = Info.env.versionCode >= 25000 || isCanary()
fun atLeast_28_0() = Info.env.versionCode >= 28000 || isCanary()
fun isCanary() = isCanary(Info.env.versionCode)
fun isCanary(ver: Int) = ver > 0 && ver % 100 != 0
fun atLeast_30_1() = Info.env.versionCode >= 30100 || isCanary()
}
object ID {
@@ -43,13 +42,9 @@ object Const {
const val PATREON_URL = "https://www.patreon.com/topjohnwu"
const val SOURCE_CODE_URL = "https://github.com/topjohnwu/Magisk"
val CHANGELOG_URL = if (APP_IS_CANARY) Info.remote.magisk.note
else "https://topjohnwu.github.io/Magisk/releases/${BuildConfig.APP_VERSION_CODE}.md"
const val GITHUB_RAW_URL = "https://raw.githubusercontent.com/"
const val GITHUB_API_URL = "https://api.github.com/"
const val GITHUB_PAGE_URL = "https://topjohnwu.github.io/magisk-files/"
const val JS_DELIVR_URL = "https://cdn.jsdelivr.net/gh/"
const val INVALID_URL = "https://example.com/"
}
object Key {

View File

@@ -19,12 +19,18 @@ object Info {
var stub: StubApk.Data? = null
val EMPTY_REMOTE = UpdateInfo()
var remote = EMPTY_REMOTE
suspend fun getRemote(svc: NetworkService): UpdateInfo? {
return if (remote === EMPTY_REMOTE) {
svc.fetchUpdate()?.apply { remote = this }
} else remote
private val EMPTY_UPDATE = UpdateInfo()
var update = EMPTY_UPDATE
private set
suspend fun fetchUpdate(svc: NetworkService): UpdateInfo? {
return if (update === EMPTY_UPDATE) {
svc.fetchUpdate()?.apply { update = this }
} else update
}
fun resetUpdate() {
update = EMPTY_UPDATE
}
var isRooted = false
@@ -41,6 +47,8 @@ object Info {
private set
var slot = ""
private set
var isVendorBoot = false
private set
@JvmField val isZygiskEnabled = System.getenv("ZYGISK_ENABLED") == "1"
@JvmStatic val isFDE get() = crypto == "block"
@JvmStatic var ramdisk = false
@@ -107,6 +115,7 @@ object Info {
crypto = getVar("CRYPTOTYPE")
slot = getVar("SLOT")
legacySAR = getBool("LEGACYSAR")
isVendorBoot = getBool("VENDORBOOT")
// Default presets
Config.recovery = getBool("RECOVERYMODE")

View File

@@ -75,9 +75,8 @@ class JobService : BaseJobService() {
private fun checkUpdate(params: JobParameters): Boolean {
GlobalScope.launch(Dispatchers.IO) {
ServiceLocator.networkService.fetchUpdate()?.let {
Info.remote = it
if (Info.env.isActive && BuildConfig.APP_VERSION_CODE < it.magisk.versionCode)
Info.fetchUpdate(ServiceLocator.networkService)?.let {
if (Info.env.isActive && BuildConfig.APP_VERSION_CODE < it.versionCode)
Notifications.updateAvailable()
jobFinished(params, false)
}

View File

@@ -1,45 +0,0 @@
package com.topjohnwu.magisk.core.data
import com.topjohnwu.magisk.core.model.BranchInfo
import com.topjohnwu.magisk.core.model.ModuleJson
import com.topjohnwu.magisk.core.model.UpdateInfo
import okhttp3.ResponseBody
import retrofit2.http.GET
import retrofit2.http.Headers
import retrofit2.http.Path
import retrofit2.http.Streaming
import retrofit2.http.Url
private const val BRANCH = "branch"
private const val REPO = "repo"
private const val FILE = "file"
interface GithubPageServices {
@GET
suspend fun fetchUpdateJSON(@Url file: String): UpdateInfo
}
interface RawServices {
@GET
@Streaming
suspend fun fetchFile(@Url url: String): ResponseBody
@GET
suspend fun fetchString(@Url url: String): String
@GET
suspend fun fetchModuleJson(@Url url: String): ModuleJson
}
interface GithubApiServices {
@GET("repos/{$REPO}/branches/{$BRANCH}")
@Headers("Accept: application/vnd.github.v3+json")
suspend fun fetchBranch(
@Path(REPO, encoded = true) repo: String,
@Path(BRANCH) branch: String
): BranchInfo
}

View File

@@ -0,0 +1,48 @@
package com.topjohnwu.magisk.core.data
import com.topjohnwu.magisk.core.model.ModuleJson
import com.topjohnwu.magisk.core.model.Release
import com.topjohnwu.magisk.core.model.UpdateJson
import okhttp3.ResponseBody
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Headers
import retrofit2.http.Path
import retrofit2.http.Query
import retrofit2.http.Streaming
import retrofit2.http.Url
interface RawUrl {
@GET
@Streaming
suspend fun fetchFile(@Url url: String): ResponseBody
@GET
suspend fun fetchString(@Url url: String): String
@GET
suspend fun fetchModuleJson(@Url url: String): ModuleJson
@GET
suspend fun fetchUpdateJson(@Url url: String): UpdateJson
}
interface GithubApiServices {
@GET("/repos/{owner}/{repo}/releases")
@Headers("Accept: application/vnd.github+json")
suspend fun fetchReleases(
@Path("owner") owner: String = "topjohnwu",
@Path("repo") repo: String = "Magisk",
@Query("per_page") per: Int = 10,
@Query("page") page: Int = 1,
): Response<MutableList<Release>>
@GET("/repos/{owner}/{repo}/releases/latest")
@Headers("Accept: application/vnd.github+json")
suspend fun fetchLatestRelease(
@Path("owner") owner: String = "topjohnwu",
@Path("repo") repo: String = "Magisk",
): Release
}

View File

@@ -5,6 +5,7 @@ import com.squareup.moshi.Moshi
import com.topjohnwu.magisk.ProviderInstaller
import com.topjohnwu.magisk.core.BuildConfig
import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.model.DateTimeAdapter
import com.topjohnwu.magisk.core.utils.LocaleSetting
import okhttp3.Cache
import okhttp3.ConnectionSpec
@@ -77,7 +78,7 @@ fun createOkHttpClient(context: Context): OkHttpClient {
}
fun createMoshiConverterFactory(): MoshiConverterFactory {
val moshi = Moshi.Builder().build()
val moshi = Moshi.Builder().add(DateTimeAdapter()).build()
return MoshiConverterFactory.create(moshi)
}

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