mirror of
				https://github.com/topjohnwu/Magisk
				synced 2025-10-27 04:02:14 +01:00 
			
		
		
		
	Compare commits
	
		
			99 Commits
		
	
	
		
			manager-v4
			...
			manager-v5
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 4dc9419d2e | ||
|   | d2bcac813e | ||
|   | 080c37a7f6 | ||
|   | f9a3838db6 | ||
|   | 1e61db104b | ||
|   | 30a9c7718d | ||
|   | 34b052b5d3 | ||
|   | aaa12853ad | ||
|   | b0ab55b0bf | ||
|   | d2f8496f4e | ||
|   | 1a69b16d36 | ||
|   | b5e8673e62 | ||
|   | 264c6a50b6 | ||
|   | 493642eb38 | ||
|   | 28d42b9164 | ||
|   | 42f29062ca | ||
|   | c4377ed6c2 | ||
|   | 7d283ed65f | ||
|   | bf1f941e50 | ||
|   | 789fef34ba | ||
|   | 1daf5a611c | ||
|   | 6aed1db67e | ||
|   | cf68854770 | ||
|   | 711392c73b | ||
|   | 9573c32481 | ||
|   | a15f80f79d | ||
|   | 23e7475f06 | ||
|   | 1eb571b787 | ||
|   | dd3b716d85 | ||
|   | 28649c07e3 | ||
|   | 961e02be0d | ||
|   | a161491bfd | ||
|   | e0b4d1c1e4 | ||
|   | fd4aaab137 | ||
|   | 42d14d5ca2 | ||
|   | d3ff482c9b | ||
|   | f682368eeb | ||
|   | 4a5d033efb | ||
|   | 343161b195 | ||
|   | bc576a9659 | ||
|   | 19e407fcc4 | ||
|   | bc7327d004 | ||
|   | 666fa1c797 | ||
|   | 0eda4a7821 | ||
|   | 862058fd2b | ||
|   | 69e5bcd57d | ||
|   | efeddda328 | ||
|   | ff6938280e | ||
|   | 1e4425b30f | ||
|   | b5d1d8cdad | ||
|   | 029be5ccca | ||
|   | 29c2d785b5 | ||
|   | abda8cfa32 | ||
|   | 44e7d79d4c | ||
|   | 9a1dc8ee0e | ||
|   | 27879c3f01 | ||
|   | 29096eb5d7 | ||
|   | a573baea03 | ||
|   | 5af07c4531 | ||
|   | 44e36feb09 | ||
|   | 2a7d996881 | ||
|   | 738f943a68 | ||
|   | 47e62a5681 | ||
|   | 1ecbfd7590 | ||
|   | 67c139a04b | ||
|   | 31cc008249 | ||
|   | 9cb026439d | ||
|   | e6f10176c6 | ||
|   | 0917c79470 | ||
|   | 597baa986d | ||
|   | 75cc4b4843 | ||
|   | aac088d496 | ||
|   | a822e5bbc5 | ||
|   | c527249c21 | ||
|   | 9ef798f534 | ||
|   | e69b99f089 | ||
|   | 55b8079e86 | ||
|   | e272dbe9af | ||
|   | 962f8354ac | ||
|   | 20e4a960f7 | ||
|   | 82249cb50a | ||
|   | fad417e553 | ||
|   | 5ba692f50c | ||
|   | 907e01e524 | ||
|   | b8ed23efa7 | ||
|   | 2b3bbf7e67 | ||
|   | 464fe627a3 | ||
|   | 6a9e39c470 | ||
|   | 7fec9a3cc6 | ||
|   | 008f6ef462 | ||
|   | 2440c108ca | ||
|   | 430baad8a4 | ||
|   | 51132e74b4 | ||
|   | a4f33e106a | ||
|   | baba3190e0 | ||
|   | 47b13aa5ea | ||
|   | ae88d3054d | ||
|   | 411b600e14 | ||
|   | 0a0ad9a184 | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,6 @@ | ||||
| /local.properties | ||||
| .idea/ | ||||
| /build | ||||
| app/app-release.apk | ||||
| app/release | ||||
| *.hprof | ||||
| app/.externalNativeBuild/ | ||||
|   | ||||
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,4 +1,6 @@ | ||||
| # Magisk Manager | ||||
| The project should be built with Android Studio version 2.2.0+   | ||||
| I use Java 8 features, which requires Jack compiler and it's only available in 2.2.0+   | ||||
| Also, you need to install CMake and NDK to build the zipadjust library for zip preprocessing | ||||
| # Magisk Manager  | ||||
| You need to install CMake and NDK to build the zipadjust library for zip preprocessing | ||||
|  | ||||
| ## Pre-built Binaries | ||||
| Busybox (arm and x86) compiled by osm0sis (`libbusybox.so` under `app\src\main\jniLibs`)   | ||||
| Source and more info: [osm0sis' Odds and Ends](https://forum.xda-developers.com/showthread.php?t=2239421) | ||||
|   | ||||
| @@ -1,28 +1,25 @@ | ||||
| apply plugin: 'com.android.application' | ||||
|  | ||||
| android { | ||||
|     compileSdkVersion 25 | ||||
|     buildToolsVersion "25.0.2" | ||||
|     compileSdkVersion 26 | ||||
|     buildToolsVersion "26.0.0" | ||||
|  | ||||
|     defaultConfig { | ||||
|         applicationId "com.topjohnwu.magisk" | ||||
|         minSdkVersion 21 | ||||
|         targetSdkVersion 25 | ||||
|         versionCode 27 | ||||
|         versionName "4.3.0" | ||||
|         jackOptions { | ||||
|             enabled true | ||||
|             jackInProcess true | ||||
|         } | ||||
|         targetSdkVersion 26 | ||||
|         versionCode 45 | ||||
|         versionName "5.0.5" | ||||
|         ndk { | ||||
|             moduleName 'zipadjust' | ||||
|             abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' | ||||
|             abiFilters 'x86', 'armeabi-v7a' | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     buildTypes { | ||||
|         release { | ||||
|             minifyEnabled true | ||||
|             shrinkResources true | ||||
|             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | ||||
|         } | ||||
|     } | ||||
| @@ -31,34 +28,36 @@ android { | ||||
|         targetCompatibility JavaVersion.VERSION_1_8 | ||||
|     } | ||||
|     dexOptions { | ||||
|         preDexLibraries = true | ||||
|         preDexLibraries true | ||||
|         javaMaxHeapSize "2g" | ||||
|     } | ||||
|     externalNativeBuild { | ||||
|         cmake { | ||||
|             path 'src/main/jni/CMakeLists.txt' | ||||
|         } | ||||
|     } | ||||
|     lintOptions { | ||||
|         disable 'MissingTranslation' | ||||
|     } | ||||
| } | ||||
| repositories { | ||||
|     jcenter() | ||||
|     maven { url "https://jitpack.io" } | ||||
|     maven { url "https://maven.google.com" } | ||||
| } | ||||
|  | ||||
| dependencies { | ||||
|     compile fileTree(include: ['*.jar'], dir: 'libs') | ||||
|  | ||||
|     compile 'com.android.support:recyclerview-v7:25.3.1' | ||||
|     compile 'com.android.support:cardview-v7:25.3.1' | ||||
|     compile 'com.android.support:design:25.3.1' | ||||
|     compile 'com.android.support:support-v4:25.3.1' | ||||
|     compile 'com.jakewharton:butterknife:8.5.1' | ||||
|     compile 'com.github.clans:fab:1.6.4' | ||||
|     compile 'com.thoughtbot:expandablerecyclerview:1.4' | ||||
|     compile 'us.feras.mdv:markdownview:1.1.0' | ||||
|     compile 'com.madgag.spongycastle:core:1.54.0.0' | ||||
|     compile 'com.madgag.spongycastle:prov:1.54.0.0' | ||||
|     compile 'com.madgag.spongycastle:pkix:1.54.0.0' | ||||
|     compile 'com.madgag.spongycastle:pg:1.54.0.0' | ||||
|     compile 'com.google.android.gms:play-services-safetynet:9.0.1' | ||||
|     annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1' | ||||
|     implementation fileTree(include: ['*.jar'], dir: 'libs') | ||||
|     implementation 'com.android.support:recyclerview-v7:26.0.0-beta2' | ||||
|     implementation 'com.android.support:cardview-v7:26.0.0-beta2' | ||||
|     implementation 'com.android.support:design:26.0.0-beta2' | ||||
|     implementation 'com.android.support:support-v4:26.0.0-beta2' | ||||
|     implementation 'com.jakewharton:butterknife:8.7.0' | ||||
|     implementation 'com.thoughtbot:expandablerecyclerview:1.4' | ||||
|     implementation 'us.feras.mdv:markdownview:1.1.0' | ||||
|     implementation 'com.madgag.spongycastle:core:1.54.0.0' | ||||
|     implementation 'com.madgag.spongycastle:prov:1.54.0.0' | ||||
|     implementation 'com.madgag.spongycastle:pkix:1.54.0.0' | ||||
|     implementation 'com.google.android.gms:play-services-safetynet:9.0.1' | ||||
|     annotationProcessor 'com.jakewharton:butterknife-compiler:8.7.0' | ||||
| } | ||||
|   | ||||
							
								
								
									
										12
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							| @@ -16,10 +16,10 @@ | ||||
| #   public *; | ||||
| #} | ||||
|  | ||||
| -keep class android.support.v7.internal.** { *; } | ||||
| -keep interface android.support.v7.internal.** { *; } | ||||
| -keep class android.support.v7.** { *; } | ||||
| -keep interface android.support.v7.** { *; } | ||||
|  | ||||
| # SpongyCastle | ||||
| -keep class org.spongycastle.** {*;} | ||||
| -keep class org.spongycastle.** { *; } | ||||
| -dontwarn javax.naming.** | ||||
|  | ||||
| -dontwarn android.content.** | ||||
| -dontwarn android.animation.** | ||||
|  | ||||
|   | ||||
| @@ -1,14 +1,15 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <manifest package="com.topjohnwu.magisk" | ||||
|           xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|  | ||||
|           xmlns:tools="http://schemas.android.com/tools"> | ||||
| <manifest | ||||
|     package="com.topjohnwu.magisk" | ||||
|     xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools"> | ||||
|  | ||||
|     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> | ||||
|     <uses-permission android:name="android.permission.INTERNET" /> | ||||
|     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> | ||||
|     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> | ||||
|     <uses-permission android:name="android.permission.VIBRATE" /> | ||||
|     <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> | ||||
|  | ||||
|     <application | ||||
|         android:name=".MagiskManager" | ||||
| @@ -52,8 +53,7 @@ | ||||
|             android:taskAffinity="internal.superuser" | ||||
|             android:theme="@style/SuRequest" /> | ||||
|  | ||||
|         <receiver | ||||
|             android:name=".superuser.SuReceiver" /> | ||||
|         <receiver android:name=".superuser.SuReceiver" /> | ||||
|  | ||||
|         <receiver android:name=".receivers.BootReceiver"> | ||||
|             <intent-filter> | ||||
| @@ -61,7 +61,17 @@ | ||||
|             </intent-filter> | ||||
|         </receiver> | ||||
|  | ||||
|         <service android:name=".services.BootupIntentService" /> | ||||
|         <receiver android:name=".receivers.PackageReceiver"> | ||||
|             <intent-filter> | ||||
|                 <action android:name="android.intent.action.PACKAGE_REPLACED" /> | ||||
|                 <action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" /> | ||||
|                 <data android:scheme="package" /> | ||||
|             </intent-filter> | ||||
|         </receiver> | ||||
|  | ||||
|         <receiver android:name=".receivers.ManagerUpdate" /> | ||||
|  | ||||
|         <service android:name=".services.OnBootIntentService" /> | ||||
|  | ||||
|         <service | ||||
|             android:name=".services.UpdateCheckService" | ||||
|   | ||||
| @@ -1,133 +1,137 @@ | ||||
| #!/system/bin/sh | ||||
| ########################################################################################## | ||||
| # | ||||
| # Magisk Uninstaller | ||||
| # by topjohnwu | ||||
| #  | ||||
| # This script can be placed in /cache/magisk_uninstaller.sh | ||||
| # The Magisk main binary will pick up the script, and uninstall itself, following a reboot | ||||
| # This script can also be used in flashable zip with the uninstaller_loader.sh | ||||
| #  | ||||
| # This script will try to do restoration with the following: | ||||
| # 1-1. Find and restore the original stock boot image dump (OTA proof) | ||||
| # 1-2. If 1-1 fails, restore ramdisk from the internal backup | ||||
| #      (ramdisk fully restored, not OTA friendly) | ||||
| # 1-3. If 1-2 fails, it will remove added files in ramdisk, however modified files | ||||
| #      are remained modified, because we have no backups. By doing so, Magisk will  | ||||
| #      not be started at boot, but this isn't actually 100% cleaned up | ||||
| # 2. Remove all Magisk related files | ||||
| #    (The list is LARGE, most likely due to bad decision in early versions | ||||
| #    the latest versions has much less bloat to cleanup) | ||||
| # | ||||
| ########################################################################################## | ||||
|  | ||||
| # Call ui_print_wrap if exists, or else simply use echo | ||||
| # Useful when wrapped in flashable zip | ||||
| ui_print_wrap() { | ||||
|   type ui_print >/dev/null 2>&1 && ui_print "$1" || echo "$1" | ||||
| } | ||||
|  | ||||
| # Call abort if exists, or else show error message and exit | ||||
| # Essential when wrapped in flashable zip | ||||
| abort_wrap() { | ||||
|   type abort >/dev/null 2>&1 | ||||
|   if [ $? -ne 0 ]; then | ||||
|     ui_print_wrap "$1" | ||||
|     exit 1 | ||||
|   else | ||||
|     abort "$1" | ||||
|   fi | ||||
| } | ||||
|  | ||||
| if [ ! -d $MAGISKBIN -o ! -f $MAGISKBIN/magiskboot -o ! -f $MAGISKBIN/util_functions.sh ]; then | ||||
|   ui_print_wrap "! Cannot find $MAGISKBIN" | ||||
|   exit 1 | ||||
| fi | ||||
|  | ||||
| [ -z $BOOTMODE ] && BOOTMODE=false | ||||
|  | ||||
| # This path should work in any cases | ||||
| TMPDIR=/dev/tmp | ||||
|  | ||||
| BOOTTMP=$TMPDIR/boottmp | ||||
| MAGISKBIN=/data/magisk | ||||
| CHROMEDIR=$MAGISKBIN/chromeos | ||||
|  | ||||
| SYSTEMLIB=/system/lib | ||||
| [ -d /system/lib64 ] && SYSTEMLIB=/system/lib64 | ||||
|  | ||||
| # Default permissions | ||||
| umask 022 | ||||
|  | ||||
| ui_print_wrapper() { | ||||
|   type ui_print >/dev/null && ui_print "$1" || echo "$1" | ||||
| } | ||||
|  | ||||
| grep_prop() { | ||||
|   REGEX="s/^$1=//p" | ||||
|   shift | ||||
|   FILES=$@ | ||||
|   if [ -z "$FILES" ]; then | ||||
|     FILES='/system/build.prop' | ||||
|   fi | ||||
|   cat $FILES 2>/dev/null | sed -n "$REGEX" | head -n 1 | ||||
| } | ||||
|  | ||||
| find_boot_image() { | ||||
|   if [ -z "$BOOTIMAGE" ]; then | ||||
|     for PARTITION in kern-a KERN-A android_boot ANDROID_BOOT kernel KERNEL boot BOOT lnx LNX; do | ||||
|       BOOTIMAGE=`readlink /dev/block/by-name/$PARTITION || readlink /dev/block/platform/*/by-name/$PARTITION || readlink /dev/block/platform/*/*/by-name/$PARTITION` | ||||
|       if [ ! -z "$BOOTIMAGE" ]; then break; fi | ||||
|     done | ||||
|   fi | ||||
|   if [ -z "$BOOTIMAGE" ]; then | ||||
|     FSTAB="/etc/recovery.fstab" | ||||
|     [ ! -f "$FSTAB" ] && FSTAB="/etc/recovery.fstab.bak" | ||||
|     [ -f "$FSTAB" ] && BOOTIMAGE=`grep -E '\b/boot\b' "$FSTAB" | grep -oE '/dev/[a-zA-Z0-9_./-]*'` | ||||
|   fi | ||||
| } | ||||
|  | ||||
| # Environments | ||||
| # Set permissions | ||||
| chmod -R 755 $CHROMEDIR/futility $MAGISKBIN 2>/dev/null | ||||
| # Temporary busybox for installation | ||||
| mkdir -p $TMPDIR/busybox | ||||
| $MAGISKBIN/busybox --install -s $TMPDIR/busybox | ||||
| rm -f $TMPDIR/busybox/su $TMPDIR/busybox/sh $TMPDIR/busybox/reboot | ||||
| PATH=$TMPDIR/busybox:$PATH | ||||
| # Load utility functions | ||||
| . $MAGISKBIN/util_functions.sh | ||||
|  | ||||
| # Find the boot image | ||||
| find_boot_image | ||||
| if [ -z "$BOOTIMAGE" ]; then | ||||
|   ui_print_wrapper "! Unable to detect boot image" | ||||
|   exit 1 | ||||
| fi | ||||
| [ -z $BOOTIMAGE ] && abort "! Unable to detect boot image" | ||||
|  | ||||
| ui_print_wrapper "- Found Boot Image: $BOOTIMAGE" | ||||
| ui_print_wrap "- Found Boot Image: $BOOTIMAGE" | ||||
|  | ||||
| rm -rf $BOOTTMP 2>/dev/null | ||||
| mkdir -p $BOOTTMP | ||||
| cd $BOOTTMP | ||||
| cd $MAGISKBIN | ||||
|  | ||||
| ui_print_wrapper "- Unpacking boot image" | ||||
| LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --unpack $BOOTIMAGE | ||||
| if [ $? -ne 0 ]; then | ||||
|   ui_print_wrapper "! Unable to unpack boot image" | ||||
|   exit 1 | ||||
| fi | ||||
| ui_print_wrap "- Unpacking boot image" | ||||
| ./magiskboot --unpack "$BOOTIMAGE" | ||||
| [ $? -ne 0 ] && abort_wrap "! Unable to unpack boot image" | ||||
|  | ||||
| # Update our previous backup to new format if exists | ||||
| if [ -f /data/stock_boot.img ]; then | ||||
|   SHA1=`LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --sha1 /data/stock_boot.img | tail -n 1` | ||||
|   SHA1=`./magiskboot --sha1 /data/stock_boot.img | tail -n 1` | ||||
|   STOCKDUMP=/data/stock_boot_${SHA1}.img | ||||
|   mv /data/stock_boot.img $STOCKDUMP | ||||
|   LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --compress $STOCKDUMP | ||||
|   ./magiskboot --compress $STOCKDUMP | ||||
| fi | ||||
|  | ||||
| # Detect boot image state | ||||
| LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --cpio-test ramdisk.cpio | ||||
| ./magiskboot --cpio-test ramdisk.cpio | ||||
| case $? in | ||||
|   0 ) | ||||
|     ui_print_wrapper "! Magisk is not installed!" | ||||
|     ui_print_wrapper "! Nothing to uninstall" | ||||
|   0 )  # Stock boot | ||||
|     ui_print_wrap "- Stock boot image detected!" | ||||
|     ui_print_wrap "! Magisk is not installed!" | ||||
|     exit | ||||
|     ;; | ||||
|   1 ) | ||||
|   1 )  # Magisk patched | ||||
|     ui_print_wrap "- Magisk patched image detected!" | ||||
|     # Find SHA1 of stock boot image | ||||
|     if [ -z $SHA1 ]; then | ||||
|       LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --cpio-extract ramdisk.cpio init.magisk.rc init.magisk.rc | ||||
|       SHA1=`grep_prop "# STOCKSHA1" init.magisk.rc` | ||||
|       [ ! -z $SHA1 ] && STOCKDUMP=/data/stock_boot_${SHA1}.img | ||||
|       rm -f init.magisk.rc | ||||
|       ./magiskboot --cpio-extract ramdisk.cpio init.magisk.rc init.magisk.rc.old | ||||
|       SHA1=`grep_prop "# STOCKSHA1" init.magisk.rc.old` | ||||
|       rm -f init.magisk.rc.old | ||||
|     fi | ||||
|     [ ! -z $SHA1 ] && STOCKDUMP=/data/stock_boot_${SHA1}.img | ||||
|     if [ -f ${STOCKDUMP}.gz ]; then | ||||
|       ui_print_wrapper "- Boot image backup found!" | ||||
|       LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --decompress ${STOCKDUMP}.gz stock_boot.img | ||||
|       ui_print_wrap "- Boot image backup found!" | ||||
|       ./magiskboot --decompress ${STOCKDUMP}.gz stock_boot.img | ||||
|     else | ||||
|       ui_print_wrapper "! Boot image backup unavailable" | ||||
|       ui_print_wrapper "- Restoring ramdisk with backup" | ||||
|       LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --cpio-restore ramdisk.cpio | ||||
|       LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --repack $BOOTIMAGE stock_boot.img | ||||
|       ui_print_wrap "! Boot image backup unavailable" | ||||
|       ui_print_wrap "- Restoring ramdisk with backup" | ||||
|       ./magiskboot --cpio-restore ramdisk.cpio | ||||
|       ./magiskboot --repack $BOOTIMAGE stock_boot.img | ||||
|     fi | ||||
|     ;; | ||||
|   2 ) | ||||
|     ui_print_wrapper "- SuperSU patched image detected" | ||||
|     LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --cpio-restore ramdisk.cpio | ||||
|     LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --repack $BOOTIMAGE stock_boot.img | ||||
|   2 ) # Other patched | ||||
|     ui_print_wrap "! Boot image patched by other programs!" | ||||
|     abort_wrap "! Cannot uninstall with this uninstaller" | ||||
|     ;; | ||||
| esac | ||||
|  | ||||
| # Sign chromeos boot | ||||
| if [ -f chromeos ]; then | ||||
|   echo > config | ||||
|   echo > bootloader | ||||
|   LD_LIBRARY_PATH=$SYSTEMLIB $CHROMEDIR/futility vbutil_kernel --pack stock_boot.img.signed --keyblock $CHROMEDIR/kernel.keyblock --signprivate $CHROMEDIR/kernel_data_key.vbprivk --version 1 --vmlinuz stock_boot.img --config config --arch arm --bootloader bootloader --flags 0x1 | ||||
|   rm -f stock_boot.img | ||||
|   echo > empty | ||||
|  | ||||
|   LD_LIBRARY_PATH=$SYSTEMLIB $CHROMEDIR/futility vbutil_kernel --pack stock_boot.img.signed \ | ||||
|   --keyblock $CHROMEDIR/kernel.keyblock --signprivate $CHROMEDIR/kernel_data_key.vbprivk \ | ||||
|   --version 1 --vmlinuz stock_boot.img --config empty --arch arm --bootloader empty --flags 0x1 | ||||
|  | ||||
|   rm -f empty stock_boot.img | ||||
|   mv stock_boot.img.signed stock_boot.img | ||||
| fi | ||||
|  | ||||
| ui_print_wrapper "- Flashing stock/reverted image" | ||||
| [ ! -L "$BOOTIMAGE" ] && dd if=/dev/zero of=$BOOTIMAGE bs=4096 2>/dev/null | ||||
| dd if=stock_boot.img of=$BOOTIMAGE bs=4096 | ||||
| ui_print_wrap "- Flashing stock/reverted image" | ||||
| if [ -L "$BOOTIMAGE" ]; then | ||||
|   dd if=stock_boot.img of="$BOOTIMAGE" bs=4096 | ||||
| else | ||||
|   cat stock_boot.img /dev/zero | dd of="$BOOTIMAGE" bs=4096 >/dev/null 2>&1 | ||||
| fi | ||||
| rm -f stock_boot.img | ||||
|  | ||||
| ui_print_wrapper "- Removing Magisk files" | ||||
| ui_print_wrap "- Removing Magisk files" | ||||
| rm -rf  /cache/magisk.log /cache/last_magisk.log /cache/magiskhide.log /cache/.disable_magisk \ | ||||
|         /cache/magisk /cache/magisk_merge /cache/magisk_mount  /cache/unblock /cache/magisk_uninstaller.sh \ | ||||
|         /data/Magisk.apk /data/magisk.apk /data/magisk.img /data/magisk_merge.img \ | ||||
|         /data/Magisk.apk /data/magisk.apk /data/magisk.img /data/magisk_merge.img /data/magisk_debug.log \ | ||||
|         /data/busybox /data/magisk /data/custom_ramdisk_patch.sh 2>/dev/null | ||||
|  | ||||
| $BOOTMODE && reboot | ||||
|   | ||||
							
								
								
									
										192
									
								
								app/src/main/assets/util_functions.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								app/src/main/assets/util_functions.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,192 @@ | ||||
| ########################################################################################## | ||||
| #  | ||||
| # Magisk General Utility Functions | ||||
| # by topjohnwu | ||||
| #  | ||||
| # Used in flash_script.sh, addon.d.sh, magisk module installers, and uninstaller | ||||
| #  | ||||
| ########################################################################################## | ||||
|  | ||||
| get_outfd() { | ||||
|   readlink /proc/$$/fd/$OUTFD 2>/dev/null | grep /tmp >/dev/null | ||||
|   if [ "$?" -eq "0" ]; then | ||||
|     OUTFD=0 | ||||
|  | ||||
|     for FD in `ls /proc/$$/fd`; do | ||||
|       readlink /proc/$$/fd/$FD 2>/dev/null | grep pipe >/dev/null | ||||
|       if [ "$?" -eq "0" ]; then | ||||
|         ps | grep " 3 $FD " | grep -v grep >/dev/null | ||||
|         if [ "$?" -eq "0" ]; then | ||||
|           OUTFD=$FD | ||||
|           break | ||||
|         fi | ||||
|       fi | ||||
|     done | ||||
|   fi | ||||
| } | ||||
|  | ||||
| ui_print() { | ||||
|   if $BOOTMODE; then | ||||
|     echo "$1" | ||||
|   else  | ||||
|     echo -n -e "ui_print $1\n" >> /proc/self/fd/$OUTFD | ||||
|     echo -n -e "ui_print\n" >> /proc/self/fd/$OUTFD | ||||
|   fi | ||||
| } | ||||
|  | ||||
| getvar() { | ||||
|   local VARNAME=$1 | ||||
|   local VALUE=$(eval echo \$"$VARNAME"); | ||||
|   for FILE in /dev/.magisk /data/.magisk /cache/.magisk /system/.magisk; do | ||||
|     if [ -z "$VALUE" ]; then | ||||
|       LINE=$(cat $FILE 2>/dev/null | grep "$VARNAME=") | ||||
|       if [ ! -z "$LINE" ]; then | ||||
|         VALUE=${LINE#*=} | ||||
|       fi | ||||
|     fi | ||||
|   done | ||||
|   eval $VARNAME=\$VALUE | ||||
| } | ||||
|  | ||||
| find_boot_image() { | ||||
|   if [ -z "$BOOTIMAGE" ]; then | ||||
|     for BLOCK in boot_a BOOT_A kern-a KERN-A android_boot ANDROID_BOOT kernel KERNEL boot BOOT lnx LNX; do | ||||
|       BOOTIMAGE=`ls /dev/block/by-name/$BLOCK || ls /dev/block/platform/*/by-name/$BLOCK || ls /dev/block/platform/*/*/by-name/$BLOCK` 2>/dev/null | ||||
|       [ ! -z $BOOTIMAGE ] && break | ||||
|     done | ||||
|   fi | ||||
|   # Recovery fallback | ||||
|   if [ -z "$BOOTIMAGE" ]; then | ||||
|     for FSTAB in /etc/*fstab*; do | ||||
|       BOOTIMAGE=`grep -E '\b/boot\b' $FSTAB | grep -v "#" | grep -oE '/dev/[a-zA-Z0-9_./-]*'` | ||||
|       [ ! -z $BOOTIMAGE ] && break | ||||
|     done | ||||
|   fi | ||||
|   [ -L "$BOOTIMAGE" ] && BOOTIMAGE=`readlink $BOOTIMAGE` | ||||
| } | ||||
|  | ||||
| is_mounted() { | ||||
|   if [ ! -z "$2" ]; then | ||||
|     cat /proc/mounts | grep $1 | grep $2, >/dev/null | ||||
|   else | ||||
|     cat /proc/mounts | grep $1 >/dev/null | ||||
|   fi | ||||
|   return $? | ||||
| } | ||||
|  | ||||
| grep_prop() { | ||||
|   REGEX="s/^$1=//p" | ||||
|   shift | ||||
|   FILES=$@ | ||||
|   if [ -z "$FILES" ]; then | ||||
|     FILES='/system/build.prop' | ||||
|   fi | ||||
|   cat $FILES 2>/dev/null | sed -n "$REGEX" | head -n 1 | ||||
| } | ||||
|  | ||||
| remove_system_su() { | ||||
|   if [ -f /system/bin/su -o -f /system/xbin/su ] && [ ! -f /su/bin/su ]; then | ||||
|     ui_print "! System installed root detected, mount rw :(" | ||||
|     mount -o rw,remount /system | ||||
|     # SuperSU | ||||
|     if [ -e /system/bin/.ext/.su ]; then | ||||
|       mv -f /system/bin/app_process32_original /system/bin/app_process32 2>/dev/null | ||||
|       mv -f /system/bin/app_process64_original /system/bin/app_process64 2>/dev/null | ||||
|       mv -f /system/bin/install-recovery_original.sh /system/bin/install-recovery.sh 2>/dev/null | ||||
|       cd /system/bin | ||||
|       if [ -e app_process64 ]; then | ||||
|         ln -sf app_process64 app_process | ||||
|       else | ||||
|         ln -sf app_process32 app_process | ||||
|       fi | ||||
|     fi | ||||
|     rm -rf /system/.pin /system/bin/.ext /system/etc/.installed_su_daemon /system/etc/.has_su_daemon \ | ||||
|     /system/xbin/daemonsu /system/xbin/su /system/xbin/sugote /system/xbin/sugote-mksh /system/xbin/supolicy \ | ||||
|     /system/bin/app_process_init /system/bin/su /cache/su /system/lib/libsupol.so /system/lib64/libsupol.so \ | ||||
|     /system/su.d /system/etc/install-recovery.sh /system/etc/init.d/99SuperSUDaemon /cache/install-recovery.sh \ | ||||
|     /system/.supersu /cache/.supersu /data/.supersu \ | ||||
|     /system/app/Superuser.apk /system/app/SuperSU /cache/Superuser.apk  2>/dev/null | ||||
|   fi | ||||
| } | ||||
|  | ||||
| api_level_arch_detect() { | ||||
|   API=`grep_prop ro.build.version.sdk` | ||||
|   ABI=`grep_prop ro.product.cpu.abi | cut -c-3` | ||||
|   ABI2=`grep_prop ro.product.cpu.abi2 | cut -c-3` | ||||
|   ABILONG=`grep_prop ro.product.cpu.abi` | ||||
|  | ||||
|   ARCH=arm | ||||
|   IS64BIT=false | ||||
|   if [ "$ABI" = "x86" ]; then ARCH=x86; fi; | ||||
|   if [ "$ABI2" = "x86" ]; then ARCH=x86; fi; | ||||
|   if [ "$ABILONG" = "arm64-v8a" ]; then ARCH=arm64; IS64BIT=true; fi; | ||||
|   if [ "$ABILONG" = "x86_64" ]; then ARCH=x64; IS64BIT=true; fi; | ||||
| } | ||||
|  | ||||
| recovery_actions() { | ||||
|   # TWRP bug fix | ||||
|   mount -o bind /dev/urandom /dev/random | ||||
|   # Temporarily block out all custom recovery binaries/libs | ||||
|   mv /sbin /sbin_tmp | ||||
|   # Add all possible library paths | ||||
|   OLD_LD_PATH=$LD_LIBRARY_PATH | ||||
|   $IS64BIT && export LD_LIBRARY_PATH=/system/lib64:/system/vendor/lib64 || export LD_LIBRARY_PATH=/system/lib:/system/vendor/lib | ||||
| } | ||||
|  | ||||
| recovery_cleanup() { | ||||
|   mv /sbin_tmp /sbin | ||||
|   # Clear LD_LIBRARY_PATH | ||||
|   export LD_LIBRARY_PATH=$OLD_LD_PATH | ||||
|   ui_print "- Unmounting partitions" | ||||
|   umount -l /system | ||||
|   umount -l /vendor 2>/dev/null | ||||
|   umount -l /dev/random | ||||
| } | ||||
|  | ||||
| abort() { | ||||
|   ui_print "$1" | ||||
|   mv /sbin_tmp /sbin 2>/dev/null | ||||
|   exit 1 | ||||
| } | ||||
|  | ||||
| set_perm() { | ||||
|   chown $2:$3 $1 || exit 1 | ||||
|   chmod $4 $1 || exit 1 | ||||
|   if [ ! -z $5 ]; then | ||||
|     chcon $5 $1 2>/dev/null | ||||
|   else | ||||
|     chcon 'u:object_r:system_file:s0' $1 2>/dev/null | ||||
|   fi | ||||
| } | ||||
|  | ||||
| set_perm_recursive() { | ||||
|   find $1 -type d 2>/dev/null | while read dir; do | ||||
|     set_perm $dir $2 $3 $4 $6 | ||||
|   done | ||||
|   find $1 -type f 2>/dev/null | while read file; do | ||||
|     set_perm $file $2 $3 $5 $6 | ||||
|   done | ||||
| } | ||||
|  | ||||
| mktouch() { | ||||
|   mkdir -p ${1%/*} | ||||
|   if [ -z "$2" ]; then | ||||
|     touch $1 | ||||
|   else | ||||
|     echo $2 > $1 | ||||
|   fi | ||||
|   chmod 644 $1 | ||||
| } | ||||
|  | ||||
| request_size_check() { | ||||
|   reqSizeM=`du -s $1 | cut -f1` | ||||
|   reqSizeM=$((reqSizeM / 1024 + 1)) | ||||
| } | ||||
|  | ||||
| image_size_check() { | ||||
|   SIZE="`$MAGISKBIN/magisk --imgsize $IMG`" | ||||
|   curUsedM=`echo "$SIZE" | cut -d" " -f1` | ||||
|   curSizeM=`echo "$SIZE" | cut -d" " -f2` | ||||
|   curFreeM=$((curSizeM - curUsedM)) | ||||
| } | ||||
|  | ||||
| @@ -3,7 +3,6 @@ package com.topjohnwu.magisk; | ||||
| import android.content.Intent; | ||||
| import android.net.Uri; | ||||
| import android.os.Bundle; | ||||
| import android.preference.PreferenceManager; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.v7.app.ActionBar; | ||||
| import android.support.v7.app.AlertDialog; | ||||
| @@ -13,13 +12,11 @@ import android.text.Spanned; | ||||
| import android.text.TextUtils; | ||||
| import android.text.method.LinkMovementMethod; | ||||
| import android.view.View; | ||||
| import android.view.WindowManager; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import com.topjohnwu.magisk.components.AboutCardRow; | ||||
| import com.topjohnwu.magisk.components.Activity; | ||||
| import com.topjohnwu.magisk.components.AlertDialogBuilder; | ||||
| import com.topjohnwu.magisk.utils.Logger; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| @@ -29,7 +26,7 @@ import butterknife.ButterKnife; | ||||
|  | ||||
| public class AboutActivity extends Activity { | ||||
|  | ||||
|     private static final String DONATION_URL = "http://topjohnwu.github.io/donate"; | ||||
|     private static final String DONATION_URL = "https://www.paypal.me/topjohnwu"; | ||||
|     private static final String XDA_THREAD = "http://forum.xda-developers.com/showthread.php?t=3432382"; | ||||
|     private static final String SOURCE_CODE_URL = "https://github.com/topjohnwu/MagiskManager"; | ||||
|  | ||||
| @@ -45,10 +42,8 @@ public class AboutActivity extends Activity { | ||||
|     @Override | ||||
|     protected void onCreate(@Nullable Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         String theme = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("theme", ""); | ||||
|         Logger.dev("AboutActivity: Theme is " + theme); | ||||
|         if (getApplicationContext().isDarkTheme) { | ||||
|             setTheme(R.style.AppTheme_Dark); | ||||
|             setTheme(R.style.AppTheme_Transparent_Dark); | ||||
|         } | ||||
|         setContentView(R.layout.activity_about); | ||||
|         ButterKnife.bind(this); | ||||
| @@ -135,18 +130,4 @@ public class AboutActivity extends Activity { | ||||
|         setFloating(); | ||||
|     } | ||||
|  | ||||
|     public void setFloating() { | ||||
|         boolean isTablet = getResources().getBoolean(R.bool.isTablet); | ||||
|         if (isTablet) { | ||||
|             WindowManager.LayoutParams params = getWindow().getAttributes(); | ||||
|             params.height = getResources().getDimensionPixelSize(R.dimen.floating_height); | ||||
|             params.width = getResources().getDimensionPixelSize(R.dimen.floating_width); | ||||
|             params.alpha = 1.0f; | ||||
|             params.dimAmount = 0.6f; | ||||
|             params.flags |= 2; | ||||
|             getWindow().setAttributes(params); | ||||
|             setFinishOnTouchOutside(true); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,228 +0,0 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.app.ProgressDialog; | ||||
| import android.content.Intent; | ||||
| import android.net.Uri; | ||||
| import android.os.Bundle; | ||||
| import android.os.CountDownTimer; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.design.widget.Snackbar; | ||||
| import android.support.v7.widget.CardView; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.ArrayAdapter; | ||||
| import android.widget.Button; | ||||
| import android.widget.CheckBox; | ||||
| import android.widget.Spinner; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import com.topjohnwu.magisk.asyncs.ProcessMagiskZip; | ||||
| import com.topjohnwu.magisk.components.AlertDialogBuilder; | ||||
| import com.topjohnwu.magisk.components.Fragment; | ||||
| import com.topjohnwu.magisk.components.SnackbarMaker; | ||||
| import com.topjohnwu.magisk.receivers.DownloadReceiver; | ||||
| import com.topjohnwu.magisk.utils.CallbackEvent; | ||||
| import com.topjohnwu.magisk.utils.Shell; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.FileOutputStream; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.Locale; | ||||
|  | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
| import butterknife.OnClick; | ||||
| import butterknife.Unbinder; | ||||
|  | ||||
| public class InstallFragment extends Fragment implements CallbackEvent.Listener<Void> { | ||||
|  | ||||
|  | ||||
|     private static final String UNINSTALLER = "magisk_uninstaller.sh"; | ||||
|  | ||||
|     @BindView(R.id.current_version_title) TextView currentVersionTitle; | ||||
|     @BindView(R.id.install_title) TextView installTitle; | ||||
|     @BindView(R.id.block_spinner) Spinner spinner; | ||||
|     @BindView(R.id.detect_bootimage) Button detectButton; | ||||
|     @BindView(R.id.install_button) CardView installButton; | ||||
|     @BindView(R.id.install_text) TextView installText; | ||||
|     @BindView(R.id.uninstall_button) CardView uninstallButton; | ||||
|     @BindView(R.id.keep_force_enc) CheckBox keepEncChkbox; | ||||
|     @BindView(R.id.keep_verity) CheckBox keepVerityChkbox; | ||||
|  | ||||
|     @OnClick(R.id.detect_bootimage) | ||||
|     public void toAutoDetect() { | ||||
|         if (magiskManager.bootBlock != null) { | ||||
|             spinner.setSelection(0); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @OnClick(R.id.install_button) | ||||
|     public void install() { | ||||
|         String bootImage = null; | ||||
|         if (magiskManager.blockList != null) { | ||||
|             int idx = spinner.getSelectedItemPosition(); | ||||
|             if (magiskManager.bootBlock != null) { | ||||
|                 if (idx > 0) { | ||||
|                     bootImage = magiskManager.blockList.get(idx - 1); | ||||
|                 } | ||||
|             } else { | ||||
|                 if (idx > 0)  { | ||||
|                     bootImage = magiskManager.blockList.get(idx - 1); | ||||
|                 } else { | ||||
|                     SnackbarMaker.make(getActivity(), R.string.manual_boot_image, Snackbar.LENGTH_LONG); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         final String finalBootImage = bootImage; | ||||
|         String filename = "Magisk-v" + magiskManager.remoteMagiskVersion + ".zip"; | ||||
|         new AlertDialogBuilder(getActivity()) | ||||
|                 .setTitle(getString(R.string.repo_install_title, getString(R.string.magisk))) | ||||
|                 .setMessage(getString(R.string.repo_install_msg, filename)) | ||||
|                 .setCancelable(true) | ||||
|                 .setPositiveButton(Shell.rootAccess() ? R.string.install : R.string.download, | ||||
|                     (dialogInterface, i) -> Utils.dlAndReceive( | ||||
|                         getActivity(), | ||||
|                         new DownloadReceiver() { | ||||
|                             private String boot = finalBootImage; | ||||
|                             private boolean enc = keepEncChkbox.isChecked(); | ||||
|                             private boolean verity = keepVerityChkbox.isChecked(); | ||||
|  | ||||
|                             @Override | ||||
|                             public void onDownloadDone(Uri uri) { | ||||
|                                 new ProcessMagiskZip(getActivity(), uri, boot, enc, verity).exec(); | ||||
|                             } | ||||
|                         }, | ||||
|                         magiskManager.magiskLink, | ||||
|                         Utils.getLegalFilename(filename))) | ||||
|                 .setNeutralButton(R.string.release_notes, (dialog, which) -> { | ||||
|                     if (magiskManager.releaseNoteLink != null) { | ||||
|                         Intent openReleaseNoteLink = new Intent(Intent.ACTION_VIEW, Uri.parse(magiskManager.releaseNoteLink)); | ||||
|                         openReleaseNoteLink.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||
|                         magiskManager.startActivity(openReleaseNoteLink); | ||||
|                     } | ||||
|                 }) | ||||
|                 .setNegativeButton(R.string.no_thanks, null) | ||||
|                 .show(); | ||||
|     } | ||||
|  | ||||
|     @OnClick(R.id.uninstall_button) | ||||
|     public void uninstall() { | ||||
|         new AlertDialogBuilder(getActivity()) | ||||
|                 .setTitle(R.string.uninstall_magisk_title) | ||||
|                 .setMessage(R.string.uninstall_magisk_msg) | ||||
|                 .setPositiveButton(R.string.yes, (dialogInterface, i) -> { | ||||
|                     try { | ||||
|                         InputStream in = magiskManager.getAssets().open(UNINSTALLER); | ||||
|                         File uninstaller = new File(magiskManager.getCacheDir(), UNINSTALLER); | ||||
|                         FileOutputStream out = new FileOutputStream(uninstaller); | ||||
|                         byte[] bytes = new byte[1024]; | ||||
|                         int read; | ||||
|                         while ((read = in.read(bytes)) != -1) { | ||||
|                             out.write(bytes, 0, read); | ||||
|                         } | ||||
|                         in.close(); | ||||
|                         out.close(); | ||||
|                         ProgressDialog progress = new ProgressDialog(getActivity()); | ||||
|                         progress.setTitle(R.string.reboot); | ||||
|                         progress.show(); | ||||
|                         new CountDownTimer(5000, 1000) { | ||||
|                             @Override | ||||
|                             public void onTick(long millisUntilFinished) { | ||||
|                                 progress.setMessage(getString(R.string.reboot_countdown, millisUntilFinished / 1000)); | ||||
|                             } | ||||
|  | ||||
|                             @Override | ||||
|                             public void onFinish() { | ||||
|                                 progress.setMessage(getString(R.string.reboot_countdown, 0)); | ||||
|                                 Shell.su(true, "mv -f " + uninstaller + " /cache/" + UNINSTALLER, | ||||
|                                         "reboot"); | ||||
|                             } | ||||
|                         }.start(); | ||||
|                     } catch (IOException e) { | ||||
|                         e.printStackTrace(); | ||||
|                     } | ||||
|                 }) | ||||
|                 .setNegativeButton(R.string.no_thanks, null) | ||||
|                 .show(); | ||||
|     } | ||||
|  | ||||
|     private Unbinder unbinder; | ||||
|     private MagiskManager magiskManager; | ||||
|  | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { | ||||
|         View v = inflater.inflate(R.layout.fragment_install, container, false); | ||||
|         unbinder = ButterKnife.bind(this, v); | ||||
|         magiskManager = getApplication(); | ||||
|         if (magiskManager.magiskVersion < 0) { | ||||
|             currentVersionTitle.setText(getString(R.string.current_magisk_title, getString(R.string.version_none))); | ||||
|         } else { | ||||
|             currentVersionTitle.setText(getString(R.string.current_magisk_title, "v" + magiskManager.magiskVersionString)); | ||||
|         } | ||||
|         installTitle.setText(getString(R.string.install_magisk_title, "v" + String.format(Locale.US, "%.1f", magiskManager.remoteMagiskVersion))); | ||||
|  | ||||
|         updateUI(); | ||||
|         return v; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onTrigger(CallbackEvent<Void> event) { | ||||
|         updateUI(); | ||||
|     } | ||||
|  | ||||
|     private void updateUI() { | ||||
|         if (magiskManager.blockList == null || !Shell.rootAccess()) { | ||||
|             uninstallButton.setVisibility(View.GONE); | ||||
|             installText.setText(R.string.download); | ||||
|             detectButton.setEnabled(false); | ||||
|             keepEncChkbox.setEnabled(false); | ||||
|             keepVerityChkbox.setEnabled(false); | ||||
|             spinner.setEnabled(false); | ||||
|         } else { | ||||
|             uninstallButton.setVisibility(magiskManager.magiskVersion > 10.3 ? View.VISIBLE : View.GONE); | ||||
|             installText.setText(R.string.download_install); | ||||
|             detectButton.setEnabled(true); | ||||
|             keepEncChkbox.setEnabled(true); | ||||
|             keepVerityChkbox.setEnabled(true); | ||||
|             spinner.setEnabled(true); | ||||
|  | ||||
|             List<String> items = new ArrayList<>(); | ||||
|             if (magiskManager.bootBlock != null) { | ||||
|                 items.add(getString(R.string.auto_detect, magiskManager.bootBlock)); | ||||
|             } else { | ||||
|                 items.add(getString(R.string.cannot_auto_detect)); | ||||
|             } | ||||
|             items.addAll(magiskManager.blockList); | ||||
|             ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(), | ||||
|                     android.R.layout.simple_spinner_item, items); | ||||
|             adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); | ||||
|             spinner.setAdapter(adapter); | ||||
|             toAutoDetect(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onStart() { | ||||
|         super.onStart(); | ||||
|         getActivity().setTitle(R.string.install); | ||||
|         magiskManager.blockDetectionDone.register(this); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onStop() { | ||||
|         magiskManager.blockDetectionDone.unRegister(this); | ||||
|         super.onStop(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroyView() { | ||||
|         super.onDestroyView(); | ||||
|         unbinder.unbind(); | ||||
|     } | ||||
| } | ||||
| @@ -31,14 +31,14 @@ public class LogFragment extends Fragment { | ||||
|  | ||||
|         TabFragmentAdapter adapter = new TabFragmentAdapter(getChildFragmentManager()); | ||||
|  | ||||
|         adapter.addTab(new MagiskLogFragment(), getString(R.string.magisk)); | ||||
|  | ||||
|         if (getApplication().isSuClient) { | ||||
|             adapter.addTab(new SuLogFragment(), getString(R.string.superuser)); | ||||
|             tab.setupWithViewPager(viewPager); | ||||
|             tab.setVisibility(View.VISIBLE); | ||||
|         } | ||||
|  | ||||
|         adapter.addTab(new MagiskLogFragment(), getString(R.string.magisk)); | ||||
|  | ||||
|         viewPager.setAdapter(adapter); | ||||
|  | ||||
|         return v; | ||||
|   | ||||
							
								
								
									
										509
									
								
								app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										509
									
								
								app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -24,7 +24,7 @@ import android.widget.ScrollView; | ||||
| import android.widget.TextView; | ||||
| import android.widget.Toast; | ||||
|  | ||||
| import com.topjohnwu.magisk.asyncs.SerialTask; | ||||
| import com.topjohnwu.magisk.asyncs.RootTask; | ||||
| import com.topjohnwu.magisk.components.Fragment; | ||||
| import com.topjohnwu.magisk.components.SnackbarMaker; | ||||
| import com.topjohnwu.magisk.utils.Shell; | ||||
| @@ -127,14 +127,14 @@ public class MagiskLogFragment extends Fragment { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public class LogManager extends SerialTask<Object, Void, Object> { | ||||
|     private class LogManager extends RootTask<Object, Void, Object> { | ||||
|  | ||||
|         int mode; | ||||
|         File targetFile; | ||||
|  | ||||
|         @SuppressLint("DefaultLocale") | ||||
|         @Override | ||||
|         protected Object doInBackground(Object... params) { | ||||
|         protected Object doInRoot(Object... params) { | ||||
|             mode = (int) params[0]; | ||||
|             switch (mode) { | ||||
|                 case 0: | ||||
|   | ||||
| @@ -1,17 +1,22 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.app.Application; | ||||
| import android.app.NotificationChannel; | ||||
| import android.app.NotificationManager; | ||||
| import android.content.Context; | ||||
| import android.content.SharedPreferences; | ||||
| import android.content.pm.ApplicationInfo; | ||||
| import android.os.Build; | ||||
| import android.os.Handler; | ||||
| import android.preference.PreferenceManager; | ||||
| import android.util.SparseArray; | ||||
| import android.text.TextUtils; | ||||
| import android.widget.Toast; | ||||
|  | ||||
| import com.topjohnwu.magisk.database.SuDatabaseHelper; | ||||
| import com.topjohnwu.magisk.module.Module; | ||||
| import com.topjohnwu.magisk.module.Repo; | ||||
| import com.topjohnwu.magisk.superuser.Policy; | ||||
| import com.topjohnwu.magisk.utils.CallbackEvent; | ||||
| import com.topjohnwu.magisk.utils.SafetyNetHelper; | ||||
| import com.topjohnwu.magisk.utils.Shell; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
| import com.topjohnwu.magisk.utils.ValueSortedMap; | ||||
| @@ -22,10 +27,15 @@ import java.util.List; | ||||
| public class MagiskManager extends Application { | ||||
|  | ||||
|     public static final String MAGISK_DISABLE_FILE = "/cache/.disable_magisk"; | ||||
|     public static final String MAGISK_HIDE_PATH = "/magisk/.core/magiskhide/"; | ||||
|     public static final String TMP_FOLDER_PATH = "/dev/tmp"; | ||||
|     public static final String MAGISK_PATH = "/magisk"; | ||||
|     public static final String UNINSTALLER = "magisk_uninstaller.sh"; | ||||
|     public static final String UTIL_FUNCTIONS= "util_functions.sh"; | ||||
|     public static final String INTENT_SECTION = "section"; | ||||
|     public static final String BUSYBOX_VERSION = "1.26.2"; | ||||
|     public static final String MAGISKHIDE_PROP = "persist.magisk.hide"; | ||||
|     public static final String DISABLE_INDICATION_PROP = "ro.magisk.disable"; | ||||
|     public static final String NOTIFICATION_CHANNEL = "magisk_update_notice"; | ||||
|  | ||||
|     // Events | ||||
|     public final CallbackEvent<Void> blockDetectionDone = new CallbackEvent<>(); | ||||
| @@ -35,20 +45,22 @@ public class MagiskManager extends Application { | ||||
|     public final CallbackEvent<Void> repoLoadDone = new CallbackEvent<>(); | ||||
|     public final CallbackEvent<Void> updateCheckDone = new CallbackEvent<>(); | ||||
|     public final CallbackEvent<Void> safetyNetDone = new CallbackEvent<>(); | ||||
|     public final SparseArray<CallbackEvent<Policy>> uidSuRequest = new SparseArray<>(); | ||||
|  | ||||
|     // Info | ||||
|     public double magiskVersion; | ||||
|     public String magiskVersionString; | ||||
|     public double remoteMagiskVersion = -1; | ||||
|     public int magiskVersionCode = -1; | ||||
|     public String remoteMagiskVersionString; | ||||
|     public int remoteMagiskVersionCode = -1; | ||||
|     public String magiskLink; | ||||
|     public String releaseNoteLink; | ||||
|     public int SNCheckResult = -1; | ||||
|     public String remoteManagerVersionString; | ||||
|     public int remoteManagerVersionCode = -1; | ||||
|     public String managerLink; | ||||
|     public SafetyNetHelper.Result SNCheckResult; | ||||
|     public String bootBlock = null; | ||||
|     public boolean isSuClient = false; | ||||
|     public String suVersion = null; | ||||
|     public boolean disabled; | ||||
|     public boolean magiskHideStarted; | ||||
|  | ||||
|     // Data | ||||
|     public ValueSortedMap<String, Repo> repoMap; | ||||
| @@ -64,13 +76,18 @@ public class MagiskManager extends Application { | ||||
|     public boolean magiskHide; | ||||
|     public boolean isDarkTheme; | ||||
|     public boolean updateNotification; | ||||
|     public boolean suReauth; | ||||
|     public int suRequestTimeout; | ||||
|     public int suLogTimeout = 14; | ||||
|     public int suAccessState; | ||||
|     public int multiuserMode; | ||||
|     public int suResponseType; | ||||
|     public int suNotificationType; | ||||
|     public int suNamespaceMode; | ||||
|  | ||||
|     // Global resources | ||||
|     public SharedPreferences prefs; | ||||
|     public SuDatabaseHelper suDB; | ||||
|  | ||||
|     private static Handler mHandler = new Handler(); | ||||
|  | ||||
| @@ -90,80 +107,111 @@ public class MagiskManager extends Application { | ||||
|  | ||||
|     public void init() { | ||||
|         isDarkTheme = prefs.getBoolean("dark_theme", false); | ||||
|         devLogging = prefs.getBoolean("developer_logging", false); | ||||
|         shellLogging = prefs.getBoolean("shell_logging", false); | ||||
|         magiskHide = prefs.getBoolean("magiskhide", false); | ||||
|         if (BuildConfig.DEBUG) { | ||||
|             devLogging = prefs.getBoolean("developer_logging", false); | ||||
|             shellLogging = prefs.getBoolean("shell_logging", false); | ||||
|         } else { | ||||
|             devLogging = false; | ||||
|             shellLogging = false; | ||||
|         } | ||||
|         magiskHide = prefs.getBoolean("magiskhide", true); | ||||
|         updateNotification = prefs.getBoolean("notification", true); | ||||
|         initSU(); | ||||
|         // Always start a new root shell manually, just for safety | ||||
|         Shell.init(); | ||||
|         updateMagiskInfo(); | ||||
|         initSuAccess(); | ||||
|         initSuConfigs(); | ||||
|         // Initialize busybox | ||||
|         File busybox = new File(getApplicationInfo().dataDir + "/busybox/busybox"); | ||||
|         if (!busybox.exists() || !TextUtils.equals(prefs.getString("busybox_version", ""), BUSYBOX_VERSION)) { | ||||
|             busybox.getParentFile().mkdirs(); | ||||
|             Shell.su( | ||||
|                     "cp -f " + new File(getApplicationInfo().nativeLibraryDir, "libbusybox.so") + " " + busybox, | ||||
|                     "chmod -R 755 " + busybox.getParent(), | ||||
|                     busybox + " --install -s " + busybox.getParent() | ||||
|             ); | ||||
|         } | ||||
|         // Initialize prefs | ||||
|         prefs.edit() | ||||
|                 .putBoolean("dark_theme", isDarkTheme) | ||||
|                 .putBoolean("magiskhide", magiskHide) | ||||
|                 .putBoolean("notification", updateNotification) | ||||
|                 .putBoolean("busybox", Utils.commandExists("busybox")) | ||||
|                 .putBoolean("hosts", new File("/magisk/.core/hosts").exists()) | ||||
|                 .putBoolean("disable", Utils.itemExist(MAGISK_DISABLE_FILE)) | ||||
|                 .putBoolean("su_reauth", suReauth) | ||||
|                 .putString("su_request_timeout", String.valueOf(suRequestTimeout)) | ||||
|                 .putString("su_auto_response", String.valueOf(suResponseType)) | ||||
|                 .putString("su_notification", String.valueOf(suNotificationType)) | ||||
|                 .putString("su_access", String.valueOf(suAccessState)) | ||||
|                 .putString("multiuser_mode", String.valueOf(multiuserMode)) | ||||
|                 .putString("mnt_ns", String.valueOf(suNamespaceMode)) | ||||
|                 .putString("busybox_version", BUSYBOX_VERSION) | ||||
|                 .apply(); | ||||
|         // Add busybox to PATH | ||||
|         Shell.su("PATH=$PATH:" + busybox.getParent()); | ||||
|  | ||||
|         // Create notification channel on Android O | ||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||||
|             NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL, | ||||
|                     getString(R.string.magisk_updates), NotificationManager.IMPORTANCE_DEFAULT); | ||||
|             ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public void initSuConfigs() { | ||||
|     public void initSUConfig() { | ||||
|         suDB = new SuDatabaseHelper(this); | ||||
|         suRequestTimeout = Utils.getPrefsInt(prefs, "su_request_timeout", 10); | ||||
|         suResponseType = Utils.getPrefsInt(prefs, "su_auto_response", 0); | ||||
|         suNotificationType = Utils.getPrefsInt(prefs, "su_notification", 1); | ||||
|         suReauth = prefs.getBoolean("su_reauth", false); | ||||
|     } | ||||
|  | ||||
|     public void initSuAccess() { | ||||
|     public void initSU() { | ||||
|         // Create the app data directory, so su binary can work properly | ||||
|         new File(getApplicationInfo().dataDir).mkdirs(); | ||||
|  | ||||
|         initSUConfig(); | ||||
|  | ||||
|         List<String> ret = Shell.sh("su -v"); | ||||
|         if (Utils.isValidShellResponse(ret)) { | ||||
|             suVersion = ret.get(0); | ||||
|             isSuClient = suVersion.toUpperCase().contains("MAGISK"); | ||||
|         } | ||||
|         if (isSuClient) { | ||||
|             ret = Shell.sh("getprop persist.sys.root_access"); | ||||
|             if (Utils.isValidShellResponse(ret)) { | ||||
|                 suAccessState = Integer.parseInt(ret.get(0)); | ||||
|             } else { | ||||
|                 Shell.su(true, "setprop persist.sys.root_access 3"); | ||||
|                 suAccessState = 3; | ||||
|             } | ||||
|             suAccessState = suDB.getSettings(SuDatabaseHelper.ROOT_ACCESS, 3); | ||||
|             multiuserMode = suDB.getSettings(SuDatabaseHelper.MULTIUSER_MODE, 0); | ||||
|             suNamespaceMode = suDB.getSettings(SuDatabaseHelper.MNT_NS, 1); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void updateMagiskInfo() { | ||||
|         List<String> ret = Shell.sh("getprop magisk.version"); | ||||
|         List<String> ret; | ||||
|         ret = Shell.sh("magisk -v"); | ||||
|         if (!Utils.isValidShellResponse(ret)) { | ||||
|             magiskVersion = -1; | ||||
|         } else { | ||||
|             try { | ||||
|                 magiskVersionString = ret.get(0); | ||||
|                 magiskVersion = Double.parseDouble(ret.get(0)); | ||||
|             } catch (NumberFormatException e) { | ||||
|                 // Custom version don't need to receive updates | ||||
|                 magiskVersion = Double.POSITIVE_INFINITY; | ||||
|             ret = Shell.sh("getprop magisk.version"); | ||||
|             if (Utils.isValidShellResponse(ret)) { | ||||
|                 try { | ||||
|                     magiskVersionString = ret.get(0); | ||||
|                     magiskVersionCode = (int) Double.parseDouble(ret.get(0)) * 10; | ||||
|                 } catch (NumberFormatException ignored) {} | ||||
|             } | ||||
|         } else { | ||||
|             magiskVersionString = ret.get(0).split(":")[0]; | ||||
|             ret = Shell.sh("magisk -V"); | ||||
|             try { | ||||
|                 magiskVersionCode = Integer.parseInt(ret.get(0)); | ||||
|             } catch (NumberFormatException ignored) {} | ||||
|         } | ||||
|         ret = Shell.sh("getprop ro.magisk.disable"); | ||||
|         ret = Shell.sh("getprop " + DISABLE_INDICATION_PROP); | ||||
|         try { | ||||
|             disabled = Utils.isValidShellResponse(ret) && Integer.parseInt(ret.get(0)) != 0; | ||||
|         } catch (NumberFormatException e) { | ||||
|             disabled = false; | ||||
|         } | ||||
|         ret = Shell.sh("getprop persist.magisk.hide"); | ||||
|         ret = Shell.sh("getprop " + MAGISKHIDE_PROP); | ||||
|         try { | ||||
|             magiskHideStarted = Utils.isValidShellResponse(ret) && Integer.parseInt(ret.get(0)) != 0; | ||||
|             magiskHide = !Utils.isValidShellResponse(ret) || Integer.parseInt(ret.get(0)) != 0; | ||||
|         } catch (NumberFormatException e) { | ||||
|             magiskHideStarted = false; | ||||
|         } | ||||
|  | ||||
|         if (magiskHideStarted) { | ||||
|             magiskHide = true; | ||||
|         } | ||||
|          | ||||
|   | ||||
| @@ -22,6 +22,7 @@ import android.view.View; | ||||
| import com.topjohnwu.magisk.components.Activity; | ||||
| import com.topjohnwu.magisk.utils.CallbackEvent; | ||||
| import com.topjohnwu.magisk.utils.Shell; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
|  | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
| @@ -81,7 +82,6 @@ public class MainActivity extends Activity | ||||
|  | ||||
|         navigationView.setNavigationItemSelectedListener(this); | ||||
|         getApplicationContext().reloadMainActivity.register(this); | ||||
|         getApplicationContext().updateCheckDone.register(this); | ||||
|  | ||||
|     } | ||||
|  | ||||
| @@ -91,31 +91,21 @@ public class MainActivity extends Activity | ||||
|         checkHideSection(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onRestoreInstanceState(Bundle savedInstanceState) { | ||||
|         super.onRestoreInstanceState(savedInstanceState); | ||||
|         navigate(savedInstanceState.getInt(MagiskManager.INTENT_SECTION, R.id.status)); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onSaveInstanceState(Bundle outState) { | ||||
|         super.onSaveInstanceState(outState); | ||||
|         outState.putInt(MagiskManager.INTENT_SECTION, mDrawerItem); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onDestroy() { | ||||
|         getApplicationContext().reloadMainActivity.unRegister(this); | ||||
|         getApplicationContext().updateCheckDone.unRegister(this); | ||||
|         super.onDestroy(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onBackPressed() { | ||||
|         if (drawer.isDrawerOpen(navigationView)) | ||||
|         if (drawer.isDrawerOpen(navigationView)) { | ||||
|             drawer.closeDrawer(navigationView); | ||||
|         else | ||||
|         } else if (mDrawerItem != R.id.magisk) { | ||||
|             navigate(R.id.magisk); | ||||
|         } else { | ||||
|             finish(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -128,37 +118,30 @@ public class MainActivity extends Activity | ||||
|  | ||||
|     @Override | ||||
|     public void onTrigger(CallbackEvent<Void> event) { | ||||
|         if (event == getApplicationContext().reloadMainActivity) { | ||||
|             recreate(); | ||||
|         } else if (event == getApplicationContext().updateCheckDone) { | ||||
|             checkHideSection(); | ||||
|         } | ||||
|         recreate(); | ||||
|     } | ||||
|  | ||||
|     private void checkHideSection() { | ||||
|     public void checkHideSection() { | ||||
|         Menu menu = navigationView.getMenu(); | ||||
|         menu.findItem(R.id.magiskhide).setVisible( | ||||
|                 Shell.rootAccess() && getApplicationContext().magiskVersion >= 8 | ||||
|                 Shell.rootAccess() && getApplicationContext().magiskVersionCode >= 1300 | ||||
|                         && prefs.getBoolean("magiskhide", false)); | ||||
|         menu.findItem(R.id.modules).setVisible( | ||||
|                 Shell.rootAccess() && getApplicationContext().magiskVersion >= 4); | ||||
|         menu.findItem(R.id.downloads).setVisible( | ||||
|                 Shell.rootAccess() && getApplicationContext().magiskVersion >= 4); | ||||
|                 Shell.rootAccess() && getApplicationContext().magiskVersionCode >= 0); | ||||
|         menu.findItem(R.id.downloads).setVisible(Utils.checkNetworkStatus(this) && | ||||
|                 Shell.rootAccess() && getApplicationContext().magiskVersionCode >= 0); | ||||
|         menu.findItem(R.id.log).setVisible(Shell.rootAccess()); | ||||
|         menu.findItem(R.id.superuser).setVisible( | ||||
|                 Shell.rootAccess() && getApplicationContext().isSuClient); | ||||
|         menu.findItem(R.id.install).setVisible(getApplicationContext().remoteMagiskVersion > 0); | ||||
|     } | ||||
|  | ||||
|     public void navigate(String item) { | ||||
|         int itemId = R.id.status; | ||||
|         int itemId = R.id.magisk; | ||||
|         if (item != null) { | ||||
|             switch (item) { | ||||
|                 case "status": | ||||
|                     itemId = R.id.status; | ||||
|                     break; | ||||
|                 case "magisk": | ||||
|                 case "install": | ||||
|                     itemId = R.id.install; | ||||
|                     itemId = R.id.magisk; | ||||
|                     break; | ||||
|                 case "superuser": | ||||
|                     itemId = R.id.superuser; | ||||
| @@ -191,11 +174,8 @@ public class MainActivity extends Activity | ||||
|         mDrawerItem = itemId; | ||||
|         navigationView.setCheckedItem(itemId); | ||||
|         switch (itemId) { | ||||
|             case R.id.status: | ||||
|                 displayFragment(new StatusFragment(), "status", true); | ||||
|                 break; | ||||
|             case R.id.install: | ||||
|                 displayFragment(new InstallFragment(), "install", true); | ||||
|             case R.id.magisk: | ||||
|                 displayFragment(new MagiskFragment(), "magisk", true); | ||||
|                 break; | ||||
|             case R.id.superuser: | ||||
|                 displayFragment(new SuperuserFragment(), "superuser", true); | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import android.content.Intent; | ||||
| import android.net.Uri; | ||||
| import android.os.Bundle; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.design.widget.FloatingActionButton; | ||||
| import android.support.v4.widget.SwipeRefreshLayout; | ||||
| import android.support.v7.widget.RecyclerView; | ||||
| import android.view.LayoutInflater; | ||||
| @@ -12,7 +13,6 @@ import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import com.github.clans.fab.FloatingActionButton; | ||||
| import com.topjohnwu.magisk.adapters.ModulesAdapter; | ||||
| import com.topjohnwu.magisk.asyncs.FlashZip; | ||||
| import com.topjohnwu.magisk.asyncs.LoadModules; | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.content.SharedPreferences; | ||||
| import android.os.Build; | ||||
| import android.os.Bundle; | ||||
| import android.preference.ListPreference; | ||||
| import android.preference.PreferenceCategory; | ||||
| @@ -10,13 +11,12 @@ import android.preference.PreferenceScreen; | ||||
| import android.preference.SwitchPreference; | ||||
| import android.support.v7.app.ActionBar; | ||||
| import android.support.v7.widget.Toolbar; | ||||
| import android.view.WindowManager; | ||||
| import android.widget.Toast; | ||||
|  | ||||
| import com.topjohnwu.magisk.asyncs.MagiskHide; | ||||
| import com.topjohnwu.magisk.asyncs.SerialTask; | ||||
| import com.topjohnwu.magisk.components.Activity; | ||||
| import com.topjohnwu.magisk.components.AlertDialogBuilder; | ||||
| import com.topjohnwu.magisk.database.SuDatabaseHelper; | ||||
| import com.topjohnwu.magisk.utils.Logger; | ||||
| import com.topjohnwu.magisk.utils.Shell; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
| @@ -32,10 +32,10 @@ public class SettingsActivity extends Activity { | ||||
|     protected void onCreate(Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         if (getApplicationContext().isDarkTheme) { | ||||
|             setTheme(R.style.AppTheme_Dark); | ||||
|             setTheme(R.style.AppTheme_Transparent_Dark); | ||||
|         } | ||||
|  | ||||
|         setContentView(R.layout.activity_container); | ||||
|         setContentView(R.layout.activity_settings); | ||||
|         ButterKnife.bind(this); | ||||
|  | ||||
|         setSupportActionBar(toolbar); | ||||
| @@ -56,31 +56,14 @@ public class SettingsActivity extends Activity { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public void setFloating() { | ||||
|         boolean isTablet = getResources().getBoolean(R.bool.isTablet); | ||||
|         if (isTablet) { | ||||
|             WindowManager.LayoutParams params = getWindow().getAttributes(); | ||||
|             params.height = getResources().getDimensionPixelSize(R.dimen.floating_height); | ||||
|             params.width = getResources().getDimensionPixelSize(R.dimen.floating_width); | ||||
|             params.alpha = 1.0f; | ||||
|             params.dimAmount = 0.6f; | ||||
|             params.flags |= 2; | ||||
|             getWindow().setAttributes(params); | ||||
|             setFinishOnTouchOutside(true); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static class SettingsFragment extends PreferenceFragment | ||||
|             implements SharedPreferences.OnSharedPreferenceChangeListener { | ||||
|  | ||||
|         private SharedPreferences prefs; | ||||
|         private PreferenceScreen prefScreen; | ||||
|  | ||||
|         private ListPreference suAccess, autoRes, suNotification, requestTimeout; | ||||
|  | ||||
|         private MagiskManager getApplication() { | ||||
|             return Utils.getMagiskManager(getActivity()); | ||||
|         } | ||||
|         private ListPreference suAccess, autoRes, suNotification, requestTimeout, multiuserMode, namespaceMode; | ||||
|         private MagiskManager magiskManager; | ||||
|  | ||||
|         @Override | ||||
|         public void onCreate(Bundle savedInstanceState) { | ||||
| @@ -88,30 +71,50 @@ public class SettingsActivity extends Activity { | ||||
|             addPreferencesFromResource(R.xml.app_settings); | ||||
|             prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); | ||||
|             prefScreen = getPreferenceScreen(); | ||||
|             magiskManager = Utils.getMagiskManager(getActivity()); | ||||
|  | ||||
|             PreferenceCategory magiskCategory = (PreferenceCategory) findPreference("magisk"); | ||||
|             PreferenceCategory suCategory = (PreferenceCategory) findPreference("superuser"); | ||||
|             PreferenceCategory developer = (PreferenceCategory) findPreference("developer"); | ||||
|  | ||||
|             suAccess = (ListPreference) findPreference("su_access"); | ||||
|             autoRes = (ListPreference) findPreference("su_auto_response"); | ||||
|             requestTimeout = (ListPreference) findPreference("su_request_timeout"); | ||||
|             suNotification = (ListPreference) findPreference("su_notification"); | ||||
|             multiuserMode = (ListPreference) findPreference("multiuser_mode"); | ||||
|             namespaceMode = (ListPreference) findPreference("mnt_ns"); | ||||
|             SwitchPreference reauth = (SwitchPreference) findPreference("su_reauth"); | ||||
|  | ||||
|             setSummary(); | ||||
|  | ||||
|             // Disable dangerous settings in user mode if selected owner manage | ||||
|             if (getActivity().getApplicationInfo().uid > 99999) { | ||||
|                 prefScreen.removePreference(magiskCategory); | ||||
|                 prefScreen.removePreference(suCategory); | ||||
|             } | ||||
|  | ||||
|             // Remove re-authentication option on Android O, it will not work | ||||
|             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||||
|                 suCategory.removePreference(reauth); | ||||
|             } | ||||
|  | ||||
|             findPreference("clear").setOnPreferenceClickListener((pref) -> { | ||||
|                 Utils.clearRepoCache(getActivity()); | ||||
|                 return true; | ||||
|             }); | ||||
|  | ||||
|             if (!BuildConfig.DEBUG) { | ||||
|                 prefScreen.removePreference(developer); | ||||
|             } | ||||
|  | ||||
|             if (!Shell.rootAccess()) { | ||||
|                 prefScreen.removePreference(magiskCategory); | ||||
|                 prefScreen.removePreference(suCategory); | ||||
|             } else { | ||||
|                 if (!getApplication().isSuClient) { | ||||
|                 if (!magiskManager.isSuClient) { | ||||
|                     prefScreen.removePreference(suCategory); | ||||
|                 } | ||||
|                 if (getApplication().magiskVersion < 11) { | ||||
|                 if (magiskManager.magiskVersionCode < 1300) { | ||||
|                     prefScreen.removePreference(magiskCategory); | ||||
|                 } | ||||
|             } | ||||
| @@ -137,51 +140,25 @@ public class SettingsActivity extends Activity { | ||||
|             switch (key) { | ||||
|                 case "dark_theme": | ||||
|                     enabled = prefs.getBoolean("dark_theme", false); | ||||
|                     if (getApplication().isDarkTheme != enabled) { | ||||
|                         getApplication().isDarkTheme = enabled; | ||||
|                         getApplication().reloadMainActivity.trigger(); | ||||
|                     if (magiskManager.isDarkTheme != enabled) { | ||||
|                         magiskManager.isDarkTheme = enabled; | ||||
|                         magiskManager.reloadMainActivity.trigger(); | ||||
|                         getActivity().recreate(); | ||||
|                     } | ||||
|                     break; | ||||
|                 case "disable": | ||||
|                     enabled = prefs.getBoolean("disable", false); | ||||
|                     new SerialTask<Void, Void, Void>() { | ||||
|                         private boolean enable = enabled; | ||||
|                         @Override | ||||
|                         protected Void doInBackground(Void... voids) { | ||||
|                             if (enable) { | ||||
|                                 Utils.createFile(MagiskManager.MAGISK_DISABLE_FILE); | ||||
|                             } else { | ||||
|                                 Utils.removeItem(MagiskManager.MAGISK_DISABLE_FILE); | ||||
|                             } | ||||
|                             return null; | ||||
|                         } | ||||
|                     }.exec(); | ||||
|                     if (enabled) { | ||||
|                         Utils.createFile(MagiskManager.MAGISK_DISABLE_FILE); | ||||
|                     } else { | ||||
|                         Utils.removeItem(MagiskManager.MAGISK_DISABLE_FILE); | ||||
|                     } | ||||
|                     Toast.makeText(getActivity(), R.string.settings_reboot_toast, Toast.LENGTH_LONG).show(); | ||||
|                     break; | ||||
|                 case "busybox": | ||||
|                     enabled = prefs.getBoolean("busybox", false); | ||||
|                     new SerialTask<Void, Void, Void>() { | ||||
|                         private boolean enable = enabled; | ||||
|                         @Override | ||||
|                         protected Void doInBackground(Void... voids) { | ||||
|                             if (enable) { | ||||
|                                 Shell.su( | ||||
|                                         "setprop persist.magisk.busybox 1", | ||||
|                                         "sh /sbin/magic_mask.sh mount_busybox"); | ||||
|                             } else { | ||||
|                                 Shell.su( | ||||
|                                         "setprop persist.magisk.busybox 0", | ||||
|                                         "umount /system/xbin"); | ||||
|                             } | ||||
|                             return null; | ||||
|                         } | ||||
|                     }.exec(); | ||||
|                     break; | ||||
|                 case "magiskhide": | ||||
|                     enabled = prefs.getBoolean("magiskhide", false); | ||||
|                     if (enabled) { | ||||
|                         if (!getApplication().isSuClient) { | ||||
|                         if (!magiskManager.isSuClient) { | ||||
|                             new AlertDialogBuilder(getActivity()) | ||||
|                                     .setTitle(R.string.no_magisksu_title) | ||||
|                                     .setMessage(R.string.no_magisksu_msg) | ||||
| @@ -197,33 +174,36 @@ public class SettingsActivity extends Activity { | ||||
|                     break; | ||||
|                 case "hosts": | ||||
|                     enabled = prefs.getBoolean("hosts", false); | ||||
|                     new SerialTask<Void, Void, Void>() { | ||||
|                         private boolean enable = enabled; | ||||
|                         @Override | ||||
|                         protected Void doInBackground(Void... voids) { | ||||
|                             if (enable) { | ||||
|                                 Shell.su("cp -af /system/etc/hosts /magisk/.core/hosts", | ||||
|                                         "mount -o bind /magisk/.core/hosts /system/etc/hosts"); | ||||
|                             } else { | ||||
|                                 Shell.su("umount -l /system/etc/hosts", | ||||
|                                         "rm -f /magisk/.core/hosts"); | ||||
|                             } | ||||
|                             return null; | ||||
|                         } | ||||
|                     }.exec(); | ||||
|                     if (enabled) { | ||||
|                         Shell.su_async(null, | ||||
|                                 "cp -af /system/etc/hosts /magisk/.core/hosts", | ||||
|                                 "mount -o bind /magisk/.core/hosts /system/etc/hosts"); | ||||
|                     } else { | ||||
|                         Shell.su_async(null, | ||||
|                                 "umount -l /system/etc/hosts", | ||||
|                                 "rm -f /magisk/.core/hosts"); | ||||
|                     } | ||||
|                     break; | ||||
|                 case "su_access": | ||||
|                     getApplication().suAccessState = Utils.getPrefsInt(prefs, "su_access", 0); | ||||
|                     Shell.su("setprop persist.sys.root_access " + getApplication().suAccessState); | ||||
|                     magiskManager.suAccessState = Utils.getPrefsInt(prefs, "su_access", 3); | ||||
|                     magiskManager.suDB.setSettings(SuDatabaseHelper.ROOT_ACCESS, magiskManager.suAccessState); | ||||
|                     break; | ||||
|                 case "multiuser_mode": | ||||
|                     magiskManager.multiuserMode = Utils.getPrefsInt(prefs, "multiuser_mode", 0); | ||||
|                     magiskManager.suDB.setSettings(SuDatabaseHelper.MULTIUSER_MODE, magiskManager.multiuserMode); | ||||
|                     break; | ||||
|                 case "mnt_ns": | ||||
|                     magiskManager.suNamespaceMode = Utils.getPrefsInt(prefs, "mnt_ns", 1); | ||||
|                     magiskManager.suDB.setSettings(SuDatabaseHelper.MNT_NS, magiskManager.suNamespaceMode); | ||||
|                     break; | ||||
|                 case "su_request_timeout": | ||||
|                     getApplication().suRequestTimeout = Utils.getPrefsInt(prefs, "su_request_timeout", 10); | ||||
|                     magiskManager.suRequestTimeout = Utils.getPrefsInt(prefs, "su_request_timeout", 10); | ||||
|                     break; | ||||
|                 case "su_auto_response": | ||||
|                     getApplication().suResponseType = Utils.getPrefsInt(prefs, "su_auto_response", 0); | ||||
|                     magiskManager.suResponseType = Utils.getPrefsInt(prefs, "su_auto_response", 0); | ||||
|                     break; | ||||
|                 case "su_notification": | ||||
|                     getApplication().suNotificationType = Utils.getPrefsInt(prefs, "su_notification", 1); | ||||
|                     magiskManager.suNotificationType = Utils.getPrefsInt(prefs, "su_notification", 1); | ||||
|                     break; | ||||
|                 case "developer_logging": | ||||
|                     MagiskManager.devLogging = prefs.getBoolean("developer_logging", false); | ||||
| @@ -237,13 +217,17 @@ public class SettingsActivity extends Activity { | ||||
|  | ||||
|         private void setSummary() { | ||||
|             suAccess.setSummary(getResources() | ||||
|                     .getStringArray(R.array.su_access)[getApplication().suAccessState]); | ||||
|                     .getStringArray(R.array.su_access)[magiskManager.suAccessState]); | ||||
|             autoRes.setSummary(getResources() | ||||
|                     .getStringArray(R.array.auto_response)[getApplication().suResponseType]); | ||||
|                     .getStringArray(R.array.auto_response)[magiskManager.suResponseType]); | ||||
|             suNotification.setSummary(getResources() | ||||
|                     .getStringArray(R.array.su_notification)[getApplication().suNotificationType]); | ||||
|                     .getStringArray(R.array.su_notification)[magiskManager.suNotificationType]); | ||||
|             requestTimeout.setSummary( | ||||
|                     getString(R.string.request_timeout_summary, prefs.getString("su_request_timeout", "10"))); | ||||
|             multiuserMode.setSummary(getResources() | ||||
|                     .getStringArray(R.array.multiuser_summary)[magiskManager.multiuserMode]); | ||||
|             namespaceMode.setSummary(getResources() | ||||
|                     .getStringArray(R.array.namespace_summary)[magiskManager.suNamespaceMode]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -6,15 +6,15 @@ import android.content.ComponentName; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.os.Bundle; | ||||
| import android.text.TextUtils; | ||||
|  | ||||
| import com.topjohnwu.magisk.asyncs.CheckUpdates; | ||||
| import com.topjohnwu.magisk.asyncs.GetBootBlocks; | ||||
| import com.topjohnwu.magisk.asyncs.LoadApps; | ||||
| import com.topjohnwu.magisk.asyncs.LoadModules; | ||||
| import com.topjohnwu.magisk.asyncs.LoadRepos; | ||||
| import com.topjohnwu.magisk.asyncs.MagiskHide; | ||||
| import com.topjohnwu.magisk.components.Activity; | ||||
| import com.topjohnwu.magisk.services.UpdateCheckService; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
|  | ||||
| public class SplashActivity extends Activity{ | ||||
|  | ||||
| @@ -25,49 +25,34 @@ public class SplashActivity extends Activity{ | ||||
|  | ||||
|         super.onCreate(savedInstanceState); | ||||
|  | ||||
|         MagiskManager magiskManager = getApplicationContext(); | ||||
|  | ||||
|         // Init the info and configs and root shell | ||||
|         magiskManager.init(); | ||||
|  | ||||
|         // Initialize the update check service, notify every 3 hours | ||||
|         if (!"install".equals(getIntent().getStringExtra(MagiskManager.INTENT_SECTION))) { | ||||
|             ComponentName service = new ComponentName(magiskManager, UpdateCheckService.class); | ||||
|             JobInfo jobInfo = new JobInfo.Builder(UPDATE_SERVICE_ID, service) | ||||
|                     .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) | ||||
|                     .setPersisted(true) | ||||
|                     .setPeriodic(3 * 60 * 60 * 1000) | ||||
|                     .build(); | ||||
|             JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); | ||||
|             scheduler.schedule(jobInfo); | ||||
|         } | ||||
|         getApplicationContext().init(); | ||||
|  | ||||
|         // Now fire all async tasks | ||||
|         new GetBootBlocks(this).exec(); | ||||
|         if (magiskManager.magiskHide && magiskManager.magiskVersion > 11 && | ||||
|                 !magiskManager.magiskHideStarted) { | ||||
|             new MagiskHide().enable(); | ||||
|         } | ||||
|         new LoadModules(this) { | ||||
|             @Override | ||||
|             protected void onPostExecute(Void v) { | ||||
|                 super.onPostExecute(v); | ||||
|                 new LoadRepos(activity).exec(); | ||||
|             } | ||||
|         }.exec(); | ||||
|         new LoadModules(this).setCallBack(() -> new LoadRepos(this).exec()).exec(); | ||||
|         new LoadApps(this).exec(); | ||||
|         new CheckUpdates(this, false){ | ||||
|             @Override | ||||
|             protected void onPostExecute(Void v) { | ||||
|                 super.onPostExecute(v); | ||||
|                 String section = getIntent().getStringExtra(MagiskManager.INTENT_SECTION); | ||||
|                 Intent intent = new Intent(magiskManager, MainActivity.class); | ||||
|                 if (section != null) { | ||||
|                     intent.putExtra(MagiskManager.INTENT_SECTION, section); | ||||
|                 } | ||||
|                 startActivity(intent); | ||||
|                 finish(); | ||||
|  | ||||
|         if (Utils.checkNetworkStatus(this)) { | ||||
|             // Initialize the update check service, notify every 8 hours | ||||
|             if (!TextUtils.equals("install", getIntent().getStringExtra(MagiskManager.INTENT_SECTION))) { | ||||
|                 ComponentName service = new ComponentName(this, UpdateCheckService.class); | ||||
|                 JobInfo jobInfo = new JobInfo.Builder(UPDATE_SERVICE_ID, service) | ||||
|                         .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) | ||||
|                         .setPersisted(true) | ||||
|                         .setPeriodic(8 * 60 * 60 * 1000) | ||||
|                         .build(); | ||||
|                 JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); | ||||
|                 scheduler.schedule(jobInfo); | ||||
|             } | ||||
|         }.exec(); | ||||
|         } | ||||
|  | ||||
|         Intent intent = new Intent(this, MainActivity.class); | ||||
|         String section = getIntent().getStringExtra(MagiskManager.INTENT_SECTION); | ||||
|         if (section != null) { | ||||
|             intent.putExtra(MagiskManager.INTENT_SECTION, section); | ||||
|         } | ||||
|         startActivity(intent); | ||||
|         finish(); | ||||
|     } | ||||
| } | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -13,7 +13,6 @@ import android.widget.TextView; | ||||
|  | ||||
| import com.topjohnwu.magisk.adapters.SuLogAdapter; | ||||
| import com.topjohnwu.magisk.components.Fragment; | ||||
| import com.topjohnwu.magisk.database.SuLogDatabaseHelper; | ||||
| import com.topjohnwu.magisk.superuser.SuLogEntry; | ||||
|  | ||||
| import java.util.List; | ||||
| @@ -28,7 +27,7 @@ public class SuLogFragment extends Fragment { | ||||
|     @BindView(R.id.recyclerView) RecyclerView recyclerView; | ||||
|  | ||||
|     private Unbinder unbinder; | ||||
|     private SuLogDatabaseHelper dbHelper; | ||||
|     private MagiskManager magiskManager; | ||||
|  | ||||
|     @Override | ||||
|     public void onCreate(@Nullable Bundle savedInstanceState) { | ||||
| @@ -48,8 +47,7 @@ public class SuLogFragment extends Fragment { | ||||
|         // Inflate the layout for this fragment | ||||
|         View v = inflater.inflate(R.layout.fragment_su_log, container, false); | ||||
|         unbinder = ButterKnife.bind(this, v); | ||||
|  | ||||
|         dbHelper = new SuLogDatabaseHelper(getActivity()); | ||||
|         magiskManager = getApplication(); | ||||
|  | ||||
|         updateList(); | ||||
|  | ||||
| @@ -57,7 +55,7 @@ public class SuLogFragment extends Fragment { | ||||
|     } | ||||
|  | ||||
|     private void updateList() { | ||||
|         List<SuLogEntry> logs = dbHelper.getLogList(); | ||||
|         List<SuLogEntry> logs = magiskManager.suDB.getLogList(); | ||||
|  | ||||
|         if (logs.size() == 0) { | ||||
|             emptyRv.setVisibility(View.VISIBLE); | ||||
| @@ -76,7 +74,7 @@ public class SuLogFragment extends Fragment { | ||||
|                 updateList(); | ||||
|                 return true; | ||||
|             case R.id.menu_clear: | ||||
|                 dbHelper.clearLogs(); | ||||
|                 magiskManager.suDB.clearLogs(); | ||||
|                 updateList(); | ||||
|                 return true; | ||||
|             default: | ||||
|   | ||||
| @@ -11,7 +11,6 @@ import android.widget.TextView; | ||||
|  | ||||
| import com.topjohnwu.magisk.adapters.PolicyAdapter; | ||||
| import com.topjohnwu.magisk.components.Fragment; | ||||
| import com.topjohnwu.magisk.database.SuDatabaseHelper; | ||||
| import com.topjohnwu.magisk.superuser.Policy; | ||||
|  | ||||
| import java.util.List; | ||||
| @@ -33,15 +32,15 @@ public class SuperuserFragment extends Fragment { | ||||
|         unbinder = ButterKnife.bind(this, view); | ||||
|  | ||||
|         PackageManager pm = getActivity().getPackageManager(); | ||||
|         MagiskManager magiskManager = getApplication(); | ||||
|  | ||||
|         SuDatabaseHelper dbHelper = new SuDatabaseHelper(getActivity()); | ||||
|         List<Policy> policyList = dbHelper.getPolicyList(pm); | ||||
|         List<Policy> policyList = magiskManager.suDB.getPolicyList(pm); | ||||
|  | ||||
|         if (policyList.size() == 0) { | ||||
|             emptyRv.setVisibility(View.VISIBLE); | ||||
|             recyclerView.setVisibility(View.GONE); | ||||
|         } else { | ||||
|             recyclerView.setAdapter(new PolicyAdapter(policyList, dbHelper, pm)); | ||||
|             recyclerView.setAdapter(new PolicyAdapter(policyList, magiskManager.suDB, pm)); | ||||
|             emptyRv.setVisibility(View.GONE); | ||||
|             recyclerView.setVisibility(View.VISIBLE); | ||||
|         } | ||||
|   | ||||
| @@ -12,7 +12,6 @@ import android.widget.ImageView; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import com.topjohnwu.magisk.R; | ||||
| import com.topjohnwu.magisk.asyncs.SerialTask; | ||||
| import com.topjohnwu.magisk.components.SnackbarMaker; | ||||
| import com.topjohnwu.magisk.module.Module; | ||||
| import com.topjohnwu.magisk.utils.Shell; | ||||
| @@ -53,44 +52,31 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold | ||||
|  | ||||
|         holder.checkBox.setOnCheckedChangeListener(null); | ||||
|         holder.checkBox.setChecked(module.isEnabled()); | ||||
|         holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> new SerialTask<Void, Void, Void>() { | ||||
|             @Override | ||||
|             protected Void doInBackground(Void... voids) { | ||||
|                 if (isChecked) { | ||||
|                     module.removeDisableFile(); | ||||
|                 } else { | ||||
|                     module.createDisableFile(); | ||||
|                 } | ||||
|                 return null; | ||||
|         holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> { | ||||
|             int snack; | ||||
|             if (isChecked) { | ||||
|                 module.removeDisableFile(); | ||||
|                 snack = R.string.disable_file_removed; | ||||
|             } else { | ||||
|                 module.createDisableFile(); | ||||
|                 snack = R.string.disable_file_created; | ||||
|             } | ||||
|             SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show(); | ||||
|         }); | ||||
|  | ||||
|             @Override | ||||
|             protected void onPostExecute(Void v) { | ||||
|                 int snack = isChecked ? R.string.disable_file_removed : R.string.disable_file_created; | ||||
|                 SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show(); | ||||
|         holder.delete.setOnClickListener(v -> { | ||||
|             boolean removed = module.willBeRemoved(); | ||||
|             int snack; | ||||
|             if (removed) { | ||||
|                 module.deleteRemoveFile(); | ||||
|                 snack = R.string.remove_file_deleted; | ||||
|             } else { | ||||
|                 module.createRemoveFile(); | ||||
|                 snack = R.string.remove_file_created; | ||||
|             } | ||||
|         }.exec()); | ||||
|  | ||||
|         holder.delete.setOnClickListener(v -> new SerialTask<Void, Void, Void>() { | ||||
|             private final boolean removed = module.willBeRemoved(); | ||||
|  | ||||
|             @Override | ||||
|             protected Void doInBackground(Void... voids) { | ||||
|                 if (removed) { | ||||
|                     module.deleteRemoveFile(); | ||||
|                 } else { | ||||
|                     module.createRemoveFile(); | ||||
|                 } | ||||
|                 return null; | ||||
|             } | ||||
|  | ||||
|             @Override | ||||
|             protected void onPostExecute(Void v) { | ||||
|                 int snack = removed ? R.string.remove_file_deleted : R.string.remove_file_created; | ||||
|                 SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show(); | ||||
|                 updateDeleteButton(holder, module); | ||||
|             } | ||||
|         }.exec()); | ||||
|             SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show(); | ||||
|             updateDeleteButton(holder, module); | ||||
|         }); | ||||
|  | ||||
|         if (module.isUpdated()) { | ||||
|             holder.notice.setVisibility(View.VISIBLE); | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,5 +1,6 @@ | ||||
| package com.topjohnwu.magisk.adapters; | ||||
|  | ||||
| import android.app.Activity; | ||||
| import android.content.Context; | ||||
| import android.net.Uri; | ||||
| import android.support.v7.widget.RecyclerView; | ||||
| @@ -65,6 +66,7 @@ public class ReposAdapter extends RecyclerView.Adapter<ReposAdapter.ViewHolder> | ||||
|                             new DownloadReceiver() { | ||||
|                                 @Override | ||||
|                                 public void onDownloadDone(Uri uri) { | ||||
|                                     Activity activity = (Activity) mContext; | ||||
|                                     new ProcessRepoZip(activity, uri, true).exec(); | ||||
|                                 } | ||||
|                             }, | ||||
| @@ -75,6 +77,7 @@ public class ReposAdapter extends RecyclerView.Adapter<ReposAdapter.ViewHolder> | ||||
|                             new DownloadReceiver() { | ||||
|                                 @Override | ||||
|                                 public void onDownloadDone(Uri uri) { | ||||
|                                     Activity activity = (Activity) mContext; | ||||
|                                     new ProcessRepoZip(activity, uri, false).exec(); | ||||
|                                 } | ||||
|                             }, | ||||
|   | ||||
| @@ -1,17 +1,13 @@ | ||||
| package com.topjohnwu.magisk.adapters; | ||||
|  | ||||
| import android.animation.Animator; | ||||
| import android.animation.ValueAnimator; | ||||
| import android.content.Context; | ||||
| import android.support.v7.widget.RecyclerView; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.view.ViewTreeObserver; | ||||
| import android.view.animation.Animation; | ||||
| import android.view.animation.RotateAnimation; | ||||
| import android.widget.ImageView; | ||||
| import android.widget.LinearLayout; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import com.thoughtbot.expandablerecyclerview.ExpandableRecyclerViewAdapter; | ||||
| @@ -19,6 +15,7 @@ import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup; | ||||
| import com.thoughtbot.expandablerecyclerview.viewholders.ChildViewHolder; | ||||
| import com.thoughtbot.expandablerecyclerview.viewholders.GroupViewHolder; | ||||
| import com.topjohnwu.magisk.R; | ||||
| import com.topjohnwu.magisk.components.ExpandableViewHolder; | ||||
| import com.topjohnwu.magisk.superuser.SuLogEntry; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| @@ -90,7 +87,7 @@ public class SuLogAdapter { | ||||
|             SuLogEntry logEntry = (SuLogEntry) group.getItems().get(childIndex); | ||||
|             holder.setExpanded(expandList.contains(logEntry)); | ||||
|             holder.itemView.setOnClickListener(view -> { | ||||
|                 if (holder.mExpanded) { | ||||
|                 if (holder.getExpanded()) { | ||||
|                     holder.collapse(); | ||||
|                     expandList.remove(logEntry); | ||||
|                 } else { | ||||
| @@ -150,92 +147,50 @@ public class SuLogAdapter { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Wrapper class | ||||
|     static class LogViewHolder extends ChildViewHolder { | ||||
|  | ||||
|         private InternalViewHolder expandableViewHolder; | ||||
|  | ||||
|         @BindView(R.id.app_name) TextView appName; | ||||
|         @BindView(R.id.action) TextView action; | ||||
|         @BindView(R.id.time) TextView time; | ||||
|         @BindView(R.id.fromPid) TextView fromPid; | ||||
|         @BindView(R.id.toUid) TextView toUid; | ||||
|         @BindView(R.id.command) TextView command; | ||||
|         @BindView(R.id.expand_layout) LinearLayout expandLayout; | ||||
|  | ||||
|         private ValueAnimator mAnimator; | ||||
|         private boolean mExpanded = false; | ||||
|         private static int expandHeight = 0; | ||||
|  | ||||
|         public LogViewHolder(View itemView) { | ||||
|         LogViewHolder(View itemView) { | ||||
|             super(itemView); | ||||
|             ButterKnife.bind(this, itemView); | ||||
|             expandLayout.getViewTreeObserver().addOnPreDrawListener( | ||||
|                     new ViewTreeObserver.OnPreDrawListener() { | ||||
|             expandableViewHolder = new InternalViewHolder(itemView); | ||||
|         } | ||||
|  | ||||
|                         @Override | ||||
|                         public boolean onPreDraw() { | ||||
|                             if (expandHeight == 0) { | ||||
|                                 final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); | ||||
|                                 final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); | ||||
|                                 expandLayout.measure(widthSpec, heightSpec); | ||||
|                                 expandHeight = expandLayout.getMeasuredHeight(); | ||||
|                             } | ||||
|         private class InternalViewHolder extends ExpandableViewHolder { | ||||
|  | ||||
|                             expandLayout.getViewTreeObserver().removeOnPreDrawListener(this); | ||||
|                             expandLayout.setVisibility(View.GONE); | ||||
|                             mAnimator = slideAnimator(0, expandHeight); | ||||
|                             return true; | ||||
|                         } | ||||
|             InternalViewHolder(View itemView) { | ||||
|                 super(itemView); | ||||
|             } | ||||
|  | ||||
|                     }); | ||||
|             @Override | ||||
|             public void setExpandLayout(View itemView) { | ||||
|                 expandLayout = itemView.findViewById(R.id.expand_layout); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private boolean getExpanded() { | ||||
|             return expandableViewHolder.mExpanded; | ||||
|         } | ||||
|  | ||||
|         private void setExpanded(boolean expanded) { | ||||
|             mExpanded = expanded; | ||||
|             ViewGroup.LayoutParams layoutParams = expandLayout.getLayoutParams(); | ||||
|             layoutParams.height = expanded ? expandHeight : 0; | ||||
|             expandLayout.setLayoutParams(layoutParams); | ||||
|             expandLayout.setVisibility(expanded ? View.VISIBLE : View.GONE); | ||||
|             expandableViewHolder.setExpanded(expanded); | ||||
|         } | ||||
|  | ||||
|         private void expand() { | ||||
|             expandLayout.setVisibility(View.VISIBLE); | ||||
|             mAnimator.start(); | ||||
|             mExpanded = true; | ||||
|             expandableViewHolder.expand(); | ||||
|         } | ||||
|  | ||||
|         private void collapse() { | ||||
|             if (!mExpanded) return; | ||||
|             int finalHeight = expandLayout.getHeight(); | ||||
|             ValueAnimator mAnimator = slideAnimator(finalHeight, 0); | ||||
|             mAnimator.addListener(new Animator.AnimatorListener() { | ||||
|                 @Override | ||||
|                 public void onAnimationEnd(Animator animator) { | ||||
|                     expandLayout.setVisibility(View.GONE); | ||||
|                 } | ||||
|  | ||||
|                 @Override | ||||
|                 public void onAnimationStart(Animator animator) {} | ||||
|  | ||||
|                 @Override | ||||
|                 public void onAnimationCancel(Animator animator) {} | ||||
|  | ||||
|                 @Override | ||||
|                 public void onAnimationRepeat(Animator animator) {} | ||||
|             }); | ||||
|             mAnimator.start(); | ||||
|             mExpanded = false; | ||||
|         } | ||||
|  | ||||
|         private ValueAnimator slideAnimator(int start, int end) { | ||||
|  | ||||
|             ValueAnimator animator = ValueAnimator.ofInt(start, end); | ||||
|  | ||||
|             animator.addUpdateListener(valueAnimator -> { | ||||
|                 int value = (Integer) valueAnimator.getAnimatedValue(); | ||||
|                 ViewGroup.LayoutParams layoutParams = expandLayout.getLayoutParams(); | ||||
|                 layoutParams.height = value; | ||||
|                 expandLayout.setLayoutParams(layoutParams); | ||||
|             }); | ||||
|             return animator; | ||||
|             expandableViewHolder.collapse(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,15 +1,8 @@ | ||||
| package com.topjohnwu.magisk.asyncs; | ||||
|  | ||||
| import android.app.NotificationManager; | ||||
| import android.app.PendingIntent; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.support.v4.app.TaskStackBuilder; | ||||
| import android.support.v7.app.NotificationCompat; | ||||
|  | ||||
| import com.topjohnwu.magisk.MagiskManager; | ||||
| import com.topjohnwu.magisk.R; | ||||
| import com.topjohnwu.magisk.SplashActivity; | ||||
| import com.topjohnwu.magisk.BuildConfig; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
| import com.topjohnwu.magisk.utils.WebService; | ||||
|  | ||||
| @@ -18,8 +11,7 @@ import org.json.JSONObject; | ||||
|  | ||||
| public class CheckUpdates extends ParallelTask<Void, Void, Void> { | ||||
|  | ||||
|     private static final String UPDATE_JSON = "https://raw.githubusercontent.com/topjohnwu/MagiskManager/updates/magisk_update.json"; | ||||
|     private static final int NOTIFICATION_ID = 1; | ||||
|     private static final String UPDATE_JSON = "https://raw.githubusercontent.com/topjohnwu/MagiskManager/update/magisk_update.json"; | ||||
|  | ||||
|     private boolean showNotification = false; | ||||
|  | ||||
| @@ -38,35 +30,28 @@ public class CheckUpdates extends ParallelTask<Void, Void, Void> { | ||||
|         try { | ||||
|             JSONObject json = new JSONObject(jsonStr); | ||||
|             JSONObject magisk = json.getJSONObject("magisk"); | ||||
|             magiskManager.remoteMagiskVersion = magisk.getDouble("versionCode"); | ||||
|             magiskManager.remoteMagiskVersionString = magisk.getString("version"); | ||||
|             magiskManager.remoteMagiskVersionCode = magisk.getInt("versionCode"); | ||||
|             magiskManager.magiskLink = magisk.getString("link"); | ||||
|             magiskManager.releaseNoteLink = magisk.getString("note"); | ||||
|         } catch (JSONException ignored) { | ||||
|         } | ||||
|             JSONObject manager = json.getJSONObject("app"); | ||||
|             magiskManager.remoteManagerVersionString = manager.getString("version"); | ||||
|             magiskManager.remoteManagerVersionCode = manager.getInt("versionCode"); | ||||
|             magiskManager.managerLink = manager.getString("link"); | ||||
|         } catch (JSONException ignored) {} | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onPostExecute(Void v) { | ||||
|         if (magiskManager.magiskVersion < magiskManager.remoteMagiskVersion | ||||
|                 && showNotification && magiskManager.updateNotification) { | ||||
|             NotificationCompat.Builder builder = new NotificationCompat.Builder(magiskManager); | ||||
|             builder.setSmallIcon(R.drawable.ic_magisk) | ||||
|                     .setContentTitle(magiskManager.getString(R.string.magisk_update_title)) | ||||
|                     .setContentText(magiskManager.getString(R.string.magisk_update_available, magiskManager.remoteMagiskVersion)) | ||||
|                     .setVibrate(new long[]{0, 100, 100, 100}) | ||||
|                     .setAutoCancel(true); | ||||
|             Intent intent = new Intent(magiskManager, SplashActivity.class); | ||||
|             intent.putExtra(MagiskManager.INTENT_SECTION, "install"); | ||||
|             TaskStackBuilder stackBuilder = TaskStackBuilder.create(magiskManager); | ||||
|             stackBuilder.addParentStack(SplashActivity.class); | ||||
|             stackBuilder.addNextIntent(intent); | ||||
|             PendingIntent pendingIntent = stackBuilder.getPendingIntent(NOTIFICATION_ID, PendingIntent.FLAG_UPDATE_CURRENT); | ||||
|             builder.setContentIntent(pendingIntent); | ||||
|             NotificationManager notificationManager = | ||||
|                     (NotificationManager) magiskManager.getSystemService(Context.NOTIFICATION_SERVICE); | ||||
|             notificationManager.notify(NOTIFICATION_ID, builder.build()); | ||||
|         if (showNotification && magiskManager.updateNotification) { | ||||
|             if (BuildConfig.VERSION_CODE < magiskManager.remoteManagerVersionCode) { | ||||
|                 Utils.showManagerUpdate(magiskManager); | ||||
|             } else if (magiskManager.magiskVersionCode < magiskManager.remoteMagiskVersionCode) { | ||||
|                 Utils.showMagiskUpdate(magiskManager); | ||||
|             } | ||||
|         } | ||||
|         magiskManager.updateCheckDone.trigger(); | ||||
|         super.onPostExecute(v); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -21,7 +21,7 @@ import java.io.InputStream; | ||||
| import java.io.OutputStream; | ||||
| import java.util.List; | ||||
|  | ||||
| public class FlashZip extends SerialTask<Void, String, Integer> { | ||||
| public class FlashZip extends RootTask<Void, String, Integer> { | ||||
|  | ||||
|     private Uri mUri; | ||||
|     private File mCachedFile, mScriptFile, mCheckFile; | ||||
| @@ -68,7 +68,7 @@ public class FlashZip extends SerialTask<Void, String, Integer> { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected boolean unzipAndCheck() throws Exception { | ||||
|     private boolean unzipAndCheck() throws Exception { | ||||
|         ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android"); | ||||
|         List<String> ret; | ||||
|         ret = Utils.readFile(mCheckFile.getPath()); | ||||
| @@ -96,7 +96,7 @@ public class FlashZip extends SerialTask<Void, String, Integer> { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected Integer doInBackground(Void... voids) { | ||||
|     protected Integer doInRoot(Void... voids) { | ||||
|         Logger.dev("FlashZip Running... " + mFilename); | ||||
|         List<String> ret; | ||||
|         try { | ||||
| @@ -124,7 +124,6 @@ public class FlashZip extends SerialTask<Void, String, Integer> { | ||||
|     // -1 = error, manual install; 0 = invalid zip; 1 = success | ||||
|     @Override | ||||
|     protected void onPostExecute(Integer result) { | ||||
|         super.onPostExecute(result); | ||||
|         progress.dismiss(); | ||||
|         switch (result) { | ||||
|             case -1: | ||||
| @@ -138,6 +137,7 @@ public class FlashZip extends SerialTask<Void, String, Integer> { | ||||
|                 onSuccess(); | ||||
|                 break; | ||||
|         } | ||||
|         super.onPostExecute(result); | ||||
|     } | ||||
|  | ||||
|     protected void onSuccess() { | ||||
|   | ||||
| @@ -5,15 +5,17 @@ import android.app.Activity; | ||||
| import com.topjohnwu.magisk.utils.Shell; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
|  | ||||
| public class GetBootBlocks extends SerialTask<Void, Void, Void> { | ||||
| public class GetBootBlocks extends RootTask<Void, Void, Void> { | ||||
|  | ||||
|     public GetBootBlocks(Activity context) { | ||||
|         super(context); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected Void doInBackground(Void... params) { | ||||
|         magiskManager.blockList = Shell.su("ls /dev/block | grep mmc"); | ||||
|     protected Void doInRoot(Void... params) { | ||||
|         magiskManager.blockList = Shell.su( | ||||
|                 "find /dev/block -type b -maxdepth 1 | grep -v -E \"loop|ram|dm-0\"" | ||||
|         ); | ||||
|         if (magiskManager.bootBlock == null) { | ||||
|             magiskManager.bootBlock = Utils.detectBootImage(); | ||||
|         } | ||||
| @@ -23,5 +25,6 @@ public class GetBootBlocks extends SerialTask<Void, Void, Void> { | ||||
|     @Override | ||||
|     protected void onPostExecute(Void v) { | ||||
|         magiskManager.blockDetectionDone.trigger(); | ||||
|         super.onPostExecute(v); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -9,14 +9,14 @@ import com.topjohnwu.magisk.utils.Logger; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
| import com.topjohnwu.magisk.utils.ValueSortedMap; | ||||
|  | ||||
| public class LoadModules extends SerialTask<Void, Void, Void> { | ||||
| public class LoadModules extends RootTask<Void, Void, Void> { | ||||
|  | ||||
|     public LoadModules(Activity context) { | ||||
|         super(context); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected Void doInBackground(Void... voids) { | ||||
|     protected Void doInRoot(Void... voids) { | ||||
|         Logger.dev("LoadModules: Loading modules"); | ||||
|  | ||||
|         magiskManager.moduleMap = new ValueSortedMap<>(); | ||||
| @@ -37,5 +37,6 @@ public class LoadModules extends SerialTask<Void, Void, Void> { | ||||
|     @Override | ||||
|     protected void onPostExecute(Void v) { | ||||
|         magiskManager.moduleLoadDone.trigger(); | ||||
|         super.onPostExecute(v); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -12,14 +12,15 @@ import com.topjohnwu.magisk.utils.ValueSortedMap; | ||||
| import com.topjohnwu.magisk.utils.WebService; | ||||
|  | ||||
| import org.json.JSONArray; | ||||
| import org.json.JSONException; | ||||
| import org.json.JSONObject; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.text.ParseException; | ||||
| import java.text.SimpleDateFormat; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| import java.util.Date; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Locale; | ||||
| import java.util.Map; | ||||
|  | ||||
| @@ -27,99 +28,162 @@ public class LoadRepos extends ParallelTask<Void, Void, Void> { | ||||
|  | ||||
|     public static final String ETAG_KEY = "ETag"; | ||||
|  | ||||
|     private static final String REPO_URL = "https://api.github.com/orgs/Magisk-Modules-Repo/repos"; | ||||
|     private static final String REPO_URL = "https://api.github.com/users/Magisk-Modules-Repo/repos?per_page=100&page=%d"; | ||||
|     private static final String IF_NONE_MATCH = "If-None-Match"; | ||||
|     private static final String LINK_KEY = "Link"; | ||||
|  | ||||
|     private String prefsPath; | ||||
|     private static final int CHECK_ETAG = 0; | ||||
|     private static final int LOAD_NEXT = 1; | ||||
|     private static final int LOAD_PREV = 2; | ||||
|  | ||||
|     private List<String> etags; | ||||
|     private ValueSortedMap<String, Repo> cached, fetched; | ||||
|     private RepoDatabaseHelper repoDB; | ||||
|     private SharedPreferences prefs; | ||||
|  | ||||
|     public LoadRepos(Activity context) { | ||||
|         super(context); | ||||
|         prefsPath = context.getApplicationInfo().dataDir + "/shared_prefs"; | ||||
|         prefs = magiskManager.prefs; | ||||
|         String prefsPath = context.getApplicationInfo().dataDir + "/shared_prefs"; | ||||
|         repoDB = new RepoDatabaseHelper(magiskManager); | ||||
|         // Legacy data cleanup | ||||
|         File old = new File(prefsPath, "RepoMap.xml"); | ||||
|         if (old.exists() || !prefs.getString("repomap", "empty").equals("empty")) { | ||||
|             old.delete(); | ||||
|             prefs.edit().remove("version").remove("repomap").remove(ETAG_KEY).apply(); | ||||
|             repoDB.clearRepo(); | ||||
|         } | ||||
|         etags = new ArrayList<>( | ||||
|                 Arrays.asList(magiskManager.prefs.getString(ETAG_KEY, "").split(","))); | ||||
|     } | ||||
|  | ||||
|     private void loadJSON(String jsonString) throws Exception { | ||||
|         JSONArray jsonArray = new JSONArray(jsonString); | ||||
|  | ||||
|         for (int i = 0; i < jsonArray.length(); i++) { | ||||
|             JSONObject jsonobject = jsonArray.getJSONObject(i); | ||||
|             String id = jsonobject.getString("description"); | ||||
|             String name = jsonobject.getString("name"); | ||||
|             String lastUpdate = jsonobject.getString("pushed_at"); | ||||
|             SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); | ||||
|             Date updatedDate = format.parse(lastUpdate); | ||||
|             Repo repo = cached.get(id); | ||||
|             try { | ||||
|                 if (repo == null) { | ||||
|                     Logger.dev("LoadRepos: Create new repo " + id); | ||||
|                     repo = new Repo(name, updatedDate); | ||||
|                 } else { | ||||
|                     // Popout from cached | ||||
|                     cached.remove(id); | ||||
|                     repo.update(updatedDate); | ||||
|                 } | ||||
|                 if (repo.getId() != null) { | ||||
|                     fetched.put(id, repo); | ||||
|                 } | ||||
|             } catch (BaseModule.CacheModException ignored) {} | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private boolean loadPage(int page, String url, int mode) { | ||||
|         Logger.dev("LoadRepos: Loading page: " + (page + 1)); | ||||
|         Map<String, String> header = new HashMap<>(); | ||||
|         if (mode == CHECK_ETAG && page < etags.size() && !TextUtils.isEmpty(etags.get(page))) { | ||||
|             Logger.dev("ETAG: " + etags.get(page)); | ||||
|             header.put(IF_NONE_MATCH, etags.get(page)); | ||||
|         } | ||||
|         if (url == null) { | ||||
|             url = String.format(Locale.US, REPO_URL, page + 1); | ||||
|         } | ||||
|         String jsonString = WebService.request(url, WebService.GET, header, true); | ||||
|         if (TextUtils.isEmpty(jsonString)) { | ||||
|             // At least check the pages we know | ||||
|             return page + 1 < etags.size() && loadPage(page + 1, null, CHECK_ETAG); | ||||
|         } | ||||
|  | ||||
|         // The request succeed, parse the new stuffs | ||||
|         try { | ||||
|             loadJSON(jsonString); | ||||
|         } catch (Exception e) { | ||||
|             e.printStackTrace(); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Update the ETAG | ||||
|         String newEtag = header.get(ETAG_KEY); | ||||
|         newEtag = newEtag.substring(newEtag.indexOf('\"'), newEtag.lastIndexOf('\"') + 1); | ||||
|         Logger.dev("New ETAG: " + newEtag); | ||||
|         if (page < etags.size()) { | ||||
|             etags.set(page, newEtag); | ||||
|         } else { | ||||
|             etags.add(newEtag); | ||||
|         } | ||||
|  | ||||
|         String links = header.get(LINK_KEY); | ||||
|         if (links != null) { | ||||
|             if (mode == CHECK_ETAG || mode == LOAD_NEXT) { | ||||
|                 // Try to check next page URL | ||||
|                 url = null; | ||||
|                 for (String s : links.split(", ")) { | ||||
|                     if (s.contains("next")) { | ||||
|                         url = s.substring(s.indexOf("<") + 1, s.indexOf(">; ")); | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 if (url != null) { | ||||
|                     loadPage(page + 1, url, LOAD_NEXT); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (mode == CHECK_ETAG || mode == LOAD_PREV) { | ||||
|                 // Try to check prev page URL | ||||
|                 url = null; | ||||
|                 for (String s : links.split(", ")) { | ||||
|                     if (s.contains("prev")) { | ||||
|                         url = s.substring(s.indexOf("<") + 1, s.indexOf(">; ")); | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 if (url != null) { | ||||
|                     loadPage(page - 1, url, LOAD_PREV); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected Void doInBackground(Void... voids) { | ||||
|         Logger.dev("LoadRepos: Loading repos"); | ||||
|  | ||||
|         SharedPreferences prefs = magiskManager.prefs; | ||||
|         cached = repoDB.getRepoMap(false); | ||||
|         fetched = new ValueSortedMap<>(); | ||||
|  | ||||
|         RepoDatabaseHelper dbHelper = new RepoDatabaseHelper(magiskManager); | ||||
|  | ||||
|         // Legacy data cleanup | ||||
|         File old = new File(prefsPath, "RepoMap.xml"); | ||||
|         if (old.exists() || !prefs.getString("repomap", "empty").equals("empty")) { | ||||
|             old.delete(); | ||||
|             prefs.edit().remove("version").remove("repomap").remove(ETAG_KEY).apply(); | ||||
|             dbHelper.clearRepo(); | ||||
|         if (!loadPage(0, null, CHECK_ETAG)) { | ||||
|             magiskManager.repoMap = repoDB.getRepoMap(); | ||||
|             Logger.dev("LoadRepos: No updates, use DB"); | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         Map<String, String> header = new HashMap<>(); | ||||
|         // Get cached ETag to add in the request header | ||||
|         String etag = prefs.getString(ETAG_KEY, ""); | ||||
|         header.put("If-None-Match", etag); | ||||
|         repoDB.addRepoMap(fetched); | ||||
|         repoDB.removeRepo(cached); | ||||
|  | ||||
|         // Make a request to main URL for repo info | ||||
|         String jsonString = WebService.request(REPO_URL, WebService.GET, null, header, false); | ||||
|  | ||||
|         ValueSortedMap<String, Repo> cached = dbHelper.getRepoMap(false), fetched = new ValueSortedMap<>(); | ||||
|  | ||||
|         if (!TextUtils.isEmpty(jsonString)) { | ||||
|             try { | ||||
|                 JSONArray jsonArray = new JSONArray(jsonString); | ||||
|                 // If it gets to this point, the response is valid, update ETag | ||||
|                 etag = WebService.getLastResponseHeader().get(ETAG_KEY).get(0); | ||||
|                 // Maybe bug in Android build tools, sometimes the ETag has crap in it... | ||||
|                 etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1); | ||||
|  | ||||
|                 // Update repo info | ||||
|                 for (int i = 0; i < jsonArray.length(); i++) { | ||||
|                     JSONObject jsonobject = jsonArray.getJSONObject(i); | ||||
|                     String id = jsonobject.getString("description"); | ||||
|                     String name = jsonobject.getString("name"); | ||||
|                     String lastUpdate = jsonobject.getString("pushed_at"); | ||||
|                     SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); | ||||
|                     Date updatedDate; | ||||
|                     try { | ||||
|                         updatedDate = format.parse(lastUpdate); | ||||
|                     } catch (ParseException e) { | ||||
|                         continue; | ||||
|                     } | ||||
|                     Repo repo = cached.get(id); | ||||
|                     try { | ||||
|                         if (repo == null) { | ||||
|                             Logger.dev("LoadRepos: Create new repo " + id); | ||||
|                             repo = new Repo(name, updatedDate); | ||||
|                         } else { | ||||
|                             // Popout from cached | ||||
|                             cached.remove(id); | ||||
|                             Logger.dev("LoadRepos: Update cached repo " + id); | ||||
|                             repo.update(updatedDate); | ||||
|                         } | ||||
|                         if (repo.getId() != null) { | ||||
|                             fetched.put(id, repo); | ||||
|                         } | ||||
|                     } catch (BaseModule.CacheModException ignored) {} | ||||
|  | ||||
|                     // Update the database | ||||
|                     dbHelper.addRepoMap(fetched); | ||||
|                     // The leftover cached are those removed remote, cleanup db | ||||
|                     dbHelper.removeRepo(cached); | ||||
|                     // Update ETag | ||||
|                     prefs.edit().putString(ETAG_KEY, etag).apply(); | ||||
|                 } | ||||
|  | ||||
|             } catch (JSONException e) { | ||||
|                 e.printStackTrace(); | ||||
|             } | ||||
|         // Update ETag | ||||
|         StringBuilder etagBuilder = new StringBuilder(); | ||||
|         for (int i = 0; i < etags.size(); ++i) { | ||||
|             if (i != 0) etagBuilder.append(","); | ||||
|             etagBuilder.append(etags.get(i)); | ||||
|         } | ||||
|         prefs.edit().putString(ETAG_KEY, etagBuilder.toString()).apply(); | ||||
|  | ||||
|         magiskManager.repoMap = dbHelper.getRepoMap(); | ||||
|  | ||||
|         Logger.dev("LoadRepos: Repo load done"); | ||||
|         magiskManager.repoMap = repoDB.getRepoMap(); | ||||
|         Logger.dev("LoadRepos: Done"); | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onPostExecute(Void v) { | ||||
|         magiskManager.repoLoadDone.trigger(); | ||||
|         super.onPostExecute(v); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,12 +2,11 @@ package com.topjohnwu.magisk.asyncs; | ||||
|  | ||||
| import android.app.Activity; | ||||
|  | ||||
| import com.topjohnwu.magisk.MagiskManager; | ||||
| import com.topjohnwu.magisk.utils.Shell; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| public class MagiskHide extends SerialTask<Object, Void, Void> { | ||||
| public class MagiskHide extends RootTask<Object, Void, Void> { | ||||
|  | ||||
|     private boolean isList = false; | ||||
|  | ||||
| @@ -18,9 +17,9 @@ public class MagiskHide extends SerialTask<Object, Void, Void> { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected Void doInBackground(Object... params) { | ||||
|     protected Void doInRoot(Object... params) { | ||||
|         String command = (String) params[0]; | ||||
|         List<String> ret = Shell.su(MagiskManager.MAGISK_HIDE_PATH + command); | ||||
|         List<String> ret = Shell.su("magiskhide --" + command); | ||||
|         if (isList) { | ||||
|             magiskManager.magiskHideList = ret; | ||||
|         } | ||||
| @@ -32,6 +31,7 @@ public class MagiskHide extends SerialTask<Object, Void, Void> { | ||||
|         if (isList) { | ||||
|             magiskManager.magiskHideDone.trigger(); | ||||
|         } | ||||
|         super.onPostExecute(v); | ||||
|     } | ||||
|  | ||||
|     public void add(CharSequence packageName) { | ||||
| @@ -43,17 +43,17 @@ public class MagiskHide extends SerialTask<Object, Void, Void> { | ||||
|     } | ||||
|  | ||||
|     public void enable() { | ||||
|         exec("enable; setprop persist.magisk.hide 1"); | ||||
|         exec("enable"); | ||||
|     } | ||||
|  | ||||
|     public void disable() { | ||||
|         exec("disable; setprop persist.magisk.hide 0"); | ||||
|         exec("disable"); | ||||
|     } | ||||
|  | ||||
|     public void list() { | ||||
|         isList = true; | ||||
|         if (magiskManager == null) return; | ||||
|         exec("list"); | ||||
|         exec("ls"); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -7,9 +7,12 @@ import com.topjohnwu.magisk.MagiskManager; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
|  | ||||
| public abstract class ParallelTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> { | ||||
|  | ||||
|     protected Activity activity; | ||||
|     protected MagiskManager magiskManager; | ||||
|  | ||||
|     private Runnable callback = null; | ||||
|  | ||||
|     public ParallelTask() {} | ||||
|  | ||||
|     public ParallelTask(Activity context) { | ||||
| @@ -17,8 +20,18 @@ public abstract class ParallelTask<Params, Progress, Result> extends AsyncTask<P | ||||
|         magiskManager = Utils.getMagiskManager(context); | ||||
|     } | ||||
|  | ||||
|     @SafeVarargs | ||||
|     public final void exec(Params... params) { | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public void exec(Params... params) { | ||||
|         executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onPostExecute(Result result) { | ||||
|         if (callback != null) callback.run(); | ||||
|     } | ||||
|  | ||||
|     public ParallelTask<Params, Progress, Result> setCallBack(Runnable next) { | ||||
|         callback = next; | ||||
|         return this; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -4,14 +4,9 @@ import android.app.Activity; | ||||
| import android.app.ProgressDialog; | ||||
| import android.net.Uri; | ||||
|  | ||||
| import com.topjohnwu.magisk.MagiskManager; | ||||
| import com.topjohnwu.magisk.R; | ||||
| import com.topjohnwu.magisk.utils.Logger; | ||||
| import com.topjohnwu.magisk.utils.Shell; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
| import com.topjohnwu.magisk.utils.ZipUtils; | ||||
|  | ||||
| import java.io.File; | ||||
|  | ||||
| public class ProcessMagiskZip extends ParallelTask<Void, Void, Boolean> { | ||||
|  | ||||
| @@ -38,26 +33,12 @@ public class ProcessMagiskZip extends ParallelTask<Void, Void, Boolean> { | ||||
|     @Override | ||||
|     protected Boolean doInBackground(Void... params) { | ||||
|         if (Shell.rootAccess()) { | ||||
|             try { | ||||
|                 // We might not have busybox yet, unzip with Java | ||||
|                 // We shall have complete busybox after Magisk installation | ||||
|                 File tempdir = new File(magiskManager.getCacheDir(), "magisk"); | ||||
|                 ZipUtils.unzip(magiskManager.getContentResolver().openInputStream(mUri), tempdir); | ||||
|                 // Running in parallel mode, open new shell | ||||
|                 Shell.su(true, | ||||
|                         "rm -f /dev/.magisk", | ||||
|                         (mBoot != null) ? "echo \"BOOTIMAGE=/dev/block/" + mBoot + "\" >> /dev/.magisk" : "", | ||||
|             synchronized (Shell.lock) { | ||||
|                 Shell.su("rm -f /dev/.magisk", | ||||
|                         (mBoot != null) ? "echo \"BOOTIMAGE=" + mBoot + "\" >> /dev/.magisk" : "", | ||||
|                         "echo \"KEEPFORCEENCRYPT=" + String.valueOf(mEnc) + "\" >> /dev/.magisk", | ||||
|                         "echo \"KEEPVERITY=" + String.valueOf(mVerity) + "\" >> /dev/.magisk", | ||||
|                         "mkdir -p " + MagiskManager.TMP_FOLDER_PATH, | ||||
|                         "cp -af " + tempdir + "/. " + MagiskManager.TMP_FOLDER_PATH + "/magisk", | ||||
|                         "mv -f " + tempdir + "/META-INF " + magiskManager.getCacheDir() + "/META-INF", | ||||
|                         "rm -rf " + tempdir | ||||
|                         "echo \"KEEPVERITY=" + String.valueOf(mVerity) + "\" >> /dev/.magisk" | ||||
|                 ); | ||||
|             } catch (Exception e) { | ||||
|                 Logger.error("ProcessMagiskZip: Error!"); | ||||
|                 e.printStackTrace(); | ||||
|                 return false; | ||||
|             } | ||||
|             return true; | ||||
|         } | ||||
| @@ -68,29 +49,10 @@ public class ProcessMagiskZip extends ParallelTask<Void, Void, Boolean> { | ||||
|     protected void onPostExecute(Boolean result) { | ||||
|         progressDialog.dismiss(); | ||||
|         if (result) { | ||||
|             new FlashZip(activity, mUri) { | ||||
|                 @Override | ||||
|                 protected boolean unzipAndCheck() throws Exception { | ||||
|                     // Don't need to check, as it is downloaded in correct form | ||||
|                     return true; | ||||
|                 } | ||||
|  | ||||
|                 @Override | ||||
|                 protected void onSuccess() { | ||||
|                     new SerialTask<Void, Void, Void>(activity) { | ||||
|                         @Override | ||||
|                         protected Void doInBackground(Void... params) { | ||||
|                             Shell.su("setprop magisk.version " | ||||
|                                     + String.valueOf(magiskManager.remoteMagiskVersion)); | ||||
|                             magiskManager.updateCheckDone.trigger(); | ||||
|                             return null; | ||||
|                         } | ||||
|                     }.exec(); | ||||
|                     super.onSuccess(); | ||||
|                 } | ||||
|             }.exec(); | ||||
|             new FlashZip(activity, mUri).exec(); | ||||
|         } else { | ||||
|             Utils.showUriSnack(activity, mUri); | ||||
|         } | ||||
|         super.onPostExecute(result); | ||||
|     } | ||||
| } | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user