mirror of
https://github.com/topjohnwu/Magisk
synced 2025-10-27 04:02:14 +01:00
Compare commits
471 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0f2164bc5 | ||
|
|
f6e4a27fdd | ||
|
|
257ceb99f7 | ||
|
|
706d53065b | ||
|
|
0f95a7babe | ||
|
|
7cb2806878 | ||
|
|
9c0e18975c | ||
|
|
3da318b48e | ||
|
|
dfe1f2c108 | ||
|
|
f42a87b51a | ||
|
|
ab25857176 | ||
|
|
7da36079c1 | ||
|
|
2bef967af1 | ||
|
|
7e4194418a | ||
|
|
aa02057895 | ||
|
|
fb8dc07599 | ||
|
|
66e30a7723 | ||
|
|
0298ab99c4 | ||
|
|
d11358671e | ||
|
|
8b5cb4c7b0 | ||
|
|
aad52ae743 | ||
|
|
8ddab84745 | ||
|
|
6865652125 | ||
|
|
ed4d0867e8 | ||
|
|
1c71e02454 | ||
|
|
f332e87cab | ||
|
|
023dbc6cb5 | ||
|
|
4dd3f55407 | ||
|
|
7b9a71c9af | ||
|
|
901d22cdfa | ||
|
|
93e1266ee7 | ||
|
|
0a4e7eea41 | ||
|
|
e3801d6965 | ||
|
|
336f1687c1 | ||
|
|
d4e2f2df6e | ||
|
|
f152b4c26e | ||
|
|
bd935b0553 | ||
|
|
a9b3b7a359 | ||
|
|
7a007b342a | ||
|
|
0783f3d5b6 | ||
|
|
afe3c2bc1b | ||
|
|
82f8948fd4 | ||
|
|
b9cdc755d1 | ||
|
|
a6f81c66e5 | ||
|
|
1ff45ac5f5 | ||
|
|
48bde7375f | ||
|
|
0601fa3b3d | ||
|
|
d0d3c8dbfd | ||
|
|
8057de1973 | ||
|
|
43c1105d62 | ||
|
|
6adf516b30 | ||
|
|
bf80b08b5f | ||
|
|
3e0b1df46d | ||
|
|
84811c80b6 | ||
|
|
45e0df9c57 | ||
|
|
bc51ce7c7b | ||
|
|
b693d13b93 | ||
|
|
39982d57ef | ||
|
|
15e27e54fb | ||
|
|
851404205b | ||
|
|
117ae71025 | ||
|
|
027ec70262 | ||
|
|
55fdee4d65 | ||
|
|
0d42f937dd | ||
|
|
ac8372dd26 | ||
|
|
5e56a6bbee | ||
|
|
3c6c409df0 | ||
|
|
d05408c89f | ||
|
|
ee0ec3fbfa | ||
|
|
122a73e086 | ||
|
|
29a9b18c4c | ||
|
|
1561272109 | ||
|
|
3e61ab0d25 | ||
|
|
a49dc6ccb7 | ||
|
|
60f3d62f00 | ||
|
|
e613855a4f | ||
|
|
22662d7e03 | ||
|
|
6e7e5be1a2 | ||
|
|
8b2ab778c9 | ||
|
|
35f3766ecf | ||
|
|
995304dabb | ||
|
|
803982a271 | ||
|
|
9164bf22c2 | ||
|
|
911a576893 | ||
|
|
79ee85c0f9 | ||
|
|
483dbcdc40 | ||
|
|
a1096b5bf0 | ||
|
|
5ac0e64edb | ||
|
|
60b2624607 | ||
|
|
d2e2847b03 | ||
|
|
b9669f54f7 | ||
|
|
8c7bd77d33 | ||
|
|
ba1ce16b8b | ||
|
|
68090943f4 | ||
|
|
a4fb1297b0 | ||
|
|
860a05abf2 | ||
|
|
8bb2f356c0 | ||
|
|
4950020635 | ||
|
|
0a6140c6eb | ||
|
|
bba2ac8817 | ||
|
|
331b1f542f | ||
|
|
ccb55205e6 | ||
|
|
9cc91b30b3 | ||
|
|
e836caf31e | ||
|
|
beaa1e5be2 | ||
|
|
ea545bae26 | ||
|
|
1c9ec2df45 | ||
|
|
b76c80e2ce | ||
|
|
236990f4a3 | ||
|
|
1ed32df20d | ||
|
|
8476eb9f4b | ||
|
|
735af7843b | ||
|
|
ded73e958b | ||
|
|
6dcb84d4f4 | ||
|
|
501bc9f438 | ||
|
|
f88e812b63 | ||
|
|
be6386c410 | ||
|
|
2af4fd17c4 | ||
|
|
f870418bd0 | ||
|
|
00659e4795 | ||
|
|
cdda10207e | ||
|
|
701700279f | ||
|
|
a9d804724a | ||
|
|
e033a9ab47 | ||
|
|
059e5fb8aa | ||
|
|
a78f255928 | ||
|
|
1d10e69288 | ||
|
|
63590d379c | ||
|
|
5f63e88984 | ||
|
|
75584e2b19 | ||
|
|
1426ee2ebd | ||
|
|
b6643b7bfc | ||
|
|
721dfdf553 | ||
|
|
2963747d14 | ||
|
|
e7350d5041 | ||
|
|
f37e8f4ca8 | ||
|
|
594c2accc0 | ||
|
|
7acfac6a91 | ||
|
|
0646f48e14 | ||
|
|
37565fd067 | ||
|
|
26b2e7dc5d | ||
|
|
c3313623e4 | ||
|
|
2089223690 | ||
|
|
52e1b84d41 | ||
|
|
8794141b7f | ||
|
|
f6126dd20e | ||
|
|
18acfda99b | ||
|
|
bec5edca84 | ||
|
|
6fb20b3ee5 | ||
|
|
eaf4d8064b | ||
|
|
2a5f5b1bba | ||
|
|
c538a77937 | ||
|
|
aa9e7b1ed1 | ||
|
|
a3066eddab | ||
|
|
d1729fa787 | ||
|
|
93961dde2c | ||
|
|
1024e68eb6 | ||
|
|
6ae2c9387d | ||
|
|
fba83e2330 | ||
|
|
f1295cb7d6 | ||
|
|
dc61dfbde6 | ||
|
|
21466426da | ||
|
|
3f0136362b | ||
|
|
e92d77bbec | ||
|
|
07bd36c94b | ||
|
|
b1dbbdef12 | ||
|
|
3e479726ec | ||
|
|
4cc41eccb3 | ||
|
|
8f08ae59ac | ||
|
|
e8d4e492d6 | ||
|
|
b8090a8e18 | ||
|
|
c609a01e55 | ||
|
|
c97fb385cd | ||
|
|
da6c57750e | ||
|
|
6951d926f7 | ||
|
|
6623195bd5 | ||
|
|
ec31bb9a82 | ||
|
|
8618cc383a | ||
|
|
4b01e3a3c7 | ||
|
|
7f748c23c1 | ||
|
|
963d248cc7 | ||
|
|
657056e636 | ||
|
|
9d5efea66e | ||
|
|
658d74e026 | ||
|
|
5113f6d375 | ||
|
|
96405c26d0 | ||
|
|
4ea5f34bf3 | ||
|
|
dbd13a2019 | ||
|
|
06773235da | ||
|
|
e57556a8af | ||
|
|
b54b78c29d | ||
|
|
317336f771 | ||
|
|
b4e52f6135 | ||
|
|
f2ca042915 | ||
|
|
1060dd2906 | ||
|
|
2e0f7a82fa | ||
|
|
5798536559 | ||
|
|
ab9a83c82f | ||
|
|
c87fdbea0f | ||
|
|
ec8fffe61c | ||
|
|
61d52991f1 | ||
|
|
9100186dce | ||
|
|
d2bc2cfcf8 | ||
|
|
5a71998b4e | ||
|
|
42278f12ff | ||
|
|
f5593e051c | ||
|
|
a27e30cf54 | ||
|
|
79140c7636 | ||
|
|
1f4c595cd3 | ||
|
|
b5b62e03af | ||
|
|
67e2a4720e | ||
|
|
f5c2d72429 | ||
|
|
2f5331ab48 | ||
|
|
7f8257152f | ||
|
|
0cd80f2556 | ||
|
|
1717387876 | ||
|
|
109363ebf6 | ||
|
|
716c4fa386 | ||
|
|
9a09b4eb20 | ||
|
|
95a5b57265 | ||
|
|
13fbf397d1 | ||
|
|
20be99ec8a | ||
|
|
04c53c3578 | ||
|
|
51bc27a869 | ||
|
|
71b083794c | ||
|
|
b100d0c503 | ||
|
|
76061296c9 | ||
|
|
bb303d2da1 | ||
|
|
c91c070343 | ||
|
|
aec06a6f61 | ||
|
|
e8ba671fc2 | ||
|
|
1860e5d133 | ||
|
|
f2cb3c38fe | ||
|
|
9a28dd4f6e | ||
|
|
d2acd59ea8 | ||
|
|
79dfdb29e7 | ||
|
|
eb21c8b42e | ||
|
|
541bb53553 | ||
|
|
fe8997efae | ||
|
|
23455c722c | ||
|
|
5ce29c30d2 | ||
|
|
70d67728fd | ||
|
|
e546884b08 | ||
|
|
b36e6d987d | ||
|
|
53c3dd5e8b | ||
|
|
da723b207a | ||
|
|
e050f77198 | ||
|
|
540b4b7ea9 | ||
|
|
bbef22daf7 | ||
|
|
9ed110c91b | ||
|
|
a30d510eb1 | ||
|
|
ef98eaed8f | ||
|
|
2a257f327c | ||
|
|
4060c2107c | ||
|
|
cd23d27048 | ||
|
|
18b86e4fd2 | ||
|
|
5f2e22a259 | ||
|
|
4e97b18977 | ||
|
|
f9bde347bc | ||
|
|
947a7d6a2f | ||
|
|
872ab2e99b | ||
|
|
90b8813bb7 | ||
|
|
88d0f63294 | ||
|
|
79fa0d3a90 | ||
|
|
8e61080a4a | ||
|
|
3f9a64417b | ||
|
|
eb959379e8 | ||
|
|
41a644afb9 | ||
|
|
6b42db943d | ||
|
|
1c325459eb | ||
|
|
6d88d8ad95 | ||
|
|
246997f273 | ||
|
|
b6144ae582 | ||
|
|
afe17c73b4 | ||
|
|
b51b884fc7 | ||
|
|
d3e4b29e62 | ||
|
|
24059e7403 | ||
|
|
107a2a6682 | ||
|
|
56b4ab6672 | ||
|
|
4662454938 | ||
|
|
db4f78d463 | ||
|
|
880de21596 | ||
|
|
622dd84c9e | ||
|
|
f983bfc883 | ||
|
|
45cdb3fdb0 | ||
|
|
9a707236b8 | ||
|
|
e9e6ad3bb0 | ||
|
|
ab78a81d15 | ||
|
|
18340099b7 | ||
|
|
a013696a41 | ||
|
|
8a2a6d9232 | ||
|
|
12aa6d86e4 | ||
|
|
7d08969d28 | ||
|
|
dda4aa8488 | ||
|
|
cdaef3d801 | ||
|
|
9159166128 | ||
|
|
dc0882e043 | ||
|
|
c811f015ef | ||
|
|
d8f0b66fe1 | ||
|
|
dc3d57deba | ||
|
|
d089698475 | ||
|
|
8ed2dd6687 | ||
|
|
50305ca1fe | ||
|
|
3e91567636 | ||
|
|
0b4dd63d36 | ||
|
|
38d0f85deb | ||
|
|
c5b452f369 | ||
|
|
6ce9225f52 | ||
|
|
13a8820603 | ||
|
|
503997a09a | ||
|
|
17efdff134 | ||
|
|
984f32f994 | ||
|
|
eee7f097e3 | ||
|
|
086059ec30 | ||
|
|
7ff22c68c7 | ||
|
|
1232113772 | ||
|
|
039d4936cb | ||
|
|
784dd80965 | ||
|
|
1ffe9bd83b | ||
|
|
0c28b23224 | ||
|
|
ec1af9dc1e | ||
|
|
ff4cea229a | ||
|
|
3f81f9371f | ||
|
|
60e89a7d22 | ||
|
|
c50daa5c9e | ||
|
|
58d00ab863 | ||
|
|
ce916459c5 | ||
|
|
4094d560ab | ||
|
|
4dbf7eb04b | ||
|
|
a39577c44d | ||
|
|
125ee46685 | ||
|
|
ce84f1762c | ||
|
|
a687d1347b | ||
|
|
6d9db20614 | ||
|
|
c62dfc1bcc | ||
|
|
aabe2696fe | ||
|
|
ae0d605310 | ||
|
|
2a694596b5 | ||
|
|
ff0a76606e | ||
|
|
dead74801d | ||
|
|
ab207a1bb3 | ||
|
|
f152e8c33d | ||
|
|
797ba4fbf4 | ||
|
|
a848f10bba | ||
|
|
552ec1eb35 | ||
|
|
1385d2a4f4 | ||
|
|
3b5c9abf7a | ||
|
|
e0fa032bd3 | ||
|
|
7b69650fcd | ||
|
|
08a8df489f | ||
|
|
9f35a8a520 | ||
|
|
0df891b336 | ||
|
|
385853a290 | ||
|
|
fa3ef8a1c1 | ||
|
|
c93ada03c7 | ||
|
|
0064b01ae0 | ||
|
|
1469b82aa2 | ||
|
|
2d5cf8a6fe | ||
|
|
290959f74c | ||
|
|
4d9f58ee72 | ||
|
|
9241246de6 | ||
|
|
58a5d52b78 | ||
|
|
2906178ac3 | ||
|
|
e0afbb647b | ||
|
|
50be50cf6a | ||
|
|
77a9d3a5bc | ||
|
|
f9c7a4c933 | ||
|
|
2b759b84b0 | ||
|
|
1e45c63ea5 | ||
|
|
b14a260827 | ||
|
|
ade1597e03 | ||
|
|
2739d3cb67 | ||
|
|
dc5e78e142 | ||
|
|
e9759a5868 | ||
|
|
e7ab802498 | ||
|
|
42672c2e27 | ||
|
|
e65d61d313 | ||
|
|
076da5c7c4 | ||
|
|
9deaf2507c | ||
|
|
5c114c67de | ||
|
|
d904cb0441 | ||
|
|
bd1dd9d863 | ||
|
|
afebe734b8 | ||
|
|
e21a78164e | ||
|
|
1e0f96d0fd | ||
|
|
bf650332d8 | ||
|
|
f32e0af830 | ||
|
|
4c94f90e5d | ||
|
|
ffb4224640 | ||
|
|
89fff4830b | ||
|
|
16e4c67992 | ||
|
|
cf47214ee4 | ||
|
|
0feab753fb | ||
|
|
d0b6318b90 | ||
|
|
966e23b846 | ||
|
|
5b8a1fc2a7 | ||
|
|
02ea3ca525 | ||
|
|
0632b146b8 | ||
|
|
1b0b180761 | ||
|
|
0d11f73a1d | ||
|
|
533cb8eb58 | ||
|
|
8ac1181e9a | ||
|
|
5ca1892eb0 | ||
|
|
e32db6a0e8 | ||
|
|
82fff615d6 | ||
|
|
24a8f0808d | ||
|
|
4a7c3c06bc | ||
|
|
da93bbc1fe | ||
|
|
fa2dbe981e | ||
|
|
ce6cceae8b | ||
|
|
7b26e8b818 | ||
|
|
2da5fcb00b | ||
|
|
a079966f97 | ||
|
|
468796c23d | ||
|
|
5833aadef5 | ||
|
|
eb261c8026 | ||
|
|
a4c48847d1 | ||
|
|
43288be091 | ||
|
|
1ad7a6fe93 | ||
|
|
4e0a3f5e72 | ||
|
|
d7c33f647d | ||
|
|
9087207dc0 | ||
|
|
2760f37e6b | ||
|
|
3fa3426032 | ||
|
|
2e4dc91b96 | ||
|
|
aaaaa3d044 | ||
|
|
1edc4449d5 | ||
|
|
f3cd4da026 | ||
|
|
872c55207c | ||
|
|
339ca6d666 | ||
|
|
4aeac3b8f4 | ||
|
|
d625beb7f3 | ||
|
|
735b65c50c | ||
|
|
efb1eab327 | ||
|
|
49d4785da0 | ||
|
|
28e65ce383 | ||
|
|
c3b6a48373 | ||
|
|
a42ebd429b | ||
|
|
8f89010752 | ||
|
|
105a18f719 | ||
|
|
eb04ca4c4a | ||
|
|
6092d7ca88 | ||
|
|
66cad101c0 | ||
|
|
0a14f43f9c | ||
|
|
311c1f0dfd | ||
|
|
0499588107 | ||
|
|
d4d837a562 | ||
|
|
fbcbb20178 | ||
|
|
0914700fc6 | ||
|
|
eeced2fb5b | ||
|
|
6509e3d4f5 | ||
|
|
317052604b | ||
|
|
5538f7168c | ||
|
|
dcb9e4cd93 | ||
|
|
d9382f59bf | ||
|
|
403a0c770a | ||
|
|
f0f1cdc501 | ||
|
|
4e272b70ef | ||
|
|
8dc62a0232 | ||
|
|
9225b47568 | ||
|
|
d462873e74 | ||
|
|
fc19b50290 | ||
|
|
333fe6da0e | ||
|
|
75fcda9f81 | ||
|
|
44ba2a9903 | ||
|
|
2fceb1ad96 | ||
|
|
bacb5fa462 | ||
|
|
67f8dc494e | ||
|
|
3e4caabecb | ||
|
|
dcd5183b24 | ||
|
|
d80c6b42a6 |
37
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
37
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
|
||||
## READ BEFORE OPENING ISSUES
|
||||
|
||||
All bug reports require you to **USE CANARY BUILDS**. Please include the version name and version code in the bug report.
|
||||
|
||||
If you experience a bootloop, attach a `dmesg` (kernel logs) when the device refuse to boot. This may very likely require a custom kernel on some devices as `last_kmsg` or `pstore ramoops` are usually not enabled by default. In addition, please also upload the result of `cat /proc/mounts` when your device is working correctly **WITHOUT ROOT**.
|
||||
|
||||
If you experience issues during installation, in recovery, upload the recovery logs, or in Magisk, upload the install logs. Please also upload the `boot.img` or `recovery.img` that you are using for patching.
|
||||
|
||||
If you experience a crash of Magisk app, dump the full `logcat` **when the crash happens**.
|
||||
|
||||
If you experience other issues related to Magisk, upload `magisk.log`, and preferably also include a boot `logcat` (start dumping `logcat` when the device boots up)
|
||||
|
||||
**DO NOT** open issues regarding root detection.
|
||||
|
||||
**DO NOT** ask for instructions.
|
||||
|
||||
**DO NOT** report issues if you have any modules installed.
|
||||
|
||||
Without following the rules above, your issue will be closed without explanation.
|
||||
|
||||
-->
|
||||
|
||||
Device:
|
||||
Android version:
|
||||
Magisk version name:
|
||||
Magisk version code:
|
||||
6
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
6
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: XDA Community Support
|
||||
url: https://forum.xda-developers.com/f/magisk.5903/
|
||||
about: Please ask and answer questions here.
|
||||
|
||||
90
.github/workflows/build.yml
vendored
Normal file
90
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
name: Magisk Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- 'app/**'
|
||||
- 'native/**'
|
||||
- 'stub/**'
|
||||
- 'buildSrc/**'
|
||||
- 'build.py'
|
||||
- 'gradle.properties'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ ubuntu-latest, windows-latest, macOS-latest ]
|
||||
|
||||
steps:
|
||||
- name: Check out
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
|
||||
- name: Set up Python 3
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Set up GitHub env (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
$ndk_ver = Select-String -Path "gradle.properties" -Pattern "^magisk.fullNdkVersion=" | % { $_ -replace ".*=" }
|
||||
echo "ANDROID_SDK_ROOT=$env:ANDROID_SDK_ROOT" >> $env:GITHUB_ENV
|
||||
echo "MAGISK_NDK_VERSION=$ndk_ver" >> $env:GITHUB_ENV
|
||||
echo "GRADLE_OPTS=-Dorg.gradle.daemon=false" >> $env:GITHUB_ENV
|
||||
|
||||
- name: Set up GitHub env (Unix)
|
||||
if: runner.os != 'Windows'
|
||||
run: |
|
||||
ndk_ver=$(sed -n 's/^magisk.fullNdkVersion=//p' gradle.properties)
|
||||
echo ANDROID_SDK_ROOT=$ANDROID_SDK_ROOT >> $GITHUB_ENV
|
||||
echo MAGISK_NDK_VERSION=$ndk_ver >> $GITHUB_ENV
|
||||
|
||||
- name: Cache Gradle
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
|
||||
restore-keys: ${{ runner.os }}-gradle-
|
||||
|
||||
- name: Cache NDK
|
||||
id: ndk-cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.ANDROID_SDK_ROOT }}/ndk/magisk
|
||||
key: ${{ runner.os }}-ndk-${{ env.MAGISK_NDK_VERSION }}
|
||||
|
||||
- name: Set up NDK
|
||||
if: steps.ndk-cache.outputs.cache-hit != 'true'
|
||||
run: python build.py ndk
|
||||
|
||||
- name: Build release
|
||||
run: python build.py -vr all
|
||||
|
||||
- name: Build debug
|
||||
run: python build.py -v all
|
||||
|
||||
# Only upload artifacts built on Linux
|
||||
- name: Upload build artifact
|
||||
if: runner.os == 'Linux'
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ github.sha }}
|
||||
path: out
|
||||
26
.github/workflows/issues.yml
vendored
Normal file
26
.github/workflows/issues.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Check Issues
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out
|
||||
uses: actions/checkout@v2
|
||||
- name: Read latest version code
|
||||
run: |
|
||||
ver=$(sed -n 's/^magisk.versionCode=//p' gradle.properties)
|
||||
echo MAGISK_VERSION_CODE=$ver >> $GITHUB_ENV
|
||||
- if: contains(github.event.issue.body, format('Magisk version code{0} ', ':')) != true
|
||||
id: close
|
||||
name: Close Issue(template)
|
||||
uses: peter-evans/close-issue@v1
|
||||
with:
|
||||
comment: This issue is being automatically closed because it does not follow the issue template.
|
||||
- if: steps.close.conclusion == 'skipped' && contains(github.event.issue.body, format('Magisk version code{0} {1}', ':', env.MAGISK_VERSION_CODE)) != true
|
||||
name: Close Issue(latest canary)
|
||||
uses: peter-evans/close-issue@v1
|
||||
with:
|
||||
comment: This issue is being automatically closed because latest canary Magisk version code is ${{ env.MAGISK_VERSION_CODE }}.
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -25,6 +25,12 @@
|
||||
[submodule "pcre"]
|
||||
path = native/jni/external/pcre
|
||||
url = https://android.googlesource.com/platform/external/pcre
|
||||
[submodule "xhook"]
|
||||
path = native/jni/external/xhook
|
||||
url = https://github.com/iqiyi/xHook.git
|
||||
[submodule "libcxx"]
|
||||
path = native/jni/external/libcxx
|
||||
url = https://github.com/topjohnwu/libcxx.git
|
||||
[submodule "termux-elf-cleaner"]
|
||||
path = tools/termux-elf-cleaner
|
||||
url = https://github.com/termux/termux-elf-cleaner.git
|
||||
|
||||
30
README.MD
30
README.MD
@@ -1,7 +1,6 @@
|
||||

|
||||
|
||||

|
||||

|
||||
[](https://raw.githubusercontent.com/topjohnwu/magisk-files/count/count.json)
|
||||
|
||||
## Introduction
|
||||
|
||||
@@ -15,17 +14,17 @@ Here are some feature highlights:
|
||||
|
||||
## Downloads
|
||||
|
||||
[](https://github.com/topjohnwu/Magisk/releases/download/manager-v7.5.1/MagiskManager-v7.5.1.apk)
|
||||
[](https://raw.githubusercontent.com/topjohnwu/magisk_files/canary/app-debug.apk)
|
||||
<br>
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v20.4)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v20.4)
|
||||
Please note that the only source of official Magisk information and downloads is [Github](https://github.com/topjohnwu/Magisk/). Beware of any other websites.
|
||||
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v22.0)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v22.0)
|
||||
[](https://raw.githubusercontent.com/topjohnwu/magisk-files/canary/app-debug.apk)
|
||||
|
||||
## Useful Links
|
||||
|
||||
- [Installation Instruction](https://topjohnwu.github.io/Magisk/install.html)
|
||||
- [Frequently Asked Questions](https://topjohnwu.github.io/Magisk/faq.html)
|
||||
- [Full Official Docs](https://topjohnwu.github.io/Magisk/)
|
||||
- [Magisk Documentation](https://topjohnwu.github.io/Magisk/)
|
||||
- [Magisk Troubleshoot Wiki](https://www.didgeridoohan.com/magisk/HomePage) (by [@Didgeridoohan](https://github.com/Didgeridoohan))
|
||||
|
||||
## Android Version Support
|
||||
@@ -34,17 +33,14 @@ Here are some feature highlights:
|
||||
- Android 4.4+: All core features available
|
||||
- Android 6.0+: Guaranteed MagiskHide support
|
||||
- Android 7.0+: Full MagiskHide protection
|
||||
- Android 9.0+: Magisk Manager stealth mode
|
||||
|
||||
## Bug Reports
|
||||
|
||||
Canary Channels are cutting edge builds for those adventurous. To access canary builds, install the Canary Magisk Manager, switch to the Canary Channel in settings and upgrade.
|
||||
|
||||
**Only bug reports from Canary builds will be accepted.**
|
||||
|
||||
For installation issues, upload both boot image and install logs.<br>
|
||||
For Magisk issues, upload boot logcat or dmesg.<br>
|
||||
For Magisk Manager crashes, record and upload the logcat when the crash occurs.
|
||||
For Magisk app crashes, record and upload the logcat when the crash occurs.
|
||||
|
||||
## Building and Development
|
||||
|
||||
@@ -56,17 +52,17 @@ For Magisk Manager crashes, record and upload the logcat when the crash occurs.
|
||||
- macOS: `export JAVA_HOME="/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home"`
|
||||
- Linux: `export PATH="/path/to/androidstudio/jre/bin:$PATH"`
|
||||
- Windows: Add `C:\Path\To\Android Studio\jre\bin` to environment variable `PATH`
|
||||
- Set environment variable `ANDROID_HOME` to the Android SDK folder (can be found in Android Studio settings)
|
||||
- Set environment variable `ANDROID_SDK_ROOT` to the Android SDK folder (can be found in Android Studio settings)
|
||||
- Run `./build.py ndk` to let the script download and install NDK for you
|
||||
- Set configurations in `config.prop`. A sample `config.prop.sample` is provided.
|
||||
- To start building, run `build.py` to see your options. \
|
||||
For each action, use `-h` to access help (e.g. `./build.py all -h`)
|
||||
- To start development, open the project in Android Studio. Both app (Kotlin/Java) and native (C++/C) source code can be properly developed using the IDE, but *always* use `build.py` for building.
|
||||
- `build.py` builds in debug mode by default. If you want release builds (with `-r, --release`), you need a Java Keystore to sign APKs and zips. For more information, check [Google's Documentation](https://developer.android.com/studio/publish/app-signing.html#generate-key).
|
||||
- To start development, open the project with Android Studio. The IDE can be used for both app (Kotlin/Java) and native (C++/C) sources.
|
||||
- Optionally, set custom configs with `config.prop`. A sample `config.prop.sample` is provided.
|
||||
- To sign APKs and zips with your own private keys, set signing configs in `config.prop`. For more info, check [Google's Documentation](https://developer.android.com/studio/publish/app-signing.html#generate-key).
|
||||
|
||||
## Translation Contributions
|
||||
|
||||
Default string resources for Magisk Manager and its stub APK are located here:
|
||||
Default string resources for the Magisk app and its stub APK are located here:
|
||||
|
||||
- `app/src/main/res/values/strings.xml`
|
||||
- `stub/src/main/res/values/strings.xml`
|
||||
|
||||
5
app/.gitignore
vendored
5
app/.gitignore
vendored
@@ -6,6 +6,7 @@
|
||||
app/release
|
||||
*.hprof
|
||||
.externalNativeBuild/
|
||||
public.certificate.x509.pem
|
||||
private.key.pk8
|
||||
*.apk
|
||||
src/main/assets
|
||||
src/main/jniLibs
|
||||
src/main/resources
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import org.apache.tools.ant.filters.FixCrLfFilter
|
||||
import java.io.PrintStream
|
||||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
kotlin("android")
|
||||
kotlin("android.extensions")
|
||||
kotlin("plugin.parcelize")
|
||||
kotlin("kapt")
|
||||
id("androidx.navigation.safeargs.kotlin")
|
||||
}
|
||||
@@ -20,12 +23,12 @@ android {
|
||||
applicationId = "com.topjohnwu.magisk"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
multiDexEnabled = true
|
||||
versionName = Config["appVersion"]
|
||||
versionCode = Config["appVersionCode"]?.toInt()
|
||||
buildConfigField("int", "LATEST_MAGISK", Config["versionCode"] ?: "Integer.MAX_VALUE")
|
||||
versionName = Config.version
|
||||
versionCode = Config.versionCode
|
||||
ndk.abiFilters("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
|
||||
|
||||
javaCompileOptions.annotationProcessorOptions.arguments(
|
||||
mapOf("room.incremental" to "true")
|
||||
mapOf("room.incremental" to "true")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -50,12 +53,14 @@ android {
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude("/META-INF/**")
|
||||
exclude("/androidsupportmultidexversion.txt")
|
||||
exclude("/META-INF/*")
|
||||
exclude("/org/bouncycastle/**")
|
||||
exclude("/kotlin/**")
|
||||
exclude("/kotlinx/**")
|
||||
exclude("/okhttp3/**")
|
||||
exclude("/*.txt")
|
||||
exclude("/*.bin")
|
||||
doNotStrip("**/*.so")
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
@@ -63,39 +68,146 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
androidExtensions {
|
||||
isExperimental = true
|
||||
val syncLibs by tasks.registering(Sync::class) {
|
||||
into("src/main/jniLibs")
|
||||
into("armeabi-v7a") {
|
||||
from(rootProject.file("native/out/armeabi-v7a")) {
|
||||
include("busybox", "magiskboot", "magiskinit", "magisk")
|
||||
rename { if (it == "magisk") "libmagisk32.so" else "lib$it.so" }
|
||||
}
|
||||
from(rootProject.file("native/out/arm64-v8a")) {
|
||||
include("magisk")
|
||||
rename { if (it == "magisk") "libmagisk64.so" else "lib$it.so" }
|
||||
}
|
||||
}
|
||||
into("x86") {
|
||||
from(rootProject.file("native/out/x86")) {
|
||||
include("busybox", "magiskboot", "magiskinit", "magisk")
|
||||
rename { if (it == "magisk") "libmagisk32.so" else "lib$it.so" }
|
||||
}
|
||||
from(rootProject.file("native/out/x86_64")) {
|
||||
include("magisk")
|
||||
rename { if (it == "magisk") "libmagisk64.so" else "lib$it.so" }
|
||||
}
|
||||
}
|
||||
onlyIf {
|
||||
if (inputs.sourceFiles.files.size != 10)
|
||||
throw StopExecutionException("Please build binaries first! (./build.py binary)")
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
val copyUtils = tasks.register("copyUtils", Copy::class) {
|
||||
from(rootProject.file("scripts/util_functions.sh"))
|
||||
into("src/main/res/raw")
|
||||
val createStubLibs by tasks.registering {
|
||||
dependsOn(syncLibs)
|
||||
doLast {
|
||||
val arm64 = project.file("src/main/jniLibs/arm64-v8a/libstub.so")
|
||||
arm64.parentFile.mkdirs()
|
||||
arm64.createNewFile()
|
||||
val x64 = project.file("src/main/jniLibs/x86_64/libstub.so")
|
||||
x64.parentFile.mkdirs()
|
||||
x64.createNewFile()
|
||||
}
|
||||
}
|
||||
|
||||
tasks["preBuild"]?.dependsOn(copyUtils)
|
||||
val syncAssets by tasks.registering(Sync::class) {
|
||||
dependsOn(createStubLibs)
|
||||
inputs.property("version", Config.version)
|
||||
inputs.property("versionCode", Config.versionCode)
|
||||
into("src/main/assets")
|
||||
from(rootProject.file("scripts")) {
|
||||
include("util_functions.sh", "boot_patch.sh", "uninstaller.sh", "addon.d.sh")
|
||||
}
|
||||
into("chromeos") {
|
||||
from(rootProject.file("tools/futility"))
|
||||
from(rootProject.file("tools/keys")) {
|
||||
include("kernel_data_key.vbprivk", "kernel.keyblock")
|
||||
}
|
||||
}
|
||||
filesMatching("**/util_functions.sh") {
|
||||
filter {
|
||||
it.replace("#MAGISK_VERSION_STUB",
|
||||
"MAGISK_VER='${Config.version}'\n" +
|
||||
"MAGISK_VER_CODE=${Config.versionCode}")
|
||||
}
|
||||
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
|
||||
}
|
||||
}
|
||||
|
||||
val syncResources by tasks.registering(Sync::class) {
|
||||
dependsOn(syncAssets)
|
||||
into("src/main/resources/META-INF/com/google/android")
|
||||
from(rootProject.file("scripts/update_binary.sh")) {
|
||||
rename { "update-binary" }
|
||||
}
|
||||
from(rootProject.file("scripts/flash_script.sh")) {
|
||||
rename { "updater-script" }
|
||||
}
|
||||
}
|
||||
|
||||
tasks["preBuild"]?.dependsOn(syncResources)
|
||||
|
||||
android.applicationVariants.all {
|
||||
val keysDir = rootProject.file("tools/keys")
|
||||
val outSrcDir = File(buildDir, "generated/source/keydata/$name")
|
||||
val outSrc = File(outSrcDir, "com/topjohnwu/signing/KeyData.java")
|
||||
|
||||
fun PrintStream.newField(name: String, file: File) {
|
||||
println("public static byte[] $name() {")
|
||||
print("byte[] buf = {")
|
||||
val bytes = file.readBytes()
|
||||
print(bytes.joinToString(",") { "(byte)(${it.toInt() and 0xff})" })
|
||||
println("};")
|
||||
println("return buf;")
|
||||
println("}")
|
||||
}
|
||||
|
||||
val genSrcTask = tasks.register("generate${name.capitalize()}KeyData") {
|
||||
inputs.dir(keysDir)
|
||||
outputs.file(outSrc)
|
||||
doLast {
|
||||
outSrc.parentFile.mkdirs()
|
||||
PrintStream(outSrc).use {
|
||||
it.println("package com.topjohnwu.signing;")
|
||||
it.println("public final class KeyData {")
|
||||
|
||||
it.newField("testCert", File(keysDir, "testkey.x509.pem"))
|
||||
it.newField("testKey", File(keysDir, "testkey.pk8"))
|
||||
it.newField("verityCert", File(keysDir, "verity.x509.pem"))
|
||||
it.newField("verityKey", File(keysDir, "verity.pk8"))
|
||||
|
||||
it.println("}")
|
||||
}
|
||||
}
|
||||
}
|
||||
registerJavaGeneratingTask(genSrcTask.get(), outSrcDir)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
|
||||
implementation(kotlin("stdlib"))
|
||||
implementation(project(":app:shared"))
|
||||
implementation(project(":app:signing"))
|
||||
|
||||
implementation("com.github.topjohnwu:jtar:1.0.0")
|
||||
implementation("com.github.topjohnwu:indeterminate-checkbox:1.0.7")
|
||||
implementation("com.github.topjohnwu:lz4-java:1.7.1")
|
||||
implementation("com.jakewharton.timber:timber:4.7.1")
|
||||
|
||||
val vBC = "1.68"
|
||||
implementation("org.bouncycastle:bcprov-jdk15on:${vBC}")
|
||||
implementation("org.bouncycastle:bcpkix-jdk15on:${vBC}")
|
||||
|
||||
val vBAdapt = "4.0.0"
|
||||
val bindingAdapter = "me.tatarka.bindingcollectionadapter2:bindingcollectionadapter"
|
||||
implementation("${bindingAdapter}:${vBAdapt}")
|
||||
implementation("${bindingAdapter}-recyclerview:${vBAdapt}")
|
||||
|
||||
val vMarkwon = "4.6.0"
|
||||
val vMarkwon = "4.6.2"
|
||||
implementation("io.noties.markwon:core:${vMarkwon}")
|
||||
implementation("io.noties.markwon:html:${vMarkwon}")
|
||||
implementation("io.noties.markwon:image:${vMarkwon}")
|
||||
implementation("com.caverock:androidsvg:1.4")
|
||||
|
||||
val vLibsu = "3.0.2"
|
||||
val vLibsu = "3.1.1"
|
||||
implementation("com.github.topjohnwu.libsu:core:${vLibsu}")
|
||||
implementation("com.github.topjohnwu.libsu:io:${vLibsu}")
|
||||
|
||||
@@ -118,11 +230,11 @@ dependencies {
|
||||
implementation("com.squareup.okhttp3:logging-interceptor:${vOkHttp}")
|
||||
implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:${vOkHttp}")
|
||||
|
||||
val vMoshi = "1.10.0"
|
||||
val vMoshi = "1.11.0"
|
||||
implementation("com.squareup.moshi:moshi:${vMoshi}")
|
||||
kapt("com.squareup.moshi:moshi-kotlin-codegen:${vMoshi}")
|
||||
|
||||
val vRoom = "2.2.5"
|
||||
val vRoom = "2.3.0-beta03"
|
||||
implementation("androidx.room:room-runtime:${vRoom}")
|
||||
implementation("androidx.room:room-ktx:${vRoom}")
|
||||
kapt("androidx.room:room-compiler:${vRoom}")
|
||||
@@ -131,17 +243,16 @@ dependencies {
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:${vNav}")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:${vNav}")
|
||||
|
||||
implementation("androidx.biometric:biometric:1.0.1")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.0.1")
|
||||
implementation("androidx.biometric:biometric:1.1.0")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.0.4")
|
||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||
implementation("androidx.browser:browser:1.2.0")
|
||||
implementation("androidx.browser:browser:1.3.0")
|
||||
implementation("androidx.preference:preference:1.1.1")
|
||||
implementation("androidx.recyclerview:recyclerview:1.1.0")
|
||||
implementation("androidx.fragment:fragment-ktx:1.2.5")
|
||||
implementation("androidx.work:work-runtime-ktx:2.4.0")
|
||||
implementation("androidx.transition:transition:1.3.1")
|
||||
implementation("androidx.fragment:fragment-ktx:1.3.1")
|
||||
implementation("androidx.work:work-runtime-ktx:2.5.0")
|
||||
implementation("androidx.transition:transition:1.4.0")
|
||||
implementation("androidx.multidex:multidex:2.0.1")
|
||||
implementation("androidx.core:core-ktx:1.3.1")
|
||||
implementation("androidx.localbroadcastmanager:localbroadcastmanager:1.0.0")
|
||||
implementation("com.google.android.material:material:1.2.1")
|
||||
implementation("androidx.core:core-ktx:1.3.2")
|
||||
implementation("com.google.android.material:material:1.3.0")
|
||||
}
|
||||
|
||||
21
app/proguard-rules.pro
vendored
21
app/proguard-rules.pro
vendored
@@ -18,16 +18,10 @@
|
||||
|
||||
# Kotlin
|
||||
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
|
||||
public static void checkExpressionValueIsNotNull(...);
|
||||
public static void checkNotNullExpressionValue(...);
|
||||
public static void checkReturnedValueIsNotNull(...);
|
||||
public static void checkFieldIsNotNull(...);
|
||||
public static void checkParameterIsNotNull(...);
|
||||
public static void check*(...);
|
||||
public static void throw*(...);
|
||||
}
|
||||
|
||||
# Stubs
|
||||
-keep class a.* { *; }
|
||||
|
||||
# Snet
|
||||
-keepclassmembers class com.topjohnwu.magisk.ui.safetynet.SafetyNetHelper { *; }
|
||||
-keep,allowobfuscation interface com.topjohnwu.magisk.ui.safetynet.SafetyNetHelper$Callback
|
||||
@@ -35,20 +29,17 @@
|
||||
void onResponse(org.json.JSONObject);
|
||||
}
|
||||
|
||||
# Fragments
|
||||
# TODO: Remove when AGP 4.1 release
|
||||
# https://issuetracker.google.com/issues/142601969
|
||||
-keep,allowobfuscation class * extends androidx.fragment.app.Fragment
|
||||
-keepnames class androidx.navigation.fragment.NavHostFragment
|
||||
# Stub
|
||||
-keep class com.topjohnwu.magisk.core.App { <init>(java.lang.Object); }
|
||||
|
||||
# Strip Timber verbose and debug logging
|
||||
-assumenosideeffects class timber.log.Timber.Tree {
|
||||
-assumenosideeffects class timber.log.Timber$Tree {
|
||||
public void v(**);
|
||||
public void d(**);
|
||||
}
|
||||
|
||||
# Excessive obfuscation
|
||||
-repackageclasses
|
||||
-repackageclasses 'a'
|
||||
-allowaccessmodification
|
||||
|
||||
# QOL
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.topjohnwu.shared">
|
||||
package="com.topjohnwu.shared"
|
||||
android:installLocation="internalOnly">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="29"
|
||||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission
|
||||
android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||
tools:ignore="QueryAllPackagesPermission" />
|
||||
|
||||
<application
|
||||
android:label="Magisk Manager"
|
||||
android:installLocation="internalOnly"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:allowBackup="false"
|
||||
android:label="Magisk"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar"
|
||||
tools:ignore="UnusedAttribute">
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:ignore="UnusedAttribute" />
|
||||
|
||||
<provider
|
||||
android:name="a.p"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
</provider>
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package a;
|
||||
|
||||
import com.topjohnwu.magisk.FileProvider;
|
||||
|
||||
public class p extends FileProvider {
|
||||
/* Stub */
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.provider.OpenableColumns;
|
||||
@@ -34,8 +33,6 @@ public class FileProvider extends ContentProvider {
|
||||
|
||||
private PathStrategy mStrategy;
|
||||
|
||||
public static ProviderCallHandler callHandler;
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return true;
|
||||
@@ -131,13 +128,6 @@ public class FileProvider extends ContentProvider {
|
||||
return file.delete() ? 1 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle call(String method, String arg, Bundle extras) {
|
||||
if (callHandler != null)
|
||||
return callHandler.call(getContext(), method, arg, extras);
|
||||
return Bundle.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelFileDescriptor openFile(Uri uri, String mode)
|
||||
throws FileNotFoundException {
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package com.topjohnwu.magisk;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
public interface ProviderCallHandler {
|
||||
Bundle call(Context context, String method, String arg, Bundle extras);
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
package com.topjohnwu.magisk.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
|
||||
@@ -10,20 +13,36 @@ import com.topjohnwu.magisk.FileProvider;
|
||||
import java.io.File;
|
||||
|
||||
public class APKInstall {
|
||||
|
||||
public static Intent installIntent(Context c, File apk) {
|
||||
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
intent.setData(FileProvider.getUriForFile(c, c.getPackageName() + ".provider", apk));
|
||||
} else {
|
||||
//noinspection ResultOfMethodCallIgnored SetWorldReadable
|
||||
apk.setReadable(true, false);
|
||||
intent.setData(Uri.fromFile(apk));
|
||||
}
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static void install(Context c, File apk) {
|
||||
c.startActivity(installIntent(c, apk));
|
||||
}
|
||||
|
||||
public static Intent installIntent(Context c, File apk) {
|
||||
Intent install = new Intent(Intent.ACTION_INSTALL_PACKAGE);
|
||||
install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
install.setData(FileProvider.getUriForFile(c, c.getPackageName() + ".provider", apk));
|
||||
} else {
|
||||
apk.setReadable(true, false);
|
||||
install.setData(Uri.fromFile(apk));
|
||||
}
|
||||
return install;
|
||||
public static void registerInstallReceiver(Context c, BroadcastReceiver r) {
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||
filter.addDataScheme("package");
|
||||
c.getApplicationContext().registerReceiver(r, filter);
|
||||
}
|
||||
|
||||
public static void installHideResult(Activity c, File apk) {
|
||||
Intent intent = installIntent(c, apk);
|
||||
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
|
||||
c.startActivityForResult(intent, 0); // Ignore result, use install receiver
|
||||
}
|
||||
}
|
||||
|
||||
1
app/signing/.gitignore
vendored
1
app/signing/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/build
|
||||
@@ -1,35 +0,0 @@
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
|
||||
plugins {
|
||||
id("java-library")
|
||||
id("java")
|
||||
id("com.github.johnrengelman.shadow") version "6.0.0"
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
val jar by tasks.getting(Jar::class) {
|
||||
manifest {
|
||||
attributes["Main-Class"] = "com.topjohnwu.signing.ZipSigner"
|
||||
}
|
||||
}
|
||||
|
||||
val shadowJar by tasks.getting(ShadowJar::class) {
|
||||
archiveBaseName.set("zipsigner")
|
||||
archiveClassifier.set(null as String?)
|
||||
archiveVersion.set("4.0")
|
||||
}
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
|
||||
|
||||
api("org.bouncycastle:bcprov-jdk15on:1.66")
|
||||
api("org.bouncycastle:bcpkix-jdk15on:1.66")
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package com.topjohnwu.signing;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class BootSigner {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length > 0 && "-verify".equals(args[0])) {
|
||||
String certPath = "";
|
||||
if (args.length >= 2) {
|
||||
/* args[1] is the path to a public key certificate */
|
||||
certPath = args[1];
|
||||
}
|
||||
boolean signed = SignBoot.verifySignature(System.in,
|
||||
certPath.isEmpty() ? null : new FileInputStream(certPath));
|
||||
System.exit(signed ? 0 : 1);
|
||||
} else if (args.length > 0 && "-sign".equals(args[0])) {
|
||||
InputStream cert = null;
|
||||
InputStream key = null;
|
||||
String name = "/boot";
|
||||
|
||||
if (args.length >= 3) {
|
||||
cert = new FileInputStream(args[1]);
|
||||
key = new FileInputStream(args[2]);
|
||||
}
|
||||
if (args.length == 2) {
|
||||
name = args[1];
|
||||
} else if (args.length >= 4) {
|
||||
name = args[3];
|
||||
}
|
||||
|
||||
boolean success = SignBoot.doSignature(name, System.in, System.out, cert, key);
|
||||
System.exit(success ? 0 : 1);
|
||||
} else {
|
||||
System.err.println(
|
||||
"BootSigner <actions> [args]\n" +
|
||||
"Input from stdin, outputs to stdout\n" +
|
||||
"\n" +
|
||||
"Actions:\n" +
|
||||
" -verify [x509.pem]\n" +
|
||||
" verify image, cert is optional\n" +
|
||||
" -sign [x509.pem] [pk8] [name]\n" +
|
||||
" sign image, name, cert and key pair are optional\n" +
|
||||
" name should be /boot (default) or /recovery\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package com.topjohnwu.signing;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.Security;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
public class ZipSigner {
|
||||
|
||||
private static void usage() {
|
||||
System.err.println("ZipSigner usage:");
|
||||
System.err.println(" zipsigner.jar input.jar output.jar");
|
||||
System.err.println(" sign jar with AOSP test keys");
|
||||
System.err.println(" zipsigner.jar x509.pem pk8 input.jar output.jar");
|
||||
System.err.println(" sign jar with certificate / private key pair");
|
||||
System.err.println(" zipsigner.jar keyStore keyStorePass alias keyPass input.jar output.jar");
|
||||
System.err.println(" sign jar with Java KeyStore");
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
private static void sign(JarMap input, FileOutputStream output) throws Exception {
|
||||
sign(SignApk.class.getResourceAsStream("/keys/testkey.x509.pem"),
|
||||
SignApk.class.getResourceAsStream("/keys/testkey.pk8"), input, output);
|
||||
}
|
||||
|
||||
private static void sign(InputStream certIs, InputStream keyIs,
|
||||
JarMap input, FileOutputStream output) throws Exception {
|
||||
X509Certificate cert = CryptoUtils.readCertificate(certIs);
|
||||
PrivateKey key = CryptoUtils.readPrivateKey(keyIs);
|
||||
SignApk.sign(cert, key, input, output);
|
||||
}
|
||||
|
||||
private static void sign(String keyStore, String keyStorePass, String alias, String keyPass,
|
||||
JarMap in, FileOutputStream out) throws Exception {
|
||||
KeyStore ks;
|
||||
try {
|
||||
ks = KeyStore.getInstance("JKS");
|
||||
try (InputStream is = new FileInputStream(keyStore)) {
|
||||
ks.load(is, keyStorePass.toCharArray());
|
||||
}
|
||||
} catch (KeyStoreException|IOException|CertificateException|NoSuchAlgorithmException e) {
|
||||
ks = KeyStore.getInstance("PKCS12");
|
||||
try (InputStream is = new FileInputStream(keyStore)) {
|
||||
ks.load(is, keyStorePass.toCharArray());
|
||||
}
|
||||
}
|
||||
X509Certificate cert = (X509Certificate) ks.getCertificate(alias);
|
||||
PrivateKey key = (PrivateKey) ks.getKey(alias, keyPass.toCharArray());
|
||||
SignApk.sign(cert, key, in, out);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length != 2 && args.length != 4 && args.length != 6)
|
||||
usage();
|
||||
|
||||
Security.insertProviderAt(new BouncyCastleProvider(), 1);
|
||||
|
||||
try (JarMap in = JarMap.open(args[args.length - 2], false);
|
||||
FileOutputStream out = new FileOutputStream(args[args.length - 1])) {
|
||||
if (args.length == 2) {
|
||||
sign(in, out);
|
||||
} else if (args.length == 4) {
|
||||
try (InputStream cert = new FileInputStream(args[0]);
|
||||
InputStream key = new FileInputStream(args[1])) {
|
||||
sign(cert, key, in, out);
|
||||
}
|
||||
} else if (args.length == 6) {
|
||||
sign(args[0], args[1], args[2], args[3], in, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,22 +3,19 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.topjohnwu.magisk">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28" />
|
||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
||||
|
||||
<application
|
||||
android:name=".core.App"
|
||||
android:extractNativeLibs="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:name="a.e"
|
||||
android:allowBackup="true"
|
||||
android:multiArch="true"
|
||||
tools:ignore="UnusedAttribute,GoogleAppIndexingWarning">
|
||||
|
||||
<!-- Splash -->
|
||||
<activity
|
||||
android:name="a.c"
|
||||
android:name=".core.SplashActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/SplashTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
@@ -31,39 +28,47 @@
|
||||
</activity>
|
||||
|
||||
<!-- Main -->
|
||||
<activity android:name="a.b" />
|
||||
<activity android:name=".ui.MainActivity" />
|
||||
|
||||
<!-- Superuser -->
|
||||
<activity
|
||||
android:name="a.m"
|
||||
android:name=".ui.surequest.SuRequestActivity"
|
||||
android:directBootAware="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="false"
|
||||
tools:ignore="AppLinkUrlError">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Receiver -->
|
||||
<receiver
|
||||
android:name="a.h"
|
||||
android:directBootAware="true">
|
||||
android:name=".core.Receiver"
|
||||
android:directBootAware="true"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.REBOOT" />
|
||||
<action android:name="android.intent.action.LOCALE_CHANGED" />
|
||||
<action android:name="android.intent.action.UID_REMOVED" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.PACKAGE_REPLACED" />
|
||||
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
|
||||
|
||||
<data android:scheme="package" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- DownloadService -->
|
||||
<service android:name="a.j" />
|
||||
<service android:name=".core.download.DownloadService" />
|
||||
|
||||
<!-- FileProvider -->
|
||||
<provider
|
||||
android:name=".core.Provider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:directBootAware="true"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true" />
|
||||
|
||||
<!-- Hardcode GMS version -->
|
||||
<meta-data
|
||||
@@ -74,16 +79,12 @@
|
||||
<provider
|
||||
android:name="androidx.work.impl.WorkManagerInitializer"
|
||||
android:authorities="${applicationId}.workmanager-init"
|
||||
tools:node="remove" />
|
||||
tools:node="remove"
|
||||
tools:ignore="ExportedContentProvider" />
|
||||
|
||||
<!-- We don't invalidate Room -->
|
||||
<service
|
||||
android:name="androidx.room.MultiInstanceInvalidationService"
|
||||
tools:node="remove"/>
|
||||
|
||||
<!-- We don't use Device Credentials -->
|
||||
<activity
|
||||
android:name="androidx.biometric.DeviceCredentialHandlerActivity"
|
||||
tools:node="remove" />
|
||||
|
||||
</application>
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
@file:JvmName("a")
|
||||
package a
|
||||
|
||||
import com.topjohnwu.magisk.core.App
|
||||
import com.topjohnwu.magisk.core.GeneralReceiver
|
||||
import com.topjohnwu.magisk.core.SplashActivity
|
||||
import com.topjohnwu.magisk.core.download.DownloadService
|
||||
import com.topjohnwu.magisk.ui.MainActivity
|
||||
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
|
||||
import com.topjohnwu.signing.BootSigner
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
BootSigner.main(args)
|
||||
}
|
||||
|
||||
class b : MainActivity()
|
||||
|
||||
class c : SplashActivity()
|
||||
|
||||
class e : App {
|
||||
constructor() : super()
|
||||
constructor(o: Any) : super(o)
|
||||
}
|
||||
|
||||
class h : GeneralReceiver()
|
||||
|
||||
class j : DownloadService()
|
||||
|
||||
class m : SuRequestActivity()
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.topjohnwu.magisk.arch
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
@@ -8,13 +10,13 @@ import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.content.res.use
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.databinding.ViewDataBinding
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavDirections
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.base.BaseActivity
|
||||
import com.topjohnwu.magisk.ui.inflater.LayoutInflaterFactory
|
||||
import com.topjohnwu.magisk.ui.theme.Theme
|
||||
|
||||
abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
@@ -25,7 +27,7 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
protected open val themeRes: Int = Theme.selected.themeRes
|
||||
|
||||
private val navHostFragment by lazy {
|
||||
supportFragmentManager.findFragmentById(navHost) as? NavHostFragment
|
||||
supportFragmentManager.findFragmentById(navHostId) as? NavHostFragment
|
||||
}
|
||||
private val topFragment get() = navHostFragment?.childFragmentManager?.fragments?.getOrNull(0)
|
||||
protected val currentFragment get() = topFragment as? BaseUIFragment<*, *>
|
||||
@@ -33,7 +35,7 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
override val viewRoot: View get() = binding.root
|
||||
open val navigation: NavController? get() = navHostFragment?.navController
|
||||
|
||||
open val navHost: Int = 0
|
||||
open val navHostId: Int = 0
|
||||
open val snackbarView get() = binding.root
|
||||
|
||||
init {
|
||||
@@ -41,12 +43,9 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
AppCompatDelegate.setDefaultNightMode(theme)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
currentFragment?.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
layoutInflater.factory2 = LayoutInflaterFactory(delegate)
|
||||
|
||||
setTheme(themeRes)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@@ -58,11 +57,28 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
.use { it.getDrawable(0) }
|
||||
.also { window.setBackgroundDrawable(it) }
|
||||
|
||||
directionsDispatcher.observe(this) {
|
||||
it?.navigate()
|
||||
// we don't want the directions to be re-dispatched, so we preemptively set them to null
|
||||
if (it != null) {
|
||||
directionsDispatcher.value = null
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
window?.decorView?.let {
|
||||
it.systemUiVisibility = (it.systemUiVisibility
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
window?.decorView?.post {
|
||||
// If navigation bar is short enough (gesture navigation enabled), make it transparent
|
||||
if (window.decorView.rootWindowInsets?.systemWindowInsetBottom ?: 0 < Resources.getSystem().displayMetrics.density * 40) {
|
||||
window.navigationBarColor = Color.TRANSPARENT
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
window.navigationBarDividerColor = Color.TRANSPARENT
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
window.isNavigationBarContrastEnforced = false
|
||||
window.isStatusBarContrastEnforced = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,8 +88,10 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
it.setVariable(BR.viewModel, viewModel)
|
||||
it.lifecycleOwner = this
|
||||
}
|
||||
}
|
||||
|
||||
ensureInsets()
|
||||
fun setAccessibilityDelegate(delegate: View.AccessibilityDelegate?) {
|
||||
viewRoot.rootView.accessibilityDelegate = delegate
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
@@ -85,7 +103,7 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
return currentFragment?.onKeyEvent(event) == true || super.dispatchKeyEvent(event)
|
||||
}
|
||||
|
||||
override fun onEventDispatched(event: ViewEvent) = when(event) {
|
||||
override fun onEventDispatched(event: ViewEvent) = when (event) {
|
||||
is ContextExecutor -> event(this)
|
||||
is ActivityExecutor -> event(this)
|
||||
else -> Unit
|
||||
@@ -100,14 +118,4 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
fun NavDirections.navigate() {
|
||||
navigation?.navigate(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val directionsDispatcher = MutableLiveData<NavDirections?>()
|
||||
|
||||
fun postDirections(navDirections: NavDirections) =
|
||||
directionsDispatcher.postValue(navDirections)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
package com.topjohnwu.magisk.arch
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
|
||||
interface BaseUIComponent<VM : BaseViewModel>: LifecycleOwner {
|
||||
interface BaseUIComponent<VM : BaseViewModel> : LifecycleOwner {
|
||||
|
||||
val viewRoot: View
|
||||
val viewModel: VM
|
||||
@@ -17,47 +14,8 @@ interface BaseUIComponent<VM : BaseViewModel>: LifecycleOwner {
|
||||
}
|
||||
}
|
||||
|
||||
fun consumeSystemWindowInsets(insets: Insets): Insets? = null
|
||||
|
||||
/**
|
||||
* Called for all [ViewEvent]s published by associated viewModel.
|
||||
*/
|
||||
fun onEventDispatched(event: ViewEvent) {}
|
||||
|
||||
fun ensureInsets() {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(viewRoot) { _, insets ->
|
||||
insets.asInsets()
|
||||
.also { viewModel.insets = it }
|
||||
.let { consumeSystemWindowInsets(it) }
|
||||
?.subtractBy(insets) ?: insets
|
||||
}
|
||||
if (ViewCompat.isAttachedToWindow(viewRoot)) {
|
||||
ViewCompat.requestApplyInsets(viewRoot)
|
||||
} else {
|
||||
viewRoot.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
|
||||
override fun onViewDetachedFromWindow(v: View) = Unit
|
||||
override fun onViewAttachedToWindow(v: View) {
|
||||
ViewCompat.requestApplyInsets(v)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun WindowInsetsCompat.asInsets() = Insets.of(
|
||||
systemWindowInsetLeft,
|
||||
systemWindowInsetTop,
|
||||
systemWindowInsetRight,
|
||||
systemWindowInsetBottom
|
||||
)
|
||||
|
||||
private fun Insets.subtractBy(insets: WindowInsetsCompat) =
|
||||
WindowInsetsCompat.Builder(insets).setSystemWindowInsets(
|
||||
Insets.of(
|
||||
insets.systemWindowInsetLeft - left,
|
||||
insets.systemWindowInsetTop - top,
|
||||
insets.systemWindowInsetRight - right,
|
||||
insets.systemWindowInsetBottom - bottom
|
||||
)
|
||||
).build()
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.databinding.OnRebindCallback
|
||||
import androidx.databinding.ViewDataBinding
|
||||
@@ -17,15 +16,13 @@ import com.topjohnwu.magisk.ktx.startAnimations
|
||||
abstract class BaseUIFragment<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
Fragment(), BaseUIComponent<VM> {
|
||||
|
||||
protected val activity get() = requireActivity() as BaseUIActivity<*, *>
|
||||
val activity get() = requireActivity() as BaseUIActivity<*, *>
|
||||
protected lateinit var binding: Binding
|
||||
protected abstract val layoutRes: Int
|
||||
|
||||
override val viewRoot: View get() = binding.root
|
||||
private val navigation get() = activity.navigation
|
||||
|
||||
override fun consumeSystemWindowInsets(insets: Insets) = insets
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
startObserveEvents()
|
||||
@@ -43,6 +40,11 @@ abstract class BaseUIFragment<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
activity.supportActionBar?.subtitle = null
|
||||
}
|
||||
|
||||
override fun onEventDispatched(event: ViewEvent) = when(event) {
|
||||
is ContextExecutor -> event(requireContext())
|
||||
is ActivityExecutor -> event(activity)
|
||||
@@ -65,7 +67,6 @@ abstract class BaseUIFragment<VM : BaseViewModel, Binding : ViewDataBinding> :
|
||||
return true
|
||||
}
|
||||
})
|
||||
ensureInsets()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.topjohnwu.magisk.arch
|
||||
|
||||
import android.Manifest
|
||||
import android.os.Build
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.databinding.Bindable
|
||||
@@ -107,7 +106,7 @@ abstract class BaseViewModel(
|
||||
_viewEvents.postValue(this)
|
||||
}
|
||||
|
||||
fun NavDirections.publish() {
|
||||
fun NavDirections.navigate() {
|
||||
_viewEvents.postValue(NavigationEvent(this))
|
||||
}
|
||||
|
||||
|
||||
@@ -9,18 +9,18 @@ import me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapter
|
||||
import me.tatarka.bindingcollectionadapter2.ItemBinding
|
||||
import me.tatarka.bindingcollectionadapter2.OnItemBind
|
||||
|
||||
inline fun <T : ComparableRvItem<*>> diffListOf(
|
||||
fun <T : ComparableRvItem<*>> diffListOf(
|
||||
vararg newItems: T
|
||||
) = diffListOf(newItems.toList())
|
||||
|
||||
inline fun <T : ComparableRvItem<*>> diffListOf(
|
||||
fun <T : ComparableRvItem<*>> diffListOf(
|
||||
newItems: List<T>
|
||||
) = DiffObservableList(object : DiffObservableList.Callback<T> {
|
||||
override fun areItemsTheSame(oldItem: T, newItem: T) = oldItem.genericItemSameAs(newItem)
|
||||
override fun areContentsTheSame(oldItem: T, newItem: T) = oldItem.genericContentSameAs(newItem)
|
||||
}).also { it.update(newItems) }
|
||||
|
||||
inline fun <T : ComparableRvItem<*>> filterableListOf(
|
||||
fun <T : ComparableRvItem<*>> filterableListOf(
|
||||
vararg newItems: T
|
||||
) = FilterableDiffObservableList(object : DiffObservableList.Callback<T> {
|
||||
override fun areItemsTheSame(oldItem: T, newItem: T) = oldItem.genericItemSameAs(newItem)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.topjohnwu.magisk.arch
|
||||
|
||||
import android.content.Context
|
||||
import androidx.fragment.app.Fragment
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
/**
|
||||
@@ -23,5 +22,5 @@ interface ActivityExecutor {
|
||||
}
|
||||
|
||||
interface FragmentExecutor {
|
||||
operator fun invoke(fragment: Fragment)
|
||||
operator fun invoke(fragment: BaseUIFragment<*, *>)
|
||||
}
|
||||
|
||||
@@ -10,10 +10,9 @@ import androidx.multidex.MultiDex
|
||||
import androidx.work.WorkManager
|
||||
import com.topjohnwu.magisk.BuildConfig
|
||||
import com.topjohnwu.magisk.DynAPK
|
||||
import com.topjohnwu.magisk.FileProvider
|
||||
import com.topjohnwu.magisk.core.su.SuCallbackHandler
|
||||
import com.topjohnwu.magisk.core.utils.AppShellInit
|
||||
import com.topjohnwu.magisk.core.utils.BusyBoxInit
|
||||
import com.topjohnwu.magisk.core.utils.IODispatcherExecutor
|
||||
import com.topjohnwu.magisk.core.utils.RootInit
|
||||
import com.topjohnwu.magisk.core.utils.updateConfig
|
||||
import com.topjohnwu.magisk.di.koinModules
|
||||
import com.topjohnwu.magisk.ktx.unwrap
|
||||
@@ -21,6 +20,7 @@ import com.topjohnwu.superuser.Shell
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.core.context.startKoin
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
open class App() : Application() {
|
||||
@@ -33,10 +33,9 @@ open class App() : Application() {
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||
Shell.setDefaultBuilder(Shell.Builder.create()
|
||||
.setFlags(Shell.FLAG_MOUNT_MASTER)
|
||||
.setInitializers(RootInit::class.java)
|
||||
.setInitializers(BusyBoxInit::class.java, AppShellInit::class.java)
|
||||
.setTimeout(2))
|
||||
Shell.EXECUTOR = IODispatcherExecutor()
|
||||
FileProvider.callHandler = SuCallbackHandler
|
||||
|
||||
// Always log full stack trace with Timber
|
||||
Timber.plant(Timber.DebugTree())
|
||||
@@ -64,12 +63,18 @@ open class App() : Application() {
|
||||
val wrapped = impl.wrap()
|
||||
super.attachBaseContext(wrapped)
|
||||
|
||||
val info = base.applicationInfo
|
||||
val libDir = runCatching {
|
||||
info.javaClass.getDeclaredField("secondaryNativeLibraryDir").get(info) as String?
|
||||
}.getOrNull() ?: info.nativeLibraryDir
|
||||
Const.NATIVE_LIB_DIR = File(libDir)
|
||||
|
||||
// Normal startup
|
||||
startKoin {
|
||||
androidContext(wrapped)
|
||||
modules(koinModules)
|
||||
}
|
||||
ResMgr.init(impl)
|
||||
AssetHack.init(impl)
|
||||
app.registerActivityLifecycleCallbacks(ForegroundTracker)
|
||||
WorkManager.initialize(impl.wrapJob(), androidx.work.Configuration.Builder().build())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.topjohnwu.magisk.core
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.util.Xml
|
||||
@@ -9,19 +10,15 @@ import com.topjohnwu.magisk.BuildConfig
|
||||
import com.topjohnwu.magisk.core.magiskdb.SettingsDao
|
||||
import com.topjohnwu.magisk.core.magiskdb.StringDao
|
||||
import com.topjohnwu.magisk.core.utils.BiometricHelper
|
||||
import com.topjohnwu.magisk.core.utils.defaultLocale
|
||||
import com.topjohnwu.magisk.core.utils.refreshLocale
|
||||
import com.topjohnwu.magisk.data.preference.PreferenceModel
|
||||
import com.topjohnwu.magisk.data.repository.DBConfig
|
||||
import com.topjohnwu.magisk.di.Protected
|
||||
import com.topjohnwu.magisk.ktx.get
|
||||
import com.topjohnwu.magisk.ktx.inject
|
||||
import com.topjohnwu.magisk.ui.theme.Theme
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.io.SuFile
|
||||
import com.topjohnwu.superuser.io.SuFileInputStream
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
|
||||
object Config : PreferenceModel, DBConfig {
|
||||
|
||||
@@ -29,6 +26,15 @@ object Config : PreferenceModel, DBConfig {
|
||||
override val settingsDao: SettingsDao by inject()
|
||||
override val context: Context by inject(Protected)
|
||||
|
||||
@get:SuppressLint("ApplySharedPref")
|
||||
val prefsFile: File get() {
|
||||
// Flush prefs to disk
|
||||
prefs.edit().apply {
|
||||
remove(Key.ASKED_HOME)
|
||||
}.commit()
|
||||
return File("${context.filesDir.parent}/shared_prefs", "${fileName}.xml")
|
||||
}
|
||||
|
||||
object Key {
|
||||
// db configs
|
||||
const val ROOT_ACCESS = "root_access"
|
||||
@@ -43,6 +49,7 @@ object Config : PreferenceModel, DBConfig {
|
||||
const val SU_AUTO_RESPONSE = "su_auto_response"
|
||||
const val SU_NOTIFICATION = "su_notification"
|
||||
const val SU_REAUTH = "su_reauth"
|
||||
const val SU_TAPJACK = "su_tapjack"
|
||||
const val CHECK_UPDATES = "check_update"
|
||||
const val UPDATE_CHANNEL = "update_channel"
|
||||
const val CUSTOM_CHANNEL = "custom_channel"
|
||||
@@ -119,7 +126,7 @@ object Config : PreferenceModel, DBConfig {
|
||||
var repoOrder by preference(Key.REPO_ORDER, Value.ORDER_DATE)
|
||||
|
||||
var suDefaultTimeout by preferenceStrInt(Key.SU_REQUEST_TIMEOUT, 10)
|
||||
var suAutoReponse by preferenceStrInt(Key.SU_AUTO_RESPONSE, Value.SU_PROMPT)
|
||||
var suAutoResponse by preferenceStrInt(Key.SU_AUTO_RESPONSE, Value.SU_PROMPT)
|
||||
var suNotification by preferenceStrInt(Key.SU_NOTIFICATION, Value.NOTIFICATION_TOAST)
|
||||
var updateChannel by preferenceStrInt(Key.UPDATE_CHANNEL, defaultChannel)
|
||||
|
||||
@@ -127,8 +134,9 @@ object Config : PreferenceModel, DBConfig {
|
||||
var darkTheme by preference(Key.DARK_THEME, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||
var themeOrdinal by preference(Key.THEME_ORDINAL, Theme.Piplup.ordinal)
|
||||
var suReAuth by preference(Key.SU_REAUTH, false)
|
||||
var suTapjack by preference(Key.SU_TAPJACK, true)
|
||||
var checkUpdate by preference(Key.CHECK_UPDATES, true)
|
||||
var doh by preference(Key.DOH, defaultLocale.country == "CN")
|
||||
var doh by preference(Key.DOH, false)
|
||||
var magiskHide by preference(Key.MAGISKHIDE, true)
|
||||
var showSystemApp by preference(Key.SHOW_SYSTEM_APP, false)
|
||||
|
||||
@@ -150,8 +158,13 @@ object Config : PreferenceModel, DBConfig {
|
||||
|
||||
private const val SU_FINGERPRINT = "su_fingerprint"
|
||||
|
||||
fun initialize() {
|
||||
prefs.edit { parsePrefs() }
|
||||
fun load(pkg: String?) {
|
||||
// Only try to load prefs when fresh install and a previous package name is set
|
||||
if (pkg != null && prefs.all.isEmpty()) runCatching {
|
||||
context.contentResolver.openInputStream(Provider.PREFS_URI(pkg))?.use {
|
||||
prefs.edit { parsePrefs(it) }
|
||||
}
|
||||
}
|
||||
|
||||
prefs.edit {
|
||||
// Settings migration
|
||||
@@ -173,10 +186,8 @@ object Config : PreferenceModel, DBConfig {
|
||||
}
|
||||
}
|
||||
|
||||
private fun SharedPreferences.Editor.parsePrefs() {
|
||||
val config = SuFile.open("/data/adb", Const.MANAGER_CONFIGS)
|
||||
if (config.exists()) runCatching {
|
||||
val input = SuFileInputStream(config)
|
||||
private fun SharedPreferences.Editor.parsePrefs(input: InputStream) {
|
||||
runCatching {
|
||||
val parser = Xml.newPullParser()
|
||||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
|
||||
parser.setInput(input, "UTF-8")
|
||||
@@ -220,21 +231,6 @@ object Config : PreferenceModel, DBConfig {
|
||||
else -> parser.next()
|
||||
}
|
||||
}
|
||||
config.delete()
|
||||
}
|
||||
}
|
||||
|
||||
fun export() {
|
||||
// Flush prefs to disk
|
||||
prefs.edit().apply {
|
||||
remove(Key.ASKED_HOME)
|
||||
}.commit()
|
||||
val context = get<Context>(Protected)
|
||||
val xml = File(
|
||||
"${context.filesDir.parent}/shared_prefs",
|
||||
"${context.packageName}_preferences.xml"
|
||||
)
|
||||
Shell.su("cat $xml > /data/adb/${Const.MANAGER_CONFIGS}").exec()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,67 +1,77 @@
|
||||
package com.topjohnwu.magisk.core
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Process
|
||||
import com.topjohnwu.magisk.BuildConfig
|
||||
import java.io.File
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
object Const {
|
||||
|
||||
val CPU_ABI: String
|
||||
val CPU_ABI_32: String
|
||||
|
||||
init {
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
CPU_ABI = Build.SUPPORTED_ABIS[0]
|
||||
CPU_ABI_32 = Build.SUPPORTED_32_BIT_ABIS.firstOrNull() ?: CPU_ABI
|
||||
} else {
|
||||
CPU_ABI = Build.CPU_ABI
|
||||
CPU_ABI_32 = CPU_ABI
|
||||
}
|
||||
}
|
||||
|
||||
// Paths
|
||||
lateinit var MAGISKTMP: String
|
||||
lateinit var NATIVE_LIB_DIR: File
|
||||
val MAGISK_PATH get() = "$MAGISKTMP/modules"
|
||||
const val TMP_FOLDER_PATH = "/dev/tmp"
|
||||
const val TMPDIR = "/dev/tmp"
|
||||
const val MAGISK_LOG = "/cache/magisk.log"
|
||||
|
||||
// Versions
|
||||
const val SNET_EXT_VER = 15
|
||||
const val SNET_REVISION = "d494bc726e86166913a13629e3b1336728ec5d7f"
|
||||
const val BOOTCTL_REVISION = "a6c47f86f10b310358afa9dbe837037dd5d561df"
|
||||
const val SNET_REVISION = "22.0"
|
||||
const val BOOTCTL_REVISION = "22.0"
|
||||
|
||||
// Misc
|
||||
const val ANDROID_MANIFEST = "AndroidManifest.xml"
|
||||
const val MAGISK_INSTALL_LOG_FILENAME = "magisk_install_log_%s.log"
|
||||
const val MANAGER_CONFIGS = ".tmp.magisk.config"
|
||||
val USER_ID = Process.myUid() / 100000
|
||||
|
||||
object Version {
|
||||
const val MIN_VERSION = "v19.0"
|
||||
const val MIN_VERCODE = 19000
|
||||
const val MIN_VERSION = "v20.4"
|
||||
const val MIN_VERCODE = 20400
|
||||
|
||||
fun atLeast_20_2() = Info.env.magiskVersionCode >= 20200 || isCanary()
|
||||
fun atLeast_20_4() = Info.env.magiskVersionCode >= 20400 || isCanary()
|
||||
fun atLeast_21_0() = Info.env.magiskVersionCode >= 21000 || isCanary()
|
||||
fun atLeast_21_2() = Info.env.magiskVersionCode >= 21200 || isCanary()
|
||||
fun isCanary() = Info.env.magiskVersionCode % 100 != 0
|
||||
}
|
||||
|
||||
object ID {
|
||||
const val FETCH_ZIP = 2
|
||||
const val SELECT_BOOT = 3
|
||||
|
||||
// notifications
|
||||
const val MAGISK_UPDATE_NOTIFICATION_ID = 4
|
||||
const val APK_UPDATE_NOTIFICATION_ID = 5
|
||||
const val DTBO_NOTIFICATION_ID = 7
|
||||
const val HIDE_MANAGER_NOTIFICATION_ID = 8
|
||||
const val UPDATE_NOTIFICATION_CHANNEL = "update"
|
||||
const val PROGRESS_NOTIFICATION_CHANNEL = "progress"
|
||||
const val CHECK_MAGISK_UPDATE_WORKER_ID = "magisk_update"
|
||||
}
|
||||
|
||||
object Url {
|
||||
const val ZIP_URL = "https://github.com/Magisk-Modules-Repo/%s/archive/master.zip"
|
||||
const val PATREON_URL = "https://www.patreon.com/topjohnwu"
|
||||
const val SOURCE_CODE_URL = "https://github.com/topjohnwu/Magisk"
|
||||
|
||||
val CHANGELOG_URL = if (BuildConfig.VERSION_CODE % 100 != 0) Info.remote.magisk.note
|
||||
else "https://topjohnwu.github.io/Magisk/releases/${BuildConfig.VERSION_CODE}.md"
|
||||
|
||||
const val GITHUB_RAW_URL = "https://raw.githubusercontent.com/"
|
||||
const val GITHUB_API_URL = "https://api.github.com/users/Magisk-Modules-Repo/"
|
||||
const val GITHUB_PAGE_URL = "https://topjohnwu.github.io/magisk_files/"
|
||||
const val GITHUB_API_URL = "https://api.github.com/"
|
||||
const val GITHUB_PAGE_URL = "https://topjohnwu.github.io/magisk-files/"
|
||||
const val GITHUB_OLD_PAGE_URL = "https://topjohnwu.github.io/magisk_files/"
|
||||
const val JS_DELIVR_URL = "https://cdn.jsdelivr.net/gh/"
|
||||
const val OFFICIAL_REPO = "https://magisk-modules-repo.github.io/submission/modules.json"
|
||||
}
|
||||
|
||||
object Key {
|
||||
// others
|
||||
const val LINK_KEY = "Link"
|
||||
const val IF_NONE_MATCH = "If-None-Match"
|
||||
const val ETAG_KEY = "ETag"
|
||||
// intents
|
||||
const val OPEN_SECTION = "section"
|
||||
const val PREV_PKG = "prev_pkg"
|
||||
}
|
||||
|
||||
object Value {
|
||||
|
||||
@@ -14,44 +14,38 @@ import android.content.Intent
|
||||
import android.content.res.AssetManager
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.util.DisplayMetrics
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.topjohnwu.magisk.DynAPK
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.download.DownloadService
|
||||
import com.topjohnwu.magisk.core.utils.refreshLocale
|
||||
import com.topjohnwu.magisk.core.utils.updateConfig
|
||||
import com.topjohnwu.magisk.ktx.forceGetDeclaredField
|
||||
import com.topjohnwu.magisk.ui.MainActivity
|
||||
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
|
||||
|
||||
fun AssetManager.addAssetPath(path: String) {
|
||||
DynAPK.addAssetPath(this, path)
|
||||
}
|
||||
|
||||
fun Context.wrap(global: Boolean = true): Context =
|
||||
if (global) GlobalResContext(this) else ResContext(this)
|
||||
fun Context.wrap(inject: Boolean = false): Context =
|
||||
if (inject) ReInjectedContext(this) else InjectedContext(this)
|
||||
|
||||
fun Context.wrapJob(): Context = object : GlobalResContext(this) {
|
||||
fun Context.wrapJob(): Context = object : InjectedContext(this) {
|
||||
|
||||
override fun getApplicationContext(): Context {
|
||||
return this
|
||||
}
|
||||
override fun getApplicationContext() = this
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
override fun getSystemService(name: String): Any? {
|
||||
return if (!isRunningAsStub) super.getSystemService(name) else
|
||||
when (name) {
|
||||
Context.JOB_SCHEDULER_SERVICE ->
|
||||
JobSchedulerWrapper(super.getSystemService(name) as JobScheduler)
|
||||
else -> super.getSystemService(name)
|
||||
return super.getSystemService(name).let {
|
||||
when {
|
||||
!isRunningAsStub -> it
|
||||
name == JOB_SCHEDULER_SERVICE -> JobSchedulerWrapper(it as JobScheduler)
|
||||
else -> it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Class<*>.cmp(pkg: String): ComponentName {
|
||||
val name = ClassMap[this].name
|
||||
return ComponentName(pkg, Info.stub?.classToComponent?.get(name) ?: name)
|
||||
}
|
||||
fun Class<*>.cmp(pkg: String) =
|
||||
ComponentName(pkg, Info.stub?.classToComponent?.get(name) ?: name)
|
||||
|
||||
inline fun <reified T> Activity.redirect() = Intent(intent)
|
||||
.setComponent(T::class.java.cmp(packageName))
|
||||
@@ -59,34 +53,27 @@ inline fun <reified T> Activity.redirect() = Intent(intent)
|
||||
|
||||
inline fun <reified T> Context.intent() = Intent().setComponent(T::class.java.cmp(packageName))
|
||||
|
||||
private open class GlobalResContext(base: Context) : ContextWrapper(base) {
|
||||
open val mRes: Resources get() = ResMgr.resource
|
||||
|
||||
override fun getResources(): Resources {
|
||||
return mRes
|
||||
}
|
||||
|
||||
override fun getClassLoader(): ClassLoader {
|
||||
return javaClass.classLoader!!
|
||||
}
|
||||
|
||||
private open class InjectedContext(base: Context) : ContextWrapper(base) {
|
||||
open val res: Resources get() = AssetHack.resource
|
||||
override fun getAssets(): AssetManager = res.assets
|
||||
override fun getResources() = res
|
||||
override fun getClassLoader() = javaClass.classLoader!!
|
||||
override fun createConfigurationContext(config: Configuration): Context {
|
||||
return ResContext(super.createConfigurationContext(config))
|
||||
return super.createConfigurationContext(config).wrap(true)
|
||||
}
|
||||
}
|
||||
|
||||
private class ResContext(base: Context) : GlobalResContext(base) {
|
||||
override val mRes by lazy { base.resources.patch() }
|
||||
|
||||
private class ReInjectedContext(base: Context) : InjectedContext(base) {
|
||||
override val res by lazy { base.resources.patch() }
|
||||
private fun Resources.patch(): Resources {
|
||||
updateConfig()
|
||||
if (isRunningAsStub)
|
||||
assets.addAssetPath(ResMgr.apk)
|
||||
assets.addAssetPath(AssetHack.apk)
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
object ResMgr {
|
||||
object AssetHack {
|
||||
|
||||
lateinit var resource: Resources
|
||||
lateinit var apk: String
|
||||
@@ -101,62 +88,39 @@ object ResMgr {
|
||||
apk = context.packageResourcePath
|
||||
}
|
||||
}
|
||||
|
||||
fun newResource(): Resources {
|
||||
val asset = AssetManager::class.java.newInstance()
|
||||
asset.addAssetPath(apk)
|
||||
val config = Configuration(resource.configuration)
|
||||
val metrics = DisplayMetrics()
|
||||
metrics.setTo(resource.displayMetrics)
|
||||
return Resources(asset, metrics, config)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(28)
|
||||
private class JobSchedulerWrapper(private val base: JobScheduler) : JobScheduler() {
|
||||
|
||||
override fun schedule(job: JobInfo): Int {
|
||||
return base.schedule(job.patch())
|
||||
}
|
||||
|
||||
override fun enqueue(job: JobInfo, work: JobWorkItem): Int {
|
||||
return base.enqueue(job.patch(), work)
|
||||
}
|
||||
|
||||
override fun cancel(jobId: Int) {
|
||||
base.cancel(jobId)
|
||||
}
|
||||
|
||||
override fun cancelAll() {
|
||||
base.cancelAll()
|
||||
}
|
||||
|
||||
override fun getAllPendingJobs(): List<JobInfo> {
|
||||
return base.allPendingJobs
|
||||
}
|
||||
|
||||
override fun getPendingJob(jobId: Int): JobInfo? {
|
||||
return base.getPendingJob(jobId)
|
||||
}
|
||||
|
||||
override fun schedule(job: JobInfo) = base.schedule(job.patch())
|
||||
override fun enqueue(job: JobInfo, work: JobWorkItem) = base.enqueue(job.patch(), work)
|
||||
override fun cancel(jobId: Int) = base.cancel(jobId)
|
||||
override fun cancelAll() = base.cancelAll()
|
||||
override fun getAllPendingJobs(): List<JobInfo> = base.allPendingJobs
|
||||
override fun getPendingJob(jobId: Int) = base.getPendingJob(jobId)
|
||||
private fun JobInfo.patch(): JobInfo {
|
||||
// We need to swap out the service of JobInfo
|
||||
val name = service.className
|
||||
val component = ComponentName(
|
||||
service.packageName,
|
||||
Info.stubChk.classToComponent[name] ?: name
|
||||
)
|
||||
// Swap out the service of JobInfo
|
||||
val component = service.run {
|
||||
ComponentName(packageName,
|
||||
Info.stub?.classToComponent?.get(className) ?: className)
|
||||
}
|
||||
javaClass.getDeclaredField("service").apply {
|
||||
isAccessible = true
|
||||
}.set(this, component)
|
||||
|
||||
javaClass.forceGetDeclaredField("service")?.set(this, component)
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
private object ClassMap {
|
||||
|
||||
private val map = mapOf(
|
||||
App::class.java to a.e::class.java,
|
||||
MainActivity::class.java to a.b::class.java,
|
||||
SplashActivity::class.java to a.c::class.java,
|
||||
GeneralReceiver::class.java to a.h::class.java,
|
||||
DownloadService::class.java to a.j::class.java,
|
||||
SuRequestActivity::class.java to a.m::class.java
|
||||
)
|
||||
|
||||
operator fun get(c: Class<*>) = map.getOrElse(c) { c }
|
||||
}
|
||||
|
||||
// Keep a reference to these resources to prevent it from
|
||||
// being removed when running "remove unused resources"
|
||||
val shouldKeepResources = listOf(
|
||||
|
||||
@@ -1,41 +1,45 @@
|
||||
package com.topjohnwu.magisk.core
|
||||
|
||||
import android.os.Build
|
||||
import androidx.databinding.ObservableBoolean
|
||||
import com.topjohnwu.magisk.DynAPK
|
||||
import com.topjohnwu.magisk.core.model.UpdateInfo
|
||||
import com.topjohnwu.magisk.core.utils.net.NetworkObserver
|
||||
import com.topjohnwu.magisk.data.repository.NetworkService
|
||||
import com.topjohnwu.magisk.ktx.get
|
||||
import com.topjohnwu.magisk.utils.CachedValue
|
||||
import com.topjohnwu.magisk.ktx.getProperty
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.ShellUtils.fastCmd
|
||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||
import java.io.FileInputStream
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
val isRunningAsStub get() = Info.stub != null
|
||||
|
||||
object Info {
|
||||
|
||||
val envRef = CachedValue { loadState() }
|
||||
|
||||
@JvmStatic val env by envRef
|
||||
|
||||
var stub: DynAPK.Data? = null
|
||||
val stubChk: DynAPK.Data
|
||||
get() = stub as DynAPK.Data
|
||||
|
||||
var remote = UpdateInfo()
|
||||
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
|
||||
}
|
||||
|
||||
// Device state
|
||||
var crypto = ""
|
||||
@JvmStatic var isSAR = false
|
||||
@JvmStatic var isAB = false
|
||||
@JvmStatic val env by lazy { loadState() }
|
||||
@JvmField var isSAR = false
|
||||
@JvmField var isAB = false
|
||||
@JvmField val isVirtualAB = getProperty("ro.virtual_ab.enabled", "false") == "true"
|
||||
@JvmStatic val isFDE get() = crypto == "block"
|
||||
@JvmStatic var ramdisk = false
|
||||
@JvmStatic var hasGMS = true
|
||||
@JvmStatic var isPixel = false
|
||||
@JvmStatic val cryptoText get() = crypto.capitalize(Locale.US)
|
||||
@JvmField var ramdisk = false
|
||||
@JvmField var hasGMS = true
|
||||
@JvmField val isPixel = Build.BRAND == "google"
|
||||
@JvmField val isEmulator = getProperty("ro.kernel.qemu", "0") == "1"
|
||||
var crypto = ""
|
||||
var noDataExec = false
|
||||
|
||||
val isConnected by lazy {
|
||||
ObservableBoolean(false).also { field ->
|
||||
@@ -47,14 +51,12 @@ object Info {
|
||||
|
||||
val isNewReboot by lazy {
|
||||
try {
|
||||
FileInputStream("/proc/sys/kernel/random/boot_id").bufferedReader().use {
|
||||
val id = it.readLine()
|
||||
if (id != Config.bootId) {
|
||||
Config.bootId = id
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
val id = File("/proc/sys/kernel/random/boot_id").readText()
|
||||
if (id != Config.bootId) {
|
||||
Config.bootId = id
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
false
|
||||
@@ -64,7 +66,7 @@ object Info {
|
||||
private fun loadState() = Env(
|
||||
fastCmd("magisk -v").split(":".toRegex())[0],
|
||||
runCatching { fastCmd("magisk -V").toInt() }.getOrDefault(-1),
|
||||
Shell.su("magiskhide --status").exec().isSuccess
|
||||
Shell.su("magiskhide status").exec().isSuccess
|
||||
)
|
||||
|
||||
class Env(
|
||||
@@ -73,8 +75,8 @@ object Info {
|
||||
hide: Boolean = false
|
||||
) {
|
||||
val magiskHide get() = Config.magiskHide
|
||||
val magiskVersionCode = when (code) {
|
||||
in Int.MIN_VALUE..Const.Version.MIN_VERCODE -> -1
|
||||
val magiskVersionCode = when {
|
||||
code < Const.Version.MIN_VERCODE -> -1
|
||||
else -> if (Shell.rootAccess()) code else -1
|
||||
}
|
||||
val isUnsupported = code > 0 && code < Const.Version.MIN_VERCODE
|
||||
|
||||
39
app/src/main/java/com/topjohnwu/magisk/core/Provider.kt
Normal file
39
app/src/main/java/com/topjohnwu/magisk/core/Provider.kt
Normal file
@@ -0,0 +1,39 @@
|
||||
package com.topjohnwu.magisk.core
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.ProviderInfo
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.os.ParcelFileDescriptor.MODE_READ_ONLY
|
||||
import com.topjohnwu.magisk.FileProvider
|
||||
import com.topjohnwu.magisk.core.su.SuCallbackHandler
|
||||
import java.io.File
|
||||
|
||||
open class Provider : FileProvider() {
|
||||
|
||||
override fun attachInfo(context: Context, info: ProviderInfo?) {
|
||||
super.attachInfo(context.wrap(), info)
|
||||
}
|
||||
|
||||
override fun call(method: String, arg: String?, extras: Bundle?): Bundle? {
|
||||
SuCallbackHandler(context!!, method, extras)
|
||||
return Bundle.EMPTY
|
||||
}
|
||||
|
||||
override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? {
|
||||
return when (uri.encodedPath ?: return null) {
|
||||
"/apk_file" -> ParcelFileDescriptor.open(File(context!!.packageCodePath), MODE_READ_ONLY)
|
||||
"/prefs_file" -> ParcelFileDescriptor.open(Config.prefsFile, MODE_READ_ONLY)
|
||||
else -> super.openFile(uri, mode)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun APK_URI(pkg: String) =
|
||||
Uri.Builder().scheme("content").authority("$pkg.provider").path("apk_file").build()
|
||||
|
||||
fun PREFS_URI(pkg: String) =
|
||||
Uri.Builder().scheme("content").authority("$pkg.provider").path("prefs_file").build()
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user