mirror of
				https://github.com/topjohnwu/Magisk
				synced 2025-10-31 10:40:52 +01:00 
			
		
		
		
	Compare commits
	
		
			114 Commits
		
	
	
		
			manager-v8
			...
			v21.1
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 2d5cf8a6fe | ||
|   | 290959f74c | ||
|   | 4d9f58ee72 | ||
|   | 9241246de6 | ||
|   | 58a5d52b78 | ||
|   | 2906178ac3 | ||
|   | e0afbb647b | ||
|   | 50be50cf6a | ||
|   | 77a9d3a5bc | ||
|   | f9c7a4c933 | ||
|   | 2b759b84b0 | ||
|   | 1e45c63ea5 | ||
|   | b14a260827 | ||
|   | ade1597e03 | ||
|   | 2739d3cb67 | ||
|   | dc5e78e142 | ||
|   | e9759a5868 | ||
|   | e7ab802498 | ||
|   | 42672c2e27 | ||
|   | e65d61d313 | ||
|   | 076da5c7c4 | ||
|   | 9deaf2507c | ||
|   | 5c114c67de | ||
|   | d904cb0441 | ||
|   | bd1dd9d863 | ||
|   | afebe734b8 | ||
|   | e21a78164e | ||
|   | 1e0f96d0fd | ||
|   | bf650332d8 | ||
|   | f32e0af830 | ||
|   | 4c94f90e5d | ||
|   | ffb4224640 | ||
|   | 89fff4830b | ||
|   | 16e4c67992 | ||
|   | cf47214ee4 | ||
|   | 0feab753fb | ||
|   | d0b6318b90 | ||
|   | 966e23b846 | ||
|   | 5b8a1fc2a7 | ||
|   | 02ea3ca525 | ||
|   | 0632b146b8 | ||
|   | 1b0b180761 | ||
|   | 0d11f73a1d | ||
|   | 533cb8eb58 | ||
|   | 8ac1181e9a | ||
|   | 5ca1892eb0 | ||
|   | e32db6a0e8 | ||
|   | 82fff615d6 | ||
|   | 24a8f0808d | ||
|   | 4a7c3c06bc | ||
|   | da93bbc1fe | ||
|   | fa2dbe981e | ||
|   | ce6cceae8b | ||
|   | 7b26e8b818 | ||
|   | 2da5fcb00b | ||
|   | a079966f97 | ||
|   | 468796c23d | ||
|   | 5833aadef5 | ||
|   | eb261c8026 | ||
|   | a4c48847d1 | ||
|   | 43288be091 | ||
|   | 1ad7a6fe93 | ||
|   | 4e0a3f5e72 | ||
|   | d7c33f647d | ||
|   | 9087207dc0 | ||
|   | 2760f37e6b | ||
|   | 3fa3426032 | ||
|   | 2e4dc91b96 | ||
|   | aaaaa3d044 | ||
|   | 1edc4449d5 | ||
|   | f3cd4da026 | ||
|   | 872c55207c | ||
|   | 339ca6d666 | ||
|   | 4aeac3b8f4 | ||
|   | d625beb7f3 | ||
|   | 735b65c50c | ||
|   | efb1eab327 | ||
|   | 49d4785da0 | ||
|   | 28e65ce383 | ||
|   | c3b6a48373 | ||
|   | a42ebd429b | ||
|   | 8f89010752 | ||
|   | 105a18f719 | ||
|   | eb04ca4c4a | ||
|   | 6092d7ca88 | ||
|   | 66cad101c0 | ||
|   | 0a14f43f9c | ||
|   | 311c1f0dfd | ||
|   | 0499588107 | ||
|   | d4d837a562 | ||
|   | fbcbb20178 | ||
|   | 0914700fc6 | ||
|   | eeced2fb5b | ||
|   | 6509e3d4f5 | ||
|   | 317052604b | ||
|   | 5538f7168c | ||
|   | dcb9e4cd93 | ||
|   | d9382f59bf | ||
|   | 403a0c770a | ||
|   | f0f1cdc501 | ||
|   | 4e272b70ef | ||
|   | 8dc62a0232 | ||
|   | 9225b47568 | ||
|   | d462873e74 | ||
|   | fc19b50290 | ||
|   | 333fe6da0e | ||
|   | 75fcda9f81 | ||
|   | 44ba2a9903 | ||
|   | 2fceb1ad96 | ||
|   | bacb5fa462 | ||
|   | 67f8dc494e | ||
|   | 3e4caabecb | ||
|   | dcd5183b24 | ||
|   | d80c6b42a6 | 
| @@ -15,17 +15,17 @@ Here are some feature highlights: | ||||
|  | ||||
| ## Downloads | ||||
|  | ||||
| [](https://github.com/topjohnwu/Magisk/releases/download/manager-v7.5.1/MagiskManager-v7.5.1.apk) | ||||
| [](https://github.com/topjohnwu/Magisk/releases/download/manager-v8.0.2/MagiskManager-v8.0.2.apk) | ||||
| [](https://raw.githubusercontent.com/topjohnwu/magisk_files/canary/app-debug.apk) | ||||
| <br> | ||||
| [](https://github.com/topjohnwu/Magisk/releases/tag/v20.4) | ||||
| [](https://github.com/topjohnwu/Magisk/releases/tag/v20.4) | ||||
| [](https://github.com/topjohnwu/Magisk/releases/tag/v21.0) | ||||
|  | ||||
| ## Useful Links | ||||
|  | ||||
| - [Installation Instruction](https://topjohnwu.github.io/Magisk/install.html) | ||||
| - [Frequently Asked Questions](https://topjohnwu.github.io/Magisk/faq.html) | ||||
| - [Full Official Docs](https://topjohnwu.github.io/Magisk/) | ||||
| - [Magisk Documentation](https://topjohnwu.github.io/Magisk/) | ||||
| - [Magisk Troubleshoot Wiki](https://www.didgeridoohan.com/magisk/HomePage) (by [@Didgeridoohan](https://github.com/Didgeridoohan)) | ||||
|  | ||||
| ## Android Version Support | ||||
| @@ -56,7 +56,7 @@ For Magisk Manager crashes, record and upload the logcat when the crash occurs. | ||||
| 	- macOS: `export JAVA_HOME="/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home"` | ||||
| 	- Linux: `export PATH="/path/to/androidstudio/jre/bin:$PATH"` | ||||
| 	- Windows: Add `C:\Path\To\Android Studio\jre\bin` to environment variable `PATH` | ||||
| - Set environment variable `ANDROID_HOME` to the Android SDK folder (can be found in Android Studio settings) | ||||
| - Set environment variable `ANDROID_SDK_ROOT` to the Android SDK folder (can be found in Android Studio settings) | ||||
| - Run `./build.py ndk` to let the script download and install NDK for you | ||||
| - Set configurations in `config.prop`. A sample `config.prop.sample` is provided. | ||||
| - To start building, run `build.py` to see your options. \ | ||||
|   | ||||
| @@ -51,11 +51,12 @@ android { | ||||
|  | ||||
|     packagingOptions { | ||||
|         exclude("/META-INF/**") | ||||
|         exclude("/androidsupportmultidexversion.txt") | ||||
|         exclude("/org/bouncycastle/**") | ||||
|         exclude("/kotlin/**") | ||||
|         exclude("/kotlinx/**") | ||||
|         exclude("/okhttp3/**") | ||||
|         exclude("/*.txt") | ||||
|         exclude("/*.bin") | ||||
|     } | ||||
|  | ||||
|     kotlinOptions { | ||||
| @@ -82,6 +83,7 @@ dependencies { | ||||
|  | ||||
|     implementation("com.github.topjohnwu:jtar:1.0.0") | ||||
|     implementation("com.github.topjohnwu:indeterminate-checkbox:1.0.7") | ||||
|     implementation("com.github.topjohnwu:lz4-java:1.7.1") | ||||
|     implementation("com.jakewharton.timber:timber:4.7.1") | ||||
|  | ||||
|     val vBAdapt = "4.0.0" | ||||
| @@ -118,11 +120,11 @@ dependencies { | ||||
|     implementation("com.squareup.okhttp3:logging-interceptor:${vOkHttp}") | ||||
|     implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:${vOkHttp}") | ||||
|  | ||||
|     val vMoshi = "1.10.0" | ||||
|     val vMoshi = "1.11.0" | ||||
|     implementation("com.squareup.moshi:moshi:${vMoshi}") | ||||
|     kapt("com.squareup.moshi:moshi-kotlin-codegen:${vMoshi}") | ||||
|  | ||||
|     val vRoom = "2.2.5" | ||||
|     val vRoom = "2.3.0-alpha03" | ||||
|     implementation("androidx.room:room-runtime:${vRoom}") | ||||
|     implementation("androidx.room:room-ktx:${vRoom}") | ||||
|     kapt("androidx.room:room-compiler:${vRoom}") | ||||
| @@ -132,7 +134,7 @@ dependencies { | ||||
|     implementation("androidx.navigation:navigation-ui-ktx:${vNav}") | ||||
|  | ||||
|     implementation("androidx.biometric:biometric:1.0.1") | ||||
|     implementation("androidx.constraintlayout:constraintlayout:2.0.1") | ||||
|     implementation("androidx.constraintlayout:constraintlayout:2.0.4") | ||||
|     implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") | ||||
|     implementation("androidx.browser:browser:1.2.0") | ||||
|     implementation("androidx.preference:preference:1.1.1") | ||||
| @@ -141,7 +143,7 @@ dependencies { | ||||
|     implementation("androidx.work:work-runtime-ktx:2.4.0") | ||||
|     implementation("androidx.transition:transition:1.3.1") | ||||
|     implementation("androidx.multidex:multidex:2.0.1") | ||||
|     implementation("androidx.core:core-ktx:1.3.1") | ||||
|     implementation("androidx.core:core-ktx:1.3.2") | ||||
|     implementation("androidx.localbroadcastmanager:localbroadcastmanager:1.0.0") | ||||
|     implementation("com.google.android.material:material:1.2.1") | ||||
| } | ||||
|   | ||||
							
								
								
									
										6
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							| @@ -35,12 +35,6 @@ | ||||
|   void onResponse(org.json.JSONObject); | ||||
| } | ||||
|  | ||||
| # Fragments | ||||
| # TODO: Remove when AGP 4.1 release | ||||
| # https://issuetracker.google.com/issues/142601969 | ||||
| -keep,allowobfuscation class * extends androidx.fragment.app.Fragment | ||||
| -keepnames class androidx.navigation.fragment.NavHostFragment | ||||
|  | ||||
| # Strip Timber verbose and debug logging | ||||
| -assumenosideeffects class timber.log.Timber.Tree { | ||||
|   public void v(**); | ||||
|   | ||||
| @@ -12,12 +12,6 @@ | ||||
|         android:supportsRtl="true" | ||||
|         android:theme="@android:style/Theme.Translucent.NoTitleBar" | ||||
|         tools:ignore="UnusedAttribute"> | ||||
|  | ||||
|         <provider | ||||
|             android:name="a.p" | ||||
|             android:authorities="${applicationId}.provider" | ||||
|             android:exported="false" | ||||
|             android:grantUriPermissions="true"> | ||||
|         </provider> | ||||
|     </application> | ||||
|  | ||||
| </manifest> | ||||
|   | ||||
| @@ -1,7 +0,0 @@ | ||||
| package a; | ||||
|  | ||||
| import com.topjohnwu.magisk.FileProvider; | ||||
|  | ||||
| public class p extends FileProvider { | ||||
|     /* Stub */ | ||||
| } | ||||
| @@ -8,7 +8,6 @@ import android.database.Cursor; | ||||
| import android.database.MatrixCursor; | ||||
| import android.net.Uri; | ||||
| import android.os.Build; | ||||
| import android.os.Bundle; | ||||
| import android.os.Environment; | ||||
| import android.os.ParcelFileDescriptor; | ||||
| import android.provider.OpenableColumns; | ||||
| @@ -34,8 +33,6 @@ public class FileProvider extends ContentProvider { | ||||
|  | ||||
|     private PathStrategy mStrategy; | ||||
|  | ||||
|     public static ProviderCallHandler callHandler; | ||||
|  | ||||
|     @Override | ||||
|     public boolean onCreate() { | ||||
|         return true; | ||||
| @@ -131,13 +128,6 @@ public class FileProvider extends ContentProvider { | ||||
|         return file.delete() ? 1 : 0; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Bundle call(String method, String arg, Bundle extras) { | ||||
|         if (callHandler != null) | ||||
|             return callHandler.call(getContext(), method, arg, extras); | ||||
|         return Bundle.EMPTY; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public ParcelFileDescriptor openFile(Uri uri, String mode) | ||||
|             throws FileNotFoundException { | ||||
|   | ||||
| @@ -1,8 +0,0 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.content.Context; | ||||
| import android.os.Bundle; | ||||
|  | ||||
| public interface ProviderCallHandler { | ||||
|     Bundle call(Context context, String method, String arg, Bundle extras); | ||||
| } | ||||
| @@ -30,6 +30,6 @@ repositories { | ||||
| dependencies { | ||||
|     implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar")))) | ||||
|  | ||||
|     api("org.bouncycastle:bcprov-jdk15on:1.66") | ||||
|     api("org.bouncycastle:bcpkix-jdk15on:1.66") | ||||
|     api("org.bouncycastle:bcprov-jdk15on:1.67") | ||||
|     api("org.bouncycastle:bcpkix-jdk15on:1.67") | ||||
| } | ||||
|   | ||||
| @@ -4,11 +4,10 @@ | ||||
|     package="com.topjohnwu.magisk"> | ||||
|  | ||||
|     <uses-permission android:name="android.permission.INTERNET"/> | ||||
|     <uses-permission android:name="android.permission.VIBRATE" /> | ||||
|     <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> | ||||
|     <uses-permission | ||||
|         android:name="android.permission.WRITE_EXTERNAL_STORAGE" | ||||
|         android:maxSdkVersion="28" /> | ||||
|         android:maxSdkVersion="29" /> | ||||
|  | ||||
|     <application | ||||
|         android:icon="@drawable/ic_launcher" | ||||
| @@ -65,6 +64,15 @@ | ||||
|         <!-- DownloadService --> | ||||
|         <service android:name="a.j" /> | ||||
|  | ||||
|         <!-- FileProvider --> | ||||
|         <provider | ||||
|             android:name="a.p" | ||||
|             android:authorities="${applicationId}.provider" | ||||
|             android:directBootAware="true" | ||||
|             android:exported="false" | ||||
|             android:grantUriPermissions="true"> | ||||
|         </provider> | ||||
|  | ||||
|         <!-- Hardcode GMS version --> | ||||
|         <meta-data | ||||
|             android:name="com.google.android.gms.version" | ||||
|   | ||||
| @@ -2,7 +2,8 @@ | ||||
| package a | ||||
|  | ||||
| import com.topjohnwu.magisk.core.App | ||||
| import com.topjohnwu.magisk.core.GeneralReceiver | ||||
| import com.topjohnwu.magisk.core.Provider | ||||
| import com.topjohnwu.magisk.core.Receiver | ||||
| import com.topjohnwu.magisk.core.SplashActivity | ||||
| import com.topjohnwu.magisk.core.download.DownloadService | ||||
| import com.topjohnwu.magisk.ui.MainActivity | ||||
| @@ -22,8 +23,10 @@ class e : App { | ||||
|     constructor(o: Any) : super(o) | ||||
| } | ||||
|  | ||||
| class h : GeneralReceiver() | ||||
| class h : Receiver() | ||||
|  | ||||
| class j : DownloadService() | ||||
|  | ||||
| class m : SuRequestActivity() | ||||
|  | ||||
| class p : Provider() | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| package com.topjohnwu.magisk.arch | ||||
|  | ||||
| import android.content.Intent | ||||
| import android.os.Bundle | ||||
| import android.view.KeyEvent | ||||
| import android.view.View | ||||
| @@ -41,11 +40,6 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> : | ||||
|         AppCompatDelegate.setDefaultNightMode(theme) | ||||
|     } | ||||
|  | ||||
|     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { | ||||
|         super.onActivityResult(requestCode, resultCode, data) | ||||
|         currentFragment?.onActivityResult(requestCode, resultCode, data) | ||||
|     } | ||||
|  | ||||
|     override fun onCreate(savedInstanceState: Bundle?) { | ||||
|         setTheme(themeRes) | ||||
|         super.onCreate(savedInstanceState) | ||||
| @@ -76,6 +70,10 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> : | ||||
|         ensureInsets() | ||||
|     } | ||||
|  | ||||
|     fun setAccessibilityDelegate(delegate: View.AccessibilityDelegate?) { | ||||
|         viewRoot.rootView.accessibilityDelegate = delegate | ||||
|     } | ||||
|  | ||||
|     override fun onResume() { | ||||
|         super.onResume() | ||||
|         viewModel.requestRefresh() | ||||
| @@ -85,7 +83,7 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> : | ||||
|         return currentFragment?.onKeyEvent(event) == true || super.dispatchKeyEvent(event) | ||||
|     } | ||||
|  | ||||
|     override fun onEventDispatched(event: ViewEvent) = when(event) { | ||||
|     override fun onEventDispatched(event: ViewEvent) = when (event) { | ||||
|         is ContextExecutor -> event(this) | ||||
|         is ActivityExecutor -> event(this) | ||||
|         else -> Unit | ||||
|   | ||||
| @@ -17,7 +17,7 @@ import com.topjohnwu.magisk.ktx.startAnimations | ||||
| abstract class BaseUIFragment<VM : BaseViewModel, Binding : ViewDataBinding> : | ||||
|     Fragment(), BaseUIComponent<VM> { | ||||
|  | ||||
|     protected val activity get() = requireActivity() as BaseUIActivity<*, *> | ||||
|     val activity get() = requireActivity() as BaseUIActivity<*, *> | ||||
|     protected lateinit var binding: Binding | ||||
|     protected abstract val layoutRes: Int | ||||
|  | ||||
|   | ||||
| @@ -9,18 +9,18 @@ import me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapter | ||||
| import me.tatarka.bindingcollectionadapter2.ItemBinding | ||||
| import me.tatarka.bindingcollectionadapter2.OnItemBind | ||||
|  | ||||
| inline fun <T : ComparableRvItem<*>> diffListOf( | ||||
| fun <T : ComparableRvItem<*>> diffListOf( | ||||
|     vararg newItems: T | ||||
| ) = diffListOf(newItems.toList()) | ||||
|  | ||||
| inline fun <T : ComparableRvItem<*>> diffListOf( | ||||
| fun <T : ComparableRvItem<*>> diffListOf( | ||||
|     newItems: List<T> | ||||
| ) = DiffObservableList(object : DiffObservableList.Callback<T> { | ||||
|     override fun areItemsTheSame(oldItem: T, newItem: T) = oldItem.genericItemSameAs(newItem) | ||||
|     override fun areContentsTheSame(oldItem: T, newItem: T) = oldItem.genericContentSameAs(newItem) | ||||
| }).also { it.update(newItems) } | ||||
|  | ||||
| inline fun <T : ComparableRvItem<*>> filterableListOf( | ||||
| fun <T : ComparableRvItem<*>> filterableListOf( | ||||
|     vararg newItems: T | ||||
| ) = FilterableDiffObservableList(object : DiffObservableList.Callback<T> { | ||||
|     override fun areItemsTheSame(oldItem: T, newItem: T) = oldItem.genericItemSameAs(newItem) | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| package com.topjohnwu.magisk.arch | ||||
|  | ||||
| import android.content.Context | ||||
| import androidx.fragment.app.Fragment | ||||
| import kotlinx.coroutines.CoroutineScope | ||||
|  | ||||
| /** | ||||
| @@ -23,5 +22,5 @@ interface ActivityExecutor { | ||||
| } | ||||
|  | ||||
| interface FragmentExecutor { | ||||
|     operator fun invoke(fragment: Fragment) | ||||
|     operator fun invoke(fragment: BaseUIFragment<*, *>) | ||||
| } | ||||
|   | ||||
| @@ -10,8 +10,6 @@ import androidx.multidex.MultiDex | ||||
| import androidx.work.WorkManager | ||||
| import com.topjohnwu.magisk.BuildConfig | ||||
| import com.topjohnwu.magisk.DynAPK | ||||
| import com.topjohnwu.magisk.FileProvider | ||||
| import com.topjohnwu.magisk.core.su.SuCallbackHandler | ||||
| import com.topjohnwu.magisk.core.utils.IODispatcherExecutor | ||||
| import com.topjohnwu.magisk.core.utils.RootInit | ||||
| import com.topjohnwu.magisk.core.utils.updateConfig | ||||
| @@ -36,7 +34,6 @@ open class App() : Application() { | ||||
|             .setInitializers(RootInit::class.java) | ||||
|             .setTimeout(2)) | ||||
|         Shell.EXECUTOR = IODispatcherExecutor() | ||||
|         FileProvider.callHandler = SuCallbackHandler | ||||
|  | ||||
|         // Always log full stack trace with Timber | ||||
|         Timber.plant(Timber.DebugTree()) | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| package com.topjohnwu.magisk.core | ||||
|  | ||||
| import android.annotation.SuppressLint | ||||
| import android.content.Context | ||||
| import android.content.SharedPreferences | ||||
| import android.util.Xml | ||||
| @@ -9,19 +10,16 @@ import com.topjohnwu.magisk.BuildConfig | ||||
| import com.topjohnwu.magisk.core.magiskdb.SettingsDao | ||||
| import com.topjohnwu.magisk.core.magiskdb.StringDao | ||||
| import com.topjohnwu.magisk.core.utils.BiometricHelper | ||||
| import com.topjohnwu.magisk.core.utils.defaultLocale | ||||
| import com.topjohnwu.magisk.core.utils.refreshLocale | ||||
| import com.topjohnwu.magisk.data.preference.PreferenceModel | ||||
| import com.topjohnwu.magisk.data.repository.DBConfig | ||||
| import com.topjohnwu.magisk.di.Protected | ||||
| import com.topjohnwu.magisk.ktx.get | ||||
| import com.topjohnwu.magisk.ktx.inject | ||||
| import com.topjohnwu.magisk.ui.theme.Theme | ||||
| import com.topjohnwu.superuser.Shell | ||||
| import com.topjohnwu.superuser.io.SuFile | ||||
| import com.topjohnwu.superuser.io.SuFileInputStream | ||||
| import org.xmlpull.v1.XmlPullParser | ||||
| import java.io.File | ||||
| import java.io.IOException | ||||
| import java.io.InputStream | ||||
|  | ||||
| object Config : PreferenceModel, DBConfig { | ||||
|  | ||||
| @@ -29,6 +27,15 @@ object Config : PreferenceModel, DBConfig { | ||||
|     override val settingsDao: SettingsDao by inject() | ||||
|     override val context: Context by inject(Protected) | ||||
|  | ||||
|     @get:SuppressLint("ApplySharedPref") | ||||
|     val prefsFile: File get() { | ||||
|         // Flush prefs to disk | ||||
|         prefs.edit().apply { | ||||
|             remove(Key.ASKED_HOME) | ||||
|         }.commit() | ||||
|         return File("${context.filesDir.parent}/shared_prefs", "${fileName}.xml") | ||||
|     } | ||||
|  | ||||
|     object Key { | ||||
|         // db configs | ||||
|         const val ROOT_ACCESS = "root_access" | ||||
| @@ -43,6 +50,7 @@ object Config : PreferenceModel, DBConfig { | ||||
|         const val SU_AUTO_RESPONSE = "su_auto_response" | ||||
|         const val SU_NOTIFICATION = "su_notification" | ||||
|         const val SU_REAUTH = "su_reauth" | ||||
|         const val SU_TAPJACK = "su_tapjack" | ||||
|         const val CHECK_UPDATES = "check_update" | ||||
|         const val UPDATE_CHANNEL = "update_channel" | ||||
|         const val CUSTOM_CHANNEL = "custom_channel" | ||||
| @@ -119,7 +127,7 @@ object Config : PreferenceModel, DBConfig { | ||||
|     var repoOrder by preference(Key.REPO_ORDER, Value.ORDER_DATE) | ||||
|  | ||||
|     var suDefaultTimeout by preferenceStrInt(Key.SU_REQUEST_TIMEOUT, 10) | ||||
|     var suAutoReponse by preferenceStrInt(Key.SU_AUTO_RESPONSE, Value.SU_PROMPT) | ||||
|     var suAutoResponse by preferenceStrInt(Key.SU_AUTO_RESPONSE, Value.SU_PROMPT) | ||||
|     var suNotification by preferenceStrInt(Key.SU_NOTIFICATION, Value.NOTIFICATION_TOAST) | ||||
|     var updateChannel by preferenceStrInt(Key.UPDATE_CHANNEL, defaultChannel) | ||||
|  | ||||
| @@ -127,8 +135,9 @@ object Config : PreferenceModel, DBConfig { | ||||
|     var darkTheme by preference(Key.DARK_THEME, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) | ||||
|     var themeOrdinal by preference(Key.THEME_ORDINAL, Theme.Piplup.ordinal) | ||||
|     var suReAuth by preference(Key.SU_REAUTH, false) | ||||
|     var suTapjack by preference(Key.SU_TAPJACK, true) | ||||
|     var checkUpdate by preference(Key.CHECK_UPDATES, true) | ||||
|     var doh by preference(Key.DOH, defaultLocale.country == "CN") | ||||
|     var doh by preference(Key.DOH, false) | ||||
|     var magiskHide by preference(Key.MAGISKHIDE, true) | ||||
|     var showSystemApp by preference(Key.SHOW_SYSTEM_APP, false) | ||||
|  | ||||
| @@ -150,8 +159,12 @@ object Config : PreferenceModel, DBConfig { | ||||
|  | ||||
|     private const val SU_FINGERPRINT = "su_fingerprint" | ||||
|  | ||||
|     fun initialize() { | ||||
|         prefs.edit { parsePrefs() } | ||||
|     fun load(pkg: String) { | ||||
|         try { | ||||
|             context.contentResolver.openInputStream(Provider.PREFS_URI(pkg))?.use { | ||||
|                 prefs.edit { parsePrefs(it) } | ||||
|             } | ||||
|         } catch (e: IOException) {} | ||||
|  | ||||
|         prefs.edit { | ||||
|             // Settings migration | ||||
| @@ -173,10 +186,8 @@ object Config : PreferenceModel, DBConfig { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private fun SharedPreferences.Editor.parsePrefs() { | ||||
|         val config = SuFile.open("/data/adb", Const.MANAGER_CONFIGS) | ||||
|         if (config.exists()) runCatching { | ||||
|             val input = SuFileInputStream(config) | ||||
|     private fun SharedPreferences.Editor.parsePrefs(input: InputStream) { | ||||
|         runCatching { | ||||
|             val parser = Xml.newPullParser() | ||||
|             parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false) | ||||
|             parser.setInput(input, "UTF-8") | ||||
| @@ -220,21 +231,6 @@ object Config : PreferenceModel, DBConfig { | ||||
|                     else -> parser.next() | ||||
|                 } | ||||
|             } | ||||
|             config.delete() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun export() { | ||||
|         // Flush prefs to disk | ||||
|         prefs.edit().apply { | ||||
|             remove(Key.ASKED_HOME) | ||||
|         }.commit() | ||||
|         val context = get<Context>(Protected) | ||||
|         val xml = File( | ||||
|             "${context.filesDir.parent}/shared_prefs", | ||||
|             "${context.packageName}_preferences.xml" | ||||
|         ) | ||||
|         Shell.su("cat $xml > /data/adb/${Const.MANAGER_CONFIGS}").exec() | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -12,13 +12,12 @@ object Const { | ||||
|  | ||||
|     // Versions | ||||
|     const val SNET_EXT_VER = 15 | ||||
|     const val SNET_REVISION = "d494bc726e86166913a13629e3b1336728ec5d7f" | ||||
|     const val BOOTCTL_REVISION = "a6c47f86f10b310358afa9dbe837037dd5d561df" | ||||
|     const val SNET_REVISION = "18ab78817087c337ae0edd1ecac38aec49217880" | ||||
|     const val BOOTCTL_REVISION = "18ab78817087c337ae0edd1ecac38aec49217880" | ||||
|  | ||||
|     // Misc | ||||
|     const val ANDROID_MANIFEST = "AndroidManifest.xml" | ||||
|     const val MAGISK_INSTALL_LOG_FILENAME = "magisk_install_log_%s.log" | ||||
|     const val MANAGER_CONFIGS = ".tmp.magisk.config" | ||||
|     val USER_ID = Process.myUid() / 100000 | ||||
|  | ||||
|     object Version { | ||||
| @@ -33,12 +32,12 @@ object Const { | ||||
|  | ||||
|     object ID { | ||||
|         const val FETCH_ZIP = 2 | ||||
|         const val SELECT_BOOT = 3 | ||||
|         const val SELECT_FILE = 3 | ||||
|         const val MAX_ACTIVITY_RESULT = 10 | ||||
|  | ||||
|         // notifications | ||||
|         const val MAGISK_UPDATE_NOTIFICATION_ID = 4 | ||||
|         const val APK_UPDATE_NOTIFICATION_ID = 5 | ||||
|         const val DTBO_NOTIFICATION_ID = 7 | ||||
|         const val HIDE_MANAGER_NOTIFICATION_ID = 8 | ||||
|         const val UPDATE_NOTIFICATION_CHANNEL = "update" | ||||
|         const val PROGRESS_NOTIFICATION_CHANNEL = "progress" | ||||
| @@ -46,22 +45,20 @@ object Const { | ||||
|     } | ||||
|  | ||||
|     object Url { | ||||
|         const val ZIP_URL = "https://github.com/Magisk-Modules-Repo/%s/archive/master.zip" | ||||
|         const val PATREON_URL = "https://www.patreon.com/topjohnwu" | ||||
|         const val SOURCE_CODE_URL = "https://github.com/topjohnwu/Magisk" | ||||
|  | ||||
|         const val GITHUB_RAW_URL = "https://raw.githubusercontent.com/" | ||||
|         const val GITHUB_API_URL = "https://api.github.com/users/Magisk-Modules-Repo/" | ||||
|         const val GITHUB_API_URL = "https://api.github.com/" | ||||
|         const val GITHUB_PAGE_URL = "https://topjohnwu.github.io/magisk_files/" | ||||
|         const val JS_DELIVR_URL = "https://cdn.jsdelivr.net/gh/" | ||||
|         const val OFFICIAL_REPO = "https://magisk-modules-repo.github.io/submission/modules.json" | ||||
|     } | ||||
|  | ||||
|     object Key { | ||||
|         // others | ||||
|         const val LINK_KEY = "Link" | ||||
|         const val IF_NONE_MATCH = "If-None-Match" | ||||
|         const val ETAG_KEY = "ETag" | ||||
|         // intents | ||||
|         const val OPEN_SECTION = "section" | ||||
|         const val HIDDEN_PKG = "hidden_pkg" | ||||
|     } | ||||
|  | ||||
|     object Value { | ||||
|   | ||||
| @@ -149,7 +149,7 @@ private object ClassMap { | ||||
|         App::class.java to a.e::class.java, | ||||
|         MainActivity::class.java to a.b::class.java, | ||||
|         SplashActivity::class.java to a.c::class.java, | ||||
|         GeneralReceiver::class.java to a.h::class.java, | ||||
|         Receiver::class.java to a.h::class.java, | ||||
|         DownloadService::class.java to a.j::class.java, | ||||
|         SuRequestActivity::class.java to a.m::class.java | ||||
|     ) | ||||
|   | ||||
							
								
								
									
										39
									
								
								app/src/main/java/com/topjohnwu/magisk/core/Provider.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								app/src/main/java/com/topjohnwu/magisk/core/Provider.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| package com.topjohnwu.magisk.core | ||||
|  | ||||
| import android.content.Context | ||||
| import android.content.pm.ProviderInfo | ||||
| import android.net.Uri | ||||
| import android.os.Bundle | ||||
| import android.os.ParcelFileDescriptor | ||||
| import android.os.ParcelFileDescriptor.MODE_READ_ONLY | ||||
| import com.topjohnwu.magisk.FileProvider | ||||
| import com.topjohnwu.magisk.core.su.SuCallbackHandler | ||||
| import java.io.File | ||||
|  | ||||
| open class Provider : FileProvider() { | ||||
|  | ||||
|     override fun attachInfo(context: Context, info: ProviderInfo?) { | ||||
|         super.attachInfo(context.wrap(), info) | ||||
|     } | ||||
|  | ||||
|     override fun call(method: String, arg: String?, extras: Bundle?): Bundle? { | ||||
|         SuCallbackHandler(context!!, method, extras) | ||||
|         return Bundle.EMPTY | ||||
|     } | ||||
|  | ||||
|     override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? { | ||||
|         return when (uri.encodedPath ?: return null) { | ||||
|             "/apk_file" -> ParcelFileDescriptor.open(File(context!!.packageCodePath), MODE_READ_ONLY) | ||||
|             "/prefs_file" -> ParcelFileDescriptor.open(Config.prefsFile, MODE_READ_ONLY) | ||||
|             else -> super.openFile(uri, mode) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     companion object { | ||||
|         fun APK_URI(pkg: String) = | ||||
|             Uri.Builder().scheme("content").authority("$pkg.provider").path("apk_file").build() | ||||
|  | ||||
|         fun PREFS_URI(pkg: String) = | ||||
|             Uri.Builder().scheme("content").authority("$pkg.provider").path("prefs_file").build() | ||||
|     } | ||||
| } | ||||
| @@ -11,7 +11,7 @@ import kotlinx.coroutines.GlobalScope | ||||
| import kotlinx.coroutines.launch | ||||
| import org.koin.core.inject | ||||
|  | ||||
| open class GeneralReceiver : BaseReceiver() { | ||||
| open class Receiver : BaseReceiver() { | ||||
|  | ||||
|     private val policyDB: PolicyDao by inject() | ||||
|  | ||||
| @@ -3,9 +3,9 @@ package com.topjohnwu.magisk.core | ||||
| import android.app.Activity | ||||
| import android.content.Context | ||||
| import android.os.Bundle | ||||
| import com.topjohnwu.magisk.BuildConfig | ||||
| import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID | ||||
| import com.topjohnwu.magisk.R | ||||
| import com.topjohnwu.magisk.data.network.GithubRawServices | ||||
| import com.topjohnwu.magisk.data.repository.NetworkService | ||||
| import com.topjohnwu.magisk.ktx.get | ||||
| import com.topjohnwu.magisk.ui.MainActivity | ||||
| import com.topjohnwu.magisk.view.Notifications | ||||
| @@ -29,18 +29,18 @@ open class SplashActivity : Activity() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private fun handleRepackage() { | ||||
|         val pkg = Config.suManager | ||||
|         if (Config.suManager.isNotEmpty() && packageName == BuildConfig.APPLICATION_ID) { | ||||
|             Config.suManager = "" | ||||
|             Shell.su("(pm uninstall $pkg)& >/dev/null 2>&1").exec() | ||||
|         } | ||||
|         if (pkg == packageName) { | ||||
|     private fun handleRepackage(pkg: String?) { | ||||
|         if (packageName != APPLICATION_ID) { | ||||
|             runCatching { | ||||
|                 // We are the manager, remove com.topjohnwu.magisk as it could be malware | ||||
|                 packageManager.getApplicationInfo(BuildConfig.APPLICATION_ID, 0) | ||||
|                 Shell.su("(pm uninstall ${BuildConfig.APPLICATION_ID})& >/dev/null 2>&1").exec() | ||||
|                 // Hidden, remove com.topjohnwu.magisk if exist as it could be malware | ||||
|                 packageManager.getApplicationInfo(APPLICATION_ID, 0) | ||||
|                 Shell.su("(pm uninstall $APPLICATION_ID)& >/dev/null 2>&1").exec() | ||||
|             } | ||||
|         } else { | ||||
|             if (Config.suManager.isNotEmpty()) | ||||
|                 Config.suManager = "" | ||||
|             pkg ?: return | ||||
|             Shell.su("(pm uninstall $pkg)& >/dev/null 2>&1").exec() | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -48,14 +48,16 @@ open class SplashActivity : Activity() { | ||||
|         // Pre-initialize root shell | ||||
|         Shell.getShell() | ||||
|  | ||||
|         Config.initialize() | ||||
|         handleRepackage() | ||||
|         val hiddenPackage = intent.getStringExtra(Const.Key.HIDDEN_PKG) | ||||
|  | ||||
|         Config.load(hiddenPackage ?: APPLICATION_ID) | ||||
|         handleRepackage(hiddenPackage) | ||||
|         Notifications.setup(this) | ||||
|         UpdateCheckService.schedule(this) | ||||
|         Shortcuts.setupDynamic(this) | ||||
|  | ||||
|         // Pre-fetch network stuffs | ||||
|         get<GithubRawServices>() | ||||
|         // Pre-fetch network services | ||||
|         get<NetworkService>() | ||||
|  | ||||
|         DONE = true | ||||
|  | ||||
|   | ||||
| @@ -3,7 +3,7 @@ package com.topjohnwu.magisk.core | ||||
| import android.content.Context | ||||
| import androidx.work.* | ||||
| import com.topjohnwu.magisk.BuildConfig | ||||
| import com.topjohnwu.magisk.data.repository.MagiskRepository | ||||
| import com.topjohnwu.magisk.data.repository.NetworkService | ||||
| import com.topjohnwu.magisk.view.Notifications | ||||
| import com.topjohnwu.superuser.Shell | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| @@ -15,14 +15,14 @@ import java.util.concurrent.TimeUnit | ||||
| class UpdateCheckService(context: Context, workerParams: WorkerParameters) | ||||
|     : CoroutineWorker(context, workerParams), KoinComponent { | ||||
|  | ||||
|     private val magiskRepo: MagiskRepository by inject() | ||||
|     private val svc: NetworkService by inject() | ||||
|  | ||||
|     override suspend fun doWork(): Result { | ||||
|         // Make sure shell initializer was ran | ||||
|         withContext(Dispatchers.IO) { | ||||
|             Shell.getShell() | ||||
|         } | ||||
|         return magiskRepo.fetchUpdate()?.let { | ||||
|         return svc.fetchUpdate()?.let { | ||||
|             if (BuildConfig.VERSION_CODE < it.app.versionCode) | ||||
|                 Notifications.managerUpdate(applicationContext) | ||||
|             else if (Info.env.isActive && Info.env.magiskVersionCode < it.magisk.versionCode) | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| package com.topjohnwu.magisk.core.base | ||||
|  | ||||
| import android.Manifest | ||||
| import android.Manifest.permission.WRITE_EXTERNAL_STORAGE | ||||
| import android.content.ActivityNotFoundException | ||||
| import android.content.Context | ||||
| import android.content.Intent | ||||
| @@ -8,22 +8,24 @@ import android.content.pm.PackageManager | ||||
| import android.content.res.Configuration | ||||
| import android.os.Build | ||||
| import android.widget.Toast | ||||
| import androidx.annotation.CallSuper | ||||
| import androidx.appcompat.app.AppCompatActivity | ||||
| import androidx.collection.SparseArrayCompat | ||||
| import androidx.core.app.ActivityCompat | ||||
| import androidx.core.content.ContextCompat | ||||
| import com.topjohnwu.magisk.R | ||||
| import com.topjohnwu.magisk.core.Const | ||||
| import com.topjohnwu.magisk.core.utils.currentLocale | ||||
| import com.topjohnwu.magisk.core.wrap | ||||
| import com.topjohnwu.magisk.ktx.set | ||||
| import com.topjohnwu.magisk.utils.Utils | ||||
| import kotlin.random.Random | ||||
|  | ||||
| typealias RequestCallback = BaseActivity.(Int, Intent?) -> Unit | ||||
| typealias ActivityResultCallback = BaseActivity.(Int, Intent?) -> Unit | ||||
|  | ||||
| abstract class BaseActivity : AppCompatActivity() { | ||||
|  | ||||
|     private val resultCallbacks by lazy { SparseArrayCompat<RequestCallback>() } | ||||
|     private val resultCallbacks by lazy { SparseArrayCompat<ActivityResultCallback>() } | ||||
|  | ||||
|     override fun applyOverrideConfiguration(config: Configuration?) { | ||||
|         // Force applying our preferred local | ||||
| @@ -38,9 +40,8 @@ abstract class BaseActivity : AppCompatActivity() { | ||||
|     fun withPermission(permission: String, builder: PermissionRequestBuilder.() -> Unit) { | ||||
|         val request = PermissionRequestBuilder().apply(builder).build() | ||||
|  | ||||
|         if (permission == Manifest.permission.WRITE_EXTERNAL_STORAGE && | ||||
|             Build.VERSION.SDK_INT >= 29) { | ||||
|             // We do not need external rw on 29+ | ||||
|         if (permission == WRITE_EXTERNAL_STORAGE && Build.VERSION.SDK_INT >= 30) { | ||||
|             // We do not need external rw on 30+ | ||||
|             request.onSuccess() | ||||
|             return | ||||
|         } | ||||
| @@ -48,8 +49,11 @@ abstract class BaseActivity : AppCompatActivity() { | ||||
|         if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) { | ||||
|             request.onSuccess() | ||||
|         } else { | ||||
|             val requestCode = Random.nextInt(256, 512) | ||||
|             resultCallbacks[requestCode] =  { result, _ -> | ||||
|             var requestCode: Int | ||||
|             do { | ||||
|                 requestCode = Random.nextInt(Const.ID.MAX_ACTIVITY_RESULT + 1, 1 shl 15) | ||||
|             } while (resultCallbacks.containsKey(requestCode)) | ||||
|             resultCallbacks[requestCode] = { result, _ -> | ||||
|                 if (result > 0) | ||||
|                     request.onSuccess() | ||||
|                 else | ||||
| @@ -60,7 +64,7 @@ abstract class BaseActivity : AppCompatActivity() { | ||||
|     } | ||||
|  | ||||
|     fun withExternalRW(builder: PermissionRequestBuilder.() -> Unit) { | ||||
|         withPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, builder = builder) | ||||
|         withPermission(WRITE_EXTERNAL_STORAGE, builder = builder) | ||||
|     } | ||||
|  | ||||
|     override fun onRequestPermissionsResult( | ||||
| @@ -79,16 +83,17 @@ abstract class BaseActivity : AppCompatActivity() { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     @CallSuper | ||||
|     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { | ||||
|         super.onActivityResult(requestCode, resultCode, data) | ||||
|         resultCallbacks[requestCode]?.also { | ||||
|         resultCallbacks[requestCode]?.also { callback -> | ||||
|             resultCallbacks.remove(requestCode) | ||||
|             it(this, resultCode, data) | ||||
|             callback(this, resultCode, data) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun startActivityForResult(intent: Intent, requestCode: Int, listener: RequestCallback) { | ||||
|         resultCallbacks[requestCode] = listener | ||||
|     fun startActivityForResult(intent: Intent, requestCode: Int, callback: ActivityResultCallback) { | ||||
|         resultCallbacks[requestCode] = callback | ||||
|         try { | ||||
|             startActivityForResult(intent, requestCode) | ||||
|         } catch (e: ActivityNotFoundException) { | ||||
|   | ||||
| @@ -16,15 +16,6 @@ sealed class Action : Parcelable { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     sealed class APK : Action() { | ||||
|  | ||||
|         @Parcelize | ||||
|         object Upgrade : APK() | ||||
|  | ||||
|         @Parcelize | ||||
|         object Restore : APK() | ||||
|     } | ||||
|  | ||||
|     @Parcelize | ||||
|     object Download : Action() | ||||
|  | ||||
|   | ||||
| @@ -11,7 +11,7 @@ import com.topjohnwu.magisk.core.base.BaseService | ||||
| import com.topjohnwu.magisk.core.utils.MediaStoreUtils.checkSum | ||||
| import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream | ||||
| import com.topjohnwu.magisk.core.utils.ProgressInputStream | ||||
| import com.topjohnwu.magisk.data.network.GithubRawServices | ||||
| import com.topjohnwu.magisk.data.repository.NetworkService | ||||
| import com.topjohnwu.magisk.ktx.withStreams | ||||
| import com.topjohnwu.magisk.view.Notifications | ||||
| import kotlinx.coroutines.CoroutineScope | ||||
| @@ -34,7 +34,7 @@ abstract class BaseDownloader : BaseService(), KoinComponent { | ||||
|     private val notifications = Collections.synchronizedMap(HashMap<Int, Notification.Builder>()) | ||||
|     private val coroutineScope = CoroutineScope(Dispatchers.IO) | ||||
|  | ||||
|     val service: GithubRawServices by inject() | ||||
|     val service: NetworkService by inject() | ||||
|  | ||||
|     // -- Service overrides | ||||
|  | ||||
| @@ -117,7 +117,7 @@ abstract class BaseDownloader : BaseService(), KoinComponent { | ||||
|     fun Subject.notifyID() = hashCode() | ||||
|  | ||||
|     private fun notifyFail(subject: Subject) = lastNotify(subject.notifyID()) { | ||||
|         broadcast(-1f, subject) | ||||
|         broadcast(-2f, subject) | ||||
|         it.setContentText(getString(R.string.download_file_error)) | ||||
|             .setSmallIcon(android.R.drawable.stat_notify_error) | ||||
|             .setOngoing(false) | ||||
|   | ||||
| @@ -73,10 +73,7 @@ open class DownloadService : BaseDownloader() { | ||||
|     } | ||||
|  | ||||
|     private fun Notification.Builder.setIntent(subject: Manager) | ||||
|     = when (subject.action) { | ||||
|         APK.Upgrade -> setContentIntent(APKInstall.installIntent(context, subject.file.toFile())) | ||||
|         else -> setContentIntent(Intent()) | ||||
|     } | ||||
|     = setContentIntent(APKInstall.installIntent(context, subject.file.toFile())) | ||||
|  | ||||
|     private fun Notification.Builder.setContentIntent(intent: Intent) = | ||||
|         setContentIntent( | ||||
|   | ||||
| @@ -5,20 +5,16 @@ import androidx.core.net.toFile | ||||
| import com.topjohnwu.magisk.BuildConfig | ||||
| import com.topjohnwu.magisk.DynAPK | ||||
| import com.topjohnwu.magisk.R | ||||
| import com.topjohnwu.magisk.core.Config | ||||
| import com.topjohnwu.magisk.core.Info | ||||
| import com.topjohnwu.magisk.core.download.Action.APK.Restore | ||||
| import com.topjohnwu.magisk.core.download.Action.APK.Upgrade | ||||
| import com.topjohnwu.magisk.core.isRunningAsStub | ||||
| import com.topjohnwu.magisk.core.tasks.PatchAPK | ||||
| import com.topjohnwu.magisk.core.tasks.HideAPK | ||||
| import com.topjohnwu.magisk.ktx.relaunchApp | ||||
| import com.topjohnwu.magisk.ktx.writeTo | ||||
| import com.topjohnwu.superuser.Shell | ||||
| import java.io.File | ||||
|  | ||||
| private fun Context.patch(apk: File) { | ||||
|     val patched = File(apk.parent, "patched.apk") | ||||
|     PatchAPK.patch(this, apk.path, patched.path, packageName, applicationInfo.nonLocalizedLabel) | ||||
|     HideAPK.patch(this, apk.path, patched.path, packageName, applicationInfo.nonLocalizedLabel) | ||||
|     apk.delete() | ||||
|     patched.renameTo(apk) | ||||
| } | ||||
| @@ -31,7 +27,7 @@ private fun BaseDownloader.notifyHide(id: Int) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| private suspend fun BaseDownloader.upgrade(subject: Subject.Manager) { | ||||
| suspend fun BaseDownloader.handleAPK(subject: Subject.Manager) { | ||||
|     val apk = subject.file.toFile() | ||||
|     val id = subject.notifyID() | ||||
|     if (isRunningAsStub) { | ||||
| @@ -53,20 +49,3 @@ private suspend fun BaseDownloader.upgrade(subject: Subject.Manager) { | ||||
|         patch(apk) | ||||
|     } | ||||
| } | ||||
|  | ||||
| private fun BaseDownloader.restore(apk: File, id: Int) { | ||||
|     update(id) { | ||||
|         it.setProgress(0, 0, true) | ||||
|             .setProgress(0, 0, true) | ||||
|             .setContentTitle(getString(R.string.restore_img_msg)) | ||||
|             .setContentText("") | ||||
|     } | ||||
|     Config.export() | ||||
|     Shell.su("pm install $apk && pm uninstall $packageName").exec() | ||||
| } | ||||
|  | ||||
| suspend fun BaseDownloader.handleAPK(subject: Subject.Manager) = | ||||
|     when (subject.action) { | ||||
|         is Upgrade -> upgrade(subject) | ||||
|         is Restore -> restore(subject.file.toFile(), subject.notifyID()) | ||||
|     } | ||||
|   | ||||
| @@ -8,7 +8,7 @@ import com.topjohnwu.magisk.core.Info | ||||
| import com.topjohnwu.magisk.core.model.MagiskJson | ||||
| import com.topjohnwu.magisk.core.model.ManagerJson | ||||
| import com.topjohnwu.magisk.core.model.StubJson | ||||
| import com.topjohnwu.magisk.core.model.module.Repo | ||||
| import com.topjohnwu.magisk.core.model.module.OnlineModule | ||||
| import com.topjohnwu.magisk.core.utils.MediaStoreUtils | ||||
| import com.topjohnwu.magisk.ktx.cachedFile | ||||
| import com.topjohnwu.magisk.ktx.get | ||||
| @@ -26,10 +26,10 @@ sealed class Subject : Parcelable { | ||||
|  | ||||
|     @Parcelize | ||||
|     class Module( | ||||
|         val module: Repo, | ||||
|         val module: OnlineModule, | ||||
|         override val action: Action | ||||
|     ) : Subject() { | ||||
|         override val url: String get() = module.zipUrl | ||||
|         override val url: String get() = module.zip_url | ||||
|         override val title: String get() = module.downloadFilename | ||||
|  | ||||
|         @IgnoredOnParcel | ||||
| @@ -40,16 +40,12 @@ sealed class Subject : Parcelable { | ||||
|  | ||||
|     @Parcelize | ||||
|     class Manager( | ||||
|         override val action: Action.APK, | ||||
|         private val app: ManagerJson = Info.remote.app, | ||||
|         val stub: StubJson = Info.remote.stub | ||||
|     ) : Subject() { | ||||
|  | ||||
|         override val title: String | ||||
|             get() = "MagiskManager-${app.version}(${app.versionCode})" | ||||
|  | ||||
|         override val url: String | ||||
|             get() = app.link | ||||
|         override val action get() = Action.Download | ||||
|         override val title: String get() = "MagiskManager-${app.version}(${app.versionCode})" | ||||
|         override val url: String get() = app.link | ||||
|  | ||||
|         @IgnoredOnParcel | ||||
|         override val file by lazy { | ||||
|   | ||||
| @@ -41,3 +41,29 @@ data class StubJson( | ||||
|     val versionCode: Int = -1, | ||||
|     val link: String = "" | ||||
| ) : Parcelable | ||||
|  | ||||
| @JsonClass(generateAdapter = true) | ||||
| data class ModuleJson( | ||||
|     val id: String, | ||||
|     val last_update: Long, | ||||
|     val prop_url: String, | ||||
|     val zip_url: String, | ||||
|     val notes_url: String | ||||
| ) | ||||
|  | ||||
| @JsonClass(generateAdapter = true) | ||||
| data class RepoJson( | ||||
|     val name: String, | ||||
|     val last_update: Long, | ||||
|     val modules: List<ModuleJson> | ||||
| ) | ||||
|  | ||||
| @JsonClass(generateAdapter = true) | ||||
| data class CommitInfo( | ||||
|     val sha: String | ||||
| ) | ||||
|  | ||||
| @JsonClass(generateAdapter = true) | ||||
| data class BranchInfo( | ||||
|     val commit: CommitInfo | ||||
| ) | ||||
|   | ||||
| @@ -1,41 +0,0 @@ | ||||
| package com.topjohnwu.magisk.core.model.module | ||||
|  | ||||
| abstract class BaseModule : Comparable<BaseModule> { | ||||
|     abstract var id: String | ||||
|         protected set | ||||
|     abstract var name: String | ||||
|         protected set | ||||
|     abstract var author: String | ||||
|         protected set | ||||
|     abstract var version: String | ||||
|         protected set | ||||
|     abstract var versionCode: Int | ||||
|         protected set | ||||
|     abstract var description: String | ||||
|         protected set | ||||
|  | ||||
|     @Throws(NumberFormatException::class) | ||||
|     protected fun parseProps(props: List<String>) { | ||||
|         for (line in props) { | ||||
|             val prop = line.split("=".toRegex(), 2).map { it.trim() } | ||||
|             if (prop.size != 2) | ||||
|                 continue | ||||
|  | ||||
|             val key = prop[0] | ||||
|             val value = prop[1] | ||||
|             if (key.isEmpty() || key[0] == '#') | ||||
|                 continue | ||||
|  | ||||
|             when (key) { | ||||
|                 "id" -> id = value | ||||
|                 "name" -> name = value | ||||
|                 "version" -> version = value | ||||
|                 "versionCode" -> versionCode = value.toInt() | ||||
|                 "author" -> author = value | ||||
|                 "description" -> description = value | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override operator fun compareTo(other: BaseModule) = name.compareTo(other.name, true) | ||||
| } | ||||
| @@ -0,0 +1,89 @@ | ||||
| package com.topjohnwu.magisk.core.model.module | ||||
|  | ||||
| import com.topjohnwu.magisk.core.Const | ||||
| import com.topjohnwu.superuser.Shell | ||||
| import com.topjohnwu.superuser.io.SuFile | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| import kotlinx.coroutines.withContext | ||||
|  | ||||
| class LocalModule(path: String) : Module() { | ||||
|     override var id: String = "" | ||||
|     override var name: String = "" | ||||
|     override var author: String = "" | ||||
|     override var version: String = "" | ||||
|     override var versionCode: Int = -1 | ||||
|     override var description: String = "" | ||||
|  | ||||
|     private val removeFile = SuFile(path, "remove") | ||||
|     private val disableFile = SuFile(path, "disable") | ||||
|     private val updateFile = SuFile(path, "update") | ||||
|     private val ruleFile = SuFile(path, "sepolicy.rule") | ||||
|  | ||||
|     val updated: Boolean get() = updateFile.exists() | ||||
|  | ||||
|     var enable: Boolean | ||||
|         get() = !disableFile.exists() | ||||
|         set(enable) { | ||||
|             val dir = "$PERSIST/$id" | ||||
|             if (enable) { | ||||
|                 disableFile.delete() | ||||
|                 if (Const.Version.isCanary()) | ||||
|                     Shell.su("copy_sepolicy_rules").submit() | ||||
|                 else | ||||
|                     Shell.su("mkdir -p $dir", "cp -af $ruleFile $dir").submit() | ||||
|             } else { | ||||
|                 !disableFile.createNewFile() | ||||
|                 if (Const.Version.isCanary()) | ||||
|                     Shell.su("copy_sepolicy_rules").submit() | ||||
|                 else | ||||
|                     Shell.su("rm -rf $dir").submit() | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     var remove: Boolean | ||||
|         get() = removeFile.exists() | ||||
|         set(remove) { | ||||
|             if (remove) { | ||||
|                 removeFile.createNewFile() | ||||
|                 if (Const.Version.isCanary()) | ||||
|                     Shell.su("copy_sepolicy_rules").submit() | ||||
|                 else | ||||
|                     Shell.su("rm -rf $PERSIST/$id").submit() | ||||
|             } else { | ||||
|                 !removeFile.delete() | ||||
|                 if (Const.Version.isCanary()) | ||||
|                     Shell.su("copy_sepolicy_rules").submit() | ||||
|                 else | ||||
|                     Shell.su("cp -af $ruleFile $PERSIST/$id").submit() | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     init { | ||||
|         runCatching { | ||||
|             parseProps(Shell.su("dos2unix < $path/module.prop").exec().out) | ||||
|         } | ||||
|  | ||||
|         if (id.isEmpty()) { | ||||
|             val sep = path.lastIndexOf('/') | ||||
|             id = path.substring(sep + 1) | ||||
|         } | ||||
|  | ||||
|         if (name.isEmpty()) { | ||||
|             name = id | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     companion object { | ||||
|  | ||||
|         private val PERSIST get() = "${Const.MAGISKTMP}/mirror/persist/magisk" | ||||
|  | ||||
|         suspend fun installed() = withContext(Dispatchers.IO) { | ||||
|             SuFile(Const.MAGISK_PATH) | ||||
|                 .listFiles { _, name -> name != "lost+found" && name != ".core" } | ||||
|                 .orEmpty() | ||||
|                 .filter { !it.isFile } | ||||
|                 .map { LocalModule("${Const.MAGISK_PATH}/${it.name}") } | ||||
|                 .sortedBy { it.name.toLowerCase() } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,77 +1,41 @@ | ||||
| package com.topjohnwu.magisk.core.model.module | ||||
|  | ||||
| import com.topjohnwu.magisk.core.Const | ||||
| import com.topjohnwu.superuser.Shell | ||||
| import com.topjohnwu.superuser.io.SuFile | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| import kotlinx.coroutines.withContext | ||||
| abstract class Module : Comparable<Module> { | ||||
|     abstract var id: String | ||||
|         protected set | ||||
|     abstract var name: String | ||||
|         protected set | ||||
|     abstract var author: String | ||||
|         protected set | ||||
|     abstract var version: String | ||||
|         protected set | ||||
|     abstract var versionCode: Int | ||||
|         protected set | ||||
|     abstract var description: String | ||||
|         protected set | ||||
|  | ||||
| class Module(path: String) : BaseModule() { | ||||
|     override var id: String = "" | ||||
|     override var name: String = "" | ||||
|     override var author: String = "" | ||||
|     override var version: String = "" | ||||
|     override var versionCode: Int = -1 | ||||
|     override var description: String = "" | ||||
|     @Throws(NumberFormatException::class) | ||||
|     protected fun parseProps(props: List<String>) { | ||||
|         for (line in props) { | ||||
|             val prop = line.split("=".toRegex(), 2).map { it.trim() } | ||||
|             if (prop.size != 2) | ||||
|                 continue | ||||
|  | ||||
|     private val removeFile = SuFile(path, "remove") | ||||
|     private val disableFile = SuFile(path, "disable") | ||||
|     private val updateFile = SuFile(path, "update") | ||||
|     private val ruleFile = SuFile(path, "sepolicy.rule") | ||||
|             val key = prop[0] | ||||
|             val value = prop[1] | ||||
|             if (key.isEmpty() || key[0] == '#') | ||||
|                 continue | ||||
|  | ||||
|     val updated: Boolean get() = updateFile.exists() | ||||
|  | ||||
|     var enable: Boolean | ||||
|         get() = !disableFile.exists() | ||||
|         set(enable) { | ||||
|             val dir = "$PERSIST/$id" | ||||
|             if (enable) { | ||||
|                 Shell.su("mkdir -p $dir", "cp -af $ruleFile $dir").submit() | ||||
|                 disableFile.delete() | ||||
|             } else { | ||||
|                 Shell.su("rm -rf $dir").submit() | ||||
|                 !disableFile.createNewFile() | ||||
|             when (key) { | ||||
|                 "id" -> id = value | ||||
|                 "name" -> name = value | ||||
|                 "version" -> version = value | ||||
|                 "versionCode" -> versionCode = value.toInt() | ||||
|                 "author" -> author = value | ||||
|                 "description" -> description = value | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     var remove: Boolean | ||||
|         get() = removeFile.exists() | ||||
|         set(remove) { | ||||
|             if (remove) { | ||||
|                 Shell.su("rm -rf $PERSIST/$id").submit() | ||||
|                 removeFile.createNewFile() | ||||
|             } else { | ||||
|                 Shell.su("cp -af $ruleFile $PERSIST/$id").submit() | ||||
|                 !removeFile.delete() | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     init { | ||||
|         runCatching { | ||||
|             parseProps(Shell.su("dos2unix < $path/module.prop").exec().out) | ||||
|         } | ||||
|  | ||||
|         if (id.isEmpty()) { | ||||
|             val sep = path.lastIndexOf('/') | ||||
|             id = path.substring(sep + 1) | ||||
|         } | ||||
|  | ||||
|         if (name.isEmpty()) { | ||||
|             name = id | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     companion object { | ||||
|  | ||||
|         private val PERSIST get() = "${Const.MAGISKTMP}/mirror/persist/magisk" | ||||
|  | ||||
|         suspend fun installed() = withContext(Dispatchers.IO) { | ||||
|             SuFile(Const.MAGISK_PATH) | ||||
|                 .listFiles { _, name -> name != "lost+found" && name != ".core" } | ||||
|                 .orEmpty() | ||||
|                 .filter { !it.isFile } | ||||
|                 .map { Module("${Const.MAGISK_PATH}/${it.name}") } | ||||
|                 .sortedBy { it.name.toLowerCase() } | ||||
|         } | ||||
|     } | ||||
|     override operator fun compareTo(other: Module) = name.compareTo(other.name, true) | ||||
| } | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user