mirror of
				https://github.com/topjohnwu/Magisk
				synced 2025-11-03 15:52:30 +01:00 
			
		
		
		
	Compare commits
	
		
			226 Commits
		
	
	
		
			canary-280
			...
			canary-281
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					427a1ca4e5 | ||
| 
						 | 
					22884e173a | ||
| 
						 | 
					d1829308e9 | ||
| 
						 | 
					73840f8721 | ||
| 
						 | 
					c7d1af9805 | ||
| 
						 | 
					4ad26d3dfb | ||
| 
						 | 
					0c70b7670c | ||
| 
						 | 
					f44d044095 | ||
| 
						 | 
					5c1cb13472 | ||
| 
						 | 
					3327fc668e | ||
| 
						 | 
					610945ac54 | ||
| 
						 | 
					ddf5474917 | ||
| 
						 | 
					6ba1685ade | ||
| 
						 | 
					e02b5f7868 | ||
| 
						 | 
					ab2e5d1e7e | ||
| 
						 | 
					f3fef7bfe4 | ||
| 
						 | 
					c34c7838bb | ||
| 
						 | 
					c8a16b0e0c | ||
| 
						 | 
					14f9ed91a1 | ||
| 
						 | 
					7a207d4ccf | ||
| 
						 | 
					92a42d901f | ||
| 
						 | 
					084d89fcce | ||
| 
						 | 
					55b036c071 | ||
| 
						 | 
					30e79310ab | ||
| 
						 | 
					f063fa5054 | ||
| 
						 | 
					7bd901273c | ||
| 
						 | 
					c1e061603b | ||
| 
						 | 
					cb08504fe5 | ||
| 
						 | 
					c0a1fb77be | ||
| 
						 | 
					4864c1112a | ||
| 
						 | 
					9ddeab034b | ||
| 
						 | 
					c4847ed288 | ||
| 
						 | 
					b8f1523fb2 | ||
| 
						 | 
					fb7fa8a6b3 | ||
| 
						 | 
					9c7d359093 | ||
| 
						 | 
					eb54bc1fd7 | ||
| 
						 | 
					d4a0286e13 | ||
| 
						 | 
					83e66767ff | ||
| 
						 | 
					7dc010749b | ||
| 
						 | 
					8e8d013b1b | ||
| 
						 | 
					bba0373808 | ||
| 
						 | 
					1fa318dc8c | ||
| 
						 | 
					6edc5e2037 | ||
| 
						 | 
					1523ed9f78 | ||
| 
						 | 
					8e604d2ab8 | ||
| 
						 | 
					2aba7247a9 | ||
| 
						 | 
					e66fe8533e | ||
| 
						 | 
					b03fbb3917 | ||
| 
						 | 
					c2ece62e4c | ||
| 
						 | 
					8c972dcf34 | ||
| 
						 | 
					50af14f2a3 | ||
| 
						 | 
					e0a356b319 | ||
| 
						 | 
					c09a792958 | ||
| 
						 | 
					0bbfe7f44d | ||
| 
						 | 
					a396abf565 | ||
| 
						 | 
					1e3edb8883 | ||
| 
						 | 
					3b8b61bf35 | ||
| 
						 | 
					6f90456036 | ||
| 
						 | 
					f56fd4e215 | ||
| 
						 | 
					aa35aac5d5 | ||
| 
						 | 
					1f162b819d | ||
| 
						 | 
					52ef1d1cb2 | ||
| 
						 | 
					f14e3a89cc | ||
| 
						 | 
					95d3eac2e0 | ||
| 
						 | 
					8e73536e02 | ||
| 
						 | 
					12a0870bc9 | ||
| 
						 | 
					6ff82c4e86 | ||
| 
						 | 
					c64de35375 | ||
| 
						 | 
					ee5283f4e8 | ||
| 
						 | 
					bd0e954fea | ||
| 
						 | 
					675471a49e | ||
| 
						 | 
					c90e73ccec | ||
| 
						 | 
					a43c1267d8 | ||
| 
						 | 
					e8958c6b5c | ||
| 
						 | 
					e8a3bf82c6 | ||
| 
						 | 
					27fd79176a | ||
| 
						 | 
					28d86a3454 | ||
| 
						 | 
					c6c1a17ae6 | ||
| 
						 | 
					2b47d47215 | ||
| 
						 | 
					0e82df9e10 | ||
| 
						 | 
					893821ad88 | ||
| 
						 | 
					6b80fbfa99 | ||
| 
						 | 
					8c3c7d0194 | ||
| 
						 | 
					b94a3d9f2f | ||
| 
						 | 
					442d0b5ddc | ||
| 
						 | 
					494615d9a0 | ||
| 
						 | 
					afbfb81837 | ||
| 
						 | 
					3ed4e258a3 | ||
| 
						 | 
					dddd41c95b | ||
| 
						 | 
					5f2ca81e86 | ||
| 
						 | 
					c9eac0c438 | ||
| 
						 | 
					b6b34f7612 | ||
| 
						 | 
					e55c413261 | ||
| 
						 | 
					0399cde50a | ||
| 
						 | 
					019eb03823 | ||
| 
						 | 
					363410e1c0 | ||
| 
						 | 
					fc2ef21660 | ||
| 
						 | 
					18cb659ff3 | ||
| 
						 | 
					63231d97ce | ||
| 
						 | 
					9ac81a8a25 | ||
| 
						 | 
					79af2787ae | ||
| 
						 | 
					f5f9b285c0 | ||
| 
						 | 
					6c05f2ae85 | ||
| 
						 | 
					29043e1684 | ||
| 
						 | 
					b73d4a7022 | ||
| 
						 | 
					ad95e8951b | ||
| 
						 | 
					bf591fca12 | ||
| 
						 | 
					dcf027884d | ||
| 
						 | 
					584f3820fe | ||
| 
						 | 
					3c7c46307a | ||
| 
						 | 
					4d80361805 | ||
| 
						 | 
					9a74e19117 | ||
| 
						 | 
					b1e17706a4 | ||
| 
						 | 
					caad129d69 | ||
| 
						 | 
					da58571ce5 | ||
| 
						 | 
					2aa7f1c094 | ||
| 
						 | 
					823e31a91b | ||
| 
						 | 
					fb926ae302 | ||
| 
						 | 
					e0489eeffd | ||
| 
						 | 
					dc9d5a4cac | ||
| 
						 | 
					143743d0b0 | ||
| 
						 | 
					563f0d5ad5 | ||
| 
						 | 
					c99f4a591b | ||
| 
						 | 
					449204e380 | ||
| 
						 | 
					a85c4c6528 | ||
| 
						 | 
					d203a6fff6 | ||
| 
						 | 
					6c612d66d7 | ||
| 
						 | 
					540253a55b | ||
| 
						 | 
					15b7c4ccd1 | ||
| 
						 | 
					442d5335ea | ||
| 
						 | 
					8a80eea597 | ||
| 
						 | 
					5e35703091 | ||
| 
						 | 
					b7ca73f431 | ||
| 
						 | 
					a14fc90f07 | ||
| 
						 | 
					c913f7ec74 | ||
| 
						 | 
					7f6c9e8411 | ||
| 
						 | 
					bb02ea3a20 | ||
| 
						 | 
					3981c9665e | ||
| 
						 | 
					88628fdf3c | ||
| 
						 | 
					0469817781 | ||
| 
						 | 
					a786801141 | ||
| 
						 | 
					ab86732c89 | ||
| 
						 | 
					59622d1688 | ||
| 
						 | 
					58a25a3e2b | ||
| 
						 | 
					15dca29a87 | ||
| 
						 | 
					46980819c0 | ||
| 
						 | 
					4fb6a7268c | ||
| 
						 | 
					c05e963f37 | ||
| 
						 | 
					7f7f625864 | ||
| 
						 | 
					b25aa8295a | ||
| 
						 | 
					15a605765c | ||
| 
						 | 
					b575c95710 | ||
| 
						 | 
					a48a9c858a | ||
| 
						 | 
					0d8d6290a3 | ||
| 
						 | 
					4dcd733ddd | ||
| 
						 | 
					b62835cbeb | ||
| 
						 | 
					6ea740b5ab | ||
| 
						 | 
					7ab98dd5ac | ||
| 
						 | 
					fc8b3400fc | ||
| 
						 | 
					54428ba415 | ||
| 
						 | 
					95d1e69d8e | ||
| 
						 | 
					a0f13ab49f | ||
| 
						 | 
					c3e8405020 | ||
| 
						 | 
					a93593ea66 | ||
| 
						 | 
					23eff70883 | ||
| 
						 | 
					110dd4a8b9 | ||
| 
						 | 
					d9c2bffc9f | ||
| 
						 | 
					049db49dc8 | ||
| 
						 | 
					7c1d2ec61e | ||
| 
						 | 
					a1b2830c06 | ||
| 
						 | 
					82d1d19267 | ||
| 
						 | 
					4d4195c02d | ||
| 
						 | 
					5637a258fc | ||
| 
						 | 
					ee6810f417 | ||
| 
						 | 
					7098248c64 | ||
| 
						 | 
					0d31d356ef | ||
| 
						 | 
					b782e7dcb7 | ||
| 
						 | 
					a4671b4698 | ||
| 
						 | 
					7edd8be169 | ||
| 
						 | 
					24650eefe4 | ||
| 
						 | 
					8e1a44e7eb | ||
| 
						 | 
					2722875190 | ||
| 
						 | 
					3ca6d06f69 | ||
| 
						 | 
					10e47248de | ||
| 
						 | 
					e73ff679ac | ||
| 
						 | 
					53e401fa2d | ||
| 
						 | 
					d2768357da | ||
| 
						 | 
					a6c2ba7c1e | ||
| 
						 | 
					aae5b466fb | ||
| 
						 | 
					2b7be8b949 | ||
| 
						 | 
					b6511a510d | ||
| 
						 | 
					704541aef2 | ||
| 
						 | 
					005560a4c5 | ||
| 
						 | 
					231a5d1853 | ||
| 
						 | 
					9e2b59060d | ||
| 
						 | 
					08ea937f7c | ||
| 
						 | 
					2baedf74d1 | ||
| 
						 | 
					32faa4ced6 | ||
| 
						 | 
					ccdb0b5d13 | ||
| 
						 | 
					8506b672ad | ||
| 
						 | 
					ce2e33bb20 | ||
| 
						 | 
					6707b72260 | ||
| 
						 | 
					5885b8c20d | ||
| 
						 | 
					820710c086 | ||
| 
						 | 
					51cf196bf7 | ||
| 
						 | 
					dadba44cf9 | ||
| 
						 | 
					2ce4a5543b | ||
| 
						 | 
					9112a3a4f5 | ||
| 
						 | 
					24615afda1 | ||
| 
						 | 
					c5778f398b | ||
| 
						 | 
					4eb4097b9b | ||
| 
						 | 
					c512496847 | ||
| 
						 | 
					506961a10d | ||
| 
						 | 
					3414415907 | ||
| 
						 | 
					dc2ae7cfd1 | ||
| 
						 | 
					2e86d21c29 | ||
| 
						 | 
					2654382c43 | ||
| 
						 | 
					9e26b73813 | ||
| 
						 | 
					10cd13bf80 | ||
| 
						 | 
					f10ee5f887 | ||
| 
						 | 
					47cc532d96 | ||
| 
						 | 
					218327f92b | ||
| 
						 | 
					4eae66a1a7 | ||
| 
						 | 
					b09ceeb43c | ||
| 
						 | 
					4fb539c110 | ||
| 
						 | 
					849b284da5 | 
							
								
								
									
										6
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							@@ -12,13 +12,11 @@
 | 
			
		||||
 | 
			
		||||
# Denote all files that are truly binary and should not be modified.
 | 
			
		||||
tools/** binary
 | 
			
		||||
tools/rustup-wrapper/** -binary
 | 
			
		||||
tools/elf-cleaner/** -binary
 | 
			
		||||
*.jar binary
 | 
			
		||||
*.exe binary
 | 
			
		||||
*.apk binary
 | 
			
		||||
*.png binary
 | 
			
		||||
*.jpg binary
 | 
			
		||||
*.ttf binary
 | 
			
		||||
 | 
			
		||||
# Help GitHub detect languages
 | 
			
		||||
native/jni/external/** linguist-vendored
 | 
			
		||||
native/jni/systemproperties/** linguist-language=C++
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								.github/actions/setup/action.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										23
									
								
								.github/actions/setup/action.yml
									
									
									
									
										vendored
									
									
								
							@@ -26,6 +26,15 @@ runs:
 | 
			
		||||
 | 
			
		||||
    - name: Cache sccache
 | 
			
		||||
      uses: actions/cache@v4
 | 
			
		||||
      if: ${{ github.event_name != 'pull_request' }}
 | 
			
		||||
      with:
 | 
			
		||||
        path: .sccache
 | 
			
		||||
        key: sccache-${{ runner.os }}-${{ github.sha }}
 | 
			
		||||
        restore-keys: sccache-${{ runner.os }}-
 | 
			
		||||
 | 
			
		||||
    - name: Restore sccache
 | 
			
		||||
      uses: actions/cache/restore@v4
 | 
			
		||||
      if: ${{ github.event_name == 'pull_request' }}
 | 
			
		||||
      with:
 | 
			
		||||
        path: .sccache
 | 
			
		||||
        key: sccache-${{ runner.os }}-${{ github.sha }}
 | 
			
		||||
@@ -55,7 +64,7 @@ runs:
 | 
			
		||||
 | 
			
		||||
    - name: Cache Gradle dependencies
 | 
			
		||||
      uses: actions/cache@v4
 | 
			
		||||
      if: inputs.is-asset-build == 'true'
 | 
			
		||||
      if: ${{ inputs.is-asset-build == 'true' && github.event_name != 'pull_request' }}
 | 
			
		||||
      with:
 | 
			
		||||
        path: |
 | 
			
		||||
          .gradle/caches
 | 
			
		||||
@@ -66,7 +75,7 @@ runs:
 | 
			
		||||
 | 
			
		||||
    - name: Restore Gradle dependencies
 | 
			
		||||
      uses: actions/cache/restore@v4
 | 
			
		||||
      if: inputs.is-asset-build == 'false'
 | 
			
		||||
      if: ${{ inputs.is-asset-build == 'false' || github.event_name == 'pull_request' }}
 | 
			
		||||
      with:
 | 
			
		||||
        path: |
 | 
			
		||||
          .gradle/caches
 | 
			
		||||
@@ -78,19 +87,17 @@ runs:
 | 
			
		||||
 | 
			
		||||
    - name: Cache Gradle build cache
 | 
			
		||||
      uses: actions/cache@v4
 | 
			
		||||
      if: inputs.is-asset-build == 'true'
 | 
			
		||||
      if: ${{ inputs.is-asset-build == 'true' && github.event_name != 'pull_request' }}
 | 
			
		||||
      with:
 | 
			
		||||
        path: |
 | 
			
		||||
          .gradle/caches/build-cache-*
 | 
			
		||||
        path: .gradle/caches/build-cache-*
 | 
			
		||||
        key: gradle-build-cache-${{ github.sha }}
 | 
			
		||||
        restore-keys: gradle-build-cache-
 | 
			
		||||
 | 
			
		||||
    - name: Restore Gradle build cache
 | 
			
		||||
      uses: actions/cache/restore@v4
 | 
			
		||||
      if: inputs.is-asset-build == 'false'
 | 
			
		||||
      if: ${{ inputs.is-asset-build == 'false' || github.event_name == 'pull_request' }}
 | 
			
		||||
      with:
 | 
			
		||||
        path: |
 | 
			
		||||
          .gradle/caches/build-cache-*
 | 
			
		||||
        path: .gradle/caches/build-cache-*
 | 
			
		||||
        key: gradle-build-cache-${{ github.sha }}
 | 
			
		||||
        restore-keys: gradle-build-cache-
 | 
			
		||||
        enableCrossOsArchive: true
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							@@ -9,6 +9,7 @@ on:
 | 
			
		||||
      - "buildSrc/**"
 | 
			
		||||
      - "build.py"
 | 
			
		||||
      - "gradle.properties"
 | 
			
		||||
      - "gradle/libs.versions.toml"
 | 
			
		||||
      - ".github/workflows/build.yml"
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches: [master]
 | 
			
		||||
@@ -17,7 +18,7 @@ on:
 | 
			
		||||
jobs:
 | 
			
		||||
  build:
 | 
			
		||||
    name: Build Magisk artifacts
 | 
			
		||||
    runs-on: macos-14
 | 
			
		||||
    runs-on: macos-15
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
    steps:
 | 
			
		||||
@@ -60,7 +61,7 @@ jobs:
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
        os: [windows-latest, ubuntu-latest]
 | 
			
		||||
        os: [windows-2025, ubuntu-24.04]
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out
 | 
			
		||||
        uses: actions/checkout@v4
 | 
			
		||||
@@ -78,16 +79,19 @@ jobs:
 | 
			
		||||
 | 
			
		||||
  avd-test:
 | 
			
		||||
    name: Test API ${{ matrix.version }} (x86_64)
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    needs: build
 | 
			
		||||
    if: ${{ github.event_name != 'push' }}
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
        version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
 | 
			
		||||
        type: [""]
 | 
			
		||||
        include:
 | 
			
		||||
          - version: "Baklava"
 | 
			
		||||
          - version: 36
 | 
			
		||||
            type: "google_apis"
 | 
			
		||||
          - version: 36
 | 
			
		||||
            type: "google_apis_ps16k"
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out
 | 
			
		||||
@@ -122,8 +126,9 @@ jobs:
 | 
			
		||||
 | 
			
		||||
  avd-test-32:
 | 
			
		||||
    name: Test API ${{ matrix.version }} (x86)
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    needs: build
 | 
			
		||||
    if: ${{ github.event_name != 'push' }}
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
@@ -161,20 +166,19 @@ jobs:
 | 
			
		||||
            kernel.log
 | 
			
		||||
            logcat.log
 | 
			
		||||
 | 
			
		||||
  cf_test:
 | 
			
		||||
  cf-test:
 | 
			
		||||
    name: Test ${{ matrix.device }}
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    needs: build
 | 
			
		||||
    if: ${{ github.event_name != 'push' }}
 | 
			
		||||
    env:
 | 
			
		||||
      CF_HOME: /home/runner/aosp_cf_phone
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
        include:
 | 
			
		||||
          - branch: "aosp-main"
 | 
			
		||||
            device: "aosp_cf_x86_64_phone"
 | 
			
		||||
          - branch: "aosp-main-throttled"
 | 
			
		||||
            device: "aosp_cf_x86_64_phone_pgagnostic"
 | 
			
		||||
          - branch: "aosp-android-latest-release"
 | 
			
		||||
            device: "aosp_cf_x86_64_only_phone"
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							@@ -4,18 +4,12 @@
 | 
			
		||||
[submodule "lz4"]
 | 
			
		||||
	path = native/src/external/lz4
 | 
			
		||||
	url = https://github.com/lz4/lz4.git
 | 
			
		||||
[submodule "bzip2"]
 | 
			
		||||
	path = native/src/external/bzip2
 | 
			
		||||
	url = https://github.com/nemequ/bzip2.git
 | 
			
		||||
[submodule "xz"]
 | 
			
		||||
	path = native/src/external/xz
 | 
			
		||||
	url = https://github.com/xz-mirror/xz.git
 | 
			
		||||
[submodule "libcxx"]
 | 
			
		||||
	path = native/src/external/libcxx
 | 
			
		||||
	url = https://github.com/topjohnwu/libcxx.git
 | 
			
		||||
[submodule "zlib"]
 | 
			
		||||
	path = native/src/external/zlib
 | 
			
		||||
	url = https://android.googlesource.com/platform/external/zlib
 | 
			
		||||
[submodule "zopfli"]
 | 
			
		||||
	path = native/src/external/zopfli
 | 
			
		||||
	url = https://github.com/google/zopfli.git
 | 
			
		||||
@@ -31,6 +25,3 @@
 | 
			
		||||
[submodule "crt0"]
 | 
			
		||||
	path = native/src/external/crt0
 | 
			
		||||
	url = https://github.com/topjohnwu/crt0.git
 | 
			
		||||
[submodule "termux-elf-cleaner"]
 | 
			
		||||
	path = tools/termux-elf-cleaner
 | 
			
		||||
	url = https://github.com/termux/termux-elf-cleaner.git
 | 
			
		||||
 
 | 
			
		||||
@@ -20,9 +20,9 @@ Some highlight features:
 | 
			
		||||
 | 
			
		||||
Click the icon below to download Magisk apk.
 | 
			
		||||
 | 
			
		||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v27.0)
 | 
			
		||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v28.0)
 | 
			
		||||
[](https://github.com/topjohnwu/Magisk/releases/tag/canary-28003)
 | 
			
		||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v28.1)
 | 
			
		||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v28.1)
 | 
			
		||||
[](https://github.com/topjohnwu/Magisk/releases/tag/canary-28104)
 | 
			
		||||
 | 
			
		||||
## Useful Links
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,13 @@
 | 
			
		||||
package com.topjohnwu.magisk.arch
 | 
			
		||||
 | 
			
		||||
import android.content.ContentResolver
 | 
			
		||||
import android.view.KeyEvent
 | 
			
		||||
import androidx.databinding.ViewDataBinding
 | 
			
		||||
import androidx.navigation.NavController
 | 
			
		||||
import androidx.navigation.NavDirections
 | 
			
		||||
import androidx.navigation.fragment.NavHostFragment
 | 
			
		||||
import androidx.navigation.navOptions
 | 
			
		||||
import com.topjohnwu.magisk.utils.AccessibilityUtils
 | 
			
		||||
 | 
			
		||||
abstract class NavigationActivity<Binding : ViewDataBinding> : UIActivity<Binding>() {
 | 
			
		||||
 | 
			
		||||
@@ -31,7 +34,17 @@ abstract class NavigationActivity<Binding : ViewDataBinding> : UIActivity<Bindin
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        fun navigate(directions: NavDirections, navigation: NavController, cr: ContentResolver) {
 | 
			
		||||
            if (AccessibilityUtils.isAnimationEnabled(cr)) {
 | 
			
		||||
                navigation.navigate(directions)
 | 
			
		||||
            } else {
 | 
			
		||||
                navigation.navigate(directions, navOptions {})
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun NavDirections.navigate() {
 | 
			
		||||
        navigation.navigate(this)
 | 
			
		||||
        navigate(this, navigation, contentResolver)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,8 @@ import com.topjohnwu.magisk.core.Info
 | 
			
		||||
import com.topjohnwu.magisk.core.download.DownloadEngine
 | 
			
		||||
import com.topjohnwu.magisk.databinding.FragmentHomeMd2Binding
 | 
			
		||||
import com.topjohnwu.magisk.core.R as CoreR
 | 
			
		||||
import androidx.navigation.findNavController
 | 
			
		||||
import com.topjohnwu.magisk.arch.NavigationActivity
 | 
			
		||||
 | 
			
		||||
class HomeFragment : BaseFragment<FragmentHomeMd2Binding>(), MenuProvider {
 | 
			
		||||
 | 
			
		||||
@@ -68,7 +70,13 @@ class HomeFragment : BaseFragment<FragmentHomeMd2Binding>(), MenuProvider {
 | 
			
		||||
    override fun onMenuItemSelected(item: MenuItem): Boolean {
 | 
			
		||||
        when (item.itemId) {
 | 
			
		||||
            R.id.action_settings ->
 | 
			
		||||
                HomeFragmentDirections.actionHomeFragmentToSettingsFragment().navigate()
 | 
			
		||||
                activity?.let {
 | 
			
		||||
                    NavigationActivity.navigate(
 | 
			
		||||
                        HomeFragmentDirections.actionHomeFragmentToSettingsFragment(),
 | 
			
		||||
                        it.findNavController(R.id.main_nav_host),
 | 
			
		||||
                        it.contentResolver,
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            R.id.action_reboot -> activity?.let { RebootMenu.inflate(it).show() }
 | 
			
		||||
            else -> return super.onOptionsItemSelected(item)
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -41,7 +41,7 @@ object RebootMenu {
 | 
			
		||||
            activity.getSystemService<PowerManager>()?.isRebootingUserspaceSupported == true) {
 | 
			
		||||
            menu.menu.findItem(R.id.action_reboot_userspace).isVisible = true
 | 
			
		||||
        }
 | 
			
		||||
        if (Const.Version.isCanary()) {
 | 
			
		||||
        if (Const.Version.atLeast_28_0()) {
 | 
			
		||||
            menu.menu.findItem(R.id.action_reboot_safe_mode).isChecked = Config.bootloop >= 2
 | 
			
		||||
        } else {
 | 
			
		||||
            menu.menu.findItem(R.id.action_reboot_safe_mode).isVisible = false
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import android.view.Menu
 | 
			
		||||
import android.view.MenuInflater
 | 
			
		||||
import android.view.MenuItem
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.widget.HorizontalScrollView
 | 
			
		||||
import androidx.core.view.MenuProvider
 | 
			
		||||
import androidx.core.view.isVisible
 | 
			
		||||
import com.topjohnwu.magisk.R
 | 
			
		||||
@@ -12,6 +13,7 @@ import com.topjohnwu.magisk.arch.BaseFragment
 | 
			
		||||
import com.topjohnwu.magisk.arch.viewModel
 | 
			
		||||
import com.topjohnwu.magisk.databinding.FragmentLogMd2Binding
 | 
			
		||||
import com.topjohnwu.magisk.ui.MainActivity
 | 
			
		||||
import com.topjohnwu.magisk.utils.AccessibilityUtils
 | 
			
		||||
import com.topjohnwu.magisk.utils.MotionRevealHelper
 | 
			
		||||
import rikka.recyclerview.addEdgeSpacing
 | 
			
		||||
import rikka.recyclerview.addItemSpacing
 | 
			
		||||
@@ -56,6 +58,11 @@ class LogFragment : BaseFragment<FragmentLogMd2Binding>(), MenuProvider {
 | 
			
		||||
            addItemSpacing(R.dimen.l1, R.dimen.l_50, R.dimen.l1)
 | 
			
		||||
            fixEdgeEffect()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!AccessibilityUtils.isAnimationEnabled(requireContext().contentResolver)) {
 | 
			
		||||
            val scrollView = view.findViewById<HorizontalScrollView>(R.id.log_scroll_magisk)
 | 
			
		||||
            scrollView.setOverScrollMode(View.OVER_SCROLL_NEVER)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ import com.topjohnwu.magisk.BR
 | 
			
		||||
import com.topjohnwu.magisk.arch.BaseViewModel
 | 
			
		||||
import com.topjohnwu.magisk.core.AppContext
 | 
			
		||||
import com.topjohnwu.magisk.core.BuildConfig
 | 
			
		||||
import com.topjohnwu.magisk.core.Config
 | 
			
		||||
import com.topjohnwu.magisk.core.Const
 | 
			
		||||
import com.topjohnwu.magisk.core.Info
 | 
			
		||||
import com.topjohnwu.magisk.core.R
 | 
			
		||||
@@ -92,7 +93,7 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
 | 
			
		||||
            DownloadPath -> withExternalRW(doAction)
 | 
			
		||||
            UpdateChecker -> withPostNotificationPermission(doAction)
 | 
			
		||||
            Authentication -> AuthEvent(doAction).publish()
 | 
			
		||||
            Hide, Restore -> withInstallPermission(doAction)
 | 
			
		||||
            AutomaticResponse -> if (Config.suAuth) AuthEvent(doAction).publish() else doAction()
 | 
			
		||||
            else -> doAction()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,14 @@
 | 
			
		||||
package com.topjohnwu.magisk.utils
 | 
			
		||||
 | 
			
		||||
import android.content.ContentResolver
 | 
			
		||||
import android.provider.Settings
 | 
			
		||||
 | 
			
		||||
class AccessibilityUtils {
 | 
			
		||||
    companion object {
 | 
			
		||||
        fun isAnimationEnabled(cr: ContentResolver): Boolean {
 | 
			
		||||
            return !(Settings.Global.getFloat(cr, Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f) == 0.0f
 | 
			
		||||
                && Settings.Global.getFloat(cr, Settings.Global.TRANSITION_ANIMATION_SCALE, 1.0f) == 0.0f
 | 
			
		||||
                && Settings.Global.getFloat(cr, Settings.Global.WINDOW_ANIMATION_SCALE, 1.0f) == 0.0f)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -16,6 +16,7 @@
 | 
			
		||||
        android:layout_height="match_parent">
 | 
			
		||||
 | 
			
		||||
        <HorizontalScrollView
 | 
			
		||||
            android:id="@+id/log_scroll_magisk"
 | 
			
		||||
            gone="@{viewModel.loading}"
 | 
			
		||||
            android:layout_width="match_parent"
 | 
			
		||||
            android:layout_height="match_parent"
 | 
			
		||||
 
 | 
			
		||||
@@ -60,5 +60,10 @@ dependencies {
 | 
			
		||||
    implementation(libs.activity)
 | 
			
		||||
    implementation(libs.collection.ktx)
 | 
			
		||||
    implementation(libs.profileinstaller)
 | 
			
		||||
    implementation(libs.lifecycle.process)
 | 
			
		||||
 | 
			
		||||
    // We also implement all our tests in this module.
 | 
			
		||||
    // However, we don't want to bundle test dependencies.
 | 
			
		||||
    // That's why we make it compileOnly.
 | 
			
		||||
    compileOnly(libs.test.junit)
 | 
			
		||||
    compileOnly(libs.test.uiautomator)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								app/core/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								app/core/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							@@ -37,12 +37,4 @@
 | 
			
		||||
-flattenpackagehierarchy
 | 
			
		||||
-allowaccessmodification
 | 
			
		||||
 | 
			
		||||
-dontwarn org.bouncycastle.jsse.BCSSLParameters
 | 
			
		||||
-dontwarn org.bouncycastle.jsse.BCSSLSocket
 | 
			
		||||
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
 | 
			
		||||
-dontwarn org.commonmark.ext.gfm.strikethrough.Strikethrough
 | 
			
		||||
-dontwarn org.conscrypt.Conscrypt*
 | 
			
		||||
-dontwarn org.conscrypt.ConscryptHostnameVerifier
 | 
			
		||||
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
 | 
			
		||||
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
 | 
			
		||||
-dontwarn org.openjsse.net.ssl.OpenJSSE
 | 
			
		||||
-dontwarn org.junit.**
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,6 @@ import com.topjohnwu.magisk.StubApk
 | 
			
		||||
import com.topjohnwu.magisk.core.base.UntrackedActivity
 | 
			
		||||
import com.topjohnwu.magisk.core.utils.LocaleSetting
 | 
			
		||||
import com.topjohnwu.magisk.core.utils.NetworkObserver
 | 
			
		||||
import com.topjohnwu.magisk.core.utils.ProcessLifecycle
 | 
			
		||||
import com.topjohnwu.magisk.core.utils.RootUtils
 | 
			
		||||
import com.topjohnwu.magisk.core.utils.ShellInit
 | 
			
		||||
import com.topjohnwu.superuser.Shell
 | 
			
		||||
@@ -40,6 +39,7 @@ object AppContext : ContextWrapper(null),
 | 
			
		||||
 | 
			
		||||
    private var ref = WeakReference<Activity>(null)
 | 
			
		||||
    private lateinit var application: Application
 | 
			
		||||
    private lateinit var networkObserver: NetworkObserver
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        // Always log full stack trace with Timber
 | 
			
		||||
@@ -56,6 +56,10 @@ object AppContext : ContextWrapper(null),
 | 
			
		||||
        LocaleSetting.instance.updateResource(resources)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onActivityStarted(activity: Activity) {
 | 
			
		||||
        networkObserver.postCurrentState()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onActivityResumed(activity: Activity) {
 | 
			
		||||
        if (activity is UntrackedActivity) return
 | 
			
		||||
        ref = WeakReference(activity)
 | 
			
		||||
@@ -102,8 +106,7 @@ object AppContext : ContextWrapper(null),
 | 
			
		||||
            val lm = getSystemService(LocaleManager::class.java)
 | 
			
		||||
            lm.overrideLocaleConfig = LocaleSetting.localeConfig
 | 
			
		||||
        }
 | 
			
		||||
        ProcessLifecycle.init(this)
 | 
			
		||||
        NetworkObserver.init(this)
 | 
			
		||||
        networkObserver = NetworkObserver.init(this)
 | 
			
		||||
        if (!BuildConfig.DEBUG && !isRunningAsStub) {
 | 
			
		||||
            GlobalScope.launch(Dispatchers.IO) {
 | 
			
		||||
                ProfileInstaller.writeProfile(this@AppContext)
 | 
			
		||||
@@ -120,7 +123,6 @@ object AppContext : ContextWrapper(null),
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onActivityCreated(activity: Activity, bundle: Bundle?) {}
 | 
			
		||||
    override fun onActivityStarted(activity: Activity) {}
 | 
			
		||||
    override fun onActivityStopped(activity: Activity) {}
 | 
			
		||||
    override fun onActivitySaveInstanceState(activity: Activity, bundle: Bundle) {}
 | 
			
		||||
    override fun onActivityDestroyed(activity: Activity) {}
 | 
			
		||||
 
 | 
			
		||||
@@ -83,7 +83,7 @@ object Config : PreferenceConfig, DBConfig {
 | 
			
		||||
        const val SU_AUTO_ALLOW = 2
 | 
			
		||||
 | 
			
		||||
        // su timeout
 | 
			
		||||
        val TIMEOUT_LIST = intArrayOf(0, -1, 10, 20, 30, 60)
 | 
			
		||||
        val TIMEOUT_LIST = longArrayOf(0, -1, 10, 20, 30, 60)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private val defaultChannel =
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ object Const {
 | 
			
		||||
        else Build.SUPPORTED_32_BIT_ABIS.firstOrNull()
 | 
			
		||||
 | 
			
		||||
    // Paths
 | 
			
		||||
    const val MAGISK_PATH  = "/data/adb/modules"
 | 
			
		||||
    const val MODULE_PATH  = "/data/adb/modules"
 | 
			
		||||
    const val TMPDIR = "/dev/tmp"
 | 
			
		||||
    const val MAGISK_LOG = "/cache/magisk.log"
 | 
			
		||||
 | 
			
		||||
@@ -28,6 +28,7 @@ object Const {
 | 
			
		||||
 | 
			
		||||
        fun atLeast_24_0() = Info.env.versionCode >= 24000 || isCanary()
 | 
			
		||||
        fun atLeast_25_0() = Info.env.versionCode >= 25000 || isCanary()
 | 
			
		||||
        fun atLeast_28_0() = Info.env.versionCode >= 28000 || isCanary()
 | 
			
		||||
        fun isCanary() = isCanary(Info.env.versionCode)
 | 
			
		||||
 | 
			
		||||
        fun isCanary(ver: Int) = ver > 0 && ver % 100 != 0
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,6 @@ object Info {
 | 
			
		||||
        private set
 | 
			
		||||
    private var crypto = ""
 | 
			
		||||
 | 
			
		||||
    var hasGMS = true
 | 
			
		||||
    val isEmulator =
 | 
			
		||||
        Build.DEVICE.contains("vsoc")
 | 
			
		||||
            || getProperty("ro.kernel.qemu", "0") == "1"
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ import androidx.core.content.getSystemService
 | 
			
		||||
import com.topjohnwu.magisk.core.base.BaseJobService
 | 
			
		||||
import com.topjohnwu.magisk.core.di.ServiceLocator
 | 
			
		||||
import com.topjohnwu.magisk.core.download.DownloadEngine
 | 
			
		||||
import com.topjohnwu.magisk.core.download.DownloadSession
 | 
			
		||||
import com.topjohnwu.magisk.core.download.Subject
 | 
			
		||||
import com.topjohnwu.magisk.view.Notifications
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
@@ -25,7 +26,7 @@ class JobService : BaseJobService() {
 | 
			
		||||
    @TargetApi(value = 34)
 | 
			
		||||
    inner class Session(
 | 
			
		||||
        private var params: JobParameters
 | 
			
		||||
    ) : DownloadEngine.Session {
 | 
			
		||||
    ) : DownloadSession {
 | 
			
		||||
 | 
			
		||||
        override val context get() = this@JobService
 | 
			
		||||
        val engine = DownloadEngine(this)
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.core
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import com.topjohnwu.magisk.core.base.BaseProvider
 | 
			
		||||
import com.topjohnwu.magisk.core.su.SuCallbackHandler
 | 
			
		||||
import com.topjohnwu.magisk.core.su.TestHandler
 | 
			
		||||
 | 
			
		||||
class Provider : BaseProvider() {
 | 
			
		||||
 | 
			
		||||
@@ -13,7 +12,7 @@ class Provider : BaseProvider() {
 | 
			
		||||
                SuCallbackHandler.run(context!!, method, extras)
 | 
			
		||||
                Bundle.EMPTY
 | 
			
		||||
            }
 | 
			
		||||
            else -> TestHandler.run(method)
 | 
			
		||||
            else -> Bundle.EMPTY
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,9 +7,10 @@ import androidx.core.app.ServiceCompat
 | 
			
		||||
import androidx.core.content.IntentCompat
 | 
			
		||||
import com.topjohnwu.magisk.core.base.BaseService
 | 
			
		||||
import com.topjohnwu.magisk.core.download.DownloadEngine
 | 
			
		||||
import com.topjohnwu.magisk.core.download.DownloadSession
 | 
			
		||||
import com.topjohnwu.magisk.core.download.Subject
 | 
			
		||||
 | 
			
		||||
class Service : BaseService(), DownloadEngine.Session {
 | 
			
		||||
class Service : BaseService(), DownloadSession {
 | 
			
		||||
 | 
			
		||||
    private var mEngine: DownloadEngine? = null
 | 
			
		||||
    override val context get() = this
 | 
			
		||||
 
 | 
			
		||||
@@ -7,9 +7,13 @@ import kotlinx.coroutines.withContext
 | 
			
		||||
 | 
			
		||||
open class MagiskDB {
 | 
			
		||||
 | 
			
		||||
    suspend fun <R> exec(
 | 
			
		||||
    class Literal(
 | 
			
		||||
        val str: String
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    suspend inline fun <R> exec(
 | 
			
		||||
        query: String,
 | 
			
		||||
        mapper: suspend (Map<String, String>) -> R
 | 
			
		||||
        crossinline mapper: (Map<String, String>) -> R
 | 
			
		||||
    ): List<R> {
 | 
			
		||||
        return withContext(Dispatchers.IO) {
 | 
			
		||||
            val out = Shell.cmd("magisk --sqlite '$query'").await().out
 | 
			
		||||
@@ -18,13 +22,15 @@ open class MagiskDB {
 | 
			
		||||
                    .map { it.split("=", limit = 2) }
 | 
			
		||||
                    .filter { it.size == 2 }
 | 
			
		||||
                    .associate { it[0] to it[1] }
 | 
			
		||||
                    .let { mapper(it) }
 | 
			
		||||
                    .let(mapper)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend inline fun exec(query: String) {
 | 
			
		||||
        exec(query) {}
 | 
			
		||||
    suspend fun exec(query: String) {
 | 
			
		||||
        withContext(Dispatchers.IO) {
 | 
			
		||||
            Shell.cmd("magisk --sqlite '$query'").await()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun Map<String, Any>.toQuery(): String {
 | 
			
		||||
@@ -33,6 +39,7 @@ open class MagiskDB {
 | 
			
		||||
            when (it) {
 | 
			
		||||
                is Boolean -> if (it) "1" else "0"
 | 
			
		||||
                is Number -> it.toString()
 | 
			
		||||
                is Literal -> it.str
 | 
			
		||||
                else -> "\"$it\""
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,24 +3,24 @@ package com.topjohnwu.magisk.core.data.magiskdb
 | 
			
		||||
import com.topjohnwu.magisk.core.AppContext
 | 
			
		||||
import com.topjohnwu.magisk.core.Const
 | 
			
		||||
import com.topjohnwu.magisk.core.model.su.SuPolicy
 | 
			
		||||
import java.util.concurrent.TimeUnit
 | 
			
		||||
 | 
			
		||||
private const val SELECT_QUERY = "SELECT (until - strftime(\"%s\", \"now\")) AS remain, *"
 | 
			
		||||
 | 
			
		||||
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"
 | 
			
		||||
            "(until > 0 AND until < strftime(\"%s\", \"now\")) OR until < 0"
 | 
			
		||||
        exec(query)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun delete(uid: Int) {
 | 
			
		||||
        val query = "DELETE FROM ${Table.POLICY} WHERE uid == $uid"
 | 
			
		||||
        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"
 | 
			
		||||
        val query = "$SELECT_QUERY FROM ${Table.POLICY} WHERE uid=$uid LIMIT 1"
 | 
			
		||||
        return exec(query, ::toPolicy).firstOrNull()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -35,7 +35,7 @@ class PolicyDao : MagiskDB() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun fetchAll(): List<SuPolicy> {
 | 
			
		||||
        val query = "SELECT * FROM ${Table.POLICY} WHERE uid/100000 == ${Const.USER_ID}"
 | 
			
		||||
        val query = "$SELECT_QUERY FROM ${Table.POLICY} WHERE uid/100000=${Const.USER_ID}"
 | 
			
		||||
        return exec(query, ::toPolicy).filterNotNull()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -43,8 +43,15 @@ class PolicyDao : MagiskDB() {
 | 
			
		||||
        val uid = map["uid"]?.toInt() ?: return null
 | 
			
		||||
        val policy = SuPolicy(uid)
 | 
			
		||||
 | 
			
		||||
        map["until"]?.toLong()?.let { until ->
 | 
			
		||||
            if (until <= 0) {
 | 
			
		||||
                policy.remain = until
 | 
			
		||||
            } else {
 | 
			
		||||
                map["remain"]?.toLong()?.let { policy.remain = it }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ package com.topjohnwu.magisk.core.data.magiskdb
 | 
			
		||||
class SettingsDao : MagiskDB() {
 | 
			
		||||
 | 
			
		||||
    suspend fun delete(key: String) {
 | 
			
		||||
        val query = "DELETE FROM ${Table.SETTINGS} WHERE key == \"$key\""
 | 
			
		||||
        val query = "DELETE FROM ${Table.SETTINGS} WHERE key=\"$key\""
 | 
			
		||||
        exec(query)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -14,7 +14,7 @@ class SettingsDao : MagiskDB() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun fetch(key: String, default: Int = -1): Int {
 | 
			
		||||
        val query = "SELECT value FROM ${Table.SETTINGS} WHERE key == \"$key\" LIMIT 1"
 | 
			
		||||
        val query = "SELECT value FROM ${Table.SETTINGS} WHERE key=\"$key\" LIMIT 1"
 | 
			
		||||
        return exec(query) { it["value"]?.toInt() }.firstOrNull() ?: default
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ package com.topjohnwu.magisk.core.data.magiskdb
 | 
			
		||||
class StringDao : MagiskDB() {
 | 
			
		||||
 | 
			
		||||
    suspend fun delete(key: String) {
 | 
			
		||||
        val query = "DELETE FROM ${Table.STRINGS} WHERE key == \"$key\""
 | 
			
		||||
        val query = "DELETE FROM ${Table.STRINGS} WHERE key=\"$key\""
 | 
			
		||||
        exec(query)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -14,7 +14,7 @@ class StringDao : MagiskDB() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun fetch(key: String, default: String = ""): String {
 | 
			
		||||
        val query = "SELECT value FROM ${Table.STRINGS} WHERE key == \"$key\" LIMIT 1"
 | 
			
		||||
        val query = "SELECT value FROM ${Table.STRINGS} WHERE key=\"$key\" LIMIT 1"
 | 
			
		||||
        return exec(query) { it["value"] }.firstOrNull() ?: default
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@ import com.squareup.moshi.Moshi
 | 
			
		||||
import com.topjohnwu.magisk.ProviderInstaller
 | 
			
		||||
import com.topjohnwu.magisk.core.BuildConfig
 | 
			
		||||
import com.topjohnwu.magisk.core.Config
 | 
			
		||||
import com.topjohnwu.magisk.core.Info
 | 
			
		||||
import com.topjohnwu.magisk.core.utils.LocaleSetting
 | 
			
		||||
import okhttp3.Cache
 | 
			
		||||
import okhttp3.ConnectionSpec
 | 
			
		||||
@@ -72,9 +71,7 @@ fun createOkHttpClient(context: Context): OkHttpClient {
 | 
			
		||||
        chain.proceed(request.build())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!ProviderInstaller.install(context)) {
 | 
			
		||||
        Info.hasGMS = false
 | 
			
		||||
    }
 | 
			
		||||
    ProviderInstaller.install(context)
 | 
			
		||||
 | 
			
		||||
    return builder.build()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -0,0 +1,122 @@
 | 
			
		||||
package com.topjohnwu.magisk.core.download
 | 
			
		||||
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import com.topjohnwu.magisk.StubApk
 | 
			
		||||
import com.topjohnwu.magisk.core.R
 | 
			
		||||
import com.topjohnwu.magisk.core.isRunningAsStub
 | 
			
		||||
import com.topjohnwu.magisk.core.ktx.cachedFile
 | 
			
		||||
import com.topjohnwu.magisk.core.ktx.copyAll
 | 
			
		||||
import com.topjohnwu.magisk.core.ktx.copyAndClose
 | 
			
		||||
import com.topjohnwu.magisk.core.ktx.withInOut
 | 
			
		||||
import com.topjohnwu.magisk.core.ktx.writeTo
 | 
			
		||||
import com.topjohnwu.magisk.core.tasks.AppMigration
 | 
			
		||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
 | 
			
		||||
import com.topjohnwu.magisk.utils.APKInstall
 | 
			
		||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
 | 
			
		||||
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream
 | 
			
		||||
import org.apache.commons.compress.archivers.zip.ZipFile
 | 
			
		||||
import java.io.IOException
 | 
			
		||||
import java.io.InputStream
 | 
			
		||||
import java.io.OutputStream
 | 
			
		||||
 | 
			
		||||
class DownloadProcessor(notifier: DownloadNotifier) : DownloadNotifier by notifier {
 | 
			
		||||
 | 
			
		||||
    suspend fun handle(stream: InputStream, subject: Subject) {
 | 
			
		||||
        when (subject) {
 | 
			
		||||
            is Subject.App -> handleApp(stream, subject)
 | 
			
		||||
            is Subject.Module -> handleModule(stream, subject.file)
 | 
			
		||||
            else -> stream.copyAndClose(subject.file.outputStream())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun handleApp(stream: InputStream, subject: Subject.App) {
 | 
			
		||||
        val external = subject.file.outputStream()
 | 
			
		||||
 | 
			
		||||
        if (isRunningAsStub) {
 | 
			
		||||
            val updateApk = StubApk.update(context)
 | 
			
		||||
            try {
 | 
			
		||||
                // Download full APK to stub update path
 | 
			
		||||
                stream.copyAndClose(TeeOutputStream(external, updateApk.outputStream()))
 | 
			
		||||
 | 
			
		||||
                // Also upgrade stub
 | 
			
		||||
                notifyUpdate(subject.notifyId) {
 | 
			
		||||
                    it.setProgress(0, 0, true)
 | 
			
		||||
                        .setContentTitle(context.getString(R.string.hide_app_title))
 | 
			
		||||
                        .setContentText("")
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Extract stub
 | 
			
		||||
                val apk = context.cachedFile("stub.apk")
 | 
			
		||||
                ZipFile.Builder().setFile(updateApk).get().use { zf ->
 | 
			
		||||
                    apk.delete()
 | 
			
		||||
                    zf.getInputStream(zf.getEntry("assets/stub.apk")).writeTo(apk)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Patch and install
 | 
			
		||||
                subject.intent = AppMigration.upgradeStub(context, apk)
 | 
			
		||||
                    ?: throw IOException("HideAPK patch error")
 | 
			
		||||
                apk.delete()
 | 
			
		||||
            } catch (e: Exception) {
 | 
			
		||||
                // If any error occurred, do not let stub load the new APK
 | 
			
		||||
                updateApk.delete()
 | 
			
		||||
                throw e
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            val session = APKInstall.startSession(context)
 | 
			
		||||
            stream.copyAndClose(TeeOutputStream(external, session.openStream(context)))
 | 
			
		||||
            subject.intent = session.waitIntent()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun handleModule(src: InputStream, file: Uri) {
 | 
			
		||||
        val tmp = context.cachedFile("module.zip")
 | 
			
		||||
        try {
 | 
			
		||||
            // First download the entire zip into cache so we can process it
 | 
			
		||||
            src.writeTo(tmp)
 | 
			
		||||
 | 
			
		||||
            val input = ZipFile.Builder().setFile(tmp).get()
 | 
			
		||||
            val output = ZipArchiveOutputStream(file.outputStream())
 | 
			
		||||
            withInOut(input, output) { zin, zout ->
 | 
			
		||||
                zout.putArchiveEntry(ZipArchiveEntry("META-INF/"))
 | 
			
		||||
                zout.closeArchiveEntry()
 | 
			
		||||
                zout.putArchiveEntry(ZipArchiveEntry("META-INF/com/"))
 | 
			
		||||
                zout.closeArchiveEntry()
 | 
			
		||||
                zout.putArchiveEntry(ZipArchiveEntry("META-INF/com/google/"))
 | 
			
		||||
                zout.closeArchiveEntry()
 | 
			
		||||
                zout.putArchiveEntry(ZipArchiveEntry("META-INF/com/google/android/"))
 | 
			
		||||
                zout.closeArchiveEntry()
 | 
			
		||||
 | 
			
		||||
                zout.putArchiveEntry(ZipArchiveEntry("META-INF/com/google/android/update-binary"))
 | 
			
		||||
                context.assets.open("module_installer.sh").use { it.copyAll(zout) }
 | 
			
		||||
                zout.closeArchiveEntry()
 | 
			
		||||
 | 
			
		||||
                zout.putArchiveEntry(ZipArchiveEntry("META-INF/com/google/android/updater-script"))
 | 
			
		||||
                zout.write("#MAGISK\n".toByteArray())
 | 
			
		||||
                zout.closeArchiveEntry()
 | 
			
		||||
 | 
			
		||||
                // Then simply copy all entries to output
 | 
			
		||||
                zin.copyRawEntries(zout) { entry -> !entry.name.startsWith("META-INF") }
 | 
			
		||||
            }
 | 
			
		||||
        } finally {
 | 
			
		||||
            tmp.delete()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class TeeOutputStream(
 | 
			
		||||
        private val o1: OutputStream,
 | 
			
		||||
        private val o2: OutputStream
 | 
			
		||||
    ) : OutputStream() {
 | 
			
		||||
        override fun write(b: Int) {
 | 
			
		||||
            o1.write(b)
 | 
			
		||||
            o2.write(b)
 | 
			
		||||
        }
 | 
			
		||||
        override fun write(b: ByteArray?, off: Int, len: Int) {
 | 
			
		||||
            o1.write(b, off, len)
 | 
			
		||||
            o2.write(b, off, len)
 | 
			
		||||
        }
 | 
			
		||||
        override fun close() {
 | 
			
		||||
            o1.close()
 | 
			
		||||
            o2.close()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,15 @@
 | 
			
		||||
package com.topjohnwu.magisk.core.download
 | 
			
		||||
 | 
			
		||||
import android.app.Notification
 | 
			
		||||
import android.content.Context
 | 
			
		||||
 | 
			
		||||
interface DownloadSession {
 | 
			
		||||
    val context: Context
 | 
			
		||||
    fun attachNotification(id: Int, builder: Notification.Builder)
 | 
			
		||||
    fun onDownloadComplete()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface DownloadNotifier {
 | 
			
		||||
    val context: Context
 | 
			
		||||
    fun notifyUpdate(id: Int, editor: (Notification.Builder) -> Unit = {})
 | 
			
		||||
}
 | 
			
		||||
@@ -2,7 +2,11 @@ package com.topjohnwu.magisk.core.ktx
 | 
			
		||||
 | 
			
		||||
import android.annotation.SuppressLint
 | 
			
		||||
import android.app.Activity
 | 
			
		||||
import android.content.*
 | 
			
		||||
import android.content.BroadcastReceiver
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.ContextWrapper
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.content.IntentFilter
 | 
			
		||||
import android.content.pm.ApplicationInfo
 | 
			
		||||
import android.content.pm.PackageInfo
 | 
			
		||||
import android.content.pm.PackageManager
 | 
			
		||||
@@ -23,7 +27,6 @@ import com.topjohnwu.magisk.core.utils.RootUtils
 | 
			
		||||
import com.topjohnwu.magisk.utils.APKInstall
 | 
			
		||||
import com.topjohnwu.superuser.internal.UiThreadHandler
 | 
			
		||||
import java.io.File
 | 
			
		||||
import kotlin.String
 | 
			
		||||
 | 
			
		||||
fun Context.getBitmap(id: Int): Bitmap {
 | 
			
		||||
    var drawable = getDrawable(id)!!
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ import kotlinx.coroutines.flow.flatMapMerge
 | 
			
		||||
import kotlinx.coroutines.flow.flow
 | 
			
		||||
import kotlinx.coroutines.isActive
 | 
			
		||||
import kotlinx.coroutines.withContext
 | 
			
		||||
import java.io.Closeable
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.io.IOException
 | 
			
		||||
import java.io.InputStream
 | 
			
		||||
@@ -17,24 +18,14 @@ import java.text.DateFormat
 | 
			
		||||
import java.text.SimpleDateFormat
 | 
			
		||||
import java.util.Collections
 | 
			
		||||
import java.util.Locale
 | 
			
		||||
import java.util.zip.ZipEntry
 | 
			
		||||
import java.util.zip.ZipInputStream
 | 
			
		||||
 | 
			
		||||
inline fun ZipInputStream.forEach(callback: (ZipEntry) -> Unit) {
 | 
			
		||||
    var entry: ZipEntry? = nextEntry
 | 
			
		||||
    while (entry != null) {
 | 
			
		||||
        callback(entry)
 | 
			
		||||
        entry = nextEntry
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun <In : InputStream, Out : OutputStream> withStreams(
 | 
			
		||||
    inStream: In,
 | 
			
		||||
    outStream: Out,
 | 
			
		||||
inline fun <In : Closeable, Out : Closeable> withInOut(
 | 
			
		||||
    input: In,
 | 
			
		||||
    output: Out,
 | 
			
		||||
    withBoth: (In, Out) -> Unit
 | 
			
		||||
) {
 | 
			
		||||
    inStream.use { reader ->
 | 
			
		||||
        outStream.use { writer ->
 | 
			
		||||
    input.use { reader ->
 | 
			
		||||
        output.use { writer ->
 | 
			
		||||
            withBoth(reader, writer)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -64,7 +55,7 @@ suspend inline fun InputStream.copyAndClose(
 | 
			
		||||
    out: OutputStream,
 | 
			
		||||
    bufferSize: Int = DEFAULT_BUFFER_SIZE,
 | 
			
		||||
    dispatcher: CoroutineDispatcher = Dispatchers.IO
 | 
			
		||||
) = withStreams(this, out) { i, o -> i.copyAll(o, bufferSize, dispatcher) }
 | 
			
		||||
) = withInOut(this, out) { i, o -> i.copyAll(o, bufferSize, dispatcher) }
 | 
			
		||||
 | 
			
		||||
@Throws(IOException::class)
 | 
			
		||||
suspend inline fun InputStream.writeTo(
 | 
			
		||||
 
 | 
			
		||||
@@ -5,14 +5,15 @@ import com.topjohnwu.magisk.core.Const
 | 
			
		||||
import com.topjohnwu.magisk.core.di.ServiceLocator
 | 
			
		||||
import com.topjohnwu.magisk.core.utils.RootUtils
 | 
			
		||||
import com.topjohnwu.superuser.Shell
 | 
			
		||||
import com.topjohnwu.superuser.nio.ExtendedFile
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.withContext
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
import java.io.IOException
 | 
			
		||||
import java.util.*
 | 
			
		||||
import java.util.Locale
 | 
			
		||||
 | 
			
		||||
data class LocalModule(
 | 
			
		||||
    private val path: String,
 | 
			
		||||
    private val base: ExtendedFile,
 | 
			
		||||
) : Module() {
 | 
			
		||||
    private val svc get() = ServiceLocator.networkService
 | 
			
		||||
 | 
			
		||||
@@ -24,20 +25,18 @@ data class LocalModule(
 | 
			
		||||
    var description: String = ""
 | 
			
		||||
    var updateInfo: OnlineModule? = null
 | 
			
		||||
    var outdated = false
 | 
			
		||||
 | 
			
		||||
    private var updateUrl: String = ""
 | 
			
		||||
    private val removeFile = RootUtils.fs.getFile(path, "remove")
 | 
			
		||||
    private val disableFile = RootUtils.fs.getFile(path, "disable")
 | 
			
		||||
    private val updateFile = RootUtils.fs.getFile(path, "update")
 | 
			
		||||
    private val riruFolder = RootUtils.fs.getFile(path, "riru")
 | 
			
		||||
    private val zygiskFolder = RootUtils.fs.getFile(path, "zygisk")
 | 
			
		||||
    private val unloaded = RootUtils.fs.getFile(zygiskFolder, "unloaded")
 | 
			
		||||
 | 
			
		||||
    val updated: Boolean get() = updateFile.exists()
 | 
			
		||||
    val isRiru: Boolean get() = (id == "riru-core") || riruFolder.exists()
 | 
			
		||||
    val isZygisk: Boolean get() = zygiskFolder.exists()
 | 
			
		||||
    val zygiskUnloaded: Boolean get() = unloaded.exists()
 | 
			
		||||
    val hasAction: Boolean;
 | 
			
		||||
    private val removeFile = base.getChildFile("remove")
 | 
			
		||||
    private val disableFile = base.getChildFile("disable")
 | 
			
		||||
    private val updateFile = base.getChildFile("update")
 | 
			
		||||
    val zygiskFolder = base.getChildFile("zygisk")
 | 
			
		||||
 | 
			
		||||
    val updated get() = updateFile.exists()
 | 
			
		||||
    val isRiru = (id == "riru-core") || base.getChildFile("riru").exists()
 | 
			
		||||
    val isZygisk = zygiskFolder.exists()
 | 
			
		||||
    val zygiskUnloaded = zygiskFolder.getChildFile("unloaded").exists()
 | 
			
		||||
    val hasAction = base.getChildFile("action.sh").exists()
 | 
			
		||||
 | 
			
		||||
    var enable: Boolean
 | 
			
		||||
        get() = !disableFile.exists()
 | 
			
		||||
@@ -90,19 +89,16 @@ data class LocalModule(
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        runCatching {
 | 
			
		||||
            parseProps(Shell.cmd("dos2unix < $path/module.prop").exec().out)
 | 
			
		||||
            parseProps(Shell.cmd("dos2unix < $base/module.prop").exec().out)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (id.isEmpty()) {
 | 
			
		||||
            val sep = path.lastIndexOf('/')
 | 
			
		||||
            id = path.substring(sep + 1)
 | 
			
		||||
            id = base.name
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (name.isEmpty()) {
 | 
			
		||||
            name = id
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        hasAction = RootUtils.fs.getFile(path, "action.sh").exists()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun fetch(): Boolean {
 | 
			
		||||
@@ -125,14 +121,14 @@ data class LocalModule(
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 | 
			
		||||
        fun loaded() = RootUtils.fs.getFile(Const.MAGISK_PATH).exists()
 | 
			
		||||
        fun loaded() = RootUtils.fs.getFile(Const.MODULE_PATH).exists()
 | 
			
		||||
 | 
			
		||||
        suspend fun installed() = withContext(Dispatchers.IO) {
 | 
			
		||||
            RootUtils.fs.getFile(Const.MAGISK_PATH)
 | 
			
		||||
            RootUtils.fs.getFile(Const.MODULE_PATH)
 | 
			
		||||
                .listFiles()
 | 
			
		||||
                .orEmpty()
 | 
			
		||||
                .filter { !it.isFile && !it.isHidden }
 | 
			
		||||
                .map { LocalModule("${Const.MAGISK_PATH}/${it.name}") }
 | 
			
		||||
                .map { LocalModule(it) }
 | 
			
		||||
                .sortedBy { it.name.lowercase(Locale.ROOT) }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user