mirror of
https://github.com/topjohnwu/Magisk
synced 2025-11-07 22:52:31 +01:00
Compare commits
197 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cacf873645 | ||
|
|
11e1e7ee36 | ||
|
|
87801b6f23 | ||
|
|
7ce4789e17 | ||
|
|
9dc6d9afce | ||
|
|
d6a5354bff | ||
|
|
07af37475b | ||
|
|
1b9c273b10 | ||
|
|
262c52db56 | ||
|
|
eb777296d4 | ||
|
|
fc70a384d3 | ||
|
|
34b2f525a3 | ||
|
|
569e9ad937 | ||
|
|
c495b3d183 | ||
|
|
8b16bfbb54 | ||
|
|
b2f1fd9966 | ||
|
|
317153c53a | ||
|
|
fa60daf9b5 | ||
|
|
aadb2d825c | ||
|
|
0e7fe537e3 | ||
|
|
409de3ac44 | ||
|
|
759055eaa5 | ||
|
|
9016e6727d | ||
|
|
a3381da7ed | ||
|
|
351e094440 | ||
|
|
2106751ea4 | ||
|
|
7ae3cd1c43 | ||
|
|
edfd4dcddf | ||
|
|
fb89cf1367 | ||
|
|
b7b345cf8a | ||
|
|
0be487e47e | ||
|
|
5471147422 | ||
|
|
6305159c5e | ||
|
|
2ed092c9db | ||
|
|
5c6a7ffa6f | ||
|
|
9ab7550970 | ||
|
|
47e7a0a434 | ||
|
|
4cc5e9f986 | ||
|
|
6a2ae89846 | ||
|
|
3c93539e02 | ||
|
|
05e5ac2ad2 | ||
|
|
10b1782732 | ||
|
|
e029994ef8 | ||
|
|
9679874874 | ||
|
|
8186f253e8 | ||
|
|
d4fe8632ec | ||
|
|
d7776f6597 | ||
|
|
3219d945f5 | ||
|
|
8a73a16029 | ||
|
|
ce90f9b60d | ||
|
|
bdf54d562f | ||
|
|
e744cc8ea6 | ||
|
|
babcf36495 | ||
|
|
e4094c0caa | ||
|
|
2e51fe20a1 | ||
|
|
c29636c452 | ||
|
|
22017a5543 | ||
|
|
50e2f33d1c | ||
|
|
5e6eb8dd01 | ||
|
|
18acb97dfe | ||
|
|
bf2f823b8c | ||
|
|
d0c4226997 | ||
|
|
4ea8bd0229 | ||
|
|
ee0d58a9b8 | ||
|
|
bf04fa134b | ||
|
|
297662cafb | ||
|
|
f464a9b269 | ||
|
|
d19fcd5e21 | ||
|
|
c0981174a8 | ||
|
|
0b5f973b31 | ||
|
|
4159b3871c | ||
|
|
580c993c0b | ||
|
|
0cc29350a0 | ||
|
|
490a784993 | ||
|
|
9c774f96db | ||
|
|
99afe7ac07 | ||
|
|
b3f05fd925 | ||
|
|
683cfee88b | ||
|
|
3bcaf0ed5b | ||
|
|
edb76503d3 | ||
|
|
484038638f | ||
|
|
8dfb30fefe | ||
|
|
2a252d13b8 | ||
|
|
afa364cfc3 | ||
|
|
dfa36fb25d | ||
|
|
c8492b0c58 | ||
|
|
083ef803fe | ||
|
|
351f0269ae | ||
|
|
a29ae15ff7 | ||
|
|
34dded3b25 | ||
|
|
975b1a5e36 | ||
|
|
e11508f84d | ||
|
|
0772f6dcaf | ||
|
|
d3fe3a711a | ||
|
|
756d8356ca | ||
|
|
42003b4006 | ||
|
|
dc65a2b884 | ||
|
|
071ae79fa8 | ||
|
|
c11ccbae2d | ||
|
|
6ef86d8d20 | ||
|
|
985249c3d0 | ||
|
|
622e09862a | ||
|
|
7505599ea0 | ||
|
|
575c417403 | ||
|
|
9f7a3db8be | ||
|
|
029422679c | ||
|
|
05d6d2b51b | ||
|
|
4cff0384f7 | ||
|
|
68db366696 | ||
|
|
358538717c | ||
|
|
24603b3cef | ||
|
|
4eb9240806 | ||
|
|
0469f0b5ae | ||
|
|
0b8577d02b | ||
|
|
97135879a1 | ||
|
|
fef41f68c0 | ||
|
|
0ac19e3a4e | ||
|
|
2793d209a4 | ||
|
|
71e9c044e6 | ||
|
|
42e5f5150a | ||
|
|
90545057e9 | ||
|
|
cffd024e9e | ||
|
|
8c858592c4 | ||
|
|
4f1a1879e5 | ||
|
|
e88eed9a8d | ||
|
|
9581ae8245 | ||
|
|
4202b7a9dc | ||
|
|
b4c398542a | ||
|
|
081148b2d7 | ||
|
|
a32c4561ed | ||
|
|
cc79a96fa3 | ||
|
|
ff340ce3d8 | ||
|
|
134508193d | ||
|
|
c2b74aa83e | ||
|
|
3358eab991 | ||
|
|
a609e0aad4 | ||
|
|
f97866a961 | ||
|
|
e1987c42c4 | ||
|
|
18566715e1 | ||
|
|
79f0f3230c | ||
|
|
63a89d9f04 | ||
|
|
f639f39e79 | ||
|
|
b4099fc5f9 | ||
|
|
ff2513e276 | ||
|
|
f24d52436b | ||
|
|
9de6e8846b | ||
|
|
01a1213463 | ||
|
|
f0fbd9214a | ||
|
|
c4f37c550f | ||
|
|
448384af06 | ||
|
|
3f840f53a0 | ||
|
|
d8718d8ac8 | ||
|
|
2fb46a11dc | ||
|
|
9a11412719 | ||
|
|
98874be171 | ||
|
|
704f91545e | ||
|
|
efb3239cbd | ||
|
|
7e7ddeb9e2 | ||
|
|
9e8218089b | ||
|
|
3f660a3963 | ||
|
|
daeb6711b0 | ||
|
|
4e1aec28a0 | ||
|
|
5512917ec1 | ||
|
|
cd1edc5d56 | ||
|
|
4f52587586 | ||
|
|
d7ee4ef5f5 | ||
|
|
31f88e0f05 | ||
|
|
9f1740cc4f | ||
|
|
f2c15c7701 | ||
|
|
e67d0678f9 | ||
|
|
b1faa5eed4 | ||
|
|
7f1f0b9048 | ||
|
|
183e5f2ecc | ||
|
|
14efe4939a | ||
|
|
3dc7d77ea9 | ||
|
|
0f07bbb3e5 | ||
|
|
dd5a3416bf | ||
|
|
2fb49ad780 | ||
|
|
92f0e53fee | ||
|
|
876132694d | ||
|
|
1257ba41c6 | ||
|
|
2cc71ac7ed | ||
|
|
753808a4ce | ||
|
|
32cd694ad5 | ||
|
|
f008420891 | ||
|
|
fa8900be65 | ||
|
|
69c2f407d6 | ||
|
|
ffcd093db1 | ||
|
|
8dbf93750f | ||
|
|
e266a81167 | ||
|
|
e841aab9e7 | ||
|
|
49f259065d | ||
|
|
b10379e700 | ||
|
|
810d27a618 | ||
|
|
9b60c005c7 | ||
|
|
cc6ca0bda2 | ||
|
|
4512232637 |
17
.github/ISSUE_TEMPLATE/bug_report.md
vendored
17
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,19 +1,18 @@
|
|||||||
---
|
---
|
||||||
name: Bug report
|
name: Bug report
|
||||||
about: Create a report to help us improve
|
about: Create a report to help us improve
|
||||||
title: ''
|
title: ""
|
||||||
labels: ''
|
labels: ""
|
||||||
assignees: ''
|
assignees: ""
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
## READ BEFORE OPENING ISSUES
|
## 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.
|
All bug reports require you to **USE DEBUG 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 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 MAGISK**.
|
||||||
|
|
||||||
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 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.
|
||||||
|
|
||||||
@@ -31,7 +30,7 @@ Without following the rules above, your issue will be closed without explanation
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
Device:
|
Device:
|
||||||
Android version:
|
Android version:
|
||||||
Magisk version name:
|
Magisk version name:
|
||||||
Magisk version code:
|
Magisk version code:
|
||||||
|
|||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -25,9 +25,6 @@
|
|||||||
[submodule "pcre"]
|
[submodule "pcre"]
|
||||||
path = native/jni/external/pcre
|
path = native/jni/external/pcre
|
||||||
url = https://android.googlesource.com/platform/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"]
|
[submodule "libcxx"]
|
||||||
path = native/jni/external/libcxx
|
path = native/jni/external/libcxx
|
||||||
url = https://github.com/topjohnwu/libcxx.git
|
url = https://github.com/topjohnwu/libcxx.git
|
||||||
|
|||||||
17
README.MD
17
README.MD
@@ -18,9 +18,10 @@ Some highlight features:
|
|||||||
|
|
||||||
[Github](https://github.com/topjohnwu/Magisk/) is the only source where you can get official Magisk information and downloads.
|
[Github](https://github.com/topjohnwu/Magisk/) is the only source where you can get official Magisk information and downloads.
|
||||||
|
|
||||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v24.1)
|
[](https://github.com/topjohnwu/Magisk/releases/tag/v24.3)
|
||||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v24.2)
|
[](https://github.com/topjohnwu/Magisk/releases/tag/v24.3)
|
||||||
[](https://raw.githubusercontent.com/topjohnwu/magisk-files/canary/app-debug.apk)
|
[](https://raw.githubusercontent.com/topjohnwu/magisk-files/canary/app-release.apk)
|
||||||
|
[](https://raw.githubusercontent.com/topjohnwu/magisk-files/canary/app-debug.apk)
|
||||||
|
|
||||||
## Useful Links
|
## Useful Links
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ Some highlight features:
|
|||||||
|
|
||||||
## Bug Reports
|
## Bug Reports
|
||||||
|
|
||||||
**Only bug reports from Canary builds will be accepted.**
|
**Only bug reports from Debug builds will be accepted.**
|
||||||
|
|
||||||
For installation issues, upload both boot image and install logs.<br>
|
For installation issues, upload both boot image and install logs.<br>
|
||||||
For Magisk issues, upload boot logcat or dmesg.<br>
|
For Magisk issues, upload boot logcat or dmesg.<br>
|
||||||
@@ -52,7 +53,13 @@ For Magisk app crashes, record and upload the logcat when the crash occurs.
|
|||||||
For each action, use `-h` to access help (e.g. `./build.py all -h`)
|
For each action, use `-h` to access help (e.g. `./build.py all -h`)
|
||||||
- To start development, open the project with Android Studio. The IDE can be used for both app (Kotlin/Java) and native (C++/C) sources.
|
- 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.
|
- 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).
|
|
||||||
|
## Signing and Distribution
|
||||||
|
|
||||||
|
- The certificate of the key used to sign the final Magisk APK product is also directly embedded into some executables. In release builds, Magisk's root daemon will enforce this certificate check and reject and forcefully uninstall any non-matching Magisk apps to protect users from malicious and unverified Magisk APKs.
|
||||||
|
- To do any development on Magisk itself, switch to an **official debug build and reinstall Magisk** to bypass the signature check.
|
||||||
|
- To distribute your own Magisk builds signed with your own keys, set your signing configs in `config.prop`.
|
||||||
|
- Check [Google's Documentation](https://developer.android.com/studio/publish/app-signing.html#generate-key) for more details on generating your own key.
|
||||||
|
|
||||||
## Translation Contributions
|
## Translation Contributions
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ kapt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
namespace = "com.topjohnwu.magisk"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.topjohnwu.magisk"
|
applicationId = "com.topjohnwu.magisk"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
@@ -54,11 +56,6 @@ android {
|
|||||||
keepDebugSymbols += "**/*.so"
|
keepDebugSymbols += "**/*.so"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "11"
|
|
||||||
freeCompilerArgs = listOf("-Xjvm-default=enable")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setupApp()
|
setupApp()
|
||||||
@@ -74,22 +71,17 @@ dependencies {
|
|||||||
implementation("com.github.topjohnwu:jtar:1.0.0")
|
implementation("com.github.topjohnwu:jtar:1.0.0")
|
||||||
implementation("com.github.topjohnwu:indeterminate-checkbox:1.0.7")
|
implementation("com.github.topjohnwu:indeterminate-checkbox:1.0.7")
|
||||||
implementation("com.github.topjohnwu:lz4-java:1.7.1")
|
implementation("com.github.topjohnwu:lz4-java:1.7.1")
|
||||||
implementation("com.jakewharton.timber:timber:4.7.1")
|
implementation("com.jakewharton.timber:timber:5.0.1")
|
||||||
implementation("org.bouncycastle:bcpkix-jdk15on:1.70")
|
implementation("org.bouncycastle:bcpkix-jdk18on:1.71")
|
||||||
implementation("dev.rikka.rikkax.layoutinflater:layoutinflater:1.2.0")
|
implementation("dev.rikka.rikkax.layoutinflater:layoutinflater:1.2.0")
|
||||||
implementation("dev.rikka.rikkax.insets:insets:1.1.1")
|
implementation("dev.rikka.rikkax.insets:insets:1.2.0")
|
||||||
implementation("dev.rikka.rikkax.recyclerview:recyclerview-ktx:1.3.1")
|
implementation("dev.rikka.rikkax.recyclerview:recyclerview-ktx:1.3.1")
|
||||||
implementation("io.noties.markwon:core:4.6.2")
|
implementation("io.noties.markwon:core:4.6.2")
|
||||||
|
|
||||||
val vBAdapt = "4.0.0"
|
val vLibsu = "5.0.2"
|
||||||
val bindingAdapter = "me.tatarka.bindingcollectionadapter2:bindingcollectionadapter"
|
|
||||||
implementation("${bindingAdapter}:${vBAdapt}")
|
|
||||||
implementation("${bindingAdapter}-recyclerview:${vBAdapt}")
|
|
||||||
|
|
||||||
val vLibsu = "4.0.2"
|
|
||||||
implementation("com.github.topjohnwu.libsu:core:${vLibsu}")
|
implementation("com.github.topjohnwu.libsu:core:${vLibsu}")
|
||||||
implementation("com.github.topjohnwu.libsu:io:${vLibsu}")
|
|
||||||
implementation("com.github.topjohnwu.libsu:service:${vLibsu}")
|
implementation("com.github.topjohnwu.libsu:service:${vLibsu}")
|
||||||
|
implementation("com.github.topjohnwu.libsu:nio:${vLibsu}")
|
||||||
|
|
||||||
val vRetrofit = "2.9.0"
|
val vRetrofit = "2.9.0"
|
||||||
implementation("com.squareup.retrofit2:retrofit:${vRetrofit}")
|
implementation("com.squareup.retrofit2:retrofit:${vRetrofit}")
|
||||||
@@ -105,17 +97,17 @@ dependencies {
|
|||||||
implementation("com.squareup.moshi:moshi:${vMoshi}")
|
implementation("com.squareup.moshi:moshi:${vMoshi}")
|
||||||
kapt("com.squareup.moshi:moshi-kotlin-codegen:${vMoshi}")
|
kapt("com.squareup.moshi:moshi-kotlin-codegen:${vMoshi}")
|
||||||
|
|
||||||
val vRoom = "2.4.1"
|
val vRoom = "2.4.2"
|
||||||
implementation("androidx.room:room-runtime:${vRoom}")
|
implementation("androidx.room:room-runtime:${vRoom}")
|
||||||
implementation("androidx.room:room-ktx:${vRoom}")
|
implementation("androidx.room:room-ktx:${vRoom}")
|
||||||
kapt("androidx.room:room-compiler:${vRoom}")
|
kapt("androidx.room:room-compiler:${vRoom}")
|
||||||
|
|
||||||
val vNav = "2.5.0-alpha01"
|
val vNav = "2.5.0-rc01"
|
||||||
implementation("androidx.navigation:navigation-fragment-ktx:${vNav}")
|
implementation("androidx.navigation:navigation-fragment-ktx:${vNav}")
|
||||||
implementation("androidx.navigation:navigation-ui-ktx:${vNav}")
|
implementation("androidx.navigation:navigation-ui-ktx:${vNav}")
|
||||||
|
|
||||||
implementation("androidx.biometric:biometric:1.1.0")
|
implementation("androidx.biometric:biometric:1.1.0")
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.1.3")
|
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||||
implementation("androidx.appcompat:appcompat:1.4.1")
|
implementation("androidx.appcompat:appcompat:1.4.1")
|
||||||
implementation("androidx.preference:preference:1.2.0")
|
implementation("androidx.preference:preference:1.2.0")
|
||||||
@@ -123,6 +115,6 @@ dependencies {
|
|||||||
implementation("androidx.fragment:fragment-ktx:1.4.1")
|
implementation("androidx.fragment:fragment-ktx:1.4.1")
|
||||||
implementation("androidx.transition:transition:1.4.1")
|
implementation("androidx.transition:transition:1.4.1")
|
||||||
implementation("androidx.core:core-ktx:1.7.0")
|
implementation("androidx.core:core-ktx:1.7.0")
|
||||||
implementation("androidx.core:core-splashscreen:1.0.0-beta01")
|
implementation("androidx.core:core-splashscreen:1.0.0-rc01")
|
||||||
implementation("com.google.android.material:material:1.5.0")
|
implementation("com.google.android.material:material:1.6.0")
|
||||||
}
|
}
|
||||||
|
|||||||
26
app/proguard-rules.pro
vendored
26
app/proguard-rules.pro
vendored
@@ -1,21 +1,3 @@
|
|||||||
# Add project specific ProGuard rules here.
|
|
||||||
# By default, the flags in this file are appended to flags specified
|
|
||||||
# in /Users/topjohnwu/Library/Android/sdk/tools/proguard/proguard-android.txt
|
|
||||||
# You can edit the include path and order by changing the proguardFiles
|
|
||||||
# directive in build.gradle.
|
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# Add any project specific keep options here:
|
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following
|
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
|
||||||
# class:
|
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
||||||
# public *;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# Parcelable
|
# Parcelable
|
||||||
-keepclassmembers class * implements android.os.Parcelable {
|
-keepclassmembers class * implements android.os.Parcelable {
|
||||||
public static final ** CREATOR;
|
public static final ** CREATOR;
|
||||||
@@ -26,6 +8,9 @@
|
|||||||
public static void check*(...);
|
public static void check*(...);
|
||||||
public static void throw*(...);
|
public static void throw*(...);
|
||||||
}
|
}
|
||||||
|
-assumenosideeffects class java.util.Objects {
|
||||||
|
public static ** requireNonNull(...);
|
||||||
|
}
|
||||||
|
|
||||||
# Stub
|
# Stub
|
||||||
-keep class com.topjohnwu.magisk.core.App { <init>(java.lang.Object); }
|
-keep class com.topjohnwu.magisk.core.App { <init>(java.lang.Object); }
|
||||||
@@ -34,6 +19,11 @@
|
|||||||
boolean mActivityHandlesUiMode;
|
boolean mActivityHandlesUiMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# main
|
||||||
|
-keep,allowoptimization public class com.topjohnwu.magisk.signing.SignBoot {
|
||||||
|
public static void main(java.lang.String[]);
|
||||||
|
}
|
||||||
|
|
||||||
# Strip Timber verbose and debug logging
|
# Strip Timber verbose and debug logging
|
||||||
-assumenosideeffects class timber.log.Timber$Tree {
|
-assumenosideeffects class timber.log.Timber$Tree {
|
||||||
public void v(**);
|
public void v(**);
|
||||||
|
|||||||
@@ -5,9 +5,7 @@ plugins {
|
|||||||
setupCommon()
|
setupCommon()
|
||||||
|
|
||||||
android {
|
android {
|
||||||
defaultConfig {
|
namespace = "com.topjohnwu.shared"
|
||||||
consumerProguardFiles("proguard-rules.pro")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|||||||
21
app/shared/proguard-rules.pro
vendored
21
app/shared/proguard-rules.pro
vendored
@@ -1,21 +0,0 @@
|
|||||||
# Add project specific ProGuard rules here.
|
|
||||||
# You can control the set of applied configuration files using the
|
|
||||||
# proguardFiles setting in build.gradle.
|
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following
|
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
|
||||||
# class:
|
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
||||||
# public *;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# Uncomment this to preserve the line number information for
|
|
||||||
# debugging stack traces.
|
|
||||||
#-keepattributes SourceFile,LineNumberTable
|
|
||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
|
||||||
# hide the original source file name.
|
|
||||||
#-renamesourcefileattribute SourceFile
|
|
||||||
@@ -1,14 +1,20 @@
|
|||||||
package com.topjohnwu.magisk;
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.content.res.loader.ResourcesLoader;
|
||||||
|
import android.content.res.loader.ResourcesProvider;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -50,12 +56,21 @@ public class StubApk {
|
|||||||
return new File(getDynDir(info), "update.apk");
|
return new File(getDynDir(info), "update.apk");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addAssetPath(AssetManager asset, String path) {
|
public static void addAssetPath(Resources res, String path) {
|
||||||
try {
|
if (SDK_INT >= 30) {
|
||||||
if (addAssetPath == null)
|
try (var fd = ParcelFileDescriptor.open(new File(path), MODE_READ_ONLY)) {
|
||||||
addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
|
var loader = new ResourcesLoader();
|
||||||
addAssetPath.invoke(asset, path);
|
loader.addProvider(ResourcesProvider.loadFromApk(fd));
|
||||||
} catch (Exception ignored) {}
|
res.addLoaders(loader);
|
||||||
|
} catch (IOException ignored) {}
|
||||||
|
} else {
|
||||||
|
AssetManager asset = res.getAssets();
|
||||||
|
try {
|
||||||
|
if (addAssetPath == null)
|
||||||
|
addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
|
||||||
|
addAssetPath.invoke(asset, path);
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void restartProcess(Activity activity) {
|
public static void restartProcess(Activity activity) {
|
||||||
|
|||||||
@@ -5,18 +5,17 @@ import java.io.IOException;
|
|||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
|
|
||||||
import dalvik.system.DexClassLoader;
|
import dalvik.system.BaseDexClassLoader;
|
||||||
|
|
||||||
public class DynamicClassLoader extends DexClassLoader {
|
public class DynamicClassLoader extends BaseDexClassLoader {
|
||||||
|
|
||||||
private static final ClassLoader base = Object.class.getClassLoader();
|
|
||||||
|
|
||||||
public DynamicClassLoader(File apk) {
|
public DynamicClassLoader(File apk) {
|
||||||
super(apk.getPath(), apk.getParent(), null, base);
|
this(apk, getSystemClassLoader());
|
||||||
}
|
}
|
||||||
|
|
||||||
public DynamicClassLoader(File apk, ClassLoader parent) {
|
public DynamicClassLoader(File apk, ClassLoader parent) {
|
||||||
super(apk.getPath(), apk.getParent(), null, parent);
|
// Set optimizedDirectory to null to bypass DexFile's security checks
|
||||||
|
super(apk.getPath(), null, null, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -28,7 +27,7 @@ public class DynamicClassLoader extends DexClassLoader {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Then check boot classpath
|
// Then check boot classpath
|
||||||
return base.loadClass(name);
|
return getSystemClassLoader().loadClass(name);
|
||||||
} catch (ClassNotFoundException ignored) {
|
} catch (ClassNotFoundException ignored) {
|
||||||
try {
|
try {
|
||||||
// Next try current dex
|
// Next try current dex
|
||||||
@@ -46,7 +45,7 @@ public class DynamicClassLoader extends DexClassLoader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URL getResource(String name) {
|
public URL getResource(String name) {
|
||||||
URL resource = base.getResource(name);
|
URL resource = getSystemClassLoader().getResource(name);
|
||||||
if (resource != null)
|
if (resource != null)
|
||||||
return resource;
|
return resource;
|
||||||
resource = findResource(name);
|
resource = findResource(name);
|
||||||
@@ -58,7 +57,7 @@ public class DynamicClassLoader extends DexClassLoader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Enumeration<URL> getResources(String name) throws IOException {
|
public Enumeration<URL> getResources(String name) throws IOException {
|
||||||
return new CompoundEnumeration<>(base.getResources(name),
|
return new CompoundEnumeration<>(getSystemClassLoader().getResources(name),
|
||||||
findResources(name), getParent().getResources(name));
|
findResources(name), getParent().getResources(name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
package="com.topjohnwu.magisk">
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".core.App"
|
android:name=".core.App"
|
||||||
@@ -29,9 +26,7 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".ui.surequest.SuRequestActivity"
|
android:name=".ui.surequest.SuRequestActivity"
|
||||||
android:directBootAware="true"
|
android:directBootAware="true"
|
||||||
android:excludeFromRecents="true"
|
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:taskAffinity=""
|
|
||||||
tools:ignore="AppLinkUrlError">
|
tools:ignore="AppLinkUrlError">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
@@ -41,7 +36,6 @@
|
|||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".core.Receiver"
|
android:name=".core.Receiver"
|
||||||
android:directBootAware="true"
|
|
||||||
android:exported="false">
|
android:exported="false">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.LOCALE_CHANGED" />
|
<action android:name="android.intent.action.LOCALE_CHANGED" />
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// IRootUtils.aidl
|
||||||
|
package com.topjohnwu.magisk.core.utils;
|
||||||
|
|
||||||
|
// Declare any non-default types here with import statements
|
||||||
|
|
||||||
|
interface IRootUtils {
|
||||||
|
android.app.ActivityManager.RunningAppProcessInfo getAppProcess(int pid);
|
||||||
|
IBinder getFileSystem();
|
||||||
|
}
|
||||||
@@ -20,11 +20,12 @@ abstract class BaseFragment<Binding : ViewDataBinding> : Fragment(), ViewModelHo
|
|||||||
protected abstract val layoutRes: Int
|
protected abstract val layoutRes: Int
|
||||||
|
|
||||||
private val navigation get() = activity?.navigation
|
private val navigation get() = activity?.navigation
|
||||||
|
open val snackbarView: View? get() = null
|
||||||
open val snackbarAnchorView: View? get() = null
|
open val snackbarAnchorView: View? get() = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
startObserveEvents()
|
startObserveLiveData()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
@@ -36,9 +37,14 @@ abstract class BaseFragment<Binding : ViewDataBinding> : Fragment(), ViewModelHo
|
|||||||
it.setVariable(BR.viewModel, viewModel)
|
it.setVariable(BR.viewModel, viewModel)
|
||||||
it.lifecycleOwner = viewLifecycleOwner
|
it.lifecycleOwner = viewLifecycleOwner
|
||||||
}
|
}
|
||||||
|
savedInstanceState?.let { viewModel.onRestoreState(it) }
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
viewModel.onSaveState(outState)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
activity?.supportActionBar?.subtitle = null
|
activity?.supportActionBar?.subtitle = null
|
||||||
@@ -78,7 +84,7 @@ abstract class BaseFragment<Binding : ViewDataBinding> : Fragment(), ViewModelHo
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun NavDirections.navigate() {
|
fun NavDirections.navigate() {
|
||||||
navigation?.navigate(this)
|
navigation?.currentDestination?.getAction(actionId)?.let { navigation!!.navigate(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,8 @@ package com.topjohnwu.magisk.arch
|
|||||||
import android.Manifest.permission.REQUEST_INSTALL_PACKAGES
|
import android.Manifest.permission.REQUEST_INSTALL_PACKAGES
|
||||||
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import androidx.annotation.CallSuper
|
import android.os.Bundle
|
||||||
import androidx.databinding.Bindable
|
import androidx.databinding.Bindable
|
||||||
import androidx.databinding.Observable
|
|
||||||
import androidx.databinding.PropertyChangeRegistry
|
import androidx.databinding.PropertyChangeRegistry
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
@@ -14,7 +13,6 @@ import androidx.lifecycle.viewModelScope
|
|||||||
import androidx.navigation.NavDirections
|
import androidx.navigation.NavDirections
|
||||||
import com.topjohnwu.magisk.BR
|
import com.topjohnwu.magisk.BR
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.core.Info
|
|
||||||
import com.topjohnwu.magisk.databinding.ObservableHost
|
import com.topjohnwu.magisk.databinding.ObservableHost
|
||||||
import com.topjohnwu.magisk.databinding.set
|
import com.topjohnwu.magisk.databinding.set
|
||||||
import com.topjohnwu.magisk.events.BackPressEvent
|
import com.topjohnwu.magisk.events.BackPressEvent
|
||||||
@@ -40,7 +38,6 @@ abstract class BaseViewModel(
|
|||||||
@get:Bindable
|
@get:Bindable
|
||||||
val loadFailed get() = state == State.LOADING_FAILED
|
val loadFailed get() = state == State.LOADING_FAILED
|
||||||
|
|
||||||
val isConnected get() = Info.isConnected
|
|
||||||
val viewEvents: LiveData<ViewEvent> get() = _viewEvents
|
val viewEvents: LiveData<ViewEvent> get() = _viewEvents
|
||||||
|
|
||||||
var state= initialState
|
var state= initialState
|
||||||
@@ -48,15 +45,9 @@ abstract class BaseViewModel(
|
|||||||
|
|
||||||
private val _viewEvents = MutableLiveData<ViewEvent>()
|
private val _viewEvents = MutableLiveData<ViewEvent>()
|
||||||
private var runningJob: Job? = null
|
private var runningJob: Job? = null
|
||||||
private val refreshCallback = object : Observable.OnPropertyChangedCallback() {
|
|
||||||
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
|
|
||||||
requestRefresh()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
open fun onSaveState(state: Bundle) {}
|
||||||
isConnected.addOnPropertyChangedCallback(refreshCallback)
|
open fun onRestoreState(state: Bundle) {}
|
||||||
}
|
|
||||||
|
|
||||||
/** This should probably never be called manually, it's called manually via delegate. */
|
/** This should probably never be called manually, it's called manually via delegate. */
|
||||||
@Synchronized
|
@Synchronized
|
||||||
@@ -69,12 +60,6 @@ abstract class BaseViewModel(
|
|||||||
|
|
||||||
protected open fun refresh(): Job? = null
|
protected open fun refresh(): Job? = null
|
||||||
|
|
||||||
@CallSuper
|
|
||||||
override fun onCleared() {
|
|
||||||
isConnected.removeOnPropertyChangedCallback(refreshCallback)
|
|
||||||
super.onCleared()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun withPermission(permission: String, callback: (Boolean) -> Unit) {
|
fun withPermission(permission: String, callback: (Boolean) -> Unit) {
|
||||||
PermissionEvent(permission, callback).publish()
|
PermissionEvent(permission, callback).publish()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import com.google.android.material.snackbar.Snackbar
|
|||||||
import com.topjohnwu.magisk.BR
|
import com.topjohnwu.magisk.BR
|
||||||
import com.topjohnwu.magisk.core.Config
|
import com.topjohnwu.magisk.core.Config
|
||||||
import com.topjohnwu.magisk.core.base.BaseActivity
|
import com.topjohnwu.magisk.core.base.BaseActivity
|
||||||
|
import com.topjohnwu.magisk.widget.Pre23CardViewBackgroundColorFixLayoutInflaterListener
|
||||||
import rikka.insets.WindowInsetsHelper
|
import rikka.insets.WindowInsetsHelper
|
||||||
import rikka.layoutinflater.view.LayoutInflaterFactory
|
import rikka.layoutinflater.view.LayoutInflaterFactory
|
||||||
|
|
||||||
@@ -26,17 +27,21 @@ abstract class UIActivity<Binding : ViewDataBinding> : BaseActivity(), ViewModel
|
|||||||
open val snackbarAnchorView: View? get() = null
|
open val snackbarAnchorView: View? get() = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val theme = Config.darkTheme
|
AppCompatDelegate.setDefaultNightMode(Config.darkTheme)
|
||||||
AppCompatDelegate.setDefaultNightMode(theme)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
layoutInflater.factory2 = LayoutInflaterFactory(delegate)
|
layoutInflater.factory2 = LayoutInflaterFactory(delegate)
|
||||||
.addOnViewCreatedListener(WindowInsetsHelper.LISTENER)
|
.addOnViewCreatedListener(WindowInsetsHelper.LISTENER)
|
||||||
|
.apply {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||||
|
this.addOnViewCreatedListener(Pre23CardViewBackgroundColorFixLayoutInflaterListener.getInstance())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
startObserveEvents()
|
startObserveLiveData()
|
||||||
|
|
||||||
// We need to set the window background explicitly since for whatever reason it's not
|
// We need to set the window background explicitly since for whatever reason it's not
|
||||||
// propagated upstream
|
// propagated upstream
|
||||||
|
|||||||
@@ -1,15 +1,28 @@
|
|||||||
package com.topjohnwu.magisk.arch
|
package com.topjohnwu.magisk.arch
|
||||||
|
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.ViewModelStoreOwner
|
||||||
|
import com.topjohnwu.magisk.core.Info
|
||||||
|
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||||
|
import com.topjohnwu.magisk.ui.home.HomeViewModel
|
||||||
|
import com.topjohnwu.magisk.ui.install.InstallViewModel
|
||||||
|
import com.topjohnwu.magisk.ui.log.LogViewModel
|
||||||
|
import com.topjohnwu.magisk.ui.superuser.SuperuserViewModel
|
||||||
|
import com.topjohnwu.magisk.ui.surequest.SuRequestViewModel
|
||||||
|
|
||||||
interface ViewModelHolder : LifecycleOwner {
|
interface ViewModelHolder : LifecycleOwner, ViewModelStoreOwner {
|
||||||
|
|
||||||
val viewModel: BaseViewModel
|
val viewModel: BaseViewModel
|
||||||
|
|
||||||
fun startObserveEvents() {
|
fun startObserveLiveData() {
|
||||||
viewModel.viewEvents.observe(this) {
|
viewModel.viewEvents.observe(this) {
|
||||||
onEventDispatched(it)
|
onEventDispatched(it)
|
||||||
}
|
}
|
||||||
|
Info.isConnected.observe(this) {
|
||||||
|
viewModel.requestRefresh()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,3 +30,23 @@ interface ViewModelHolder : LifecycleOwner {
|
|||||||
*/
|
*/
|
||||||
fun onEventDispatched(event: ViewEvent) {}
|
fun onEventDispatched(event: ViewEvent) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object VMFactory : ViewModelProvider.Factory {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
|
return when (modelClass) {
|
||||||
|
HomeViewModel::class.java -> HomeViewModel(ServiceLocator.networkService)
|
||||||
|
LogViewModel::class.java -> LogViewModel(ServiceLocator.logRepo)
|
||||||
|
SuperuserViewModel::class.java -> SuperuserViewModel(ServiceLocator.policyDB)
|
||||||
|
InstallViewModel::class.java -> InstallViewModel(ServiceLocator.networkService)
|
||||||
|
SuRequestViewModel::class.java ->
|
||||||
|
SuRequestViewModel(ServiceLocator.policyDB, ServiceLocator.timeoutPrefs)
|
||||||
|
else -> modelClass.newInstance()
|
||||||
|
} as T
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified VM : ViewModel> ViewModelHolder.viewModel() =
|
||||||
|
lazy(LazyThreadSafetyMode.NONE) {
|
||||||
|
ViewModelProvider(this, VMFactory)[VM::class.java]
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
package com.topjohnwu.magisk.core
|
package com.topjohnwu.magisk.core
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.topjohnwu.magisk.StubApk
|
import com.topjohnwu.magisk.StubApk
|
||||||
|
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||||
import com.topjohnwu.magisk.core.utils.*
|
import com.topjohnwu.magisk.core.utils.*
|
||||||
import com.topjohnwu.magisk.di.ServiceLocator
|
|
||||||
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
|
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||||
import com.topjohnwu.superuser.ipc.RootService
|
import com.topjohnwu.superuser.ipc.RootService
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
open class App() : Application() {
|
open class App() : Application() {
|
||||||
@@ -22,9 +22,9 @@ open class App() : Application() {
|
|||||||
constructor(o: Any) : this() {
|
constructor(o: Any) : this() {
|
||||||
val data = StubApk.Data(o)
|
val data = StubApk.Data(o)
|
||||||
// Add the root service name mapping
|
// Add the root service name mapping
|
||||||
data.classToComponent[RootRegistry::class.java.name] = data.rootService.name
|
data.classToComponent[RootUtils::class.java.name] = data.rootService.name
|
||||||
// Send back the actual root service class
|
// Send back the actual root service class
|
||||||
data.rootService = RootRegistry::class.java
|
data.rootService = RootUtils::class.java
|
||||||
Info.stub = data
|
Info.stub = data
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,43 +38,38 @@ open class App() : Application() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun attachBaseContext(context: Context) {
|
override fun attachBaseContext(context: Context) {
|
||||||
Shell.setDefaultBuilder(Shell.Builder.create()
|
|
||||||
.setFlags(Shell.FLAG_MOUNT_MASTER)
|
|
||||||
.setInitializers(ShellInit::class.java)
|
|
||||||
.setTimeout(2))
|
|
||||||
Shell.EXECUTOR = DispatcherExecutor(Dispatchers.IO)
|
|
||||||
|
|
||||||
// Get the actual ContextImpl
|
// Get the actual ContextImpl
|
||||||
val app: Application
|
val app: Application
|
||||||
val base: Context
|
val base: Context
|
||||||
if (context is Application) {
|
if (context is Application) {
|
||||||
app = context
|
app = context
|
||||||
base = context.baseContext
|
base = context.baseContext
|
||||||
|
AppApkPath = StubApk.current(base).path
|
||||||
} else {
|
} else {
|
||||||
app = this
|
app = this
|
||||||
base = context
|
base = context
|
||||||
|
AppApkPath = base.packageResourcePath
|
||||||
}
|
}
|
||||||
super.attachBaseContext(base)
|
super.attachBaseContext(base)
|
||||||
ServiceLocator.context = base
|
ServiceLocator.context = base
|
||||||
|
app.registerActivityLifecycleCallbacks(ActivityTracker)
|
||||||
|
|
||||||
|
Shell.setDefaultBuilder(Shell.Builder.create()
|
||||||
|
.setFlags(Shell.FLAG_MOUNT_MASTER)
|
||||||
|
.setInitializers(ShellInit::class.java)
|
||||||
|
.setContext(base)
|
||||||
|
.setTimeout(2))
|
||||||
|
Shell.EXECUTOR = DispatcherExecutor(Dispatchers.IO)
|
||||||
|
RootUtils.bindTask = RootService.bindOrTask(
|
||||||
|
intent<RootUtils>(),
|
||||||
|
UiThreadHandler.executor,
|
||||||
|
RootUtils.Connection
|
||||||
|
)
|
||||||
|
// Pre-heat the shell ASAP
|
||||||
|
Shell.getShell(null) {}
|
||||||
|
|
||||||
refreshLocale()
|
refreshLocale()
|
||||||
AppApkPath = if (isRunningAsStub) {
|
resources.patch()
|
||||||
StubApk.current(base).path
|
|
||||||
} else {
|
|
||||||
base.packageResourcePath
|
|
||||||
}
|
|
||||||
|
|
||||||
base.resources.patch()
|
|
||||||
app.registerActivityLifecycleCallbacks(ActivityTracker)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate() {
|
|
||||||
super.onCreate()
|
|
||||||
RootRegistry.bindTask = RootService.bindOrTask(
|
|
||||||
intent<RootRegistry>(),
|
|
||||||
UiThreadHandler.executor,
|
|
||||||
RootRegistry.Connection
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
@@ -86,20 +81,21 @@ open class App() : Application() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
|
||||||
object ActivityTracker : Application.ActivityLifecycleCallbacks {
|
object ActivityTracker : Application.ActivityLifecycleCallbacks {
|
||||||
|
|
||||||
|
val foreground: Activity? get() = ref.get()
|
||||||
|
|
||||||
@Volatile
|
@Volatile
|
||||||
var foreground: Activity? = null
|
private var ref = WeakReference<Activity>(null)
|
||||||
|
|
||||||
override fun onActivityResumed(activity: Activity) {
|
override fun onActivityResumed(activity: Activity) {
|
||||||
if (activity is SuRequestActivity) return
|
if (activity is SuRequestActivity) return
|
||||||
foreground = activity
|
ref = WeakReference(activity)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityPaused(activity: Activity) {
|
override fun onActivityPaused(activity: Activity) {
|
||||||
if (activity is SuRequestActivity) return
|
if (activity is SuRequestActivity) return
|
||||||
foreground = null
|
ref.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityCreated(activity: Activity, bundle: Bundle?) {}
|
override fun onActivityCreated(activity: Activity, bundle: Bundle?) {}
|
||||||
|
|||||||
@@ -6,21 +6,23 @@ import android.util.Xml
|
|||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import com.topjohnwu.magisk.BuildConfig
|
import com.topjohnwu.magisk.BuildConfig
|
||||||
|
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||||
|
import com.topjohnwu.magisk.core.repository.BoolDBPropertyNoWrite
|
||||||
|
import com.topjohnwu.magisk.core.repository.DBConfig
|
||||||
|
import com.topjohnwu.magisk.core.repository.PreferenceConfig
|
||||||
import com.topjohnwu.magisk.core.utils.refreshLocale
|
import com.topjohnwu.magisk.core.utils.refreshLocale
|
||||||
import com.topjohnwu.magisk.data.preference.PreferenceModel
|
|
||||||
import com.topjohnwu.magisk.data.repository.DBBoolSettingsNoWrite
|
|
||||||
import com.topjohnwu.magisk.data.repository.DBConfig
|
|
||||||
import com.topjohnwu.magisk.di.ServiceLocator
|
|
||||||
import com.topjohnwu.magisk.ui.theme.Theme
|
import com.topjohnwu.magisk.ui.theme.Theme
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
import org.xmlpull.v1.XmlPullParser
|
import org.xmlpull.v1.XmlPullParser
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
object Config : PreferenceModel, DBConfig {
|
object Config : PreferenceConfig, DBConfig {
|
||||||
|
|
||||||
override val stringDB get() = ServiceLocator.stringDB
|
override val stringDB get() = ServiceLocator.stringDB
|
||||||
override val settingsDB get() = ServiceLocator.settingsDB
|
override val settingsDB get() = ServiceLocator.settingsDB
|
||||||
override val context get() = ServiceLocator.deContext
|
override val context get() = ServiceLocator.deContext
|
||||||
|
override val coroutineScope get() = GlobalScope
|
||||||
|
|
||||||
@get:SuppressLint("ApplySharedPref")
|
@get:SuppressLint("ApplySharedPref")
|
||||||
val prefsFile: File get() {
|
val prefsFile: File get() {
|
||||||
@@ -70,6 +72,7 @@ object Config : PreferenceModel, DBConfig {
|
|||||||
const val BETA_CHANNEL = 1
|
const val BETA_CHANNEL = 1
|
||||||
const val CUSTOM_CHANNEL = 2
|
const val CUSTOM_CHANNEL = 2
|
||||||
const val CANARY_CHANNEL = 3
|
const val CANARY_CHANNEL = 3
|
||||||
|
const val DEBUG_CHANNEL = 4
|
||||||
|
|
||||||
// root access mode
|
// root access mode
|
||||||
const val ROOT_ACCESS_DISABLED = 0
|
const val ROOT_ACCESS_DISABLED = 0
|
||||||
@@ -106,6 +109,8 @@ object Config : PreferenceModel, DBConfig {
|
|||||||
|
|
||||||
private val defaultChannel =
|
private val defaultChannel =
|
||||||
if (BuildConfig.DEBUG)
|
if (BuildConfig.DEBUG)
|
||||||
|
Value.DEBUG_CHANNEL
|
||||||
|
else if (Const.APP_IS_CANARY)
|
||||||
Value.CANARY_CHANNEL
|
Value.CANARY_CHANNEL
|
||||||
else
|
else
|
||||||
Value.DEFAULT_CHANNEL
|
Value.DEFAULT_CHANNEL
|
||||||
@@ -149,7 +154,7 @@ object Config : PreferenceModel, DBConfig {
|
|||||||
var suMultiuserMode by dbSettings(Key.SU_MULTIUSER_MODE, Value.MULTIUSER_MODE_OWNER_ONLY)
|
var suMultiuserMode by dbSettings(Key.SU_MULTIUSER_MODE, Value.MULTIUSER_MODE_OWNER_ONLY)
|
||||||
var suBiometric by dbSettings(Key.SU_BIOMETRIC, false)
|
var suBiometric by dbSettings(Key.SU_BIOMETRIC, false)
|
||||||
var zygisk by dbSettings(Key.ZYGISK, false)
|
var zygisk by dbSettings(Key.ZYGISK, false)
|
||||||
var denyList by DBBoolSettingsNoWrite(Key.DENYLIST, false)
|
var denyList by BoolDBPropertyNoWrite(Key.DENYLIST, false)
|
||||||
var suManager by dbStrings(Key.SU_MANAGER, "", true)
|
var suManager by dbStrings(Key.SU_MANAGER, "", true)
|
||||||
var keyStoreRaw by dbStrings(Key.KEYSTORE, "", true)
|
var keyStoreRaw by dbStrings(Key.KEYSTORE, "", true)
|
||||||
|
|
||||||
@@ -158,7 +163,7 @@ object Config : PreferenceModel, DBConfig {
|
|||||||
fun load(pkg: String?) {
|
fun load(pkg: String?) {
|
||||||
// Only try to load prefs when fresh install and a previous package name is set
|
// Only try to load prefs when fresh install and a previous package name is set
|
||||||
if (pkg != null && prefs.all.isEmpty()) runCatching {
|
if (pkg != null && prefs.all.isEmpty()) runCatching {
|
||||||
context.contentResolver.openInputStream(Provider.PREFS_URI(pkg))?.use {
|
context.contentResolver.openInputStream(Provider.preferencesUri(pkg))?.use {
|
||||||
prefs.edit { parsePrefs(it) }
|
prefs.edit { parsePrefs(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -169,10 +174,11 @@ object Config : PreferenceModel, DBConfig {
|
|||||||
suBiometric = true
|
suBiometric = true
|
||||||
remove(SU_FINGERPRINT)
|
remove(SU_FINGERPRINT)
|
||||||
prefs.getString(Key.UPDATE_CHANNEL, null).also {
|
prefs.getString(Key.UPDATE_CHANNEL, null).also {
|
||||||
if (it == null)
|
if (it == null ||
|
||||||
|
it.toInt() > Value.DEBUG_CHANNEL ||
|
||||||
|
it.toInt() < Value.DEFAULT_CHANNEL) {
|
||||||
putString(Key.UPDATE_CHANNEL, defaultChannel.toString())
|
putString(Key.UPDATE_CHANNEL, defaultChannel.toString())
|
||||||
else if (it.toInt() > Value.CANARY_CHANNEL)
|
}
|
||||||
putString(Key.UPDATE_CHANNEL, Value.CANARY_CHANNEL.toString())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ object Const {
|
|||||||
val APP_IS_CANARY get() = Version.isCanary(BuildConfig.VERSION_CODE)
|
val APP_IS_CANARY get() = Version.isCanary(BuildConfig.VERSION_CODE)
|
||||||
|
|
||||||
object Version {
|
object Version {
|
||||||
const val MIN_VERSION = "v21.0"
|
const val MIN_VERSION = "v22.0"
|
||||||
const val MIN_VERCODE = 21000
|
const val MIN_VERCODE = 22000
|
||||||
|
|
||||||
fun atLeast_21_2() = Info.env.versionCode >= 21200 || isCanary()
|
|
||||||
fun atLeast_24_0() = Info.env.versionCode >= 24000 || isCanary()
|
fun atLeast_24_0() = Info.env.versionCode >= 24000 || isCanary()
|
||||||
|
fun atLeast_25_0() = Info.env.versionCode >= 25000 || isCanary()
|
||||||
fun isCanary() = isCanary(Info.env.versionCode)
|
fun isCanary() = isCanary(Info.env.versionCode)
|
||||||
|
|
||||||
fun isCanary(ver: Int) = ver > 0 && ver % 100 != 0
|
fun isCanary(ver: Int) = ver > 0 && ver % 100 != 0
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
package com.topjohnwu.magisk.core
|
package com.topjohnwu.magisk.core
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.ContextWrapper
|
import android.content.ContextWrapper
|
||||||
@@ -13,45 +12,49 @@ import android.content.res.Resources
|
|||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.StubApk
|
import com.topjohnwu.magisk.StubApk
|
||||||
|
import com.topjohnwu.magisk.core.di.AppContext
|
||||||
import com.topjohnwu.magisk.core.utils.syncLocale
|
import com.topjohnwu.magisk.core.utils.syncLocale
|
||||||
import com.topjohnwu.magisk.di.AppContext
|
import com.topjohnwu.magisk.ktx.unwrap
|
||||||
|
|
||||||
lateinit var AppApkPath: String
|
lateinit var AppApkPath: String
|
||||||
|
|
||||||
fun AssetManager.addAssetPath(path: String) = StubApk.addAssetPath(this, path)
|
fun Resources.addAssetPath(path: String) = StubApk.addAssetPath(this, path)
|
||||||
|
|
||||||
fun Context.wrap(): Context = if (this is PatchedContext) this else PatchedContext(this)
|
|
||||||
|
|
||||||
private class PatchedContext(base: Context) : ContextWrapper(base) {
|
|
||||||
init { base.resources.patch() }
|
|
||||||
override fun getClassLoader() = javaClass.classLoader!!
|
|
||||||
override fun createConfigurationContext(config: Configuration) =
|
|
||||||
super.createConfigurationContext(config).wrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Resources.patch(): Resources {
|
fun Resources.patch(): Resources {
|
||||||
syncLocale()
|
|
||||||
if (isRunningAsStub)
|
if (isRunningAsStub)
|
||||||
assets.addAssetPath(AppApkPath)
|
addAssetPath(AppApkPath)
|
||||||
|
syncLocale()
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Context.patch(): Context {
|
||||||
|
unwrap().resources.patch()
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrapping is only necessary for ContextThemeWrapper to support configuration overrides
|
||||||
|
fun Context.wrap(): Context {
|
||||||
|
patch()
|
||||||
|
return object : ContextWrapper(this) {
|
||||||
|
override fun createConfigurationContext(config: Configuration): Context {
|
||||||
|
return super.createConfigurationContext(config).wrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun createNewResources(): Resources {
|
fun createNewResources(): Resources {
|
||||||
val asset = AssetManager::class.java.newInstance()
|
val asset = AssetManager::class.java.newInstance()
|
||||||
asset.addAssetPath(AppApkPath)
|
|
||||||
val config = Configuration(AppContext.resources.configuration)
|
val config = Configuration(AppContext.resources.configuration)
|
||||||
val metrics = DisplayMetrics()
|
val metrics = DisplayMetrics()
|
||||||
metrics.setTo(AppContext.resources.displayMetrics)
|
metrics.setTo(AppContext.resources.displayMetrics)
|
||||||
return Resources(asset, metrics, config)
|
val res = Resources(asset, metrics, config)
|
||||||
|
res.addAssetPath(AppApkPath)
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Class<*>.cmp(pkg: String) =
|
fun Class<*>.cmp(pkg: String) =
|
||||||
ComponentName(pkg, Info.stub?.classToComponent?.get(name) ?: name)
|
ComponentName(pkg, Info.stub?.classToComponent?.get(name) ?: name)
|
||||||
|
|
||||||
inline fun <reified T> Activity.redirect() = Intent(intent)
|
|
||||||
.setComponent(T::class.java.cmp(packageName))
|
|
||||||
.setFlags(0)
|
|
||||||
|
|
||||||
inline fun <reified T> Context.intent() = Intent().setComponent(T::class.java.cmp(packageName))
|
inline fun <reified T> Context.intent() = Intent().setComponent(T::class.java.cmp(packageName))
|
||||||
|
|
||||||
// Keep a reference to these resources to prevent it from
|
// Keep a reference to these resources to prevent it from
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
package com.topjohnwu.magisk.core
|
package com.topjohnwu.magisk.core
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.databinding.ObservableBoolean
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
import com.topjohnwu.magisk.StubApk
|
import com.topjohnwu.magisk.StubApk
|
||||||
|
import com.topjohnwu.magisk.core.di.AppContext
|
||||||
import com.topjohnwu.magisk.core.model.UpdateInfo
|
import com.topjohnwu.magisk.core.model.UpdateInfo
|
||||||
|
import com.topjohnwu.magisk.core.repository.NetworkService
|
||||||
import com.topjohnwu.magisk.core.utils.net.NetworkObserver
|
import com.topjohnwu.magisk.core.utils.net.NetworkObserver
|
||||||
import com.topjohnwu.magisk.data.repository.NetworkService
|
|
||||||
import com.topjohnwu.magisk.di.AppContext
|
|
||||||
import com.topjohnwu.magisk.ktx.getProperty
|
import com.topjohnwu.magisk.ktx.getProperty
|
||||||
import com.topjohnwu.superuser.Shell
|
|
||||||
import com.topjohnwu.superuser.ShellUtils.fastCmd
|
import com.topjohnwu.superuser.ShellUtils.fastCmd
|
||||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||||
|
|
||||||
@@ -36,6 +36,7 @@ object Info {
|
|||||||
@JvmField var vbmeta = false
|
@JvmField var vbmeta = false
|
||||||
var crypto = ""
|
var crypto = ""
|
||||||
var noDataExec = false
|
var noDataExec = false
|
||||||
|
var isRooted = false
|
||||||
|
|
||||||
@JvmField var hasGMS = true
|
@JvmField var hasGMS = true
|
||||||
val isSamsung = Build.MANUFACTURER.equals("samsung", ignoreCase = true)
|
val isSamsung = Build.MANUFACTURER.equals("samsung", ignoreCase = true)
|
||||||
@@ -43,26 +44,30 @@ object Info {
|
|||||||
getProperty("ro.kernel.qemu", "0") == "1" ||
|
getProperty("ro.kernel.qemu", "0") == "1" ||
|
||||||
getProperty("ro.boot.qemu", "0") == "1"
|
getProperty("ro.boot.qemu", "0") == "1"
|
||||||
|
|
||||||
val isConnected by lazy {
|
val isConnected: LiveData<Boolean> by lazy {
|
||||||
ObservableBoolean(false).also { field ->
|
MutableLiveData(false).also { field ->
|
||||||
NetworkObserver.observe(AppContext) {
|
NetworkObserver.observe(AppContext) {
|
||||||
UiThreadHandler.run { field.set(it) }
|
UiThreadHandler.run { field.value = it }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadState() = Env(
|
private fun loadState(): Env {
|
||||||
fastCmd("magisk -v").split(":".toRegex())[0],
|
val v = fastCmd("magisk -v").split(":".toRegex())
|
||||||
runCatching { fastCmd("magisk -V").toInt() }.getOrDefault(-1)
|
return Env(
|
||||||
)
|
v[0], v.size >= 3 && v[2] == "D",
|
||||||
|
runCatching { fastCmd("magisk -V").toInt() }.getOrDefault(-1)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
class Env(
|
class Env(
|
||||||
val versionString: String = "",
|
val versionString: String = "",
|
||||||
|
val isDebug: Boolean = false,
|
||||||
code: Int = -1
|
code: Int = -1
|
||||||
) {
|
) {
|
||||||
val versionCode = when {
|
val versionCode = when {
|
||||||
code < Const.Version.MIN_VERCODE -> -1
|
code < Const.Version.MIN_VERCODE -> -1
|
||||||
else -> if (Shell.rootAccess()) code else -1
|
else -> if (isRooted) code else -1
|
||||||
}
|
}
|
||||||
val isUnsupported = code > 0 && code < Const.Version.MIN_VERCODE
|
val isUnsupported = code > 0 && code < Const.Version.MIN_VERCODE
|
||||||
val isActive = versionCode >= 0
|
val isActive = versionCode >= 0
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import android.content.Context
|
|||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import com.topjohnwu.magisk.BuildConfig
|
import com.topjohnwu.magisk.BuildConfig
|
||||||
import com.topjohnwu.magisk.core.base.BaseJobService
|
import com.topjohnwu.magisk.core.base.BaseJobService
|
||||||
import com.topjohnwu.magisk.di.ServiceLocator
|
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||||
import com.topjohnwu.magisk.view.Notifications
|
import com.topjohnwu.magisk.view.Notifications
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|||||||
@@ -1,22 +1,13 @@
|
|||||||
package com.topjohnwu.magisk.core
|
package com.topjohnwu.magisk.core
|
||||||
|
|
||||||
import android.content.ContentProvider
|
|
||||||
import android.content.ContentValues
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.pm.ProviderInfo
|
|
||||||
import android.database.Cursor
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
import android.os.ParcelFileDescriptor.MODE_READ_ONLY
|
import android.os.ParcelFileDescriptor.MODE_READ_ONLY
|
||||||
|
import com.topjohnwu.magisk.core.base.BaseProvider
|
||||||
import com.topjohnwu.magisk.core.su.SuCallbackHandler
|
import com.topjohnwu.magisk.core.su.SuCallbackHandler
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class Provider : ContentProvider() {
|
class Provider : BaseProvider() {
|
||||||
|
|
||||||
override fun attachInfo(context: Context, info: ProviderInfo) {
|
|
||||||
super.attachInfo(context.wrap(), info)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun call(method: String, arg: String?, extras: Bundle?): Bundle? {
|
override fun call(method: String, arg: String?, extras: Bundle?): Bundle? {
|
||||||
SuCallbackHandler.run(context!!, method, extras)
|
SuCallbackHandler.run(context!!, method, extras)
|
||||||
@@ -25,24 +16,13 @@ class Provider : ContentProvider() {
|
|||||||
|
|
||||||
override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? {
|
override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? {
|
||||||
return when (uri.encodedPath ?: return null) {
|
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)
|
"/prefs_file" -> ParcelFileDescriptor.open(Config.prefsFile, MODE_READ_ONLY)
|
||||||
else -> super.openFile(uri, mode)
|
else -> super.openFile(uri, mode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun APK_URI(pkg: String) =
|
fun preferencesUri(pkg: String): Uri =
|
||||||
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()
|
Uri.Builder().scheme("content").authority("$pkg.provider").path("prefs_file").build()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate() = true
|
|
||||||
override fun getType(uri: Uri): String? = null
|
|
||||||
override fun insert(uri: Uri, values: ContentValues?): Uri? = null
|
|
||||||
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?) = 0
|
|
||||||
override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?) = 0
|
|
||||||
override fun query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?): Cursor? = null
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package com.topjohnwu.magisk.core
|
package com.topjohnwu.magisk.core
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.ContextWrapper
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import com.topjohnwu.magisk.core.base.BaseReceiver
|
import com.topjohnwu.magisk.core.base.BaseReceiver
|
||||||
import com.topjohnwu.magisk.di.ServiceLocator
|
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||||
import com.topjohnwu.magisk.view.Notifications
|
import com.topjohnwu.magisk.view.Notifications
|
||||||
import com.topjohnwu.magisk.view.Shortcuts
|
import com.topjohnwu.magisk.view.Shortcuts
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
@@ -26,8 +26,9 @@ open class Receiver : BaseReceiver() {
|
|||||||
return if (uid == -1) null else uid
|
return if (uid == -1) null else uid
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onReceive(context: ContextWrapper, intent: Intent?) {
|
override fun onReceive(context: Context, intent: Intent?) {
|
||||||
intent ?: return
|
intent ?: return
|
||||||
|
super.onReceive(context, intent)
|
||||||
|
|
||||||
fun rmPolicy(uid: Int) = GlobalScope.launch {
|
fun rmPolicy(uid: Int) = GlobalScope.launch {
|
||||||
policyDB.delete(uid)
|
policyDB.delete(uid)
|
||||||
|
|||||||
@@ -2,24 +2,31 @@ package com.topjohnwu.magisk.core.base
|
|||||||
|
|
||||||
import android.Manifest.permission.REQUEST_INSTALL_PACKAGES
|
import android.Manifest.permission.REQUEST_INSTALL_PACKAGES
|
||||||
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.Configuration
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.os.Parcelable
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.activity.result.ActivityResultCallback
|
||||||
import androidx.activity.result.contract.ActivityResultContracts.GetContent
|
import androidx.activity.result.contract.ActivityResultContracts.GetContent
|
||||||
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
|
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
|
||||||
import androidx.annotation.WorkerThread
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.core.isRunningAsStub
|
import com.topjohnwu.magisk.core.isRunningAsStub
|
||||||
import com.topjohnwu.magisk.core.utils.RequestInstall
|
import com.topjohnwu.magisk.core.utils.RequestInstall
|
||||||
import com.topjohnwu.magisk.core.utils.UninstallPackage
|
|
||||||
import com.topjohnwu.magisk.core.utils.currentLocale
|
|
||||||
import com.topjohnwu.magisk.core.wrap
|
import com.topjohnwu.magisk.core.wrap
|
||||||
import com.topjohnwu.magisk.ktx.reflectField
|
import com.topjohnwu.magisk.ktx.reflectField
|
||||||
import java.util.concurrent.CountDownLatch
|
import com.topjohnwu.magisk.utils.Utils
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
interface ContentResultCallback: ActivityResultCallback<Uri>, Parcelable {
|
||||||
|
fun onActivityLaunch() {}
|
||||||
|
// Make the result type explicitly non-null
|
||||||
|
override fun onActivityResult(result: Uri)
|
||||||
|
}
|
||||||
|
|
||||||
abstract class BaseActivity : AppCompatActivity() {
|
abstract class BaseActivity : AppCompatActivity() {
|
||||||
|
|
||||||
@@ -33,21 +40,22 @@ abstract class BaseActivity : AppCompatActivity() {
|
|||||||
permissionCallback = null
|
permissionCallback = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private var contentCallback: ((Uri) -> Unit)? = null
|
private var contentCallback: ContentResultCallback? = null
|
||||||
private val getContent = registerForActivityResult(GetContent()) {
|
private val getContent = registerForActivityResult(GetContent()) {
|
||||||
if (it != null) contentCallback?.invoke(it)
|
if (it != null) contentCallback?.onActivityResult(it)
|
||||||
contentCallback = null
|
contentCallback = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private var uninstallLatch = CountDownLatch(1)
|
private val mReferrerField by lazy(LazyThreadSafetyMode.NONE) {
|
||||||
private val uninstallPkg = registerForActivityResult(UninstallPackage()) {
|
Activity::class.java.reflectField("mReferrer")
|
||||||
uninstallLatch.countDown()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun applyOverrideConfiguration(config: Configuration?) {
|
val realCallingPackage: String? get() {
|
||||||
// Force applying our preferred local
|
callingPackage?.let { return it }
|
||||||
config?.setLocale(currentLocale)
|
if (Build.VERSION.SDK_INT >= 22) {
|
||||||
super.applyOverrideConfiguration(config)
|
mReferrerField.get(this)?.let { return it as String }
|
||||||
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun attachBaseContext(base: Context) {
|
override fun attachBaseContext(base: Context) {
|
||||||
@@ -62,9 +70,17 @@ abstract class BaseActivity : AppCompatActivity() {
|
|||||||
clz.reflectField("mActivityHandlesUiModeChecked").set(delegate, true)
|
clz.reflectField("mActivityHandlesUiModeChecked").set(delegate, true)
|
||||||
clz.reflectField("mActivityHandlesUiMode").set(delegate, false)
|
clz.reflectField("mActivityHandlesUiMode").set(delegate, false)
|
||||||
}
|
}
|
||||||
|
contentCallback = savedInstanceState?.getParcelable(CONTENT_CALLBACK_KEY)
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
contentCallback?.let {
|
||||||
|
outState.putParcelable(CONTENT_CALLBACK_KEY, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun withPermission(permission: String, callback: (Boolean) -> Unit) {
|
fun withPermission(permission: String, callback: (Boolean) -> Unit) {
|
||||||
if (permission == WRITE_EXTERNAL_STORAGE && Build.VERSION.SDK_INT >= 30) {
|
if (permission == WRITE_EXTERNAL_STORAGE && Build.VERSION.SDK_INT >= 30) {
|
||||||
// We do not need external rw on 30+
|
// We do not need external rw on 30+
|
||||||
@@ -79,16 +95,14 @@ abstract class BaseActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getContent(type: String, callback: (Uri) -> Unit) {
|
fun getContent(type: String, callback: ContentResultCallback) {
|
||||||
contentCallback = callback
|
contentCallback = callback
|
||||||
getContent.launch(type)
|
try {
|
||||||
}
|
getContent.launch(type)
|
||||||
|
callback.onActivityLaunch()
|
||||||
@WorkerThread
|
} catch (e: ActivityNotFoundException) {
|
||||||
fun uninstallAndWait(pkg: String) {
|
Utils.toast(R.string.app_not_found, Toast.LENGTH_SHORT)
|
||||||
uninstallLatch = CountDownLatch(1)
|
}
|
||||||
uninstallPkg.launch(pkg)
|
|
||||||
uninstallLatch.await(3, TimeUnit.SECONDS)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun recreate() {
|
override fun recreate() {
|
||||||
@@ -100,4 +114,8 @@ abstract class BaseActivity : AppCompatActivity() {
|
|||||||
startActivity(Intent(intent).setFlags(0))
|
startActivity(Intent(intent).setFlags(0))
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val CONTENT_CALLBACK_KEY = "content_callback"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ package com.topjohnwu.magisk.core.base
|
|||||||
|
|
||||||
import android.app.job.JobService
|
import android.app.job.JobService
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.topjohnwu.magisk.core.wrap
|
import com.topjohnwu.magisk.core.patch
|
||||||
|
|
||||||
abstract class BaseJobService : JobService() {
|
abstract class BaseJobService : JobService() {
|
||||||
override fun attachBaseContext(base: Context) {
|
override fun attachBaseContext(base: Context) {
|
||||||
super.attachBaseContext(base.wrap())
|
super.attachBaseContext(base.patch())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.topjohnwu.magisk.core.base
|
||||||
|
|
||||||
|
import android.content.ContentProvider
|
||||||
|
import android.content.ContentValues
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.ProviderInfo
|
||||||
|
import android.database.Cursor
|
||||||
|
import android.net.Uri
|
||||||
|
import com.topjohnwu.magisk.core.patch
|
||||||
|
|
||||||
|
open class BaseProvider : ContentProvider() {
|
||||||
|
override fun attachInfo(context: Context, info: ProviderInfo) {
|
||||||
|
super.attachInfo(context.patch(), info)
|
||||||
|
}
|
||||||
|
override fun onCreate() = true
|
||||||
|
override fun getType(uri: Uri): String? = null
|
||||||
|
override fun insert(uri: Uri, values: ContentValues?): Uri? = null
|
||||||
|
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?) = 0
|
||||||
|
override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?) = 0
|
||||||
|
override fun query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?): Cursor? = null
|
||||||
|
}
|
||||||
@@ -2,15 +2,13 @@ package com.topjohnwu.magisk.core.base
|
|||||||
|
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.ContextWrapper
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import com.topjohnwu.magisk.core.wrap
|
import androidx.annotation.CallSuper
|
||||||
|
import com.topjohnwu.magisk.core.patch
|
||||||
|
|
||||||
abstract class BaseReceiver : BroadcastReceiver() {
|
abstract class BaseReceiver : BroadcastReceiver() {
|
||||||
|
@CallSuper
|
||||||
final override fun onReceive(context: Context, intent: Intent?) {
|
override fun onReceive(context: Context, intent: Intent?) {
|
||||||
onReceive(context.wrap() as ContextWrapper, intent)
|
context.patch()
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun onReceive(context: ContextWrapper, intent: Intent?)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,13 @@ package com.topjohnwu.magisk.core.base
|
|||||||
|
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.topjohnwu.magisk.core.wrap
|
import android.content.Intent
|
||||||
|
import android.os.IBinder
|
||||||
|
import com.topjohnwu.magisk.core.patch
|
||||||
|
|
||||||
abstract class BaseService : Service() {
|
open class BaseService : Service() {
|
||||||
override fun attachBaseContext(base: Context) {
|
override fun attachBaseContext(base: Context) {
|
||||||
super.attachBaseContext(base.wrap())
|
super.attachBaseContext(base.patch())
|
||||||
}
|
}
|
||||||
|
override fun onBind(intent: Intent?): IBinder? = null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.topjohnwu.magisk.data.network
|
package com.topjohnwu.magisk.core.data
|
||||||
|
|
||||||
import com.topjohnwu.magisk.core.model.BranchInfo
|
import com.topjohnwu.magisk.core.model.BranchInfo
|
||||||
import com.topjohnwu.magisk.core.model.ModuleJson
|
import com.topjohnwu.magisk.core.model.ModuleJson
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.topjohnwu.magisk.data.database
|
package com.topjohnwu.magisk.core.data
|
||||||
|
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import com.topjohnwu.magisk.core.model.su.SuLog
|
import com.topjohnwu.magisk.core.model.su.SuLog
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.topjohnwu.magisk.core.data.magiskdb
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.ktx.await
|
||||||
|
import com.topjohnwu.superuser.Shell
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
open class MagiskDB {
|
||||||
|
|
||||||
|
suspend fun <R> exec(
|
||||||
|
query: String,
|
||||||
|
mapper: suspend (Map<String, String>) -> R
|
||||||
|
): List<R> {
|
||||||
|
return withContext(Dispatchers.IO) {
|
||||||
|
val out = Shell.cmd("magisk --sqlite '$query'").await().out
|
||||||
|
out.map { line ->
|
||||||
|
line.split("\\|".toRegex())
|
||||||
|
.map { it.split("=", limit = 2) }
|
||||||
|
.filter { it.size == 2 }
|
||||||
|
.associate { it[0] to it[1] }
|
||||||
|
.let { mapper(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend inline fun exec(query: String) {
|
||||||
|
exec(query) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Map<String, Any>.toQuery(): String {
|
||||||
|
val keys = this.keys.joinToString(",")
|
||||||
|
val values = this.values.joinToString(",") {
|
||||||
|
when (it) {
|
||||||
|
is Boolean -> if (it) "1" else "0"
|
||||||
|
is Number -> it.toString()
|
||||||
|
else -> "\"$it\""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "($keys) VALUES($values)"
|
||||||
|
}
|
||||||
|
|
||||||
|
object Table {
|
||||||
|
const val POLICY = "policies"
|
||||||
|
const val SETTINGS = "settings"
|
||||||
|
const val STRINGS = "strings"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package com.topjohnwu.magisk.core.data.magiskdb
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.core.Const
|
||||||
|
import com.topjohnwu.magisk.core.di.AppContext
|
||||||
|
import com.topjohnwu.magisk.core.model.su.SuPolicy
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
class PolicyDao : MagiskDB() {
|
||||||
|
|
||||||
|
suspend fun deleteOutdated() {
|
||||||
|
val nowSeconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())
|
||||||
|
val query = "DELETE FROM ${Table.POLICY} WHERE " +
|
||||||
|
"(until > 0 AND until < $nowSeconds) OR until < 0"
|
||||||
|
exec(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun delete(uid: Int) {
|
||||||
|
val query = "DELETE FROM ${Table.POLICY} WHERE uid == $uid"
|
||||||
|
exec(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun fetch(uid: Int): SuPolicy? {
|
||||||
|
val query = "SELECT * FROM ${Table.POLICY} WHERE uid == $uid LIMIT = 1"
|
||||||
|
return exec(query, ::toPolicy).firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun update(policy: SuPolicy) {
|
||||||
|
val map = policy.toMap()
|
||||||
|
if (!Const.Version.atLeast_25_0()) {
|
||||||
|
// Put in package_name for old database
|
||||||
|
map["package_name"] = AppContext.packageManager.getNameForUid(policy.uid)!!
|
||||||
|
}
|
||||||
|
val query = "REPLACE INTO ${Table.POLICY} ${map.toQuery()}"
|
||||||
|
exec(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun fetchAll(): List<SuPolicy> {
|
||||||
|
val query = "SELECT * FROM ${Table.POLICY} WHERE uid/100000 == ${Const.USER_ID}"
|
||||||
|
return exec(query, ::toPolicy).filterNotNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toPolicy(map: Map<String, String>): SuPolicy? {
|
||||||
|
val uid = map["uid"]?.toInt() ?: return null
|
||||||
|
val policy = SuPolicy(uid)
|
||||||
|
|
||||||
|
map["policy"]?.toInt()?.let { policy.policy = it }
|
||||||
|
map["until"]?.toLong()?.let { policy.until = it }
|
||||||
|
map["logging"]?.toInt()?.let { policy.logging = it != 0 }
|
||||||
|
map["notification"]?.toInt()?.let { policy.notification = it != 0 }
|
||||||
|
return policy
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user