mirror of
				https://github.com/topjohnwu/Magisk
				synced 2025-11-03 15:52:30 +01:00 
			
		
		
		
	Compare commits
	
		
			199 Commits
		
	
	
		
			manager-v8
			...
			v21.2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					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 | 
							
								
								
									
										87
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
			
		||||
name: Magisk Build
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: [ master ]
 | 
			
		||||
  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: |
 | 
			
		||||
          $oldAndroidPath = $env:ANDROID_SDK_ROOT
 | 
			
		||||
          $sdk_root = "C:\Android"
 | 
			
		||||
          New-Item -Path $sdk_root -ItemType SymbolicLink -Value $oldAndroidPath
 | 
			
		||||
          $ndk_ver = Select-String -Path "gradle.properties" -Pattern "^magisk.fullNdkVersion=" | % { $_ -replace ".*=" }
 | 
			
		||||
          echo "ANDROID_SDK_ROOT=$sdk_root" >> $env:GITHUB_ENV
 | 
			
		||||
          echo "ANDROID_HOME=$sdk_root" >> $env:GITHUB_ENV
 | 
			
		||||
          echo "MAGISK_NDK_VERSION=$ndk_ver" >> $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
 | 
			
		||||
            !~/.gradle/caches/**/*.lock
 | 
			
		||||
          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
 | 
			
		||||
							
								
								
									
										12
									
								
								README.MD
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								README.MD
									
									
									
									
									
								
							@@ -15,17 +15,17 @@ Here are some feature highlights:
 | 
			
		||||
 | 
			
		||||
## Downloads
 | 
			
		||||
 | 
			
		||||
[](https://github.com/topjohnwu/Magisk/releases/download/manager-v7.5.1/MagiskManager-v7.5.1.apk)
 | 
			
		||||
[](https://github.com/topjohnwu/Magisk/releases/download/manager-v8.0.3/MagiskManager-v8.0.3.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)
 | 
			
		||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v21.1)
 | 
			
		||||
 | 
			
		||||
## 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
 | 
			
		||||
@@ -56,13 +56,13 @@ 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).
 | 
			
		||||
- 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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,9 @@
 | 
			
		||||
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 +22,11 @@ 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.appVersion
 | 
			
		||||
        versionCode = Config.appVersionCode
 | 
			
		||||
 | 
			
		||||
        javaCompileOptions.annotationProcessorOptions.arguments(
 | 
			
		||||
                mapOf("room.incremental" to "true")
 | 
			
		||||
            mapOf("room.incremental" to "true")
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -51,11 +52,12 @@ android {
 | 
			
		||||
 | 
			
		||||
    packagingOptions {
 | 
			
		||||
        exclude("/META-INF/**")
 | 
			
		||||
        exclude("/androidsupportmultidexversion.txt")
 | 
			
		||||
        exclude("/org/bouncycastle/**")
 | 
			
		||||
        exclude("/kotlin/**")
 | 
			
		||||
        exclude("/kotlinx/**")
 | 
			
		||||
        exclude("/okhttp3/**")
 | 
			
		||||
        exclude("/*.txt")
 | 
			
		||||
        exclude("/*.bin")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    kotlinOptions {
 | 
			
		||||
@@ -63,27 +65,61 @@ android {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
androidExtensions {
 | 
			
		||||
    isExperimental = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
val copyUtils = tasks.register("copyUtils", Copy::class) {
 | 
			
		||||
tasks["preBuild"]?.dependsOn(tasks.register("copyUtils", Copy::class) {
 | 
			
		||||
    from(rootProject.file("scripts/util_functions.sh"))
 | 
			
		||||
    into("src/main/res/raw")
 | 
			
		||||
}
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
tasks["preBuild"]?.dependsOn(copyUtils)
 | 
			
		||||
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}")
 | 
			
		||||
@@ -118,11 +154,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-alpha04"
 | 
			
		||||
    implementation("androidx.room:room-runtime:${vRoom}")
 | 
			
		||||
    implementation("androidx.room:room-ktx:${vRoom}")
 | 
			
		||||
    kapt("androidx.room:room-compiler:${vRoom}")
 | 
			
		||||
@@ -132,16 +168,16 @@ dependencies {
 | 
			
		||||
    implementation("androidx.navigation:navigation-ui-ktx:${vNav}")
 | 
			
		||||
 | 
			
		||||
    implementation("androidx.biometric:biometric:1.0.1")
 | 
			
		||||
    implementation("androidx.constraintlayout:constraintlayout:2.0.1")
 | 
			
		||||
    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.multidex:multidex:2.0.1")
 | 
			
		||||
    implementation("androidx.core:core-ktx:1.3.1")
 | 
			
		||||
    implementation("androidx.core:core-ktx:1.3.2")
 | 
			
		||||
    implementation("androidx.localbroadcastmanager:localbroadcastmanager:1.0.0")
 | 
			
		||||
    implementation("com.google.android.material:material:1.2.1")
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							@@ -35,12 +35,6 @@
 | 
			
		||||
  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
 | 
			
		||||
 | 
			
		||||
# Strip Timber verbose and debug logging
 | 
			
		||||
-assumenosideeffects class timber.log.Timber.Tree {
 | 
			
		||||
  public void v(**);
 | 
			
		||||
 
 | 
			
		||||
@@ -12,12 +12,6 @@
 | 
			
		||||
        android:supportsRtl="true"
 | 
			
		||||
        android:theme="@android:style/Theme.Translucent.NoTitleBar"
 | 
			
		||||
        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
									
								
								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);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -4,16 +4,15 @@
 | 
			
		||||
    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" />
 | 
			
		||||
        android:maxSdkVersion="29" />
 | 
			
		||||
 | 
			
		||||
    <application
 | 
			
		||||
        android:icon="@drawable/ic_launcher"
 | 
			
		||||
        android:name="a.e"
 | 
			
		||||
        android:allowBackup="true"
 | 
			
		||||
        android:allowBackup="false"
 | 
			
		||||
        tools:ignore="UnusedAttribute,GoogleAppIndexingWarning">
 | 
			
		||||
 | 
			
		||||
        <!-- Splash -->
 | 
			
		||||
@@ -65,6 +64,15 @@
 | 
			
		||||
        <!-- DownloadService -->
 | 
			
		||||
        <service android:name="a.j" />
 | 
			
		||||
 | 
			
		||||
        <!-- FileProvider -->
 | 
			
		||||
        <provider
 | 
			
		||||
            android:name="a.p"
 | 
			
		||||
            android:authorities="${applicationId}.provider"
 | 
			
		||||
            android:directBootAware="true"
 | 
			
		||||
            android:exported="false"
 | 
			
		||||
            android:grantUriPermissions="true">
 | 
			
		||||
        </provider>
 | 
			
		||||
 | 
			
		||||
        <!-- Hardcode GMS version -->
 | 
			
		||||
        <meta-data
 | 
			
		||||
            android:name="com.google.android.gms.version"
 | 
			
		||||
 
 | 
			
		||||
@@ -2,15 +2,16 @@
 | 
			
		||||
package a
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.core.App
 | 
			
		||||
import com.topjohnwu.magisk.core.GeneralReceiver
 | 
			
		||||
import com.topjohnwu.magisk.core.Provider
 | 
			
		||||
import com.topjohnwu.magisk.core.Receiver
 | 
			
		||||
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
 | 
			
		||||
import com.topjohnwu.signing.SignBoot
 | 
			
		||||
 | 
			
		||||
fun main(args: Array<String>) {
 | 
			
		||||
    BootSigner.main(args)
 | 
			
		||||
    SignBoot.main(args)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class b : MainActivity()
 | 
			
		||||
@@ -22,8 +23,10 @@ class e : App {
 | 
			
		||||
    constructor(o: Any) : super(o)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class h : GeneralReceiver()
 | 
			
		||||
class h : Receiver()
 | 
			
		||||
 | 
			
		||||
class j : DownloadService()
 | 
			
		||||
 | 
			
		||||
class m : SuRequestActivity()
 | 
			
		||||
 | 
			
		||||
class p : Provider()
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -15,6 +17,7 @@ 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> :
 | 
			
		||||
@@ -41,12 +44,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)
 | 
			
		||||
 | 
			
		||||
@@ -65,6 +65,31 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
 | 
			
		||||
                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
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun setContentView() {
 | 
			
		||||
@@ -72,8 +97,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 +112,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
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,15 +17,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()
 | 
			
		||||
@@ -65,7 +63,6 @@ abstract class BaseUIFragment<VM : BaseViewModel, Binding : ViewDataBinding> :
 | 
			
		||||
                return true
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        ensureInsets()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onResume() {
 | 
			
		||||
 
 | 
			
		||||
@@ -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,8 +10,6 @@ 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.IODispatcherExecutor
 | 
			
		||||
import com.topjohnwu.magisk.core.utils.RootInit
 | 
			
		||||
import com.topjohnwu.magisk.core.utils.updateConfig
 | 
			
		||||
@@ -36,7 +34,6 @@ open class App() : Application() {
 | 
			
		||||
            .setInitializers(RootInit::class.java)
 | 
			
		||||
            .setTimeout(2))
 | 
			
		||||
        Shell.EXECUTOR = IODispatcherExecutor()
 | 
			
		||||
        FileProvider.callHandler = SuCallbackHandler
 | 
			
		||||
 | 
			
		||||
        // Always log full stack trace with Timber
 | 
			
		||||
        Timber.plant(Timber.DebugTree())
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,13 +12,10 @@ object Const {
 | 
			
		||||
 | 
			
		||||
    // Versions
 | 
			
		||||
    const val SNET_EXT_VER = 15
 | 
			
		||||
    const val SNET_REVISION = "d494bc726e86166913a13629e3b1336728ec5d7f"
 | 
			
		||||
    const val BOOTCTL_REVISION = "a6c47f86f10b310358afa9dbe837037dd5d561df"
 | 
			
		||||
    const val SNET_REVISION = "18ab78817087c337ae0edd1ecac38aec49217880"
 | 
			
		||||
    const val BOOTCTL_REVISION = "18ab78817087c337ae0edd1ecac38aec49217880"
 | 
			
		||||
 | 
			
		||||
    // 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 {
 | 
			
		||||
@@ -33,12 +30,12 @@ object Const {
 | 
			
		||||
 | 
			
		||||
    object ID {
 | 
			
		||||
        const val FETCH_ZIP = 2
 | 
			
		||||
        const val SELECT_BOOT = 3
 | 
			
		||||
        const val SELECT_FILE = 3
 | 
			
		||||
        const val MAX_ACTIVITY_RESULT = 10
 | 
			
		||||
 | 
			
		||||
        // 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"
 | 
			
		||||
@@ -46,22 +43,20 @@ object Const {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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"
 | 
			
		||||
 | 
			
		||||
        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_API_URL = "https://api.github.com/"
 | 
			
		||||
        const val GITHUB_PAGE_URL = "https://topjohnwu.github.io/magisk_files/"
 | 
			
		||||
        const val JS_DELIVR_URL = "https://cdn.jsdelivr.net/gh/"
 | 
			
		||||
        const val 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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -149,7 +149,7 @@ private object ClassMap {
 | 
			
		||||
        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,
 | 
			
		||||
        Receiver::class.java to a.h::class.java,
 | 
			
		||||
        DownloadService::class.java to a.j::class.java,
 | 
			
		||||
        SuRequestActivity::class.java to a.m::class.java
 | 
			
		||||
    )
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -11,7 +11,7 @@ import kotlinx.coroutines.GlobalScope
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import org.koin.core.inject
 | 
			
		||||
 | 
			
		||||
open class GeneralReceiver : BaseReceiver() {
 | 
			
		||||
open class Receiver : BaseReceiver() {
 | 
			
		||||
 | 
			
		||||
    private val policyDB: PolicyDao by inject()
 | 
			
		||||
 | 
			
		||||
@@ -3,9 +3,9 @@ package com.topjohnwu.magisk.core
 | 
			
		||||
import android.app.Activity
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import com.topjohnwu.magisk.BuildConfig
 | 
			
		||||
import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID
 | 
			
		||||
import com.topjohnwu.magisk.R
 | 
			
		||||
import com.topjohnwu.magisk.data.network.GithubRawServices
 | 
			
		||||
import com.topjohnwu.magisk.data.repository.NetworkService
 | 
			
		||||
import com.topjohnwu.magisk.ktx.get
 | 
			
		||||
import com.topjohnwu.magisk.ui.MainActivity
 | 
			
		||||
import com.topjohnwu.magisk.view.Notifications
 | 
			
		||||
@@ -29,18 +29,18 @@ open class SplashActivity : Activity() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun handleRepackage() {
 | 
			
		||||
        val pkg = Config.suManager
 | 
			
		||||
        if (Config.suManager.isNotEmpty() && packageName == BuildConfig.APPLICATION_ID) {
 | 
			
		||||
            Config.suManager = ""
 | 
			
		||||
            Shell.su("(pm uninstall $pkg)& >/dev/null 2>&1").exec()
 | 
			
		||||
        }
 | 
			
		||||
        if (pkg == packageName) {
 | 
			
		||||
    private fun handleRepackage(pkg: String?) {
 | 
			
		||||
        if (packageName != APPLICATION_ID) {
 | 
			
		||||
            runCatching {
 | 
			
		||||
                // We are the manager, remove com.topjohnwu.magisk as it could be malware
 | 
			
		||||
                packageManager.getApplicationInfo(BuildConfig.APPLICATION_ID, 0)
 | 
			
		||||
                Shell.su("(pm uninstall ${BuildConfig.APPLICATION_ID})& >/dev/null 2>&1").exec()
 | 
			
		||||
                // Hidden, remove com.topjohnwu.magisk if exist as it could be malware
 | 
			
		||||
                packageManager.getApplicationInfo(APPLICATION_ID, 0)
 | 
			
		||||
                Shell.su("(pm uninstall $APPLICATION_ID)& >/dev/null 2>&1").exec()
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (Config.suManager.isNotEmpty())
 | 
			
		||||
                Config.suManager = ""
 | 
			
		||||
            pkg ?: return
 | 
			
		||||
            Shell.su("(pm uninstall $pkg)& >/dev/null 2>&1").exec()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -48,14 +48,16 @@ open class SplashActivity : Activity() {
 | 
			
		||||
        // Pre-initialize root shell
 | 
			
		||||
        Shell.getShell()
 | 
			
		||||
 | 
			
		||||
        Config.initialize()
 | 
			
		||||
        handleRepackage()
 | 
			
		||||
        val prevPkg = intent.getStringExtra(Const.Key.PREV_PKG)
 | 
			
		||||
 | 
			
		||||
        Config.load(prevPkg)
 | 
			
		||||
        handleRepackage(prevPkg)
 | 
			
		||||
        Notifications.setup(this)
 | 
			
		||||
        UpdateCheckService.schedule(this)
 | 
			
		||||
        Shortcuts.setupDynamic(this)
 | 
			
		||||
 | 
			
		||||
        // Pre-fetch network stuffs
 | 
			
		||||
        get<GithubRawServices>()
 | 
			
		||||
        // Pre-fetch network services
 | 
			
		||||
        get<NetworkService>()
 | 
			
		||||
 | 
			
		||||
        DONE = true
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ package com.topjohnwu.magisk.core
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import androidx.work.*
 | 
			
		||||
import com.topjohnwu.magisk.BuildConfig
 | 
			
		||||
import com.topjohnwu.magisk.data.repository.MagiskRepository
 | 
			
		||||
import com.topjohnwu.magisk.data.repository.NetworkService
 | 
			
		||||
import com.topjohnwu.magisk.view.Notifications
 | 
			
		||||
import com.topjohnwu.superuser.Shell
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
@@ -15,14 +15,14 @@ import java.util.concurrent.TimeUnit
 | 
			
		||||
class UpdateCheckService(context: Context, workerParams: WorkerParameters)
 | 
			
		||||
    : CoroutineWorker(context, workerParams), KoinComponent {
 | 
			
		||||
 | 
			
		||||
    private val magiskRepo: MagiskRepository by inject()
 | 
			
		||||
    private val svc: NetworkService by inject()
 | 
			
		||||
 | 
			
		||||
    override suspend fun doWork(): Result {
 | 
			
		||||
        // Make sure shell initializer was ran
 | 
			
		||||
        withContext(Dispatchers.IO) {
 | 
			
		||||
            Shell.getShell()
 | 
			
		||||
        }
 | 
			
		||||
        return magiskRepo.fetchUpdate()?.let {
 | 
			
		||||
        return svc.fetchUpdate()?.let {
 | 
			
		||||
            if (BuildConfig.VERSION_CODE < it.app.versionCode)
 | 
			
		||||
                Notifications.managerUpdate(applicationContext)
 | 
			
		||||
            else if (Info.env.isActive && Info.env.magiskVersionCode < it.magisk.versionCode)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
package com.topjohnwu.magisk.core.base
 | 
			
		||||
 | 
			
		||||
import android.Manifest
 | 
			
		||||
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
 | 
			
		||||
import android.content.ActivityNotFoundException
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
@@ -8,22 +8,24 @@ import android.content.pm.PackageManager
 | 
			
		||||
import android.content.res.Configuration
 | 
			
		||||
import android.os.Build
 | 
			
		||||
import android.widget.Toast
 | 
			
		||||
import androidx.annotation.CallSuper
 | 
			
		||||
import androidx.appcompat.app.AppCompatActivity
 | 
			
		||||
import androidx.collection.SparseArrayCompat
 | 
			
		||||
import androidx.core.app.ActivityCompat
 | 
			
		||||
import androidx.core.content.ContextCompat
 | 
			
		||||
import com.topjohnwu.magisk.R
 | 
			
		||||
import com.topjohnwu.magisk.core.Const
 | 
			
		||||
import com.topjohnwu.magisk.core.utils.currentLocale
 | 
			
		||||
import com.topjohnwu.magisk.core.wrap
 | 
			
		||||
import com.topjohnwu.magisk.ktx.set
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils
 | 
			
		||||
import kotlin.random.Random
 | 
			
		||||
 | 
			
		||||
typealias RequestCallback = BaseActivity.(Int, Intent?) -> Unit
 | 
			
		||||
typealias ActivityResultCallback = BaseActivity.(Int, Intent?) -> Unit
 | 
			
		||||
 | 
			
		||||
abstract class BaseActivity : AppCompatActivity() {
 | 
			
		||||
 | 
			
		||||
    private val resultCallbacks by lazy { SparseArrayCompat<RequestCallback>() }
 | 
			
		||||
    private val resultCallbacks by lazy { SparseArrayCompat<ActivityResultCallback>() }
 | 
			
		||||
 | 
			
		||||
    override fun applyOverrideConfiguration(config: Configuration?) {
 | 
			
		||||
        // Force applying our preferred local
 | 
			
		||||
@@ -38,9 +40,8 @@ abstract class BaseActivity : AppCompatActivity() {
 | 
			
		||||
    fun withPermission(permission: String, builder: PermissionRequestBuilder.() -> Unit) {
 | 
			
		||||
        val request = PermissionRequestBuilder().apply(builder).build()
 | 
			
		||||
 | 
			
		||||
        if (permission == Manifest.permission.WRITE_EXTERNAL_STORAGE &&
 | 
			
		||||
            Build.VERSION.SDK_INT >= 29) {
 | 
			
		||||
            // We do not need external rw on 29+
 | 
			
		||||
        if (permission == WRITE_EXTERNAL_STORAGE && Build.VERSION.SDK_INT >= 30) {
 | 
			
		||||
            // We do not need external rw on 30+
 | 
			
		||||
            request.onSuccess()
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
@@ -48,8 +49,11 @@ abstract class BaseActivity : AppCompatActivity() {
 | 
			
		||||
        if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
 | 
			
		||||
            request.onSuccess()
 | 
			
		||||
        } else {
 | 
			
		||||
            val requestCode = Random.nextInt(256, 512)
 | 
			
		||||
            resultCallbacks[requestCode] =  { result, _ ->
 | 
			
		||||
            var requestCode: Int
 | 
			
		||||
            do {
 | 
			
		||||
                requestCode = Random.nextInt(Const.ID.MAX_ACTIVITY_RESULT + 1, 1 shl 15)
 | 
			
		||||
            } while (resultCallbacks.containsKey(requestCode))
 | 
			
		||||
            resultCallbacks[requestCode] = { result, _ ->
 | 
			
		||||
                if (result > 0)
 | 
			
		||||
                    request.onSuccess()
 | 
			
		||||
                else
 | 
			
		||||
@@ -60,7 +64,7 @@ abstract class BaseActivity : AppCompatActivity() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun withExternalRW(builder: PermissionRequestBuilder.() -> Unit) {
 | 
			
		||||
        withPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, builder = builder)
 | 
			
		||||
        withPermission(WRITE_EXTERNAL_STORAGE, builder = builder)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onRequestPermissionsResult(
 | 
			
		||||
@@ -79,16 +83,17 @@ abstract class BaseActivity : AppCompatActivity() {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @CallSuper
 | 
			
		||||
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
 | 
			
		||||
        super.onActivityResult(requestCode, resultCode, data)
 | 
			
		||||
        resultCallbacks[requestCode]?.also {
 | 
			
		||||
        resultCallbacks[requestCode]?.also { callback ->
 | 
			
		||||
            resultCallbacks.remove(requestCode)
 | 
			
		||||
            it(this, resultCode, data)
 | 
			
		||||
            callback(this, resultCode, data)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun startActivityForResult(intent: Intent, requestCode: Int, listener: RequestCallback) {
 | 
			
		||||
        resultCallbacks[requestCode] = listener
 | 
			
		||||
    fun startActivityForResult(intent: Intent, requestCode: Int, callback: ActivityResultCallback) {
 | 
			
		||||
        resultCallbacks[requestCode] = callback
 | 
			
		||||
        try {
 | 
			
		||||
            startActivityForResult(intent, requestCode)
 | 
			
		||||
        } catch (e: ActivityNotFoundException) {
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ package com.topjohnwu.magisk.core.download
 | 
			
		||||
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import android.os.Parcelable
 | 
			
		||||
import kotlinx.android.parcel.Parcelize
 | 
			
		||||
import kotlinx.parcelize.Parcelize
 | 
			
		||||
 | 
			
		||||
sealed class Action : Parcelable {
 | 
			
		||||
 | 
			
		||||
@@ -16,15 +16,6 @@ sealed class Action : Parcelable {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sealed class APK : Action() {
 | 
			
		||||
 | 
			
		||||
        @Parcelize
 | 
			
		||||
        object Upgrade : APK()
 | 
			
		||||
 | 
			
		||||
        @Parcelize
 | 
			
		||||
        object Restore : APK()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Parcelize
 | 
			
		||||
    object Download : Action()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ import com.topjohnwu.magisk.core.base.BaseService
 | 
			
		||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.checkSum
 | 
			
		||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
 | 
			
		||||
import com.topjohnwu.magisk.core.utils.ProgressInputStream
 | 
			
		||||
import com.topjohnwu.magisk.data.network.GithubRawServices
 | 
			
		||||
import com.topjohnwu.magisk.data.repository.NetworkService
 | 
			
		||||
import com.topjohnwu.magisk.ktx.withStreams
 | 
			
		||||
import com.topjohnwu.magisk.view.Notifications
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
@@ -34,7 +34,7 @@ abstract class BaseDownloader : BaseService(), KoinComponent {
 | 
			
		||||
    private val notifications = Collections.synchronizedMap(HashMap<Int, Notification.Builder>())
 | 
			
		||||
    private val coroutineScope = CoroutineScope(Dispatchers.IO)
 | 
			
		||||
 | 
			
		||||
    val service: GithubRawServices by inject()
 | 
			
		||||
    val service: NetworkService by inject()
 | 
			
		||||
 | 
			
		||||
    // -- Service overrides
 | 
			
		||||
 | 
			
		||||
@@ -117,7 +117,7 @@ abstract class BaseDownloader : BaseService(), KoinComponent {
 | 
			
		||||
    fun Subject.notifyID() = hashCode()
 | 
			
		||||
 | 
			
		||||
    private fun notifyFail(subject: Subject) = lastNotify(subject.notifyID()) {
 | 
			
		||||
        broadcast(-1f, subject)
 | 
			
		||||
        broadcast(-2f, subject)
 | 
			
		||||
        it.setContentText(getString(R.string.download_file_error))
 | 
			
		||||
            .setSmallIcon(android.R.drawable.stat_notify_error)
 | 
			
		||||
            .setOngoing(false)
 | 
			
		||||
 
 | 
			
		||||
@@ -73,10 +73,7 @@ open class DownloadService : BaseDownloader() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun Notification.Builder.setIntent(subject: Manager)
 | 
			
		||||
    = when (subject.action) {
 | 
			
		||||
        APK.Upgrade -> setContentIntent(APKInstall.installIntent(context, subject.file.toFile()))
 | 
			
		||||
        else -> setContentIntent(Intent())
 | 
			
		||||
    }
 | 
			
		||||
    = setContentIntent(APKInstall.installIntent(context, subject.file.toFile()))
 | 
			
		||||
 | 
			
		||||
    private fun Notification.Builder.setContentIntent(intent: Intent) =
 | 
			
		||||
        setContentIntent(
 | 
			
		||||
 
 | 
			
		||||
@@ -5,20 +5,16 @@ import androidx.core.net.toFile
 | 
			
		||||
import com.topjohnwu.magisk.BuildConfig
 | 
			
		||||
import com.topjohnwu.magisk.DynAPK
 | 
			
		||||
import com.topjohnwu.magisk.R
 | 
			
		||||
import com.topjohnwu.magisk.core.Config
 | 
			
		||||
import com.topjohnwu.magisk.core.Info
 | 
			
		||||
import com.topjohnwu.magisk.core.download.Action.APK.Restore
 | 
			
		||||
import com.topjohnwu.magisk.core.download.Action.APK.Upgrade
 | 
			
		||||
import com.topjohnwu.magisk.core.isRunningAsStub
 | 
			
		||||
import com.topjohnwu.magisk.core.tasks.PatchAPK
 | 
			
		||||
import com.topjohnwu.magisk.core.tasks.HideAPK
 | 
			
		||||
import com.topjohnwu.magisk.ktx.relaunchApp
 | 
			
		||||
import com.topjohnwu.magisk.ktx.writeTo
 | 
			
		||||
import com.topjohnwu.superuser.Shell
 | 
			
		||||
import java.io.File
 | 
			
		||||
 | 
			
		||||
private fun Context.patch(apk: File) {
 | 
			
		||||
    val patched = File(apk.parent, "patched.apk")
 | 
			
		||||
    PatchAPK.patch(this, apk.path, patched.path, packageName, applicationInfo.nonLocalizedLabel)
 | 
			
		||||
    HideAPK.patch(this, apk.path, patched.path, packageName, applicationInfo.nonLocalizedLabel)
 | 
			
		||||
    apk.delete()
 | 
			
		||||
    patched.renameTo(apk)
 | 
			
		||||
}
 | 
			
		||||
@@ -31,7 +27,7 @@ private fun BaseDownloader.notifyHide(id: Int) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private suspend fun BaseDownloader.upgrade(subject: Subject.Manager) {
 | 
			
		||||
suspend fun BaseDownloader.handleAPK(subject: Subject.Manager) {
 | 
			
		||||
    val apk = subject.file.toFile()
 | 
			
		||||
    val id = subject.notifyID()
 | 
			
		||||
    if (isRunningAsStub) {
 | 
			
		||||
@@ -53,20 +49,3 @@ private suspend fun BaseDownloader.upgrade(subject: Subject.Manager) {
 | 
			
		||||
        patch(apk)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private fun BaseDownloader.restore(apk: File, id: Int) {
 | 
			
		||||
    update(id) {
 | 
			
		||||
        it.setProgress(0, 0, true)
 | 
			
		||||
            .setProgress(0, 0, true)
 | 
			
		||||
            .setContentTitle(getString(R.string.restore_img_msg))
 | 
			
		||||
            .setContentText("")
 | 
			
		||||
    }
 | 
			
		||||
    Config.export()
 | 
			
		||||
    Shell.su("pm install $apk && pm uninstall $packageName").exec()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
suspend fun BaseDownloader.handleAPK(subject: Subject.Manager) =
 | 
			
		||||
    when (subject.action) {
 | 
			
		||||
        is Upgrade -> upgrade(subject)
 | 
			
		||||
        is Restore -> restore(subject.file.toFile(), subject.notifyID())
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user