mirror of
				https://github.com/topjohnwu/Magisk
				synced 2025-10-31 10:40:52 +01:00 
			
		
		
		
	Compare commits
	
		
			400 Commits
		
	
	
		
			manager-v5
			...
			manager-v5
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 49ba7ad22e | ||
|   | 6ad33d60f7 | ||
|   | 68c448bc34 | ||
|   | b885ccbd63 | ||
|   | da6f1d0f12 | ||
|   | 4c0d435b6b | ||
|   | 0e717a2de4 | ||
|   | cada862214 | ||
|   | 682c6d4e7b | ||
|   | d0a253c97e | ||
|   | c0e2b3027b | ||
|   | e7dc14b07d | ||
|   | 0da9146e90 | ||
|   | ad05a33e02 | ||
|   | 8224e038a3 | ||
|   | 03c04c2141 | ||
|   | 2e091b04e5 | ||
|   | 60296493fe | ||
|   | 20c20f8f9b | ||
|   | f1d642a4e5 | ||
|   | e0e5ea17a4 | ||
|   | 91a0ba72dc | ||
|   | c54c5a974a | ||
|   | 532b8c54ab | ||
|   | 5ac87891b5 | ||
|   | 2d905ce3fb | ||
|   | 831112abd2 | ||
|   | 153d0f5505 | ||
|   | c78896a335 | ||
|   | 316ec98e0f | ||
|   | cf58545a45 | ||
|   | e86015badc | ||
|   | c8f65fc9a1 | ||
|   | 7684602ea8 | ||
|   | 4601989d4a | ||
|   | 23f697d62b | ||
|   | 4ff39f8817 | ||
|   | 1df41003ec | ||
|   | 1f39ee41ad | ||
|   | 42d8b1ecb9 | ||
|   | a4da7b33e6 | ||
|   | e4ee9e9095 | ||
|   | 77430a282f | ||
|   | e6c1dd532d | ||
|   | d1f301e059 | ||
|   | 1e812c40ce | ||
|   | a949641342 | ||
|   | c231e88a5d | ||
|   | 79c71509f6 | ||
|   | 5dab580cfc | ||
|   | 499a157946 | ||
|   | c5a7ab2415 | ||
|   | 3dd5a6f378 | ||
|   | 7be26a0677 | ||
|   | c183fdd3ca | ||
|   | baa439457e | ||
|   | 4dbcd54b72 | ||
|   | 11062f2d4f | ||
|   | a0466085fe | ||
|   | f2f7d77847 | ||
|   | b2105f2d88 | ||
|   | 4126f3bdcb | ||
|   | 74ccfe6088 | ||
|   | 48085b5573 | ||
|   | 7b9ddc9b3b | ||
|   | 15726a759c | ||
|   | 2c7474ea87 | ||
|   | c726aee643 | ||
|   | c3e94e1480 | ||
|   | 5f1343e5b4 | ||
|   | ffb1303d61 | ||
|   | a0b0d938f0 | ||
|   | 158f5ba7d9 | ||
|   | b8cf40161c | ||
|   | fb96e6a56f | ||
|   | 6668ba2511 | ||
|   | 4668ef3020 | ||
|   | 630f2b7d19 | ||
|   | dde0a4a7c8 | ||
|   | b06f69573d | ||
|   | 8fd03f7434 | ||
|   | 90e4ac2d23 | ||
|   | 956bceae75 | ||
|   | c663be86de | ||
|   | aca78baecf | ||
|   | fbcf6b7954 | ||
|   | 84123222aa | ||
|   | e9dbcf693d | ||
|   | 1cd0a9d48f | ||
|   | 1b48e44914 | ||
|   | 0a398f03fd | ||
|   | 15ed3e52f2 | ||
|   | 8990919dab | ||
|   | e5638e4b15 | ||
|   | 404c6fac9a | ||
|   | 267395bfa2 | ||
|   | 920fc5ae99 | ||
|   | 92ed0ae51b | ||
|   | 6764a98409 | ||
|   | fd7b5f393a | ||
|   | 2ca528f93f | ||
|   | ce2e6b7d35 | ||
|   | 684c5d225a | ||
|   | b75018b03b | ||
|   | 41499d4b3c | ||
|   | 383c97c303 | ||
|   | 74b54ef371 | ||
|   | bbf7b4db79 | ||
|   | c61f0acab5 | ||
|   | 398af123b2 | ||
|   | 315fa9d7d3 | ||
|   | fb5e8ef40c | ||
|   | e79d764148 | ||
|   | ebbee0dc43 | ||
|   | ed0c16e201 | ||
|   | 209fdf349a | ||
|   | f49f2afacd | ||
|   | 8c6330a3c4 | ||
|   | 337b777125 | ||
|   | 1b756e8d96 | ||
|   | 52d478df1a | ||
|   | 0c782edf21 | ||
|   | e3948d295e | ||
|   | 5f2c742a5c | ||
|   | b30c77aab9 | ||
|   | a5916b9c49 | ||
|   | 453180e30b | ||
|   | 8bd432d391 | ||
|   | c9d3e20aef | ||
|   | d5408d1f09 | ||
|   | f334532aba | ||
|   | be77c09f3d | ||
|   | 7de6a92753 | ||
|   | 36f76f5a14 | ||
|   | b84523d557 | ||
|   | 2c78c415e9 | ||
|   | 79ccb30dd2 | ||
|   | 3c566becf6 | ||
|   | 151ca593af | ||
|   | 4132eacba0 | ||
|   | 06e6151816 | ||
|   | 70277d4edd | ||
|   | d21d2f1a9c | ||
|   | 74a7be996f | ||
|   | 3f38579529 | ||
|   | 4d5a9f6e15 | ||
|   | 41f47acd76 | ||
|   | 821dcaa7c7 | ||
|   | 7135d26419 | ||
|   | f7fd354dce | ||
|   | 0c69a65bc4 | ||
|   | 2f2ca5eab4 | ||
|   | df9c40c035 | ||
|   | 25b67017e4 | ||
|   | bc9c3346f3 | ||
|   | 1db7e19fe8 | ||
|   | 102c03ce2b | ||
|   | ec19eb4455 | ||
|   | 6d9924d50e | ||
|   | 16c4d74274 | ||
|   | e4af5fd36a | ||
|   | 702775493a | ||
|   | b2ae826066 | ||
|   | cc3e9990fa | ||
|   | 271cbddd5e | ||
|   | c1423ca9ad | ||
|   | 74379150a1 | ||
|   | c840a30c30 | ||
|   | ae5277a898 | ||
|   | bffa837825 | ||
|   | b9e7d0faea | ||
|   | 860b08d9ed | ||
|   | 691dc1d49e | ||
|   | 9d6886d367 | ||
|   | 9589b68f5a | ||
|   | 28d88af1af | ||
|   | 8b5acd1849 | ||
|   | 33dc63a7fd | ||
|   | d0a86385b7 | ||
|   | 50a49e2c8c | ||
|   | c60adb113e | ||
|   | aee015e8f6 | ||
|   | bf6af29205 | ||
|   | 329905d472 | ||
|   | 00d450d262 | ||
|   | 2365d1bd20 | ||
|   | 5b385c18e5 | ||
|   | 98c0434ec0 | ||
|   | f318d0a3bc | ||
|   | 27f5b410c0 | ||
|   | 3f55be9676 | ||
|   | b05d2d3a2d | ||
|   | 19af5f9e0b | ||
|   | f37f330670 | ||
|   | 40082d4571 | ||
|   | 00d655f346 | ||
|   | 821726e7c0 | ||
|   | 759e905c3c | ||
|   | 8bf7e42913 | ||
|   | 0dcd073554 | ||
|   | 2fe35d578d | ||
|   | 8d139e156e | ||
|   | 7c2849356a | ||
|   | 0025ffd1c0 | ||
|   | 2ef7146642 | ||
|   | 1b27e69e40 | ||
|   | 8e7b757efd | ||
|   | 1ab543cea1 | ||
|   | a3f86903e4 | ||
|   | c239c305ab | ||
|   | 2e02af994e | ||
|   | 836d9afe17 | ||
|   | 007a352742 | ||
|   | e526e5659e | ||
|   | 4a5227c7bf | ||
|   | c2c151ec4c | ||
|   | 452096e7e4 | ||
|   | 50c2a9859e | ||
|   | 677b667307 | ||
|   | 1adf331268 | ||
|   | 349b3e961b | ||
|   | 96650c06f0 | ||
|   | 26038a0a07 | ||
|   | 6a148b5dd9 | ||
|   | 0e109ef979 | ||
|   | de2285d5e9 | ||
|   | b2483ba437 | ||
|   | a82a5e5a49 | ||
|   | d161a02e71 | ||
|   | d2b6a700b1 | ||
|   | af203cef24 | ||
|   | 673e917e76 | ||
|   | a3bd41db54 | ||
|   | 0d9527921a | ||
|   | f0e4aec0af | ||
|   | b0d65b5edd | ||
|   | 75532ef591 | ||
|   | 9a6d1bd700 | ||
|   | a7ed6c15d3 | ||
|   | 5ee49ba065 | ||
|   | d34bd47bea | ||
|   | f17792380b | ||
|   | c11920110e | ||
|   | ec5a993fea | ||
|   | d250c2cc89 | ||
|   | 767e73f40c | ||
|   | 3f699c9d2f | ||
|   | 50dbd9befd | ||
|   | 760e01bf92 | ||
|   | 543f435b1e | ||
|   | 91337218b3 | ||
|   | afff3c0a49 | ||
|   | a1871e4bc3 | ||
|   | 3aa0294cd4 | ||
|   | 310b266251 | ||
|   | 21b1b5098e | ||
|   | a3a4a5d8a5 | ||
|   | 270536f33c | ||
|   | 66bb433cc6 | ||
|   | bd4ef1a03a | ||
|   | aa2d9a3bf1 | ||
|   | fd6cbb138c | ||
|   | aa75c8e5e4 | ||
|   | c461fc6daa | ||
|   | 96eaa833f5 | ||
|   | 863b13a694 | ||
|   | e6fea4e6dd | ||
|   | 83bfc13056 | ||
|   | bc4f09209b | ||
|   | 967ca17238 | ||
|   | 595c72147c | ||
|   | f3c3b5a649 | ||
|   | 1cd2c5e653 | ||
|   | b2873dd44b | ||
|   | bb80ab4026 | ||
|   | 80cabb338b | ||
|   | 2c69e2c151 | ||
|   | c1dd23f5e0 | ||
|   | f93624a41c | ||
|   | 9f4559a059 | ||
|   | fd05cad303 | ||
|   | d58b06e493 | ||
|   | 2f0b549027 | ||
|   | 87dbd7e541 | ||
|   | 96e5da36be | ||
|   | 43745edac0 | ||
|   | f5ceee547c | ||
|   | b612bce779 | ||
|   | 2e88e5e9c7 | ||
|   | 9a7aa25c90 | ||
|   | c4420fe932 | ||
|   | a5260f3a95 | ||
|   | 47ccf4b1f5 | ||
|   | a356b21895 | ||
|   | 614a36c888 | ||
|   | f520fe36bd | ||
|   | 7273a1c34d | ||
|   | dc45cbce37 | ||
|   | 708d8f75c0 | ||
|   | bd37d90228 | ||
|   | b1ad691464 | ||
|   | f4e7baf31e | ||
|   | c0e60c41f2 | ||
|   | c8dad43e00 | ||
|   | a8f124704d | ||
|   | eed2816491 | ||
|   | a6334b3e35 | ||
|   | 334beebfeb | ||
|   | 13dad848bd | ||
|   | e518f4cef8 | ||
|   | c8fd5da2da | ||
|   | 3a74729ecc | ||
|   | 49c672ac4d | ||
|   | b570cb5b77 | ||
|   | 97bf388471 | ||
|   | 1a32aaea6f | ||
|   | 4635883dec | ||
|   | 3ba6db4a50 | ||
|   | 2f1de25747 | ||
|   | f60fd42ac0 | ||
|   | ecc8f9c792 | ||
|   | e295dfdcf7 | ||
|   | fc42c25390 | ||
|   | 27d5858e06 | ||
|   | e1ef732b60 | ||
|   | 9840b95c21 | ||
|   | a6f8446d81 | ||
|   | c1c844c830 | ||
|   | 389299afd1 | ||
|   | 826543a291 | ||
|   | 4ac83cfded | ||
|   | 64c363ce53 | ||
|   | cca4347bf9 | ||
|   | 3ae3d4926a | ||
|   | 36025d6d9f | ||
|   | e171362e3e | ||
|   | 3e0bf2ae15 | ||
|   | 07aa9f4b8b | ||
|   | b2d9f3fc64 | ||
|   | 5fb3e9167e | ||
|   | 99c74b31be | ||
|   | ce5b13824e | ||
|   | c39170c42e | ||
|   | fd19fbf300 | ||
|   | 166469827f | ||
|   | a34ed538b6 | ||
|   | 5f22d3e055 | ||
|   | fdd700f3e5 | ||
|   | adf930f126 | ||
|   | 05f41928cd | ||
|   | 2ee0829871 | ||
|   | 743560825d | ||
|   | e3d84ac349 | ||
|   | 266c832b30 | ||
|   | f5374a024e | ||
|   | 4956d826fb | ||
|   | f5cc2af5d0 | ||
|   | 5880d4a6ec | ||
|   | ae05dce958 | ||
|   | 9ebe372a9a | ||
|   | e6e04cc5b3 | ||
|   | 12352510fd | ||
|   | 2b3d927937 | ||
|   | a8890740f5 | ||
|   | f60d7ee54b | ||
|   | 896ca2ef6b | ||
|   | c036f6d529 | ||
|   | 6f457c0c59 | ||
|   | 13bf1b27b4 | ||
|   | f742bb1c47 | ||
|   | aa0b9e2db2 | ||
|   | c10076f7ed | ||
|   | bcd92499f2 | ||
|   | b2bb0d4f72 | ||
|   | e140481f14 | ||
|   | 186bd11463 | ||
|   | a0490d6687 | ||
|   | beef740ade | ||
|   | 2ac7786a90 | ||
|   | a3fb5e910f | ||
|   | 319afe86b5 | ||
|   | 762ab66b86 | ||
|   | 0c239a42de | ||
|   | e9322fba26 | ||
|   | 39b6df27b3 | ||
|   | b1ee284e7f | ||
|   | e986332bf2 | ||
|   | 48f9b27381 | ||
|   | 42a6e0dd10 | ||
|   | d4798b02ac | ||
|   | 963edfe8ab | ||
|   | 53237f3ae0 | ||
|   | 64da9281a4 | ||
|   | ab7fd9799d | ||
|   | f6bcc84251 | ||
|   | 35dc3d9df9 | ||
|   | 566714a75d | ||
|   | c92f30b122 | ||
|   | 294ad094c4 | ||
|   | c1a0f520f9 | 
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -5,8 +5,8 @@ | ||||
| /build | ||||
| app/release | ||||
| *.hprof | ||||
| app/.externalNativeBuild/ | ||||
| *.sh | ||||
| .externalNativeBuild/ | ||||
| src/full/res/raw/util_functions.sh | ||||
| public.certificate.x509.pem | ||||
| private.key.pk8 | ||||
| *.apk | ||||
|   | ||||
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,7 +1,7 @@ | ||||
| # Magisk Manager | ||||
| This is one of the submodules used in Magisk. The project is licensed under GPL v3 (or newer). | ||||
| More info are written in the [Magisk Main Repo](https://github.com/topjohnwu/Magisk) | ||||
| This repo is no longer an independent component. It is a submodule of the [Magisk Project](https://github.com/topjohnwu/Magisk). | ||||
|  | ||||
| ## Building Notes | ||||
| You need to install CMake and NDK to build the zipadjust library. | ||||
| There are several files required to let Magisk Manager work properly, and they can be copied by using the build script in the [Magisk Main Repo](https://github.com/topjohnwu/Magisk). These files are: `magisk_uninstaller.sh`, `util_functions.sh`, `public.certificate.x509.pem`, and `private.key.pk8` under the `app/src/main/assets` folder. | ||||
| # Translations | ||||
| The default (English) string resources are scattered in these files: `src/full/res/values/strings.xml`, `src/main/res/values/strings.xml`, `src/stub/res/values/strings.xml`.   | ||||
| Place the translated XMLs in the corresponding folder to the locale.   | ||||
| Translations are highly appreciated via pull requests here on Github. | ||||
|   | ||||
							
								
								
									
										1
									
								
								app/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								app/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1 +0,0 @@ | ||||
| /build | ||||
| @@ -1,68 +0,0 @@ | ||||
| apply plugin: 'com.android.application' | ||||
|  | ||||
| android { | ||||
|     compileSdkVersion 26 | ||||
|     buildToolsVersion "26.0.1" | ||||
|  | ||||
|     defaultConfig { | ||||
|         applicationId "com.topjohnwu.magisk" | ||||
|         minSdkVersion 21 | ||||
|         targetSdkVersion 26 | ||||
|         versionCode 55 | ||||
|         versionName "5.3.5" | ||||
|         ndk { | ||||
|             moduleName 'zipadjust' | ||||
|             abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' | ||||
|         } | ||||
|         javaCompileOptions { | ||||
|             annotationProcessorOptions { | ||||
|                 argument('butterknife.debuggable', 'false') | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     buildTypes { | ||||
|         release { | ||||
|             minifyEnabled true | ||||
|             shrinkResources true | ||||
|             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | ||||
|         } | ||||
|     } | ||||
|     compileOptions { | ||||
|         sourceCompatibility JavaVersion.VERSION_1_8 | ||||
|         targetCompatibility JavaVersion.VERSION_1_8 | ||||
|     } | ||||
|     dexOptions { | ||||
|         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 { | ||||
|     implementation fileTree(include: ['*.jar'], dir: 'libs') | ||||
|     implementation project(':resource') | ||||
|     implementation 'com.android.support:recyclerview-v7:26.1.0' | ||||
|     implementation 'com.android.support:cardview-v7:26.1.0' | ||||
|     implementation 'com.android.support:design:26.1.0' | ||||
|     implementation 'com.android.support:support-v4:26.1.0' | ||||
|     implementation 'com.jakewharton:butterknife:8.8.1' | ||||
|     implementation 'com.atlassian.commonmark:commonmark:0.9.0' | ||||
|     implementation 'org.bouncycastle:bcprov-jdk15on:1.57' | ||||
|     implementation 'org.bouncycastle:bcpkix-jdk15on:1.57' | ||||
|     implementation 'org.kamranzafar:jtar:2.3' | ||||
|     implementation 'com.google.android.gms:play-services-safetynet:9.0.1' | ||||
|     annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' | ||||
| } | ||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 73 KiB | 
| @@ -1,133 +0,0 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.content.Intent; | ||||
| import android.net.Uri; | ||||
| import android.os.Bundle; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.v7.app.ActionBar; | ||||
| import android.support.v7.app.AlertDialog; | ||||
| import android.support.v7.widget.Toolbar; | ||||
| import android.text.Html; | ||||
| import android.text.Spanned; | ||||
| import android.text.TextUtils; | ||||
| import android.text.method.LinkMovementMethod; | ||||
| import android.view.View; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import com.topjohnwu.magisk.components.AboutCardRow; | ||||
| import com.topjohnwu.magisk.components.Activity; | ||||
| import com.topjohnwu.magisk.components.AlertDialogBuilder; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
|  | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
|  | ||||
| public class AboutActivity extends Activity { | ||||
|  | ||||
|     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"; | ||||
|  | ||||
|     @BindView(R.id.toolbar) Toolbar toolbar; | ||||
|     @BindView(R.id.app_version_info) AboutCardRow appVersionInfo; | ||||
|     @BindView(R.id.app_changelog) AboutCardRow appChangelog; | ||||
|     @BindView(R.id.app_developers) AboutCardRow appDevelopers; | ||||
|     @BindView(R.id.app_translators) AboutCardRow appTranslators; | ||||
|     @BindView(R.id.app_source_code) AboutCardRow appSourceCode; | ||||
|     @BindView(R.id.support_thread) AboutCardRow supportThread; | ||||
|     @BindView(R.id.donation) AboutCardRow donation; | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(@Nullable Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         if (getMagiskManager().isDarkTheme) { | ||||
|             setTheme(R.style.AppTheme_Transparent_Dark); | ||||
|         } | ||||
|         setContentView(R.layout.activity_about); | ||||
|         ButterKnife.bind(this); | ||||
|  | ||||
|         setSupportActionBar(toolbar); | ||||
|         toolbar.setNavigationOnClickListener(view -> finish()); | ||||
|  | ||||
|         ActionBar ab = getSupportActionBar(); | ||||
|         if (ab != null) { | ||||
|             ab.setTitle(R.string.about); | ||||
|             ab.setDisplayHomeAsUpEnabled(true); | ||||
|         } | ||||
|  | ||||
|         appVersionInfo.setSummary(BuildConfig.VERSION_NAME); | ||||
|  | ||||
|         String changes = null; | ||||
|         try (InputStream is = getAssets().open("changelog.html")) { | ||||
|             int size = is.available(); | ||||
|  | ||||
|             byte[] buffer = new byte[size]; | ||||
|             is.read(buffer); | ||||
|  | ||||
|             changes = new String(buffer); | ||||
|         } catch (IOException ignored) { | ||||
|         } | ||||
|  | ||||
|         appChangelog.removeSummary(); | ||||
|         if (changes == null) { | ||||
|             appChangelog.setVisibility(View.GONE); | ||||
|         } else { | ||||
|             Spanned result; | ||||
|             if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { | ||||
|                 result = Html.fromHtml(changes, Html.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE); | ||||
|             } else { | ||||
|                 result = Html.fromHtml(changes); | ||||
|             } | ||||
|             appChangelog.setOnClickListener(v -> { | ||||
|                 AlertDialog d = new AlertDialogBuilder(this) | ||||
|                         .setTitle(R.string.app_changelog) | ||||
|                         .setMessage(result) | ||||
|                         .setPositiveButton(android.R.string.ok, null) | ||||
|                         .show(); | ||||
|  | ||||
|                 //noinspection ConstantConditions | ||||
|                 ((TextView) d.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance()); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         appDevelopers.removeSummary(); | ||||
|         appDevelopers.setOnClickListener(view -> { | ||||
|             Spanned result; | ||||
|             if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { | ||||
|                 result = Html.fromHtml(getString(R.string.app_developers_), Html.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE); | ||||
|             } else { | ||||
|                 result = Html.fromHtml(getString(R.string.app_developers_)); | ||||
|             } | ||||
|             AlertDialog d = new AlertDialogBuilder(this) | ||||
|                     .setTitle(R.string.app_developers) | ||||
|                     .setMessage(result) | ||||
|                     .setPositiveButton(android.R.string.ok, null) | ||||
|                     .create(); | ||||
|  | ||||
|             d.show(); | ||||
|             //noinspection ConstantConditions | ||||
|             ((TextView) d.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance()); | ||||
|         }); | ||||
|  | ||||
|         String translators = getString(R.string.translators); | ||||
|         if (TextUtils.isEmpty(translators)) { | ||||
|             appTranslators.setVisibility(View.GONE); | ||||
|         } else { | ||||
|             appTranslators.setSummary(translators); | ||||
|         } | ||||
|  | ||||
|         appSourceCode.removeSummary(); | ||||
|         appSourceCode.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(SOURCE_CODE_URL)))); | ||||
|  | ||||
|         supportThread.removeSummary(); | ||||
|         supportThread.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(XDA_THREAD)))); | ||||
|  | ||||
|         donation.removeSummary(); | ||||
|         donation.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(DONATION_URL)))); | ||||
|  | ||||
|         setFloating(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,151 +0,0 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.content.Intent; | ||||
| import android.net.Uri; | ||||
| import android.os.Bundle; | ||||
| import android.support.v7.app.ActionBar; | ||||
| import android.support.v7.widget.RecyclerView; | ||||
| import android.support.v7.widget.Toolbar; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.Button; | ||||
| import android.widget.LinearLayout; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import com.topjohnwu.magisk.asyncs.FlashZip; | ||||
| import com.topjohnwu.magisk.asyncs.InstallMagisk; | ||||
| import com.topjohnwu.magisk.components.Activity; | ||||
| import com.topjohnwu.magisk.utils.AdaptiveList; | ||||
| import com.topjohnwu.magisk.utils.Shell; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
| import butterknife.OnClick; | ||||
|  | ||||
| public class FlashActivity extends Activity { | ||||
|  | ||||
|     public static final String SET_ACTION = "action"; | ||||
|     public static final String SET_BOOT = "boot"; | ||||
|     public static final String SET_ENC = "enc"; | ||||
|     public static final String SET_VERITY = "verity"; | ||||
|  | ||||
|     public static final String FLASH_ZIP = "flash"; | ||||
|     public static final String PATCH_BOOT = "patch"; | ||||
|     public static final String FLASH_MAGISK = "magisk"; | ||||
|  | ||||
|     @BindView(R.id.toolbar) Toolbar toolbar; | ||||
|     @BindView(R.id.flash_logs) RecyclerView flashLogs; | ||||
|     @BindView(R.id.button_panel) LinearLayout buttonPanel; | ||||
|     @BindView(R.id.reboot) Button reboot; | ||||
|  | ||||
|     @OnClick(R.id.no_thanks) | ||||
|     public void dismiss() { | ||||
|         finish(); | ||||
|     } | ||||
|  | ||||
|     @OnClick(R.id.reboot) | ||||
|     public void reboot() { | ||||
|         getShell().su_raw("reboot"); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         setContentView(R.layout.activity_flash); | ||||
|         ButterKnife.bind(this); | ||||
|         AdaptiveList<String> rootShellOutput = new AdaptiveList<>(flashLogs); | ||||
|         setSupportActionBar(toolbar); | ||||
|         ActionBar ab = getSupportActionBar(); | ||||
|         if (ab != null) { | ||||
|             ab.setTitle(R.string.flashing); | ||||
|         } | ||||
|         setFloating(); | ||||
|         if (!Shell.rootAccess()) | ||||
|             reboot.setVisibility(View.GONE); | ||||
|  | ||||
|         flashLogs.setAdapter(new FlashLogAdapter(rootShellOutput)); | ||||
|  | ||||
|         // We must receive a Uri of the target zip | ||||
|         Intent intent = getIntent(); | ||||
|         Uri uri = intent.getData(); | ||||
|  | ||||
|         boolean keepEnc = intent.getBooleanExtra(SET_ENC, false); | ||||
|         boolean keepVerity = intent.getBooleanExtra(SET_VERITY, false); | ||||
|  | ||||
|         switch (getIntent().getStringExtra(SET_ACTION)) { | ||||
|             case FLASH_ZIP: | ||||
|                 new FlashZip(this, uri, rootShellOutput) | ||||
|                         .setCallBack(() -> buttonPanel.setVisibility(View.VISIBLE)) | ||||
|                         .exec(); | ||||
|                 break; | ||||
|             case PATCH_BOOT: | ||||
|                 new InstallMagisk(this, rootShellOutput, uri, keepEnc, keepVerity, (Uri) intent.getParcelableExtra(SET_BOOT)) | ||||
|                         .setCallBack(() -> buttonPanel.setVisibility(View.VISIBLE)) | ||||
|                         .exec(); | ||||
|                 break; | ||||
|             case FLASH_MAGISK: | ||||
|                 String boot = intent.getStringExtra(SET_BOOT); | ||||
|                 if (getMagiskManager().remoteMagiskVersionCode < 1370) { | ||||
|                     // Use legacy installation method | ||||
|                     getShell().su_raw( | ||||
|                             "echo \"BOOTIMAGE=" + boot + "\" > /dev/.magisk", | ||||
|                             "echo \"KEEPFORCEENCRYPT=" + keepEnc + "\" >> /dev/.magisk", | ||||
|                             "echo \"KEEPVERITY=" + keepVerity + "\" >> /dev/.magisk" | ||||
|                     ); | ||||
|                     new FlashZip(this, uri, rootShellOutput) | ||||
|                             .setCallBack(() -> buttonPanel.setVisibility(View.VISIBLE)) | ||||
|                             .exec(); | ||||
|                 } else { | ||||
|                     // Use new installation method | ||||
|                     new InstallMagisk(this, rootShellOutput, uri, keepEnc, keepVerity, boot) | ||||
|                             .setCallBack(() -> buttonPanel.setVisibility(View.VISIBLE)) | ||||
|                             .exec(); | ||||
|                 } | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onBackPressed() { | ||||
|         // Prevent user accidentally press back button | ||||
|     } | ||||
|  | ||||
|     private static class FlashLogAdapter extends RecyclerView.Adapter<ViewHolder> { | ||||
|  | ||||
|         private List<String> mList; | ||||
|  | ||||
|         FlashLogAdapter(List<String> list) { | ||||
|             mList = list; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { | ||||
|             View view = LayoutInflater.from(parent.getContext()) | ||||
|                     .inflate(R.layout.list_item_flashlog, parent, false); | ||||
|             return new ViewHolder(view); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void onBindViewHolder(ViewHolder holder, int position) { | ||||
|             holder.text.setText(mList.get(position)); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public int getItemCount() { | ||||
|             return mList.size(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static class ViewHolder extends RecyclerView.ViewHolder { | ||||
|  | ||||
|         @BindView(R.id.textView) TextView text; | ||||
|  | ||||
|         public ViewHolder(View itemView) { | ||||
|             super(itemView); | ||||
|             ButterKnife.bind(this, itemView); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,231 +0,0 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.Manifest; | ||||
| import android.annotation.SuppressLint; | ||||
| import android.os.Bundle; | ||||
| import android.os.Environment; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.design.widget.Snackbar; | ||||
| import android.text.TextUtils; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.Menu; | ||||
| import android.view.MenuInflater; | ||||
| import android.view.MenuItem; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.HorizontalScrollView; | ||||
| import android.widget.ProgressBar; | ||||
| import android.widget.ScrollView; | ||||
| import android.widget.TextView; | ||||
| import android.widget.Toast; | ||||
|  | ||||
| import com.topjohnwu.magisk.asyncs.ParallelTask; | ||||
| import com.topjohnwu.magisk.components.Fragment; | ||||
| import com.topjohnwu.magisk.components.SnackbarMaker; | ||||
| import com.topjohnwu.magisk.utils.Shell; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.FileWriter; | ||||
| import java.io.IOException; | ||||
| import java.util.Calendar; | ||||
|  | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
| import butterknife.Unbinder; | ||||
|  | ||||
| public class MagiskLogFragment extends Fragment { | ||||
|  | ||||
|     private static final String MAGISK_LOG = "/cache/magisk.log"; | ||||
|  | ||||
|     private Unbinder unbinder; | ||||
|  | ||||
|     @BindView(R.id.txtLog) TextView txtLog; | ||||
|     @BindView(R.id.svLog) ScrollView svLog; | ||||
|     @BindView(R.id.hsvLog) HorizontalScrollView hsvLog; | ||||
|     @BindView(R.id.progressBar) ProgressBar progressBar; | ||||
|  | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { | ||||
|         View view = inflater.inflate(R.layout.fragment_magisk_log, container, false); | ||||
|         unbinder = ButterKnife.bind(this, view); | ||||
|         setHasOptionsMenu(true); | ||||
|  | ||||
|         txtLog.setTextIsSelectable(true); | ||||
|  | ||||
|         new LogManager().read(); | ||||
|  | ||||
|         return view; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onStart() { | ||||
|         super.onStart(); | ||||
|         getActivity().setTitle(R.string.log); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onResume() { | ||||
|         super.onResume(); | ||||
|         new LogManager().read(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroyView() { | ||||
|         super.onDestroyView(); | ||||
|         unbinder.unbind(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { | ||||
|         inflater.inflate(R.menu.menu_log, menu); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean onOptionsItemSelected(MenuItem item) { | ||||
|         switch (item.getItemId()) { | ||||
|             case R.id.menu_refresh: | ||||
|                 new LogManager().read(); | ||||
|                 return true; | ||||
|             case R.id.menu_save: | ||||
|                 Utils.runWithPermission(getActivity(), | ||||
|                         Manifest.permission.WRITE_EXTERNAL_STORAGE, | ||||
|                         () -> new LogManager().save()); | ||||
|                 return true; | ||||
|             case R.id.menu_clear: | ||||
|                 new LogManager().clear(); | ||||
|                 return true; | ||||
|             default: | ||||
|                 return true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private class LogManager extends ParallelTask<Object, Void, Object> { | ||||
|  | ||||
|         private int mode; | ||||
|         private File targetFile; | ||||
|  | ||||
|         LogManager() { | ||||
|             super(MagiskLogFragment.this.getActivity()); | ||||
|         } | ||||
|  | ||||
|         @SuppressLint("DefaultLocale") | ||||
|         @Override | ||||
|         protected Object doInBackground(Object... params) { | ||||
|             mode = (int) params[0]; | ||||
|             switch (mode) { | ||||
|                 case 0: | ||||
|                     StringBuildingList logList = new StringBuildingList(); | ||||
|                     getShell().su(logList, "cat " + MAGISK_LOG); | ||||
|                     return logList.toString(); | ||||
|  | ||||
|                 case 1: | ||||
|                     getShell().su_raw("echo -n > " + MAGISK_LOG); | ||||
|                     SnackbarMaker.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show(); | ||||
|                     return ""; | ||||
|  | ||||
|                 case 2: | ||||
|                     Calendar now = Calendar.getInstance(); | ||||
|                     String filename = String.format( | ||||
|                             "magisk_%s_%04d%02d%02d_%02d%02d%02d.log", "error", | ||||
|                             now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1, | ||||
|                             now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY), | ||||
|                             now.get(Calendar.MINUTE), now.get(Calendar.SECOND)); | ||||
|  | ||||
|                     targetFile = new File(Environment.getExternalStorageDirectory() + "/MagiskManager/" + filename); | ||||
|  | ||||
|                     if ((!targetFile.getParentFile().exists() && !targetFile.getParentFile().mkdirs()) | ||||
|                             || (targetFile.exists() && !targetFile.delete())) { | ||||
|                         return false; | ||||
|                     } | ||||
|  | ||||
|                     try (FileWriter out = new FileWriter(targetFile)) { | ||||
|                         FileWritingList fileWritingList = new FileWritingList(out); | ||||
|                         getShell().su(fileWritingList, "cat " + MAGISK_LOG); | ||||
|                     } catch (IOException e) { | ||||
|                         e.printStackTrace(); | ||||
|                         return false; | ||||
|                     } | ||||
|                     return true; | ||||
|             } | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         protected void onPostExecute(Object o) { | ||||
|             if (o == null) return; | ||||
|             switch (mode) { | ||||
|                 case 0: | ||||
|                 case 1: | ||||
|                     String llog = (String) o; | ||||
|                     progressBar.setVisibility(View.GONE); | ||||
|                     if (TextUtils.isEmpty(llog)) | ||||
|                         txtLog.setText(R.string.log_is_empty); | ||||
|                     else | ||||
|                         txtLog.setText(llog); | ||||
|                     svLog.post(() -> svLog.scrollTo(0, txtLog.getHeight())); | ||||
|                     hsvLog.post(() -> hsvLog.scrollTo(0, 0)); | ||||
|                     break; | ||||
|                 case 2: | ||||
|                     boolean bool = (boolean) o; | ||||
|                     if (bool) { | ||||
|                         getMagiskManager().toast(targetFile.toString(), Toast.LENGTH_LONG); | ||||
|                     } else { | ||||
|                         getMagiskManager().toast(R.string.logs_save_failed, Toast.LENGTH_LONG); | ||||
|                     } | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         void read() { | ||||
|             exec(0); | ||||
|         } | ||||
|  | ||||
|         void clear() { | ||||
|             exec(1); | ||||
|         } | ||||
|  | ||||
|         void save() { | ||||
|             exec(2); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static class StringBuildingList extends Shell.AbstractList<String> { | ||||
|  | ||||
|         StringBuilder builder; | ||||
|  | ||||
|         StringBuildingList() { | ||||
|             builder = new StringBuilder(); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public boolean add(String s) { | ||||
|             builder.append(s).append("\n"); | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public String toString() { | ||||
|             return builder.toString(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static class FileWritingList extends Shell.AbstractList<String> { | ||||
|  | ||||
|         private FileWriter writer; | ||||
|  | ||||
|         FileWritingList(FileWriter out) { | ||||
|             writer = out; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public boolean add(String s) { | ||||
|             try { | ||||
|                 writer.write(s + "\n"); | ||||
|             } catch (IOException ignored) {} | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,24 +0,0 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.content.Intent; | ||||
| import android.os.Bundle; | ||||
|  | ||||
| import com.topjohnwu.magisk.components.Activity; | ||||
|  | ||||
| public class SplashActivity extends Activity { | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|  | ||||
|         getMagiskManager().startup(); | ||||
|  | ||||
|         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(); | ||||
|     } | ||||
| } | ||||
| @@ -1,65 +0,0 @@ | ||||
| package com.topjohnwu.magisk.asyncs; | ||||
|  | ||||
| import android.content.Context; | ||||
| import android.os.Build; | ||||
|  | ||||
| import com.topjohnwu.magisk.MagiskManager; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
| import com.topjohnwu.magisk.utils.WebService; | ||||
|  | ||||
| import java.io.BufferedInputStream; | ||||
| import java.io.File; | ||||
| import java.io.FileOutputStream; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
|  | ||||
| public class DownloadBusybox extends ParallelTask<Void, Void, Void> { | ||||
|  | ||||
|     private static final String BUSYBOX_ARM = "https://github.com/topjohnwu/ndk-busybox/releases/download/1.27.2/busybox-arm"; | ||||
|     private static final String BUSYBOX_X86 = "https://github.com/topjohnwu/ndk-busybox/releases/download/1.27.2/busybox-x86"; | ||||
|  | ||||
|     private File busybox; | ||||
|  | ||||
|     public DownloadBusybox(Context context) { | ||||
|         super(context); | ||||
|         busybox = new File(context.getCacheDir(), "busybox"); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected Void doInBackground(Void... voids) { | ||||
|         Context context = getMagiskManager(); | ||||
|         Utils.removeItem(getShell(), context.getApplicationInfo().dataDir + "/busybox"); | ||||
|         try { | ||||
|             FileOutputStream out  = new FileOutputStream(busybox); | ||||
|             InputStream in = WebService.request(WebService.GET, | ||||
|                     Build.SUPPORTED_32_BIT_ABIS[0].contains("x86") ? | ||||
|                             BUSYBOX_X86 : | ||||
|                             BUSYBOX_ARM, | ||||
|                     null | ||||
|             ); | ||||
|             if (in == null) throw new IOException(); | ||||
|             BufferedInputStream bis = new BufferedInputStream(in); | ||||
|             byte[] buffer = new byte[4096]; | ||||
|             int len; | ||||
|             while ((len = bis.read(buffer)) != -1) { | ||||
|                 out.write(buffer, 0, len); | ||||
|             } | ||||
|             out.close(); | ||||
|             bis.close(); | ||||
|  | ||||
|         } catch (IOException e) { | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|         if (busybox.exists()) { | ||||
|             getShell().su( | ||||
|                     "rm -rf " + MagiskManager.BUSYBOXPATH, | ||||
|                     "mkdir -p " + MagiskManager.BUSYBOXPATH, | ||||
|                     "cp " + busybox + " " + MagiskManager.BUSYBOXPATH, | ||||
|                     "chmod -R 755 " + MagiskManager.BUSYBOXPATH, | ||||
|                     MagiskManager.BUSYBOXPATH + "/busybox --install -s " + MagiskManager.BUSYBOXPATH | ||||
|             ); | ||||
|             busybox.delete(); | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
| @@ -1,74 +0,0 @@ | ||||
| package com.topjohnwu.magisk.asyncs; | ||||
|  | ||||
| import android.content.Context; | ||||
| import android.content.pm.PackageManager; | ||||
| import android.os.Environment; | ||||
| import android.widget.Toast; | ||||
|  | ||||
| import com.topjohnwu.magisk.MagiskManager; | ||||
| import com.topjohnwu.magisk.R; | ||||
| import com.topjohnwu.magisk.superuser.Policy; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
| import com.topjohnwu.magisk.utils.ZipUtils; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.util.List; | ||||
|  | ||||
| public class HideManager extends ParallelTask<Void, Void, Boolean> { | ||||
|  | ||||
|     public HideManager(Context context) { | ||||
|         super(context); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onPreExecute() { | ||||
|         getMagiskManager().toast(R.string.hide_manager_toast, Toast.LENGTH_SHORT); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected Boolean doInBackground(Void... voids) { | ||||
|         MagiskManager mm = getMagiskManager(); | ||||
|         if (mm == null) | ||||
|             return false; | ||||
|  | ||||
|         // Generate a new unhide app with random package name | ||||
|         File unhideAPK = new File(Environment.getExternalStorageDirectory() + "/MagiskManager", "unhide.apk"); | ||||
|         unhideAPK.getParentFile().mkdirs(); | ||||
|         String pkg = ZipUtils.generateUnhide(mm, unhideAPK); | ||||
|  | ||||
|         // Install the application | ||||
|         List<String> ret = getShell().su("pm install " + unhideAPK + ">/dev/null && echo true || echo false"); | ||||
|         unhideAPK.delete(); | ||||
|         if (!Utils.isValidShellResponse(ret) || !Boolean.parseBoolean(ret.get(0))) | ||||
|             return false; | ||||
|  | ||||
|         try { | ||||
|             // Allow the application to gain root by default | ||||
|             PackageManager pm = mm.getPackageManager(); | ||||
|             int uid = pm.getApplicationInfo(pkg, 0).uid; | ||||
|             Policy policy = new Policy(uid, pm); | ||||
|             policy.policy = Policy.ALLOW; | ||||
|             policy.notification = false; | ||||
|             policy.logging = false; | ||||
|             mm.suDB.addPolicy(policy); | ||||
|         } catch (PackageManager.NameNotFoundException e) { | ||||
|             e.printStackTrace(); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Hide myself! | ||||
|         getShell().su_raw("pm hide " + mm.getPackageName()); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onPostExecute(Boolean b) { | ||||
|         MagiskManager mm = getMagiskManager(); | ||||
|         if (mm == null) | ||||
|             return; | ||||
|         if (!b) { | ||||
|             mm.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG); | ||||
|         } | ||||
|         super.onPostExecute(b); | ||||
|     } | ||||
| } | ||||
| @@ -1,230 +0,0 @@ | ||||
| package com.topjohnwu.magisk.asyncs; | ||||
|  | ||||
| import android.app.Activity; | ||||
| import android.net.Uri; | ||||
| import android.os.Build; | ||||
| import android.os.Environment; | ||||
| import android.text.TextUtils; | ||||
|  | ||||
| import com.topjohnwu.magisk.MagiskManager; | ||||
| import com.topjohnwu.magisk.utils.AdaptiveList; | ||||
| import com.topjohnwu.magisk.utils.Shell; | ||||
| import com.topjohnwu.magisk.utils.TarEntry; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
| import com.topjohnwu.magisk.utils.ZipUtils; | ||||
|  | ||||
| import org.kamranzafar.jtar.TarInputStream; | ||||
| import org.kamranzafar.jtar.TarOutputStream; | ||||
|  | ||||
| import java.io.BufferedInputStream; | ||||
| import java.io.BufferedOutputStream; | ||||
| import java.io.File; | ||||
| import java.io.FileInputStream; | ||||
| import java.io.FileNotFoundException; | ||||
| import java.io.FileOutputStream; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.io.OutputStream; | ||||
| import java.util.Arrays; | ||||
| import java.util.List; | ||||
|  | ||||
| public class InstallMagisk extends ParallelTask<Void, Void, Boolean> { | ||||
|  | ||||
|     private static final int PATCH_MODE = 0; | ||||
|     private static final int DIRECT_MODE = 1; | ||||
|  | ||||
|     private Uri mBootImg, mZip; | ||||
|     private AdaptiveList<String> mList; | ||||
|     private String mBootLocation; | ||||
|     private boolean mKeepEnc, mKeepVerity; | ||||
|     private int mode; | ||||
|  | ||||
|     private InstallMagisk(Activity context, AdaptiveList<String> list, Uri zip, boolean enc, boolean verity) { | ||||
|         super(context); | ||||
|         mList = list; | ||||
|         mZip = zip; | ||||
|         mKeepEnc = enc; | ||||
|         mKeepVerity = verity; | ||||
|     } | ||||
|  | ||||
|     public InstallMagisk(Activity context, AdaptiveList<String> list, Uri zip, boolean enc, boolean verity, Uri boot) { | ||||
|         this(context, list, zip, enc, verity); | ||||
|         mBootImg = boot; | ||||
|         mode = PATCH_MODE; | ||||
|     } | ||||
|  | ||||
|     public InstallMagisk(Activity context, AdaptiveList<String> list, Uri zip, boolean enc, boolean verity, String boot) { | ||||
|         this(context, list, zip, enc, verity); | ||||
|         mBootLocation = boot; | ||||
|         mode = DIRECT_MODE; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onPreExecute() { | ||||
|         // UI updates must run in the UI thread | ||||
|         mList.setCallback(this::publishProgress); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onProgressUpdate(Void... values) { | ||||
|         mList.updateView(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected Boolean doInBackground(Void... voids) { | ||||
|         MagiskManager mm = getMagiskManager(); | ||||
|         if (mm == null) return false; | ||||
|  | ||||
|         File install = new File(Utils.getEncContext(mm).getFilesDir().getParent(), "install"); | ||||
|         getShell().sh_raw("rm -rf " + install); | ||||
|  | ||||
|         List<String> abis = Arrays.asList(Build.SUPPORTED_ABIS); | ||||
|         String arch; | ||||
|         if (abis.contains("x86_64")) arch = "x64"; | ||||
|         else if (abis.contains("arm64-v8a")) arch = "arm64"; | ||||
|         else if (abis.contains("x86")) arch = "x86"; | ||||
|         else arch = "arm"; | ||||
|         mList.add("- Device platform: " + arch); | ||||
|  | ||||
|         try { | ||||
|             // Unzip files | ||||
|             mList.add("- Extracting files"); | ||||
|             try (InputStream in = mm.getContentResolver().openInputStream(mZip)) { | ||||
|                 if (in == null) throw new FileNotFoundException(); | ||||
|                 BufferedInputStream buf = new BufferedInputStream(in); | ||||
|                 buf.mark(Integer.MAX_VALUE); | ||||
|                 ZipUtils.unzip(buf, install, arch + "/", true); | ||||
|                 buf.reset(); | ||||
|                 ZipUtils.unzip(buf, install, "common/", true); | ||||
|                 buf.reset(); | ||||
|                 ZipUtils.unzip(buf, install, "chromeos/", false); | ||||
|                 buf.reset(); | ||||
|                 ZipUtils.unzip(buf, install, "META-INF/com/google/android/update-binary", true); | ||||
|             } catch (FileNotFoundException e) { | ||||
|                 mList.add("! Invalid Uri"); | ||||
|                 throw e; | ||||
|             } catch (Exception e) { | ||||
|                 mList.add("! Cannot unzip zip"); | ||||
|                 throw e; | ||||
|             } | ||||
|  | ||||
|             File boot; | ||||
|             switch (mode) { | ||||
|                 case PATCH_MODE: | ||||
|                     boot = new File(install, "boot.img"); | ||||
|                     // Copy boot image to local | ||||
|                     try ( | ||||
|                         InputStream in = mm.getContentResolver().openInputStream(mBootImg); | ||||
|                         OutputStream out = new FileOutputStream(boot) | ||||
|                     ) { | ||||
|                         InputStream source; | ||||
|                         if (in == null) throw new FileNotFoundException(); | ||||
|  | ||||
|                         if (Utils.getNameFromUri(mm, mBootImg).endsWith(".tar")) { | ||||
|                             // Extract boot.img from tar | ||||
|                             TarInputStream tar = new TarInputStream(new BufferedInputStream(in)); | ||||
|                             org.kamranzafar.jtar.TarEntry entry; | ||||
|                             while ((entry = tar.getNextEntry()) != null) { | ||||
|                                 if (entry.getName().equals("boot.img")) | ||||
|                                     break; | ||||
|                             } | ||||
|                             source = tar; | ||||
|                         } else { | ||||
|                             // Direct copy raw image | ||||
|                             source = new BufferedInputStream(in); | ||||
|                         } | ||||
|                         byte buffer[] = new byte[1024]; | ||||
|                         int length; | ||||
|                         while ((length = source.read(buffer)) > 0) | ||||
|                             out.write(buffer, 0, length); | ||||
|                     } catch (FileNotFoundException e) { | ||||
|                         mList.add("! Invalid Uri"); | ||||
|                         throw e; | ||||
|                     } catch (IOException e) { | ||||
|                         mList.add("! Copy failed"); | ||||
|                         throw e; | ||||
|                     } | ||||
|                     break; | ||||
|                 case DIRECT_MODE: | ||||
|                     boot = new File(mBootLocation); | ||||
|                     break; | ||||
|                 default: | ||||
|                     return false; | ||||
|             } | ||||
|  | ||||
|             mList.add("- Use boot image: " + boot); | ||||
|  | ||||
|             Shell shell; | ||||
|             if (mode == PATCH_MODE && Shell.rootAccess()) { | ||||
|                 // Force non-root shell | ||||
|                 shell = Shell.getShell("sh"); | ||||
|              } else { | ||||
|                 shell = getShell(); | ||||
|             } | ||||
|  | ||||
|             // Patch boot image | ||||
|             shell.sh(mList, | ||||
|                     "cd " + install, | ||||
|                     "KEEPFORCEENCRYPT=" + mKeepEnc + " KEEPVERITY=" + mKeepVerity + " sh " + | ||||
|                             "update-binary indep boot_patch.sh " + boot + | ||||
|                             " && echo 'Success!' || echo 'Failed!'" | ||||
|             ); | ||||
|  | ||||
|             if (!TextUtils.equals(mList.get(mList.size() - 1), "Success!")) | ||||
|                 return false; | ||||
|  | ||||
|             File patched_boot = new File(install, "new-boot.img"); | ||||
|             mList.add(""); | ||||
|             switch (mode) { | ||||
|                 case PATCH_MODE: | ||||
|                     File dest = new File(Environment.getExternalStorageDirectory() + "/MagiskManager/patched_boot" + mm.bootFormat); | ||||
|                     dest.getParentFile().mkdirs(); | ||||
|                     switch (mm.bootFormat) { | ||||
|                         case ".img": | ||||
|                             getShell().sh_raw("cp -f " + patched_boot + " " + dest); | ||||
|                             break; | ||||
|                         case ".img.tar": | ||||
|                             TarOutputStream tar = new TarOutputStream(new BufferedOutputStream(new FileOutputStream(dest))); | ||||
|                             tar.putNextEntry(new TarEntry(patched_boot, "boot.img")); | ||||
|                             byte buffer[] = new byte[4096]; | ||||
|                             BufferedInputStream in = new BufferedInputStream(new FileInputStream(patched_boot)); | ||||
|                             int len; | ||||
|                             while ((len = in.read(buffer)) != -1) { | ||||
|                                 tar.write(buffer, 0, len); | ||||
|                             } | ||||
|                             tar.flush(); | ||||
|                             tar.close(); | ||||
|                             in.close(); | ||||
|                             break; | ||||
|                     } | ||||
|                     mList.add("*********************************"); | ||||
|                     mList.add(" Patched Boot Image is placed in "); | ||||
|                     mList.add(" " + dest + " "); | ||||
|                     mList.add("*********************************"); | ||||
|                     break; | ||||
|                 case DIRECT_MODE: | ||||
|                     // Direct flash boot image | ||||
|                     getShell().su(mList, "flash_boot_image " + patched_boot + " " + mBootLocation); | ||||
|                     break; | ||||
|                 default: | ||||
|                     return false; | ||||
|             } | ||||
|  | ||||
|             // Finals | ||||
|             getShell().sh_raw( | ||||
|                     "cd " + install, | ||||
|                     "mv bin/busybox busybox", | ||||
|                     "rm -rf bin *.img update-binary", | ||||
|                     "cd /"); | ||||
|         } catch (Exception e) { | ||||
|             e.printStackTrace(); | ||||
|             return false; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onPostExecute(Boolean result) { | ||||
|         super.onPostExecute(result); | ||||
|     } | ||||
| } | ||||
| @@ -1,45 +0,0 @@ | ||||
| package com.topjohnwu.magisk.asyncs; | ||||
|  | ||||
| import android.content.Context; | ||||
|  | ||||
| import com.topjohnwu.magisk.MagiskManager; | ||||
| import com.topjohnwu.magisk.module.BaseModule; | ||||
| import com.topjohnwu.magisk.module.Module; | ||||
| import com.topjohnwu.magisk.utils.Logger; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
| import com.topjohnwu.magisk.utils.ValueSortedMap; | ||||
|  | ||||
| public class LoadModules extends ParallelTask<Void, Void, Void> { | ||||
|  | ||||
|     public LoadModules(Context context) { | ||||
|         super(context); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected Void doInBackground(Void... voids) { | ||||
|         MagiskManager mm = getMagiskManager(); | ||||
|         if (mm == null) return null; | ||||
|         Logger.dev("LoadModules: Loading modules"); | ||||
|  | ||||
|         mm.moduleMap = new ValueSortedMap<>(); | ||||
|  | ||||
|         for (String path : Utils.getModList(getShell(), MagiskManager.MAGISK_PATH)) { | ||||
|             Logger.dev("LoadModules: Adding modules from " + path); | ||||
|             try { | ||||
|                 Module module = new Module(getShell(), path); | ||||
|                 mm.moduleMap.put(module.getId(), module); | ||||
|             } catch (BaseModule.CacheModException ignored) {} | ||||
|         } | ||||
|  | ||||
|         Logger.dev("LoadModules: Data load done"); | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onPostExecute(Void v) { | ||||
|         MagiskManager mm = getMagiskManager(); | ||||
|         if (mm == null) return; | ||||
|         mm.moduleLoadDone.publish(); | ||||
|         super.onPostExecute(v); | ||||
|     } | ||||
| } | ||||
| @@ -1,47 +0,0 @@ | ||||
| package com.topjohnwu.magisk.asyncs; | ||||
|  | ||||
| import android.app.Activity; | ||||
| import android.support.v7.app.AlertDialog; | ||||
| import android.webkit.WebView; | ||||
|  | ||||
| import com.topjohnwu.magisk.R; | ||||
| import com.topjohnwu.magisk.utils.WebService; | ||||
|  | ||||
| import org.commonmark.node.Node; | ||||
| import org.commonmark.parser.Parser; | ||||
| import org.commonmark.renderer.html.HtmlRenderer; | ||||
|  | ||||
| public class MarkDownWindow extends ParallelTask<Void, Void, String> { | ||||
|  | ||||
|     private String mTitle, mUrl; | ||||
|  | ||||
|     public MarkDownWindow(Activity context, String title, String url) { | ||||
|         super(context); | ||||
|         mTitle = title; | ||||
|         mUrl = url; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected String doInBackground(Void... voids) { | ||||
|         String md = WebService.getString(mUrl); | ||||
|         Parser parser = Parser.builder().build(); | ||||
|         HtmlRenderer renderer = HtmlRenderer.builder().build(); | ||||
|         Node doc = parser.parse(md); | ||||
|         return String.format( | ||||
|                 "<link rel='stylesheet' type='text/css' href='file:///android_asset/%s.css'/> %s", | ||||
|                 getMagiskManager().isDarkTheme ? "dark" : "light", renderer.render(doc)); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onPostExecute(String html) { | ||||
|         AlertDialog.Builder alert = new AlertDialog.Builder(getActivity()); | ||||
|         alert.setTitle(mTitle); | ||||
|  | ||||
|         WebView wv = new WebView(getActivity()); | ||||
|         wv.loadDataWithBaseURL("fake://", html, "text/html", "UTF-8", null); | ||||
|  | ||||
|         alert.setView(wv); | ||||
|         alert.setNegativeButton(R.string.close, (dialog, id) -> dialog.dismiss()); | ||||
|         alert.show(); | ||||
|     } | ||||
| } | ||||
| @@ -1,133 +0,0 @@ | ||||
| package com.topjohnwu.magisk.asyncs; | ||||
|  | ||||
| import android.Manifest; | ||||
| import android.app.Activity; | ||||
| import android.app.ProgressDialog; | ||||
| import android.content.Intent; | ||||
| import android.net.Uri; | ||||
| import android.os.Environment; | ||||
| import android.widget.Toast; | ||||
|  | ||||
| import com.topjohnwu.magisk.FlashActivity; | ||||
| 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.WebService; | ||||
| import com.topjohnwu.magisk.utils.ZipUtils; | ||||
|  | ||||
| import java.io.BufferedInputStream; | ||||
| import java.io.BufferedOutputStream; | ||||
| import java.io.File; | ||||
| import java.io.FileInputStream; | ||||
| import java.io.FileOutputStream; | ||||
| import java.io.InputStream; | ||||
| import java.io.OutputStream; | ||||
|  | ||||
| public class ProcessRepoZip extends ParallelTask<Void, Void, Boolean> { | ||||
|  | ||||
|     private ProgressDialog progressDialog; | ||||
|     private boolean mInstall; | ||||
|     private String mLink; | ||||
|     private File mFile; | ||||
|  | ||||
|     public ProcessRepoZip(Activity context, String link, String filename, boolean install) { | ||||
|         super(context); | ||||
|         mLink = link; | ||||
|         mFile = new File(Environment.getExternalStorageDirectory() + "/MagiskManager", filename); | ||||
|         mFile.getParentFile().mkdirs(); | ||||
|         mInstall = install; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onPreExecute() { | ||||
|         Activity activity = getActivity(); | ||||
|         progressDialog = ProgressDialog.show(activity, | ||||
|                 activity.getString(R.string.zip_download_title), | ||||
|                 activity.getString(R.string.zip_download_msg)); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onProgressUpdate(Void... values) { | ||||
|         progressDialog.setTitle(R.string.zip_process_title); | ||||
|         progressDialog.setMessage(getActivity().getString(R.string.zip_process_msg)); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected Boolean doInBackground(Void... params) { | ||||
|         Activity activity = getActivity(); | ||||
|         if (activity == null) return null; | ||||
|         try { | ||||
|  | ||||
|             // Request zip from Internet | ||||
|             InputStream in = WebService.request(WebService.GET, mLink, null); | ||||
|             if (in == null) return false; | ||||
|             in = new BufferedInputStream(in); | ||||
|  | ||||
|             // Temp files | ||||
|             File temp1 = new File(activity.getCacheDir(), "1.zip"); | ||||
|             File temp2 = new File(temp1.getParentFile(), "2.zip"); | ||||
|             temp1.getParentFile().mkdir(); | ||||
|  | ||||
|             // First remove top folder in Github source zip, Web -> temp1 | ||||
|             ZipUtils.removeTopFolder(in, temp1); | ||||
|  | ||||
|             publishProgress(); | ||||
|  | ||||
|             // Then sign the zip for the first time, temp1 -> temp2 | ||||
|             ZipUtils.signZip(activity, temp1, temp2, false); | ||||
|  | ||||
|             // Adjust the zip to prevent unzip issues, temp2 -> temp1 | ||||
|             ZipUtils.zipAdjust(temp2.getPath(), temp1.getPath()); | ||||
|  | ||||
|             // Finally, sign the whole zip file again, temp1 -> temp2 | ||||
|             ZipUtils.signZip(activity, temp1, temp2, true); | ||||
|  | ||||
|             // Write it to the target zip, temp2 -> file | ||||
|             try (OutputStream out = new BufferedOutputStream(new FileOutputStream(mFile)); | ||||
|                  InputStream source = new BufferedInputStream(new FileInputStream(temp2)) | ||||
|             ) { | ||||
|                 byte[] buffer = new byte[4096]; | ||||
|                 int length; | ||||
|                 while ((length = source.read(buffer)) > 0) | ||||
|                     out.write(buffer, 0, length); | ||||
|             } | ||||
|  | ||||
|             // Delete temp files | ||||
|             temp1.delete(); | ||||
|             temp2.delete(); | ||||
|  | ||||
|             return true; | ||||
|         } catch (Exception e) { | ||||
|             Logger.error("ProcessRepoZip: Error!"); | ||||
|             e.printStackTrace(); | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onPostExecute(Boolean result) { | ||||
|         Activity activity = getActivity(); | ||||
|         if (activity == null) return; | ||||
|         progressDialog.dismiss(); | ||||
|         Uri uri = Uri.fromFile(mFile); | ||||
|         if (result) { | ||||
|             if (Shell.rootAccess() && mInstall) { | ||||
|                 Intent intent = new Intent(getActivity(), FlashActivity.class); | ||||
|                 intent.setData(uri).putExtra(FlashActivity.SET_ACTION, FlashActivity.FLASH_ZIP); | ||||
|                 activity.startActivity(intent); | ||||
|             } else { | ||||
|                 Utils.showUriSnack(activity, uri); | ||||
|             } | ||||
|         } else { | ||||
|             Utils.getMagiskManager(activity).toast(R.string.process_error, Toast.LENGTH_LONG); | ||||
|         } | ||||
|         super.onPostExecute(result); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public ParallelTask<Void, Void, Boolean> exec(Void... voids) { | ||||
|         Utils.runWithPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE, () -> super.exec(voids)); | ||||
|         return this; | ||||
|     } | ||||
| } | ||||
| @@ -1,40 +0,0 @@ | ||||
| package com.topjohnwu.magisk.asyncs; | ||||
|  | ||||
| import android.content.Context; | ||||
| import android.widget.Toast; | ||||
|  | ||||
| import com.topjohnwu.magisk.R; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| public class RestoreStockBoot extends ParallelTask<Void, Void, Boolean> { | ||||
|  | ||||
|     private String mBoot; | ||||
|  | ||||
|     public RestoreStockBoot(Context context, String boot) { | ||||
|         super(context); | ||||
|         mBoot = boot; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected Boolean doInBackground(Void... voids) { | ||||
|         List<String> ret = getShell().su("cat /init.magisk.rc | grep STOCKSHA1"); | ||||
|         if (!Utils.isValidShellResponse(ret)) | ||||
|             return false; | ||||
|         String stock_boot = "/data/stock_boot_" + ret.get(0).substring(ret.get(0).indexOf('=') + 1) + ".img.gz"; | ||||
|         if (!Utils.itemExist(getShell(), stock_boot)) | ||||
|             return false; | ||||
|         getShell().su_raw("flash_boot_image " + stock_boot + " " + mBoot); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onPostExecute(Boolean result) { | ||||
|         if (result) { | ||||
|             getMagiskManager().toast(R.string.restore_done, Toast.LENGTH_SHORT); | ||||
|         } else { | ||||
|             getMagiskManager().toast(R.string.restore_fail, Toast.LENGTH_LONG); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,190 +0,0 @@ | ||||
| package com.topjohnwu.magisk.asyncs; | ||||
|  | ||||
| import android.content.Context; | ||||
| import android.content.SharedPreferences; | ||||
| import android.text.TextUtils; | ||||
|  | ||||
| import com.topjohnwu.magisk.MagiskManager; | ||||
| import com.topjohnwu.magisk.database.RepoDatabaseHelper; | ||||
| import com.topjohnwu.magisk.module.BaseModule; | ||||
| import com.topjohnwu.magisk.module.Repo; | ||||
| import com.topjohnwu.magisk.utils.Logger; | ||||
| import com.topjohnwu.magisk.utils.WebService; | ||||
|  | ||||
| import org.json.JSONArray; | ||||
| import org.json.JSONObject; | ||||
|  | ||||
| import java.io.File; | ||||
| 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; | ||||
|  | ||||
| public class UpdateRepos extends ParallelTask<Void, Void, Void> { | ||||
|  | ||||
|     public static final String ETAG_KEY = "ETag"; | ||||
|  | ||||
|     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 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 List<String> cached; | ||||
|     private RepoDatabaseHelper repoDB; | ||||
|     private SharedPreferences prefs; | ||||
|  | ||||
|     public UpdateRepos(Context context) { | ||||
|         super(context); | ||||
|         prefs = getMagiskManager().prefs; | ||||
|         repoDB = getMagiskManager().repoDB; | ||||
|         String prefsPath = context.getApplicationInfo().dataDir + "/shared_prefs"; | ||||
|         // 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(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 = repoDB.getRepo(id); | ||||
|             try { | ||||
|                 Boolean updated; | ||||
|                 if (repo == null) { | ||||
|                     Logger.dev("UpdateRepos: Create new repo " + id); | ||||
|                     repo = new Repo(name, updatedDate); | ||||
|                     updated = true; | ||||
|                 } else { | ||||
|                     // Popout from cached | ||||
|                     cached.remove(id); | ||||
|                     updated = repo.update(updatedDate); | ||||
|                 } | ||||
|                 if (updated) { | ||||
|                     repoDB.addRepo(repo); | ||||
|                 } | ||||
|             } catch (BaseModule.CacheModException ignored) {} | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private boolean loadPage(int page, String url, int mode) { | ||||
|         Logger.dev("UpdateRepos: 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.getString(url, header); | ||||
|         if (TextUtils.isEmpty(jsonString)) { | ||||
|             // At least check the pages we know | ||||
|             return page + 1 < etags.size() && loadPage(page + 1, null, CHECK_ETAG); | ||||
|         } | ||||
|  | ||||
|         // The getString 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("UpdateRepos: Loading repos"); | ||||
|  | ||||
|         cached = repoDB.getRepoIDList(); | ||||
|  | ||||
|         if (!loadPage(0, null, CHECK_ETAG)) { | ||||
|             Logger.dev("UpdateRepos: No updates, use DB"); | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         // The leftover cached means they are removed from online repo | ||||
|         repoDB.removeRepo(cached); | ||||
|  | ||||
|         // 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(); | ||||
|  | ||||
|         Logger.dev("UpdateRepos: Done"); | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onPostExecute(Void v) { | ||||
|         MagiskManager mm = getMagiskManager(); | ||||
|         if (mm == null) return; | ||||
|         mm.repoLoadDone.publish(); | ||||
|         super.onPostExecute(v); | ||||
|     } | ||||
| } | ||||
| @@ -1,79 +0,0 @@ | ||||
| package com.topjohnwu.magisk.components; | ||||
|  | ||||
| import android.content.pm.PackageManager; | ||||
| import android.content.res.Configuration; | ||||
| import android.os.Bundle; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.v7.app.AppCompatActivity; | ||||
| import android.view.WindowManager; | ||||
|  | ||||
| import com.topjohnwu.magisk.MagiskManager; | ||||
| import com.topjohnwu.magisk.R; | ||||
| import com.topjohnwu.magisk.utils.Shell; | ||||
| import com.topjohnwu.magisk.utils.Topic; | ||||
|  | ||||
| public class Activity extends AppCompatActivity { | ||||
|  | ||||
|     private Runnable permissionGrantCallback; | ||||
|  | ||||
|     public Activity() { | ||||
|         super(); | ||||
|         Configuration configuration = new Configuration(); | ||||
|         configuration.setLocale(MagiskManager.locale); | ||||
|         applyOverrideConfiguration(configuration); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(@Nullable Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         if (this instanceof Topic.Subscriber) { | ||||
|             ((Topic.Subscriber) this).subscribeTopics(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onDestroy() { | ||||
|         if (this instanceof Topic.Subscriber) { | ||||
|             ((Topic.Subscriber) this).unsubscribeTopics(); | ||||
|         } | ||||
|         super.onDestroy(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { | ||||
|         if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { | ||||
|             if (permissionGrantCallback != null) { | ||||
|                 permissionGrantCallback.run(); | ||||
|             } | ||||
|         } | ||||
|         permissionGrantCallback = null; | ||||
|     } | ||||
|  | ||||
|     public void setPermissionGrantCallback(Runnable callback) { | ||||
|         permissionGrantCallback = callback; | ||||
|     } | ||||
|  | ||||
|     public MagiskManager getMagiskManager() { | ||||
|         return (MagiskManager) super.getApplicationContext(); | ||||
|     } | ||||
|  | ||||
|     public Shell getShell() { | ||||
|         return Shell.getShell(this); | ||||
|     } | ||||
|  | ||||
|     protected 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,89 +0,0 @@ | ||||
| package com.topjohnwu.magisk.database; | ||||
|  | ||||
| import android.content.Context; | ||||
| import android.database.Cursor; | ||||
| import android.database.sqlite.SQLiteDatabase; | ||||
| import android.database.sqlite.SQLiteOpenHelper; | ||||
|  | ||||
| import com.topjohnwu.magisk.MagiskManager; | ||||
| import com.topjohnwu.magisk.module.Repo; | ||||
| import com.topjohnwu.magisk.utils.Logger; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
|  | ||||
| import java.util.LinkedList; | ||||
| import java.util.List; | ||||
|  | ||||
| public class RepoDatabaseHelper extends SQLiteOpenHelper { | ||||
|  | ||||
|     private static final int DATABASE_VER = 2; | ||||
|     private static final String TABLE_NAME = "repos"; | ||||
|     private static final int MIN_TEMPLATE_VER = 3; | ||||
|  | ||||
|     private SQLiteDatabase mDb; | ||||
|     private MagiskManager mm; | ||||
|  | ||||
|     public RepoDatabaseHelper(Context context) { | ||||
|         super(context, "repo.db", null, DATABASE_VER); | ||||
|         mDb = getWritableDatabase(); | ||||
|         mm = Utils.getMagiskManager(context); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onCreate(SQLiteDatabase db) { | ||||
|         onUpgrade(db, 0, DATABASE_VER); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { | ||||
|         if (oldVersion == 0) { | ||||
|             db.execSQL( | ||||
|                     "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " " + | ||||
|                     "(id TEXT, name TEXT, version TEXT, versionCode INT, " + | ||||
|                     "author TEXT, description TEXT, repo_name TEXT, last_update INT, " + | ||||
|                     "PRIMARY KEY(id))"); | ||||
|             oldVersion++; | ||||
|         } | ||||
|         if (oldVersion == 1) { | ||||
|             db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD template INT"); | ||||
|             oldVersion++; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void clearRepo() { | ||||
|         mDb.delete(TABLE_NAME, null, null); | ||||
|     } | ||||
|  | ||||
|     public void removeRepo(List<String> list) { | ||||
|         for (String id : list) { | ||||
|             Logger.dev("Remove from DB: " + id); | ||||
|             mDb.delete(TABLE_NAME, "id=?", new String[] { id }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void addRepo(Repo repo) { | ||||
|         mDb.replace(TABLE_NAME, null, repo.getContentValues()); | ||||
|     } | ||||
|  | ||||
|     public Repo getRepo(String id) { | ||||
|         try (Cursor c = mDb.query(TABLE_NAME, null, "id=?", new String[] { id }, null, null, null)) { | ||||
|             if (c.moveToNext()) { | ||||
|                 return new Repo(c); | ||||
|             } | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     public Cursor getRepoCursor() { | ||||
|         return mDb.query(TABLE_NAME, null, "template>=? AND template<=?", new String[] { String.valueOf(MIN_TEMPLATE_VER), String.valueOf(mm.magiskVersionCode) }, null, null, "name COLLATE NOCASE"); | ||||
|     } | ||||
|  | ||||
|     public List<String> getRepoIDList() { | ||||
|         LinkedList<String> ret = new LinkedList<>(); | ||||
|         try (Cursor c = mDb.query(TABLE_NAME, null, null, null, null, null, null)) { | ||||
|             while (c.moveToNext()) { | ||||
|                 ret.add(c.getString(c.getColumnIndex("id"))); | ||||
|             } | ||||
|         } | ||||
|         return ret; | ||||
|     } | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,68 +0,0 @@ | ||||
| package com.topjohnwu.magisk.module; | ||||
|  | ||||
| import com.topjohnwu.magisk.utils.Logger; | ||||
| import com.topjohnwu.magisk.utils.Shell; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
|  | ||||
| public class Module extends BaseModule { | ||||
|  | ||||
|     private String mRemoveFile, mDisableFile, mUpdateFile; | ||||
|     private boolean mEnable, mRemove, mUpdated; | ||||
|  | ||||
|     public Module(Shell shell, String path) throws CacheModException { | ||||
|  | ||||
|         parseProps(Utils.readFile(shell, path + "/module.prop")); | ||||
|  | ||||
|         mRemoveFile = path + "/remove"; | ||||
|         mDisableFile = path + "/disable"; | ||||
|         mUpdateFile = path + "/update"; | ||||
|  | ||||
|         if (getId() == null) { | ||||
|             int sep = path.lastIndexOf('/'); | ||||
|             setId(path.substring(sep + 1)); | ||||
|         } | ||||
|  | ||||
|         if (getName() == null) { | ||||
|             setName(getId()); | ||||
|         } | ||||
|  | ||||
|         Logger.dev("Creating Module, id: " + getId()); | ||||
|  | ||||
|         mEnable = !Utils.itemExist(shell, mDisableFile); | ||||
|         mRemove = Utils.itemExist(shell, mRemoveFile); | ||||
|         mUpdated = Utils.itemExist(shell, mUpdateFile); | ||||
|     } | ||||
|  | ||||
|     public void createDisableFile(Shell shell) { | ||||
|         mEnable = false; | ||||
|         Utils.createFile(shell, mDisableFile); | ||||
|     } | ||||
|  | ||||
|     public void removeDisableFile(Shell shell) { | ||||
|         mEnable = true; | ||||
|         Utils.removeItem(shell, mDisableFile); | ||||
|     } | ||||
|  | ||||
|     public boolean isEnabled() { | ||||
|         return mEnable; | ||||
|     } | ||||
|  | ||||
|     public void createRemoveFile(Shell shell) { | ||||
|         mRemove = true; | ||||
|         Utils.createFile(shell, mRemoveFile); | ||||
|     } | ||||
|  | ||||
|     public void deleteRemoveFile(Shell shell) { | ||||
|         mRemove = false; | ||||
|         Utils.removeItem(shell, mRemoveFile); | ||||
|     } | ||||
|  | ||||
|     public boolean willBeRemoved() { | ||||
|         return mRemove; | ||||
|     } | ||||
|  | ||||
|     public boolean isUpdated() { | ||||
|         return mUpdated; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,76 +0,0 @@ | ||||
| package com.topjohnwu.magisk.module; | ||||
|  | ||||
| import android.content.ContentValues; | ||||
| import android.database.Cursor; | ||||
|  | ||||
| import com.topjohnwu.magisk.utils.Logger; | ||||
| import com.topjohnwu.magisk.utils.WebService; | ||||
|  | ||||
| import java.util.Date; | ||||
|  | ||||
| public class Repo extends BaseModule { | ||||
|  | ||||
|     private static final String FILE_URL = "https://raw.githubusercontent.com/Magisk-Modules-Repo/%s/master/%s"; | ||||
|     private static final String ZIP_URL = "https://github.com/Magisk-Modules-Repo/%s/archive/master.zip"; | ||||
|  | ||||
|     private String repoName; | ||||
|     private Date mLastUpdate; | ||||
|  | ||||
|     public Repo(String name, Date lastUpdate) throws CacheModException { | ||||
|         mLastUpdate = lastUpdate; | ||||
|         repoName = name; | ||||
|         update(); | ||||
|     } | ||||
|  | ||||
|     public Repo(Cursor c) { | ||||
|         super(c); | ||||
|         repoName = c.getString(c.getColumnIndex("repo_name")); | ||||
|         mLastUpdate = new Date(c.getLong(c.getColumnIndex("last_update"))); | ||||
|     } | ||||
|  | ||||
|     public void update() throws CacheModException { | ||||
|         String props = WebService.getString(getManifestUrl()); | ||||
|         String lines[] = props.split("\\n"); | ||||
|         parseProps(lines); | ||||
|         Logger.dev("Repo: Fetching prop: " + getId()); | ||||
|     } | ||||
|  | ||||
|     public boolean update(Date lastUpdate) throws CacheModException { | ||||
|         if (lastUpdate.after(mLastUpdate)) { | ||||
|             mLastUpdate = lastUpdate; | ||||
|             update(); | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     public ContentValues getContentValues() { | ||||
|         ContentValues values = new ContentValues(); | ||||
|         values.put("id", getId()); | ||||
|         values.put("name", getName()); | ||||
|         values.put("version", getVersion()); | ||||
|         values.put("versionCode", getVersionCode()); | ||||
|         values.put("author", getAuthor()); | ||||
|         values.put("description", getDescription()); | ||||
|         values.put("repo_name", repoName); | ||||
|         values.put("last_update", mLastUpdate.getTime()); | ||||
|         values.put("template", getTemplateVersion()); | ||||
|         return values; | ||||
|     } | ||||
|  | ||||
|     public String getZipUrl() { | ||||
|         return String.format(ZIP_URL, repoName); | ||||
|     } | ||||
|  | ||||
|     public String getManifestUrl() { | ||||
|         return String.format(FILE_URL, repoName, "module.prop"); | ||||
|     } | ||||
|  | ||||
|     public String getDetailUrl() { | ||||
|         return String.format(FILE_URL, repoName, "README.md"); | ||||
|     } | ||||
|  | ||||
|     public Date getLastUpdate() { | ||||
|         return mLastUpdate; | ||||
|     } | ||||
| } | ||||
| @@ -1,42 +0,0 @@ | ||||
| package com.topjohnwu.magisk.receivers; | ||||
|  | ||||
| import android.content.BroadcastReceiver; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.net.Uri; | ||||
| import android.os.Build; | ||||
| import android.support.v4.content.FileProvider; | ||||
|  | ||||
| import com.topjohnwu.magisk.MagiskManager; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
|  | ||||
| import java.io.File; | ||||
|  | ||||
| public class ManagerUpdate extends BroadcastReceiver { | ||||
|     @Override | ||||
|     public void onReceive(Context context, Intent intent) { | ||||
|         Utils.dlAndReceive( | ||||
|                 context, | ||||
|                 new DownloadReceiver() { | ||||
|                     @Override | ||||
|                     public void onDownloadDone(Uri uri) { | ||||
|                         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { | ||||
|                             Intent install = new Intent(Intent.ACTION_INSTALL_PACKAGE); | ||||
|                             install.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); | ||||
|                             Uri content = FileProvider.getUriForFile(context, | ||||
|                                     "com.topjohnwu.magisk.provider", new File(uri.getPath())); | ||||
|                             install.setData(content); | ||||
|                             context.startActivity(install); | ||||
|                         } else { | ||||
|                             Intent install = new Intent(Intent.ACTION_VIEW); | ||||
|                             install.setDataAndType(uri, "application/vnd.android.package-archive"); | ||||
|                             install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||
|                             context.startActivity(install); | ||||
|                         } | ||||
|                     } | ||||
|                 }, | ||||
|                 intent.getStringExtra(MagiskManager.INTENT_LINK), | ||||
|                 Utils.getLegalFilename("MagiskManager-v" + | ||||
|                         intent.getStringExtra(MagiskManager.INTENT_VERSION) + ".apk")); | ||||
|     } | ||||
| } | ||||
| @@ -1,40 +0,0 @@ | ||||
| package com.topjohnwu.magisk.receivers; | ||||
|  | ||||
| import android.content.BroadcastReceiver; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
|  | ||||
| import com.topjohnwu.magisk.MagiskManager; | ||||
| import com.topjohnwu.magisk.superuser.Policy; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
|  | ||||
| public class PackageReceiver extends BroadcastReceiver { | ||||
|     @Override | ||||
|     public void onReceive(Context context, Intent intent) { | ||||
|         MagiskManager magiskManager = Utils.getMagiskManager(context); | ||||
|  | ||||
|         String pkg = intent.getData().getEncodedSchemeSpecificPart(); | ||||
|         Policy policy = magiskManager.suDB.getPolicy(pkg); | ||||
|         if (policy == null) | ||||
|             return; | ||||
|  | ||||
|         switch (intent.getAction()) { | ||||
|             case Intent.ACTION_PACKAGE_REPLACED: | ||||
|                 // This will only work pre-O | ||||
|                 if (magiskManager.suReauth) { | ||||
|                     magiskManager.suDB.deletePolicy(policy); | ||||
|                 } else { | ||||
|                     int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); | ||||
|                     // Update the UID if available | ||||
|                     if (uid > 0) { | ||||
|                         policy.uid = uid % 100000; | ||||
|                     } | ||||
|                     magiskManager.suDB.updatePolicy(policy); | ||||
|                 } | ||||
|                 break; | ||||
|             case Intent.ACTION_PACKAGE_FULLY_REMOVED: | ||||
|                 magiskManager.suDB.deletePolicy(policy); | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,34 +0,0 @@ | ||||
| package com.topjohnwu.magisk.services; | ||||
|  | ||||
| import android.app.IntentService; | ||||
| import android.content.Intent; | ||||
| import android.os.Build; | ||||
| import android.support.v7.app.NotificationCompat; | ||||
|  | ||||
| import com.topjohnwu.magisk.R; | ||||
|  | ||||
| public class OnBootIntentService extends IntentService { | ||||
|  | ||||
|     private static final int ONBOOT_NOTIFICATION_ID = 3; | ||||
|  | ||||
|     public OnBootIntentService() { | ||||
|         super("OnBootIntentService"); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onCreate() { | ||||
|         super.onCreate(); | ||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||||
|             NotificationCompat.Builder builder = new NotificationCompat.Builder(this); | ||||
|             builder.setSmallIcon(R.drawable.ic_magisk) | ||||
|                     .setContentTitle("onBoot") | ||||
|                     .setContentText("Running onBoot operations..."); | ||||
|             startForeground(ONBOOT_NOTIFICATION_ID, builder.build()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onHandleIntent(Intent intent) { | ||||
|         // Currently nothing to do | ||||
|     } | ||||
| } | ||||
| @@ -1,24 +0,0 @@ | ||||
| package com.topjohnwu.magisk.superuser; | ||||
|  | ||||
| import android.content.Intent; | ||||
| import android.os.Bundle; | ||||
|  | ||||
| import com.topjohnwu.magisk.components.Activity; | ||||
|  | ||||
| public class RequestActivity extends Activity { | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|  | ||||
|         Intent intent = getIntent(); | ||||
|         if (intent == null) { | ||||
|             finish(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK).setClass(this, SuRequestActivity.class); | ||||
|         startActivity(intent); | ||||
|         finish(); | ||||
|     } | ||||
| } | ||||
| @@ -1,36 +0,0 @@ | ||||
| package com.topjohnwu.magisk.utils; | ||||
|  | ||||
| import android.support.v7.widget.RecyclerView; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
|  | ||||
| public class AdaptiveList<E> extends ArrayList<E> { | ||||
|  | ||||
|     private Runnable callback; | ||||
|     private RecyclerView mView; | ||||
|  | ||||
|     public AdaptiveList(RecyclerView v) { | ||||
|         mView = v; | ||||
|     } | ||||
|  | ||||
|     public void updateView() { | ||||
|         mView.getAdapter().notifyDataSetChanged(); | ||||
|         mView.scrollToPosition(mView.getAdapter().getItemCount() - 1); | ||||
|     } | ||||
|  | ||||
|     public void setCallback(Runnable cb) { | ||||
|         callback = cb; | ||||
|     } | ||||
|  | ||||
|     public boolean add(E e) { | ||||
|         boolean ret = super.add(e); | ||||
|         if (ret) { | ||||
|             if (callback == null) { | ||||
|                 updateView(); | ||||
|             } else { | ||||
|                 callback.run(); | ||||
|             } | ||||
|         } | ||||
|         return ret; | ||||
|     } | ||||
| } | ||||
| @@ -1,49 +0,0 @@ | ||||
| package com.topjohnwu.magisk.utils; | ||||
|  | ||||
| import android.util.Log; | ||||
|  | ||||
| import com.topjohnwu.magisk.MagiskManager; | ||||
|  | ||||
| import java.util.Locale; | ||||
|  | ||||
| public class Logger { | ||||
|  | ||||
|     public static final String MAIN_TAG = "Magisk"; | ||||
|     public static final String DEBUG_TAG = "MagiskManager"; | ||||
|  | ||||
|     public static void debug(String line) { | ||||
|         Log.d(DEBUG_TAG, "DEBUG: " + line); | ||||
|     } | ||||
|  | ||||
|     public static void debug(String fmt, Object... args) { | ||||
|         debug(String.format(Locale.US, fmt, args)); | ||||
|     } | ||||
|  | ||||
|     public static void error(String line) { | ||||
|         Log.e(MAIN_TAG, "MANAGERERROR: " + line); | ||||
|     } | ||||
|  | ||||
|     public static void error(String fmt, Object... args) { | ||||
|         error(String.format(Locale.US, fmt, args)); | ||||
|     } | ||||
|  | ||||
|     public static void dev(String line) { | ||||
|         if (MagiskManager.devLogging) { | ||||
|             Log.d(DEBUG_TAG, line); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static void dev(String fmt, Object... args) { | ||||
|         dev(String.format(Locale.US, fmt, args)); | ||||
|     } | ||||
|  | ||||
|     public static void shell(String line) { | ||||
|         if (MagiskManager.shellLogging) { | ||||
|             Log.d(DEBUG_TAG, "SHELL: " + line); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static void shell(String fmt, Object... args) { | ||||
|         shell(String.format(Locale.US, fmt, args)); | ||||
|     } | ||||
| } | ||||
| @@ -1,114 +0,0 @@ | ||||
| package com.topjohnwu.magisk.utils; | ||||
|  | ||||
| import android.os.Bundle; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.v4.app.FragmentActivity; | ||||
| import android.util.Base64; | ||||
|  | ||||
| import com.google.android.gms.common.ConnectionResult; | ||||
| import com.google.android.gms.common.api.GoogleApiClient; | ||||
| import com.google.android.gms.common.api.Status; | ||||
| import com.google.android.gms.safetynet.SafetyNet; | ||||
| import com.topjohnwu.magisk.R; | ||||
|  | ||||
| import org.json.JSONException; | ||||
| import org.json.JSONObject; | ||||
|  | ||||
| import java.security.SecureRandom; | ||||
|  | ||||
| public abstract class SafetyNetHelper | ||||
|         implements GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks { | ||||
|  | ||||
|     private static boolean isRunning = false; | ||||
|  | ||||
|     private GoogleApiClient mGoogleApiClient; | ||||
|     private Result ret; | ||||
|     protected FragmentActivity mActivity; | ||||
|  | ||||
|     public SafetyNetHelper(FragmentActivity activity) { | ||||
|         ret = new Result(); | ||||
|         mActivity = activity; | ||||
|     } | ||||
|  | ||||
|     // Entry point to start test | ||||
|     public void requestTest() { | ||||
|         if (isRunning) | ||||
|             return; | ||||
|         // Connect Google Service | ||||
|         mGoogleApiClient = new GoogleApiClient.Builder(mActivity) | ||||
|                 .enableAutoManage(mActivity, this) | ||||
|                 .addApi(SafetyNet.API) | ||||
|                 .addConnectionCallbacks(this) | ||||
|                 .build(); | ||||
|         mGoogleApiClient.connect(); | ||||
|         isRunning = true; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onConnectionFailed(@NonNull ConnectionResult result) { | ||||
|         Logger.dev("SN: Google API fail"); | ||||
|         ret.errmsg = result.getErrorMessage(); | ||||
|         handleResults(ret); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onConnectionSuspended(int i) { | ||||
|         Logger.dev("SN: Google API Suspended"); | ||||
|         switch (i) { | ||||
|             case CAUSE_NETWORK_LOST: | ||||
|                 ret.errmsg = mActivity.getString(R.string.safetyNet_network_loss); | ||||
|                 break; | ||||
|             case CAUSE_SERVICE_DISCONNECTED: | ||||
|                 ret.errmsg = mActivity.getString(R.string.safetyNet_service_disconnected); | ||||
|                 break; | ||||
|         } | ||||
|         handleResults(ret); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onConnected(@Nullable Bundle bundle) { | ||||
|         Logger.dev("SN: Google API Connected"); | ||||
|         // Create nonce | ||||
|         byte[] nonce = new byte[24]; | ||||
|         new SecureRandom().nextBytes(nonce); | ||||
|  | ||||
|         Logger.dev("SN: Check with nonce: " + Base64.encodeToString(nonce, Base64.DEFAULT)); | ||||
|  | ||||
|         // Call SafetyNet | ||||
|         SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce) | ||||
|                 .setResultCallback(result -> { | ||||
|                     Status status = result.getStatus(); | ||||
|                     if (status.isSuccess()) { | ||||
|                         String json = new String(Base64.decode(result.getJwsResult().split("\\.")[1], Base64.DEFAULT)); | ||||
|                         Logger.dev("SN: Response: " + json); | ||||
|                         try { | ||||
|                             JSONObject decoded = new JSONObject(json); | ||||
|                             ret.ctsProfile = decoded.getBoolean("ctsProfileMatch"); | ||||
|                             ret.basicIntegrity = decoded.getBoolean("basicIntegrity"); | ||||
|                             ret.failed = false; | ||||
|                         } catch (JSONException e) { | ||||
|                             ret.errmsg = mActivity.getString(R.string.safetyNet_res_invalid); | ||||
|                         } | ||||
|                     } else { | ||||
|                         Logger.dev("SN: No response"); | ||||
|                         ret.errmsg = mActivity.getString(R.string.safetyNet_no_response); | ||||
|                     } | ||||
|                     // Disconnect | ||||
|                     mGoogleApiClient.stopAutoManage(mActivity); | ||||
|                     mGoogleApiClient.disconnect(); | ||||
|                     isRunning = false; | ||||
|                     handleResults(ret); | ||||
|                 }); | ||||
|     } | ||||
|  | ||||
|     // Callback function to save the results | ||||
|     public abstract void handleResults(Result result); | ||||
|  | ||||
|     public static class Result { | ||||
|         public boolean failed = true; | ||||
|         public String errmsg; | ||||
|         public boolean ctsProfile = false; | ||||
|         public boolean basicIntegrity = false; | ||||
|     } | ||||
| } | ||||
| @@ -1,225 +0,0 @@ | ||||
| package com.topjohnwu.magisk.utils; | ||||
|  | ||||
| import android.content.Context; | ||||
| import android.text.TextUtils; | ||||
|  | ||||
| import com.topjohnwu.magisk.MagiskManager; | ||||
|  | ||||
| import java.io.BufferedReader; | ||||
| import java.io.DataInputStream; | ||||
| import java.io.DataOutputStream; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.io.InputStreamReader; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collection; | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * Modified by topjohnwu, based on Chainfire's libsuperuser | ||||
|  */ | ||||
|  | ||||
| public class Shell { | ||||
|  | ||||
|     // -1 = problematic/unknown issue; 0 = not rooted; 1 = properly rooted | ||||
|     public static int rootStatus; | ||||
|  | ||||
|     private final Process shellProcess; | ||||
|     private final DataOutputStream STDIN; | ||||
|     private final DataInputStream STDOUT; | ||||
|  | ||||
|     private boolean isValid; | ||||
|  | ||||
|     private void testRootShell(DataOutputStream in, DataInputStream out) throws IOException { | ||||
|         in.write(("id\n").getBytes("UTF-8")); | ||||
|         in.flush(); | ||||
|         String s = new BufferedReader(new InputStreamReader(out)).readLine(); | ||||
|         if (TextUtils.isEmpty(s) || !s.contains("uid=0")) { | ||||
|             in.close(); | ||||
|             out.close(); | ||||
|             throw new IOException(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private Shell() { | ||||
|         rootStatus = 1; | ||||
|         Process process = null; | ||||
|         DataOutputStream in = null; | ||||
|         DataInputStream out = null; | ||||
|  | ||||
|         try { | ||||
|             // Try getting global namespace | ||||
|             process = Runtime.getRuntime().exec("su --mount-master"); | ||||
|             in = new DataOutputStream(process.getOutputStream()); | ||||
|             out = new DataInputStream(process.getInputStream()); | ||||
|             testRootShell(in, out); | ||||
|         } catch (IOException e) { | ||||
|             // Feature not implemented, normal root shell | ||||
|             try { | ||||
|                 process = Runtime.getRuntime().exec("su"); | ||||
|                 in = new DataOutputStream(process.getOutputStream()); | ||||
|                 out = new DataInputStream(process.getInputStream()); | ||||
|                 testRootShell(in, out); | ||||
|             } catch (IOException e1) { | ||||
|                 rootStatus = 0; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (!rootAccess()) { | ||||
|             // Try to gain non-root sh | ||||
|             try { | ||||
|                 process = Runtime.getRuntime().exec("sh"); | ||||
|                 in = new DataOutputStream(process.getOutputStream()); | ||||
|                 out = new DataInputStream(process.getInputStream()); | ||||
|             } catch (IOException e) { | ||||
|                 // Nothing works.... | ||||
|                 shellProcess = null; | ||||
|                 STDIN = null; | ||||
|                 STDOUT = null; | ||||
|                 isValid = false; | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         isValid = true; | ||||
|         shellProcess = process; | ||||
|         STDIN = in; | ||||
|         STDOUT = out; | ||||
|         sh_raw("umask 022"); | ||||
|     } | ||||
|  | ||||
|     private Shell(String command) { | ||||
|         Process process; | ||||
|         DataOutputStream in; | ||||
|         DataInputStream out; | ||||
|  | ||||
|         try { | ||||
|             process = Runtime.getRuntime().exec(command); | ||||
|             in = new DataOutputStream(process.getOutputStream()); | ||||
|             out = new DataInputStream(process.getInputStream()); | ||||
|         } catch (IOException e) { | ||||
|             // Nothing works.... | ||||
|             shellProcess = null; | ||||
|             STDIN = null; | ||||
|             STDOUT = null; | ||||
|             isValid = false; | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         isValid = true; | ||||
|         shellProcess = process; | ||||
|         STDIN = in; | ||||
|         STDOUT = out; | ||||
|     } | ||||
|  | ||||
|     public static Shell getShell() { | ||||
|         return new Shell(); | ||||
|     } | ||||
|  | ||||
|     public static Shell getShell(String command) { | ||||
|         return new Shell(command); | ||||
|     } | ||||
|  | ||||
|     public static Shell getShell(Context context) { | ||||
|         MagiskManager magiskManager = Utils.getMagiskManager(context); | ||||
|         if (magiskManager.shell == null || !magiskManager.shell.isValid) { | ||||
|             // Get new shell if needed | ||||
|             magiskManager.shell = getShell(); | ||||
|         } | ||||
|         return magiskManager.shell; | ||||
|     } | ||||
|  | ||||
|     public static boolean rootAccess() { | ||||
|         return rootStatus > 0; | ||||
|     } | ||||
|  | ||||
|     public void loadInputStream(InputStream in) { | ||||
|         try { | ||||
|             int read; | ||||
|             byte[] bytes = new byte[4096]; | ||||
|             while ((read = in.read(bytes)) != -1) { | ||||
|                 STDIN.write(bytes, 0, read); | ||||
|             } | ||||
|             STDIN.flush(); | ||||
|         } catch (IOException e) { | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public List<String> sh(String... commands) { | ||||
|         List<String> res = new ArrayList<>(); | ||||
|         if (!isValid) return res; | ||||
|         sh(res, commands); | ||||
|         return res; | ||||
|     } | ||||
|  | ||||
|     public void sh_raw(String... commands) { | ||||
|         sh_raw(false, commands); | ||||
|     } | ||||
|  | ||||
|     public void sh_raw(boolean stdout, String... commands) { | ||||
|         if (!isValid) return; | ||||
|         synchronized (shellProcess) { | ||||
|             try { | ||||
|                 for (String command : commands) { | ||||
|                     Logger.shell(command); | ||||
|                     STDIN.write((command + (stdout ? "\n" : " >/dev/null\n")).getBytes("UTF-8")); | ||||
|                     STDIN.flush(); | ||||
|                 } | ||||
|             } catch (IOException e) { | ||||
|                 e.printStackTrace(); | ||||
|                 shellProcess.destroy(); | ||||
|                 isValid = false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void sh(Collection<String> output, String... commands) { | ||||
|         if (!isValid) return; | ||||
|         try { | ||||
|             shellProcess.exitValue(); | ||||
|             isValid = false; | ||||
|             return;  // The process is dead, return | ||||
|         } catch (IllegalThreadStateException ignored) { | ||||
|             // This should be the expected result | ||||
|         } | ||||
|         synchronized (shellProcess) { | ||||
|             StreamGobbler out = new StreamGobbler(STDOUT, output); | ||||
|             out.start(); | ||||
|             sh_raw(true, commands); | ||||
|             sh_raw(true, "echo \'-shell-done-\'"); | ||||
|             try { out.join(); } catch (InterruptedException ignored) {} | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public List<String> su(String... commands) { | ||||
|         if (!rootAccess()) return sh(); | ||||
|         return sh(commands); | ||||
|     } | ||||
|  | ||||
|     public void su_raw(String... commands) { | ||||
|         if (!rootAccess()) return; | ||||
|         sh_raw(commands); | ||||
|     } | ||||
|  | ||||
|     public void su(Collection<String> output, String... commands) { | ||||
|         if (!rootAccess()) return; | ||||
|         sh(output, commands); | ||||
|     } | ||||
|  | ||||
|     public static abstract class AbstractList<E> extends java.util.AbstractList<E> { | ||||
|  | ||||
|         @Override | ||||
|         public abstract boolean add(E e); | ||||
|  | ||||
|         @Override | ||||
|         public E get(int i) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public int size() { | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user