mirror of
				https://github.com/topjohnwu/Magisk
				synced 2025-10-30 09:00:52 +01:00 
			
		
		
		
	Compare commits
	
		
			118 Commits
		
	
	
		
			manager-v7
			...
			manager-v7
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 0b41cd8564 | ||
|   | 7db523071d | ||
|   | 974ee58b9c | ||
|   | 1e88f2c382 | ||
|   | 0bdcfcaaf5 | ||
|   | 5f9c78d04f | ||
|   | afa178fdec | ||
|   | 3a0e3c98f7 | ||
|   | fafa92d44b | ||
|   | 242e64d72f | ||
|   | 2262af728e | ||
|   | bce777d7c6 | ||
|   | 15bd2da824 | ||
|   | bd438ca288 | ||
|   | e0d02a61a9 | ||
|   | b3328a0ec2 | ||
|   | 3c2041933f | ||
|   | e88b1cc443 | ||
|   | 71b05b18a0 | ||
|   | b07b528e2a | ||
|   | 1aeb6315ff | ||
|   | 1b4a3d2d9f | ||
|   | 3049a81c3b | ||
|   | 2db1e5cb74 | ||
|   | 78c64d39ec | ||
|   | 46ba726232 | ||
|   | eb26e62889 | ||
|   | 7f667fed18 | ||
|   | b2cb2b8b75 | ||
|   | d19f65ce4a | ||
|   | 025b060506 | ||
|   | 7fa2625a03 | ||
|   | 33d62d7f21 | ||
|   | b336655a79 | ||
|   | 3beffd84d6 | ||
|   | 02761f5f35 | ||
|   | 3b9f7885e0 | ||
|   | 7668e45890 | ||
|   | 695c8bc5d0 | ||
|   | 06c42d05c3 | ||
|   | 404104208f | ||
|   | b4d0ad9713 | ||
|   | 4f4f54a059 | ||
|   | 12fda29280 | ||
|   | af060b3132 | ||
|   | 8c500709e4 | ||
|   | 490e6a6f23 | ||
|   | 08177c3dd8 | ||
|   | d22b9c26b6 | ||
|   | 4bb8ad19cf | ||
|   | 3e275b7dba | ||
|   | 11b7076a43 | ||
|   | 291c718ba2 | ||
|   | fcd6071c57 | ||
|   | 476b61c4c9 | ||
|   | 8cc5f096a2 | ||
|   | 474d65207e | ||
|   | 03428329ef | ||
|   | 8d21988656 | ||
|   | 72edbfc455 | ||
|   | 276535dad6 | ||
|   | e373e59661 | ||
|   | 34bb18448c | ||
|   | 01253f050a | ||
|   | 5bee1c56a9 | ||
|   | 474cc7d56d | ||
|   | bffdedddb4 | ||
|   | fd72f658c0 | ||
|   | d3b5cf82d8 | ||
|   | d26d804cc2 | ||
|   | 4f9a25ee89 | ||
|   | bb9ce0e897 | ||
|   | d6fb9868bf | ||
|   | 9aff1a57d3 | ||
|   | 7681fde4d0 | ||
|   | d3b7b41927 | ||
|   | da159e4655 | ||
|   | 7f6a6016d6 | ||
|   | 44ed0a3279 | ||
|   | 9964e1bb8e | ||
|   | 8b8f725499 | ||
|   | bab856bce2 | ||
|   | 3d285b91c6 | ||
|   | 1dc531930d | ||
|   | 3d3345acac | ||
|   | b29f0ca4d1 | ||
|   | 576efbdc1b | ||
|   | a7f0510a3e | ||
|   | 2ef088cb60 | ||
|   | 7c320b6fc4 | ||
|   | 5a4c82b860 | ||
|   | 9b297b752e | ||
|   | 1d6ba58ccd | ||
|   | 1542447822 | ||
|   | a6f0aff659 | ||
|   | 171ddab32b | ||
|   | 2aee0b0be0 | ||
|   | 817cdf7113 | ||
|   | 1a38f25bd9 | ||
|   | ad40e53349 | ||
|   | a2ddf362d8 | ||
|   | 65eca31635 | ||
|   | 8b0b4a2c39 | ||
|   | c0216c0653 | ||
|   | 61de63a518 | ||
|   | d952cc2327 | ||
|   | 46447f7cfd | ||
|   | a6e62e07a2 | ||
|   | b1d25e0503 | ||
|   | 25c557248c | ||
|   | 472cde29b8 | ||
|   | 73525d19e9 | ||
|   | 26618f8d73 | ||
|   | 6f7c13b814 | ||
|   | e7d668502c | ||
|   | 6fd357962f | ||
|   | 0c9feedb37 | ||
|   | 14ba002cbc | 
| @@ -35,13 +35,12 @@ android { | ||||
|     } | ||||
|  | ||||
|     packagingOptions { | ||||
|         exclude '/META-INF/*.version' | ||||
|         exclude '/META-INF/*.kotlin_module' | ||||
|         exclude '/META-INF/rxkotlin.properties' | ||||
|         exclude '/META-INF/**' | ||||
|         exclude '/androidsupportmultidexversion.txt' | ||||
|         exclude '/org/bouncycastle/**' | ||||
|         exclude '/kotlin/**' | ||||
|         exclude '/kotlinx/**' | ||||
|         exclude '/okhttp3/**' | ||||
|     } | ||||
|  | ||||
|     kotlinOptions { | ||||
| @@ -75,7 +74,7 @@ dependencies { | ||||
|     implementation "${bindingAdapter}:${vBAdapt}" | ||||
|     implementation "${bindingAdapter}-recyclerview:${vBAdapt}" | ||||
|  | ||||
|     def vMarkwon = '4.1.2' | ||||
|     def vMarkwon = '4.2.0' | ||||
|     implementation "io.noties.markwon:core:${vMarkwon}" | ||||
|     implementation "io.noties.markwon:html:${vMarkwon}" | ||||
|     implementation "io.noties.markwon:image:${vMarkwon}" | ||||
| @@ -85,7 +84,7 @@ dependencies { | ||||
|     implementation "com.github.topjohnwu.libsu:core:${vLibsu}" | ||||
|     implementation "com.github.topjohnwu.libsu:io:${vLibsu}" | ||||
|  | ||||
|     def vKoin = "2.0.1" | ||||
|     def vKoin = '2.0.1' | ||||
|     implementation "org.koin:koin-core:${vKoin}" | ||||
|     implementation "org.koin:koin-android:${vKoin}" | ||||
|     implementation "org.koin:koin-androidx-viewmodel:${vKoin}" | ||||
| @@ -100,10 +99,10 @@ dependencies { | ||||
|     implementation "com.squareup.okhttp3:okhttp:${vOkHttp}" | ||||
|     implementation "com.squareup.okhttp3:logging-interceptor:${vOkHttp}" | ||||
|  | ||||
|     def vMoshi = "1.8.0" | ||||
|     def vMoshi = '1.9.2' | ||||
|     implementation "com.squareup.moshi:moshi:${vMoshi}" | ||||
|  | ||||
|     def vKotshi = "2.0.1" | ||||
|     def vKotshi = '2.0.2' | ||||
|     implementation "se.ansman.kotshi:api:${vKotshi}" | ||||
|     kapt "se.ansman.kotshi:compiler:${vKotshi}" | ||||
|  | ||||
| @@ -112,23 +111,25 @@ dependencies { | ||||
|             replacedBy('com.github.topjohnwu:room-runtime') | ||||
|         } | ||||
|     } | ||||
|     def vRoom = "2.2.1" | ||||
|     def vRoom = '2.2.2' | ||||
|     implementation "com.github.topjohnwu:room-runtime:${vRoom}" | ||||
|     implementation "androidx.room:room-rxjava2:${vRoom}" | ||||
|     kapt "androidx.room:room-compiler:${vRoom}" | ||||
|  | ||||
|     def vNav = "2.1.0" | ||||
|     implementation "androidx.navigation:navigation-fragment-ktx:$vNav" | ||||
|     implementation "androidx.navigation:navigation-ui-ktx:$vNav" | ||||
|     def vNav = '2.1.0' | ||||
|     implementation "androidx.navigation:navigation-fragment-ktx:${vNav}" | ||||
|     implementation "androidx.navigation:navigation-ui-ktx:${vNav}" | ||||
|  | ||||
|     implementation 'androidx.biometric:biometric:1.0.0' | ||||
|     implementation 'androidx.constraintlayout:constraintlayout:1.1.3' | ||||
|     implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03' | ||||
|     implementation 'androidx.preference:preference:1.1.0' | ||||
|     implementation 'androidx.recyclerview:recyclerview:1.1.0-rc01' | ||||
|     implementation 'androidx.fragment:fragment-ktx:1.2.0-rc01' | ||||
|     implementation 'androidx.recyclerview:recyclerview:1.1.0' | ||||
|     implementation 'androidx.fragment:fragment-ktx:1.2.0-rc03' | ||||
|     implementation 'androidx.cardview:cardview:1.0.0' | ||||
|     implementation 'androidx.work:work-runtime:2.2.0' | ||||
|     implementation 'androidx.transition:transition:1.3.0-rc01' | ||||
|     implementation 'androidx.transition:transition:1.3.0-rc02' | ||||
|     implementation 'androidx.multidex:multidex:2.0.1' | ||||
|     implementation 'androidx.core:core-ktx:1.1.0' | ||||
|     implementation 'com.google.android.material:material:1.2.0-alpha01' | ||||
|     implementation 'com.google.android.material:material:1.2.0-alpha02' | ||||
| } | ||||
|   | ||||
| @@ -1,45 +1,21 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
|  | ||||
| <!-- | ||||
|     ** Special Requirements ** | ||||
|  | ||||
|     This AndroidManifest.xml will be copied into the stub | ||||
|     APK to allow APK delegation. This is why these special | ||||
|     requirements exist. | ||||
|  | ||||
|     * Class names * | ||||
|     Class names a.a, a.c, a.e should not be changed as they are used | ||||
|     externally. All other class names can be changed. | ||||
|  | ||||
|     * Resource IDs * | ||||
|     All resource IDs referred in AndroidManifest.xml is required to be | ||||
|     included into the "shared" module to make the ID match with stub. | ||||
|  | ||||
| --> | ||||
|  | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     package="com.topjohnwu.magisk"> | ||||
|  | ||||
|     <uses-permission android:name="android.permission.INTERNET"/> | ||||
|     <uses-permission android:name="android.permission.VIBRATE" /> | ||||
|     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> | ||||
|     <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> | ||||
|     <uses-permission android:name="android.permission.USE_FINGERPRINT" /> | ||||
|  | ||||
|     <application | ||||
|         android:name="a.e" | ||||
|         android:appComponentFactory="a.a" | ||||
|         android:allowBackup="true" | ||||
|         android:usesCleartextTraffic="true" | ||||
|         tools:ignore="UnusedAttribute,GoogleAppIndexingWarning" | ||||
|         tools:replace="android:appComponentFactory"> | ||||
|         tools:ignore="UnusedAttribute,GoogleAppIndexingWarning"> | ||||
|  | ||||
|         <!-- Splash --> | ||||
|  | ||||
|         <activity | ||||
|             android:name="a.c" | ||||
|             android:configChanges="orientation|screenSize" | ||||
|             android:theme="@style/SplashTheme"> | ||||
|             <intent-filter> | ||||
|                 <action android:name="android.intent.action.MAIN" /> | ||||
| @@ -48,36 +24,31 @@ | ||||
|         </activity> | ||||
|  | ||||
|         <!-- Main --> | ||||
|  | ||||
|         <activity | ||||
|             android:name="a.b" | ||||
|             android:configChanges="orientation|screenSize" | ||||
|             android:exported="true" /> | ||||
|         <activity android:name="a.b" /> | ||||
|  | ||||
|         <!-- Flashing  --> | ||||
|  | ||||
|         <activity | ||||
|             android:name="a.f" | ||||
|             android:configChanges="keyboardHidden|orientation|screenSize" | ||||
|             android:screenOrientation="nosensor" /> | ||||
|         <activity android:name="a.f" /> | ||||
|  | ||||
|         <!-- Superuser --> | ||||
|  | ||||
|         <activity | ||||
|             android:name="a.m" | ||||
|             android:theme="@android:style/Theme.Translucent.NoTitleBar" | ||||
|             android:directBootAware="true" | ||||
|             android:excludeFromRecents="true" | ||||
|             android:exported="false" /> | ||||
|             android:exported="false" | ||||
|             android:theme="@android:style/Theme.Translucent.NoTitleBar" | ||||
|             tools:ignore="AppLinkUrlError"> | ||||
|             <intent-filter> | ||||
|                 <action android:name="android.intent.action.VIEW"/> | ||||
|                 <category android:name="android.intent.category.DEFAULT"/> | ||||
|             </intent-filter> | ||||
|         </activity> | ||||
|  | ||||
|         <!-- Receiver --> | ||||
|  | ||||
|         <receiver | ||||
|             android:name="a.h" | ||||
|             android:directBootAware="true"> | ||||
|             <intent-filter> | ||||
|                 <action android:name="android.intent.action.REBOOT" /> | ||||
|                 <action android:name="android.intent.action.BOOT_COMPLETED" /> | ||||
|                 <action android:name="android.intent.action.LOCALE_CHANGED" /> | ||||
|             </intent-filter> | ||||
|             <intent-filter> | ||||
| @@ -89,10 +60,7 @@ | ||||
|         </receiver> | ||||
|  | ||||
|         <!-- DownloadService --> | ||||
|  | ||||
|         <service | ||||
|             android:name="a.j" | ||||
|             android:exported="false" /> | ||||
|         <service android:name="a.j" /> | ||||
|  | ||||
|         <!-- Hardcode GMS version --> | ||||
|         <meta-data | ||||
| @@ -105,6 +73,16 @@ | ||||
|             android:authorities="${applicationId}.workmanager-init" | ||||
|             tools:node="remove" /> | ||||
|  | ||||
|         <!-- We don't invalidate Room --> | ||||
|         <service | ||||
|             android:name="androidx.room.MultiInstanceInvalidationService" | ||||
|             tools:node="remove"/> | ||||
|  | ||||
|         <!-- We don't use Device Credentials --> | ||||
|         <activity | ||||
|             android:name="androidx.biometric.DeviceCredentialHandlerActivity" | ||||
|             tools:node="remove" /> | ||||
|  | ||||
|     </application> | ||||
|  | ||||
| </manifest> | ||||
|   | ||||
| @@ -1,11 +1,9 @@ | ||||
| package a; | ||||
|  | ||||
| import androidx.core.app.AppComponentFactory; | ||||
|  | ||||
| import com.topjohnwu.magisk.utils.PatchAPK; | ||||
| import com.topjohnwu.signing.BootSigner; | ||||
|  | ||||
| public class a extends AppComponentFactory { | ||||
| public class a { | ||||
|  | ||||
|     @Deprecated | ||||
|     public static boolean patchAPK(String in, String out, String pkg) { | ||||
|   | ||||
| @@ -11,11 +11,14 @@ import androidx.work.impl.WorkDatabase | ||||
| import androidx.work.impl.WorkDatabase_Impl | ||||
| import com.topjohnwu.magisk.data.database.RepoDatabase | ||||
| import com.topjohnwu.magisk.data.database.RepoDatabase_Impl | ||||
| import com.topjohnwu.magisk.data.database.SuLogDatabase | ||||
| import com.topjohnwu.magisk.data.database.SuLogDatabase_Impl | ||||
| import com.topjohnwu.magisk.di.ActivityTracker | ||||
| import com.topjohnwu.magisk.di.koinModules | ||||
| import com.topjohnwu.magisk.extensions.get | ||||
| import com.topjohnwu.magisk.extensions.unwrap | ||||
| import com.topjohnwu.magisk.utils.RootInit | ||||
| import com.topjohnwu.magisk.utils.SuHandler | ||||
| import com.topjohnwu.magisk.utils.updateConfig | ||||
| import com.topjohnwu.superuser.Shell | ||||
| import org.koin.android.ext.koin.androidContext | ||||
| @@ -34,10 +37,12 @@ open class App() : Application() { | ||||
|         Shell.Config.verboseLogging(BuildConfig.DEBUG) | ||||
|         Shell.Config.addInitializers(RootInit::class.java) | ||||
|         Shell.Config.setTimeout(2) | ||||
|         FileProvider.callHandler = SuHandler | ||||
|         Room.setFactory { | ||||
|             when (it) { | ||||
|                 WorkDatabase::class.java -> WorkDatabase_Impl() | ||||
|                 RepoDatabase::class.java -> RepoDatabase_Impl() | ||||
|                 SuLogDatabase::class.java -> SuLogDatabase_Impl() | ||||
|                 else -> null | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -11,9 +11,8 @@ import com.topjohnwu.magisk.data.repository.DBConfig | ||||
| import com.topjohnwu.magisk.di.Protected | ||||
| import com.topjohnwu.magisk.extensions.get | ||||
| import com.topjohnwu.magisk.extensions.inject | ||||
| import com.topjohnwu.magisk.extensions.packageName | ||||
| import com.topjohnwu.magisk.model.preference.PreferenceModel | ||||
| import com.topjohnwu.magisk.utils.FingerprintHelper | ||||
| import com.topjohnwu.magisk.utils.BiometricHelper | ||||
| import com.topjohnwu.magisk.utils.Utils | ||||
| import com.topjohnwu.superuser.Shell | ||||
| import com.topjohnwu.superuser.io.SuFile | ||||
| @@ -32,7 +31,7 @@ object Config : PreferenceModel, DBConfig { | ||||
|         const val ROOT_ACCESS = "root_access" | ||||
|         const val SU_MULTIUSER_MODE = "multiuser_mode" | ||||
|         const val SU_MNT_NS = "mnt_ns" | ||||
|         const val SU_FINGERPRINT = "su_fingerprint" | ||||
|         const val SU_BIOMETRIC = "su_biometric" | ||||
|         const val SU_MANAGER = "requester" | ||||
|         const val KEYSTORE = "keystore" | ||||
|  | ||||
| @@ -49,6 +48,7 @@ object Config : PreferenceModel, DBConfig { | ||||
|         const val REPO_ORDER = "repo_order" | ||||
|         const val SHOW_SYSTEM_APP = "show_system" | ||||
|         const val DOWNLOAD_PATH = "download_path" | ||||
|         const val BOOT_ID = "boot_id" | ||||
|  | ||||
|         // system state | ||||
|         const val MAGISKHIDE = "magiskhide" | ||||
| @@ -106,6 +106,8 @@ object Config : PreferenceModel, DBConfig { | ||||
|         } | ||||
|         else Value.DEFAULT_CHANNEL | ||||
|  | ||||
|     var bootId by preference(Key.BOOT_ID, "") | ||||
|  | ||||
|     var downloadPath by preference(Key.DOWNLOAD_PATH, Environment.DIRECTORY_DOWNLOADS) | ||||
|     var repoOrder by preference(Key.REPO_ORDER, Value.ORDER_DATE) | ||||
|  | ||||
| @@ -127,7 +129,7 @@ object Config : PreferenceModel, DBConfig { | ||||
|     var rootMode by dbSettings(Key.ROOT_ACCESS, Value.ROOT_ACCESS_APPS_AND_ADB) | ||||
|     var suMntNamespaceMode by dbSettings(Key.SU_MNT_NS, Value.NAMESPACE_MODE_REQUESTER) | ||||
|     var suMultiuserMode by dbSettings(Key.SU_MULTIUSER_MODE, Value.MULTIUSER_MODE_OWNER_ONLY) | ||||
|     var suFingerprint by dbSettings(Key.SU_FINGERPRINT, false) | ||||
|     var suBiometric by dbSettings(Key.SU_BIOMETRIC, false) | ||||
|     var suManager by dbStrings(Key.SU_MANAGER, "", true) | ||||
|     var keyStoreRaw by dbStrings(Key.KEYSTORE, "", true) | ||||
|  | ||||
| @@ -135,9 +137,18 @@ object Config : PreferenceModel, DBConfig { | ||||
|     val downloadDirectory get() = | ||||
|         Utils.ensureDownloadPath(downloadPath) ?: get<Context>().getExternalFilesDir(null)!! | ||||
|  | ||||
|     fun initialize() = prefs.edit { | ||||
|     private const val SU_FINGERPRINT = "su_fingerprint" | ||||
|  | ||||
|     fun initialize() = prefs.also { | ||||
|         if (it.getBoolean(SU_FINGERPRINT, false)) { | ||||
|             suBiometric = true | ||||
|         } | ||||
|     }.edit { | ||||
|         parsePrefs(this) | ||||
|  | ||||
|         // Legacy stuff | ||||
|         remove(SU_FINGERPRINT) | ||||
|  | ||||
|         // Get actual state | ||||
|         putBoolean(Key.COREONLY, Const.MAGISK_DISABLE_FILE.exists()) | ||||
|  | ||||
| @@ -145,7 +156,7 @@ object Config : PreferenceModel, DBConfig { | ||||
|         putString(Key.ROOT_ACCESS, rootMode.toString()) | ||||
|         putString(Key.SU_MNT_NS, suMntNamespaceMode.toString()) | ||||
|         putString(Key.SU_MULTIUSER_MODE, suMultiuserMode.toString()) | ||||
|         putBoolean(Key.SU_FINGERPRINT, FingerprintHelper.useFingerprint()) | ||||
|         putBoolean(Key.SU_BIOMETRIC, BiometricHelper.isEnabled) | ||||
|     }.also { | ||||
|         if (!prefs.contains(Key.UPDATE_CHANNEL)) | ||||
|             prefs.edit().putString(Key.UPDATE_CHANNEL, defaultChannel.toString()).apply() | ||||
| @@ -154,7 +165,7 @@ object Config : PreferenceModel, DBConfig { | ||||
|     private fun parsePrefs(editor: SharedPreferences.Editor) = editor.apply { | ||||
|         val config = SuFile.open("/data/adb", Const.MANAGER_CONFIGS) | ||||
|         if (config.exists()) runCatching { | ||||
|             val input = SuFileInputStream(config).buffered() | ||||
|             val input = SuFileInputStream(config) | ||||
|             val parser = Xml.newPullParser() | ||||
|             parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false) | ||||
|             parser.setInput(input, "UTF-8") | ||||
| @@ -205,9 +216,10 @@ object Config : PreferenceModel, DBConfig { | ||||
|     fun export() { | ||||
|         // Flush prefs to disk | ||||
|         prefs.edit().commit() | ||||
|         val context = get<Context>(Protected) | ||||
|         val xml = File( | ||||
|             "${get<Context>(Protected).filesDir.parent}/shared_prefs", | ||||
|             "${packageName}_preferences.xml" | ||||
|             "${context.filesDir.parent}/shared_prefs", | ||||
|             "${context.packageName}_preferences.xml" | ||||
|         ) | ||||
|         Shell.su("cat $xml > /data/adb/${Const.MANAGER_CONFIGS}").exec() | ||||
|     } | ||||
|   | ||||
| @@ -23,8 +23,10 @@ object Const { | ||||
|     val USER_ID = Process.myUid() / 100000 | ||||
|  | ||||
|     object Version { | ||||
|         const val MIN_SUPPORT = 18000 | ||||
|         const val CONNECT_MODE = 20002 | ||||
|         const val MIN_VERSION = "v18.0" | ||||
|         const val MIN_VERCODE = 18000 | ||||
|         const val CONNECT_MODE = 20100 | ||||
|         const val PROVIDER_CONNECT = 20102 | ||||
|     } | ||||
|  | ||||
|     object ID { | ||||
|   | ||||
| @@ -14,8 +14,7 @@ import android.content.res.AssetManager | ||||
| import android.content.res.Configuration | ||||
| import android.content.res.Resources | ||||
| import androidx.annotation.RequiresApi | ||||
| import androidx.annotation.StringRes | ||||
| import com.topjohnwu.magisk.extensions.langTagToLocale | ||||
| import com.topjohnwu.magisk.extensions.forceGetDeclaredField | ||||
| import com.topjohnwu.magisk.model.download.DownloadService | ||||
| import com.topjohnwu.magisk.model.receiver.GeneralReceiver | ||||
| import com.topjohnwu.magisk.model.update.UpdateCheckService | ||||
| @@ -23,11 +22,8 @@ import com.topjohnwu.magisk.ui.MainActivity | ||||
| import com.topjohnwu.magisk.ui.SplashActivity | ||||
| import com.topjohnwu.magisk.ui.flash.FlashActivity | ||||
| import com.topjohnwu.magisk.ui.surequest.SuRequestActivity | ||||
| import com.topjohnwu.magisk.utils.currentLocale | ||||
| import com.topjohnwu.magisk.utils.defaultLocale | ||||
| import com.topjohnwu.magisk.utils.refreshLocale | ||||
| import com.topjohnwu.magisk.utils.updateConfig | ||||
| import java.util.* | ||||
|  | ||||
| fun AssetManager.addAssetPath(path: String) { | ||||
|     DynAPK.addAssetPath(this, path) | ||||
| @@ -53,29 +49,22 @@ fun Context.wrapJob(): Context = object : GlobalResContext(this) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| fun Class<*>.cmp(pkg: String = BuildConfig.APPLICATION_ID): ComponentName { | ||||
| fun Class<*>.cmp(pkg: String): ComponentName { | ||||
|     val name = ClassMap[this].name | ||||
|     return ComponentName(pkg, Info.stub?.componentMap?.get(name) ?: name) | ||||
|     return ComponentName(pkg, Info.stub?.classToComponent?.get(name) ?: name) | ||||
| } | ||||
|  | ||||
| fun Context.intent(c: Class<*>): Intent { | ||||
|     val cls = ClassMap[c] | ||||
|     return Info.stub?.let { | ||||
|         val className = it.componentMap.getOrElse(cls.name) { cls.name } | ||||
|         Intent().setComponent(ComponentName(this, className)) | ||||
|     } ?: Intent(this, cls) | ||||
| } | ||||
| inline fun <reified T> Context.intent() = Intent().setComponent(T::class.java.cmp(packageName)) | ||||
|  | ||||
| private open class GlobalResContext(base: Context) : ContextWrapper(base) { | ||||
|     open val mRes: Resources get() = ResourceMgr.resource | ||||
|     private val loader by lazy { javaClass.classLoader!! } | ||||
|  | ||||
|     override fun getResources(): Resources { | ||||
|         return mRes | ||||
|     } | ||||
|  | ||||
|     override fun getClassLoader(): ClassLoader { | ||||
|         return loader | ||||
|         return javaClass.classLoader!! | ||||
|     } | ||||
|  | ||||
|     override fun createConfigurationContext(config: Configuration): Context { | ||||
| @@ -109,7 +98,7 @@ object ResourceMgr { | ||||
|     } | ||||
| } | ||||
|  | ||||
| @RequiresApi(api = 28) | ||||
| @RequiresApi(28) | ||||
| private class JobSchedulerWrapper(private val base: JobScheduler) : JobScheduler() { | ||||
|  | ||||
|     override fun schedule(job: JobInfo): Int { | ||||
| @@ -137,48 +126,14 @@ private class JobSchedulerWrapper(private val base: JobScheduler) : JobScheduler | ||||
|     } | ||||
|  | ||||
|     private fun JobInfo.patch(): JobInfo { | ||||
|         // We need to patch the component of JobInfo to access WorkManager SystemJobService | ||||
|  | ||||
|         // We need to swap out the service of JobInfo | ||||
|         val name = service.className | ||||
|         val component = ComponentName( | ||||
|             service.packageName, | ||||
|             Info.stub!!.componentMap[name] ?: name) | ||||
|             Info.stub!!.classToComponent[name] ?: name) | ||||
|  | ||||
|         // Clone the JobInfo except component | ||||
|         val builder = JobInfo.Builder(id, component) | ||||
|             .setExtras(extras) | ||||
|             .setTransientExtras(transientExtras) | ||||
|             .setClipData(clipData, clipGrantFlags) | ||||
|             .setRequiredNetwork(requiredNetwork) | ||||
|             .setEstimatedNetworkBytes(estimatedNetworkDownloadBytes, estimatedNetworkUploadBytes) | ||||
|             .setRequiresCharging(isRequireCharging) | ||||
|             .setRequiresDeviceIdle(isRequireDeviceIdle) | ||||
|             .setRequiresBatteryNotLow(isRequireBatteryNotLow) | ||||
|             .setRequiresStorageNotLow(isRequireStorageNotLow) | ||||
|             .also { | ||||
|                 triggerContentUris?.let { uris -> | ||||
|                     for (uri in uris) | ||||
|                         it.addTriggerContentUri(uri) | ||||
|                 } | ||||
|             } | ||||
|             .setTriggerContentUpdateDelay(triggerContentUpdateDelay) | ||||
|             .setTriggerContentMaxDelay(triggerContentMaxDelay) | ||||
|             .setImportantWhileForeground(isImportantWhileForeground) | ||||
|             .setPrefetch(isPrefetch) | ||||
|             .setPersisted(isPersisted) | ||||
|  | ||||
|         if (isPeriodic) { | ||||
|             builder.setPeriodic(intervalMillis, flexMillis) | ||||
|         } else { | ||||
|             if (minLatencyMillis > 0) | ||||
|                 builder.setMinimumLatency(minLatencyMillis) | ||||
|             if (maxExecutionDelayMillis > 0) | ||||
|                 builder.setOverrideDeadline(maxExecutionDelayMillis) | ||||
|         } | ||||
|         if (!isRequireDeviceIdle) | ||||
|             builder.setBackoffCriteria(initialBackoffMillis, backoffPolicy) | ||||
|  | ||||
|         return builder.build() | ||||
|         javaClass.forceGetDeclaredField("service")?.set(this, component) | ||||
|         return this | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -192,8 +147,9 @@ object ClassMap { | ||||
|         UpdateCheckService::class.java to a.g::class.java, | ||||
|         GeneralReceiver::class.java to a.h::class.java, | ||||
|         DownloadService::class.java to a.j::class.java, | ||||
|         SuRequestActivity::class.java to a.m::class.java | ||||
|         SuRequestActivity::class.java to a.m::class.java, | ||||
|         ProcessPhoenix::class.java to a.r::class.java | ||||
|     ) | ||||
|  | ||||
|     operator fun get(c: Class<*>) = map.getOrElse(c) { throw IllegalArgumentException() } | ||||
|     operator fun get(c: Class<*>) = map.getOrElse(c) { c } | ||||
| } | ||||
|   | ||||
| @@ -8,6 +8,8 @@ import com.topjohnwu.magisk.utils.CachedValue | ||||
| import com.topjohnwu.magisk.utils.KObservableField | ||||
| import com.topjohnwu.superuser.Shell | ||||
| import com.topjohnwu.superuser.ShellUtils | ||||
| import java.io.FileInputStream | ||||
| import java.io.IOException | ||||
|  | ||||
| val isRunningAsStub get() = Info.stub != null | ||||
|  | ||||
| @@ -32,28 +34,41 @@ object Info { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     val isNewReboot by lazy { | ||||
|         try { | ||||
|             FileInputStream("/proc/sys/kernel/random/boot_id").bufferedReader().use { | ||||
|                 val id = it.readLine() | ||||
|                 if (id != Config.bootId) { | ||||
|                     Config.bootId = id | ||||
|                     true | ||||
|                 } else { | ||||
|                     false | ||||
|                 } | ||||
|             } | ||||
|         } catch (e: IOException) { | ||||
|             false | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private fun loadState() = runCatching { | ||||
|         val str = ShellUtils.fastCmd("magisk -v").split(":".toRegex())[0] | ||||
|         val code = ShellUtils.fastCmd("magisk -V").toInt() | ||||
|         val hide = Shell.su("magiskhide --status").exec().isSuccess | ||||
|         var mode = -1 | ||||
|         if (code >= Const.Version.CONNECT_MODE) { | ||||
|             mode = Shell.su("magisk --connect-mode").exec().code | ||||
|             if (mode == 0) { | ||||
|                 // Manually trigger broadcast test | ||||
|                 Shell.su("magisk --broadcast-test").exec() | ||||
|             } | ||||
|         } | ||||
|         Env(code, str, hide, mode) | ||||
|         Env(str, code, hide) | ||||
|     }.getOrElse { Env() } | ||||
|  | ||||
|     class Env( | ||||
|         val magiskVersionCode: Int = -1, | ||||
|         val magiskVersionString: String = "", | ||||
|         hide: Boolean = false, | ||||
|         var connectionMode: Int = -1 | ||||
|         code: Int = -1, | ||||
|         hide: Boolean = false | ||||
|     ) { | ||||
|         val magiskHide get() = Config.magiskHide | ||||
|         val magiskVersionCode = when (code) { | ||||
|             in Int.MIN_VALUE..Const.Version.MIN_VERCODE -> -1 | ||||
|             else -> if(Shell.rootAccess()) code else -1 | ||||
|         } | ||||
|         val isUnsupported = code > 0 && code < Const.Version.MIN_VERCODE | ||||
|         val isActive = magiskVersionCode >= 0 | ||||
|  | ||||
|         init { | ||||
|             Config.magiskHide = hide | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| package com.topjohnwu.magisk.base.viewmodel | ||||
|  | ||||
| import android.app.Activity | ||||
| import com.topjohnwu.magisk.base.BaseActivity | ||||
| import com.topjohnwu.magisk.extensions.doOnSubscribeUi | ||||
| import com.topjohnwu.magisk.model.events.BackPressEvent | ||||
| import com.topjohnwu.magisk.model.events.PermissionEvent | ||||
| @@ -21,7 +21,7 @@ abstract class BaseViewModel( | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun withView(action: Activity.() -> Unit) { | ||||
|     fun withView(action: BaseActivity<*, *>.() -> Unit) { | ||||
|         ViewActionEvent(action).publish() | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,33 +0,0 @@ | ||||
| package com.topjohnwu.magisk.data.database | ||||
|  | ||||
| import com.topjohnwu.magisk.data.database.base.* | ||||
| import com.topjohnwu.magisk.model.entity.MagiskLog | ||||
| import com.topjohnwu.magisk.model.entity.toLog | ||||
| import com.topjohnwu.magisk.model.entity.toMap | ||||
| import java.util.concurrent.TimeUnit | ||||
|  | ||||
| class LogDao : BaseDao() { | ||||
|  | ||||
|     override val table = DatabaseDefinition.Table.LOG | ||||
|  | ||||
|     fun deleteOutdated( | ||||
|         suTimeout: Long = TimeUnit.DAYS.toMillis(14) | ||||
|     ) = query<Delete> { | ||||
|         condition { | ||||
|             lessThan("time", suTimeout.toString()) | ||||
|         } | ||||
|     }.ignoreElement() | ||||
|  | ||||
|     fun deleteAll() = query<Delete> {}.ignoreElement() | ||||
|  | ||||
|     fun fetchAll() = query<Select> { | ||||
|         orderBy("time", Order.DESC) | ||||
|     }.flattenAsFlowable { it } | ||||
|         .map { it.toLog() } | ||||
|         .toList() | ||||
|  | ||||
|     fun put(log: MagiskLog) = query<Insert> { | ||||
|         values(log.toMap()) | ||||
|     }.ignoreElement() | ||||
|  | ||||
| } | ||||
| @@ -3,7 +3,10 @@ package com.topjohnwu.magisk.data.database | ||||
| import android.content.Context | ||||
| import android.content.pm.PackageManager | ||||
| import com.topjohnwu.magisk.Const | ||||
| import com.topjohnwu.magisk.data.database.base.* | ||||
| import com.topjohnwu.magisk.data.database.magiskdb.BaseDao | ||||
| import com.topjohnwu.magisk.data.database.magiskdb.Delete | ||||
| import com.topjohnwu.magisk.data.database.magiskdb.Replace | ||||
| import com.topjohnwu.magisk.data.database.magiskdb.Select | ||||
| import com.topjohnwu.magisk.extensions.now | ||||
| import com.topjohnwu.magisk.model.entity.MagiskPolicy | ||||
| import com.topjohnwu.magisk.model.entity.toMap | ||||
| @@ -16,7 +19,7 @@ class PolicyDao( | ||||
|     private val context: Context | ||||
| ) : BaseDao() { | ||||
|  | ||||
|     override val table: String = DatabaseDefinition.Table.POLICY | ||||
|     override val table: String = Table.POLICY | ||||
|  | ||||
|     fun deleteOutdated( | ||||
|         nowSeconds: Long = TimeUnit.MILLISECONDS.toSeconds(now) | ||||
| @@ -72,4 +75,4 @@ class PolicyDao( | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -4,8 +4,14 @@ import androidx.room.* | ||||
| import com.topjohnwu.magisk.Config | ||||
| import com.topjohnwu.magisk.model.entity.module.Repo | ||||
|  | ||||
| @Database(version = 6, entities = [Repo::class, RepoEtag::class]) | ||||
| abstract class RepoDatabase : RoomDatabase() { | ||||
|  | ||||
|     abstract fun repoDao() : RepoDao | ||||
| } | ||||
|  | ||||
| @Dao | ||||
| abstract class RepoDao { | ||||
| abstract class RepoDao(private val db: RepoDatabase) { | ||||
|  | ||||
|     val repoIDList get() = getRepoID().map { it.id } | ||||
|  | ||||
| @@ -15,13 +21,10 @@ abstract class RepoDao { | ||||
|         } | ||||
|  | ||||
|     var etagKey: String | ||||
|         set(etag) = addEtagRaw(RepoEtag(0, etag)) | ||||
|         set(value) = addEtagRaw(RepoEtag(0, value)) | ||||
|         get() = etagRaw()?.key.orEmpty() | ||||
|  | ||||
|     fun clear() { | ||||
|         clearRepos() | ||||
|         clearEtag() | ||||
|     } | ||||
|     fun clear() = db.clearAllTables() | ||||
|  | ||||
|     @Query("SELECT * FROM repos ORDER BY last_update DESC") | ||||
|     protected abstract fun getReposDateOrder(): List<Repo> | ||||
| @@ -52,12 +55,6 @@ abstract class RepoDao { | ||||
|  | ||||
|     @Insert(onConflict = OnConflictStrategy.REPLACE) | ||||
|     protected abstract fun addEtagRaw(etag: RepoEtag) | ||||
|  | ||||
|     @Query("DELETE FROM repos") | ||||
|     protected abstract fun clearRepos() | ||||
|  | ||||
|     @Query("DELETE FROM etag") | ||||
|     protected abstract fun clearEtag() | ||||
| } | ||||
|  | ||||
| data class RepoID( | ||||
|   | ||||
| @@ -1,11 +0,0 @@ | ||||
| package com.topjohnwu.magisk.data.database | ||||
|  | ||||
| import androidx.room.Database | ||||
| import androidx.room.RoomDatabase | ||||
| import com.topjohnwu.magisk.model.entity.module.Repo | ||||
|  | ||||
| @Database(version = 6, entities = [Repo::class, RepoEtag::class]) | ||||
| abstract class RepoDatabase : RoomDatabase() { | ||||
|  | ||||
|     abstract fun repoDao() : RepoDao | ||||
| } | ||||
| @@ -1,10 +1,13 @@ | ||||
| package com.topjohnwu.magisk.data.database | ||||
|  | ||||
| import com.topjohnwu.magisk.data.database.base.* | ||||
| import com.topjohnwu.magisk.data.database.magiskdb.BaseDao | ||||
| import com.topjohnwu.magisk.data.database.magiskdb.Delete | ||||
| import com.topjohnwu.magisk.data.database.magiskdb.Replace | ||||
| import com.topjohnwu.magisk.data.database.magiskdb.Select | ||||
|  | ||||
| class SettingsDao : BaseDao() { | ||||
|  | ||||
|     override val table = DatabaseDefinition.Table.SETTINGS | ||||
|     override val table = Table.SETTINGS | ||||
|  | ||||
|     fun delete(key: String) = query<Delete> { | ||||
|         condition { equals("key", key) } | ||||
| @@ -19,4 +22,4 @@ class SettingsDao : BaseDao() { | ||||
|         condition { equals("key", key) } | ||||
|     }.map { it.firstOrNull()?.values?.firstOrNull()?.toIntOrNull() ?: default } | ||||
|  | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -1,10 +1,13 @@ | ||||
| package com.topjohnwu.magisk.data.database | ||||
|  | ||||
| import com.topjohnwu.magisk.data.database.base.* | ||||
| import com.topjohnwu.magisk.data.database.magiskdb.BaseDao | ||||
| import com.topjohnwu.magisk.data.database.magiskdb.Delete | ||||
| import com.topjohnwu.magisk.data.database.magiskdb.Replace | ||||
| import com.topjohnwu.magisk.data.database.magiskdb.Select | ||||
|  | ||||
| class StringDao : BaseDao() { | ||||
|  | ||||
|     override val table = DatabaseDefinition.Table.STRINGS | ||||
|     override val table = Table.STRINGS | ||||
|  | ||||
|     fun delete(key: String) = query<Delete> { | ||||
|         condition { equals("key", key) } | ||||
| @@ -19,4 +22,4 @@ class StringDao : BaseDao() { | ||||
|         condition { equals("key", key) } | ||||
|     }.map { it.firstOrNull()?.values?.firstOrNull() ?: default } | ||||
|  | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,36 @@ | ||||
| package com.topjohnwu.magisk.data.database | ||||
|  | ||||
| import androidx.room.* | ||||
| import com.topjohnwu.magisk.model.entity.MagiskLog | ||||
| import io.reactivex.Completable | ||||
| import io.reactivex.Single | ||||
| import java.util.* | ||||
|  | ||||
| @Database(version = 1, entities = [MagiskLog::class]) | ||||
| abstract class SuLogDatabase : RoomDatabase() { | ||||
|  | ||||
|     abstract fun suLogDao(): SuLogDao | ||||
| } | ||||
|  | ||||
| @Dao | ||||
| abstract class SuLogDao(private val db: SuLogDatabase) { | ||||
|  | ||||
|     private val twoWeeksAgo = | ||||
|         Calendar.getInstance().apply { add(Calendar.WEEK_OF_YEAR, -2) }.timeInMillis | ||||
|  | ||||
|     fun deleteAll() = Completable.fromAction { db.clearAllTables() } | ||||
|  | ||||
|     fun fetchAll() = deleteOutdated().andThen(fetch()) | ||||
|  | ||||
|     @Query("SELECT * FROM logs ORDER BY time DESC") | ||||
|     protected abstract fun fetch(): Single<MutableList<MagiskLog>> | ||||
|  | ||||
|     @Insert | ||||
|     abstract fun insert(log: MagiskLog): Completable | ||||
|  | ||||
|     @Query("DELETE FROM logs WHERE time < :timeout") | ||||
|     protected abstract fun deleteOutdated( | ||||
|         timeout: Long = twoWeeksAgo | ||||
|     ): Completable | ||||
|  | ||||
| } | ||||
| @@ -1,15 +0,0 @@ | ||||
| package com.topjohnwu.magisk.data.database.base | ||||
|  | ||||
| abstract class BaseDao { | ||||
|  | ||||
|     abstract val table: String | ||||
|  | ||||
|     inline fun <reified Builder : MagiskQueryBuilder> query(builder: Builder.() -> Unit) = | ||||
|         Builder::class.java.newInstance() | ||||
|             .apply { table = this@BaseDao.table } | ||||
|             .apply(builder) | ||||
|             .toString() | ||||
|             .let { MagiskQuery(it) } | ||||
|             .query() | ||||
|  | ||||
| } | ||||
| @@ -1,30 +0,0 @@ | ||||
| package com.topjohnwu.magisk.data.database.base | ||||
|  | ||||
| import androidx.annotation.AnyThread | ||||
| import com.topjohnwu.superuser.Shell | ||||
| import io.reactivex.Single | ||||
|  | ||||
| object DatabaseDefinition { | ||||
|  | ||||
|     object Table { | ||||
|         const val POLICY = "policies" | ||||
|         const val LOG = "logs" | ||||
|         const val SETTINGS = "settings" | ||||
|         const val STRINGS = "strings" | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @AnyThread | ||||
| fun MagiskQuery.query() = query.su() | ||||
|  | ||||
| fun String.suRaw() = Single.fromCallable { Shell.su(this).exec().out } | ||||
| fun String.su() = suRaw().map { it.toMap() } | ||||
|  | ||||
| fun List<String>.toMap() = map { it.split(Regex("\\|")) } | ||||
|     .map { it.toMapInternal() } | ||||
|  | ||||
| private fun List<String>.toMapInternal() = map { it.split("=", limit = 2) } | ||||
|     .filter { it.size == 2 } | ||||
|     .map { Pair(it[0], it[1]) } | ||||
|     .toMap() | ||||
| @@ -1,5 +0,0 @@ | ||||
| package com.topjohnwu.magisk.data.database.base | ||||
|  | ||||
| inline class MagiskQuery(private val _query: String) { | ||||
|     val query get() = "magisk --sqlite '$_query'" | ||||
| } | ||||
| @@ -0,0 +1,44 @@ | ||||
| package com.topjohnwu.magisk.data.database.magiskdb | ||||
|  | ||||
| import androidx.annotation.StringDef | ||||
| import com.topjohnwu.superuser.Shell | ||||
| import io.reactivex.Single | ||||
|  | ||||
| abstract class BaseDao { | ||||
|  | ||||
|     object Table { | ||||
|         const val POLICY = "policies" | ||||
|         const val LOG = "logs" | ||||
|         const val SETTINGS = "settings" | ||||
|         const val STRINGS = "strings" | ||||
|     } | ||||
|  | ||||
|     @StringDef(Table.POLICY, Table.LOG, Table.SETTINGS, Table.STRINGS) | ||||
|     @Retention(AnnotationRetention.SOURCE) | ||||
|     annotation class TableStrict | ||||
|  | ||||
|     @TableStrict | ||||
|     abstract val table: String | ||||
|  | ||||
|     inline fun <reified Builder : Query.Builder> query(builder: Builder.() -> Unit = {}) = | ||||
|         Builder::class.java.newInstance() | ||||
|             .apply { table = this@BaseDao.table } | ||||
|             .apply(builder) | ||||
|             .toString() | ||||
|             .let { Query(it) } | ||||
|             .query() | ||||
|  | ||||
| } | ||||
|  | ||||
| fun Query.query() = query.su() | ||||
|  | ||||
| private fun String.suRaw() = Single.fromCallable { Shell.su(this).exec().out } | ||||
| private fun String.su() = suRaw().map { it.toMap() } | ||||
|  | ||||
| private fun List<String>.toMap() = map { it.split(Regex("\\|")) } | ||||
|     .map { it.toMapInternal() } | ||||
|  | ||||
| private fun List<String>.toMapInternal() = map { it.split("=", limit = 2) } | ||||
|     .filter { it.size == 2 } | ||||
|     .map { Pair(it[0], it[1]) } | ||||
|     .toMap() | ||||
| @@ -1,27 +1,17 @@ | ||||
| package com.topjohnwu.magisk.data.database.base | ||||
| package com.topjohnwu.magisk.data.database.magiskdb | ||||
|  | ||||
| import androidx.annotation.StringDef | ||||
| import com.topjohnwu.magisk.data.database.base.Order.Companion.ASC | ||||
| import com.topjohnwu.magisk.data.database.base.Order.Companion.DESC | ||||
|  | ||||
| interface MagiskQueryBuilder { | ||||
| class Query(private val _query: String) { | ||||
|     val query get() = "magisk --sqlite '$_query'" | ||||
|  | ||||
|     val requestType: String | ||||
|     var table: String | ||||
|  | ||||
|     companion object { | ||||
|         inline operator fun <reified Builder : MagiskQueryBuilder> invoke(builder: Builder.() -> Unit): MagiskQuery = | ||||
|             Builder::class.java.newInstance() | ||||
|                 .apply(builder) | ||||
|                 .toString() | ||||
|                 .let { | ||||
|                     MagiskQuery(it) | ||||
|                 } | ||||
|     interface Builder { | ||||
|         val requestType: String | ||||
|         var table: String | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| class Delete : MagiskQueryBuilder { | ||||
| class Delete : Query.Builder { | ||||
|     override val requestType: String = "DELETE FROM" | ||||
|     override var table = "" | ||||
|  | ||||
| @@ -36,7 +26,7 @@ class Delete : MagiskQueryBuilder { | ||||
|     } | ||||
| } | ||||
|  | ||||
| class Select : MagiskQueryBuilder { | ||||
| class Select : Query.Builder { | ||||
|     override val requestType: String get() = "SELECT $fields FROM" | ||||
|     override lateinit var table: String | ||||
|  | ||||
| @@ -69,7 +59,7 @@ class Replace : Insert() { | ||||
|     override val requestType: String = "REPLACE INTO" | ||||
| } | ||||
|  | ||||
| open class Insert : MagiskQueryBuilder { | ||||
| open class Insert : Query.Builder { | ||||
|     override val requestType: String = "INSERT INTO" | ||||
|     override lateinit var table: String | ||||
|  | ||||
| @@ -137,19 +127,11 @@ class Condition { | ||||
|     } | ||||
| } | ||||
|  | ||||
| class Order { | ||||
|  | ||||
|     @set:OrderStrict | ||||
|     var order = DESC | ||||
|     var field = "" | ||||
|  | ||||
|     companion object { | ||||
|         const val ASC = "ASC" | ||||
|         const val DESC = "DESC" | ||||
|     } | ||||
|  | ||||
| object Order { | ||||
|     const val ASC = "ASC" | ||||
|     const val DESC = "DESC" | ||||
| } | ||||
|  | ||||
| @StringDef(ASC, DESC) | ||||
| @StringDef(Order.ASC, Order.DESC) | ||||
| @Retention(AnnotationRetention.SOURCE) | ||||
| annotation class OrderStrict | ||||
| annotation class OrderStrict | ||||
| @@ -29,8 +29,8 @@ interface DBConfig { | ||||
| } | ||||
|  | ||||
| class DBSettingsValue( | ||||
|         private val name: String, | ||||
|         private val default: Int | ||||
|     private val name: String, | ||||
|     private val default: Int | ||||
| ) : ReadWriteProperty<DBConfig, Int> { | ||||
|  | ||||
|     private var value: Int? = null | ||||
| @@ -47,29 +47,29 @@ class DBSettingsValue( | ||||
|             this.value = value | ||||
|         } | ||||
|         thisRef.settingsDao.put(name, value) | ||||
|                 .subscribeOn(Schedulers.io()) | ||||
|                 .subscribe() | ||||
|             .subscribeOn(Schedulers.io()) | ||||
|             .subscribe() | ||||
|     } | ||||
| } | ||||
|  | ||||
| class DBBoolSettings( | ||||
|         name: String, | ||||
|         default: Boolean | ||||
|     name: String, | ||||
|     default: Boolean | ||||
| ) : ReadWriteProperty<DBConfig, Boolean> { | ||||
|  | ||||
|     val base = DBSettingsValue(name, if (default) 1 else 0) | ||||
|  | ||||
|     override fun getValue(thisRef: DBConfig, property: KProperty<*>): Boolean | ||||
|             = base.getValue(thisRef, property) != 0 | ||||
|     override fun getValue(thisRef: DBConfig, property: KProperty<*>): Boolean = | ||||
|         base.getValue(thisRef, property) != 0 | ||||
|  | ||||
|     override fun setValue(thisRef: DBConfig, property: KProperty<*>, value: Boolean) = | ||||
|             base.setValue(thisRef, property, if (value) 1 else 0) | ||||
|         base.setValue(thisRef, property, if (value) 1 else 0) | ||||
| } | ||||
|  | ||||
| class DBStringsValue( | ||||
|         private val name: String, | ||||
|         private val default: String, | ||||
|         private val sync: Boolean | ||||
|     private val name: String, | ||||
|     private val default: String, | ||||
|     private val sync: Boolean | ||||
| ) : ReadWriteProperty<DBConfig, String> { | ||||
|  | ||||
|     private var value: String? = null | ||||
| @@ -90,16 +90,16 @@ class DBStringsValue( | ||||
|                 thisRef.stringDao.delete(name).blockingAwait() | ||||
|             } else { | ||||
|                 thisRef.stringDao.delete(name) | ||||
|                         .subscribeOn(Schedulers.io()) | ||||
|                         .subscribe() | ||||
|                     .subscribeOn(Schedulers.io()) | ||||
|                     .subscribe() | ||||
|             } | ||||
|         } else { | ||||
|             if (sync) { | ||||
|                 thisRef.stringDao.put(name, value).blockingAwait() | ||||
|             } else { | ||||
|                 thisRef.stringDao.put(name, value) | ||||
|                         .subscribeOn(Schedulers.io()) | ||||
|                         .subscribe() | ||||
|                     .subscribeOn(Schedulers.io()) | ||||
|                     .subscribe() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -1,40 +1,38 @@ | ||||
| package com.topjohnwu.magisk.data.repository | ||||
|  | ||||
| import com.topjohnwu.magisk.Const | ||||
| import com.topjohnwu.magisk.data.database.LogDao | ||||
| import com.topjohnwu.magisk.data.database.base.suRaw | ||||
| import com.topjohnwu.magisk.extensions.toSingle | ||||
| import com.topjohnwu.magisk.data.database.SuLogDao | ||||
| import com.topjohnwu.magisk.model.entity.MagiskLog | ||||
| import com.topjohnwu.magisk.model.entity.WrappedMagiskLog | ||||
| import com.topjohnwu.superuser.Shell | ||||
| import io.reactivex.Completable | ||||
| import io.reactivex.Single | ||||
| import java.util.concurrent.TimeUnit | ||||
|  | ||||
|  | ||||
| class LogRepository( | ||||
|     private val logDao: LogDao | ||||
|     private val logDao: SuLogDao | ||||
| ) { | ||||
|  | ||||
|     fun fetchLogs() = logDao.fetchAll() | ||||
|         .map { it.sortByDescending { it.date.time }; it } | ||||
|         .map { it.wrap() } | ||||
|     fun fetchLogs() = logDao.fetchAll().map { it.wrap() } | ||||
|  | ||||
|     fun fetchMagiskLogs() = "tail -n 5000 ${Const.MAGISK_LOG}".suRaw() | ||||
|         .filter { it.isNotEmpty() } | ||||
|     fun fetchMagiskLogs() = Single.fromCallable { | ||||
|         Shell.su("tail -n 5000 ${Const.MAGISK_LOG}").exec().out | ||||
|     }.flattenAsFlowable { it }.filter { it.isNotEmpty() } | ||||
|  | ||||
|     fun clearLogs() = logDao.deleteAll() | ||||
|     fun clearOutdated() = logDao.deleteOutdated() | ||||
|  | ||||
|     fun clearMagiskLogs() = Shell.su("echo -n > " + Const.MAGISK_LOG) | ||||
|         .toSingle() | ||||
|         .map { it.exec() } | ||||
|     fun clearMagiskLogs() = Completable.fromAction { | ||||
|         Shell.su("echo -n > ${Const.MAGISK_LOG}").exec() | ||||
|     } | ||||
|  | ||||
|     fun put(log: MagiskLog) = logDao.put(log) | ||||
|     fun insert(log: MagiskLog) = logDao.insert(log) | ||||
|  | ||||
|     private fun List<MagiskLog>.wrap(): List<WrappedMagiskLog> { | ||||
|         val day = TimeUnit.DAYS.toMillis(1) | ||||
|         return groupBy { it.date.time / day } | ||||
|         return groupBy { it.time / day } | ||||
|             .map { WrappedMagiskLog(it.key * day, it.value) } | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -3,7 +3,6 @@ package com.topjohnwu.magisk.data.repository | ||||
| import android.content.pm.PackageManager | ||||
| import com.topjohnwu.magisk.Config | ||||
| import com.topjohnwu.magisk.Info | ||||
| import com.topjohnwu.magisk.data.database.base.su | ||||
| import com.topjohnwu.magisk.data.network.GithubRawServices | ||||
| import com.topjohnwu.magisk.extensions.getLabel | ||||
| import com.topjohnwu.magisk.extensions.packageName | ||||
| @@ -57,7 +56,7 @@ class MagiskRepository( | ||||
|         .toList() | ||||
|  | ||||
|     fun toggleHide(isEnabled: Boolean, packageName: String, process: String) = | ||||
|         "magiskhide --%s %s %s".format(isEnabled.state, packageName, process).su().ignoreElement() | ||||
|         Shell.su("magiskhide --${isEnabled.state} $packageName $process").submit() | ||||
|  | ||||
|     private val Boolean.state get() = if (this) "add" else "rm" | ||||
|  | ||||
|   | ||||
| @@ -8,16 +8,20 @@ import org.koin.dsl.module | ||||
|  | ||||
|  | ||||
| val databaseModule = module { | ||||
|     single { LogDao() } | ||||
|     single { PolicyDao(get()) } | ||||
|     single { SettingsDao() } | ||||
|     single { StringDao() } | ||||
|     single { createRepoDatabase(get()) } | ||||
|     single { get<RepoDatabase>().repoDao() } | ||||
|     single { createRepoDatabase(get()).repoDao() } | ||||
|     single { createSuLogDatabase(get(Protected)).suLogDao() } | ||||
|     single { RepoUpdater(get(), get()) } | ||||
| } | ||||
|  | ||||
| fun createRepoDatabase(context: Context) = | ||||
|         Room.databaseBuilder(context, RepoDatabase::class.java, "repo.db") | ||||
|             .fallbackToDestructiveMigration() | ||||
|             .build() | ||||
|     Room.databaseBuilder(context, RepoDatabase::class.java, "repo.db") | ||||
|         .fallbackToDestructiveMigration() | ||||
|         .build() | ||||
|  | ||||
| fun createSuLogDatabase(context: Context) = | ||||
|     Room.databaseBuilder(context, SuLogDatabase::class.java, "sulogs.db") | ||||
|         .fallbackToDestructiveMigration() | ||||
|         .build() | ||||
|   | ||||
| @@ -1,6 +0,0 @@ | ||||
| package com.topjohnwu.magisk.extensions | ||||
|  | ||||
| import android.os.Handler | ||||
| import android.os.Looper | ||||
|  | ||||
| fun ui(body: () -> Unit) = Handler(Looper.getMainLooper()).post(body) | ||||
| @@ -2,6 +2,7 @@ package com.topjohnwu.magisk.extensions | ||||
|  | ||||
| import androidx.databinding.ObservableField | ||||
| import com.topjohnwu.magisk.utils.KObservableField | ||||
| import com.topjohnwu.superuser.internal.UiThreadHandler | ||||
| import io.reactivex.* | ||||
| import io.reactivex.android.schedulers.AndroidSchedulers | ||||
| import io.reactivex.disposables.Disposables | ||||
| @@ -43,35 +44,35 @@ typealias OnErrorListener = (Throwable) -> Unit | ||||
| /*=== ALIASES FOR OBSERVABLES ===*/ | ||||
|  | ||||
| fun <T> Observable<T>.subscribeK( | ||||
|         onError: OnErrorListener = { it.printStackTrace() }, | ||||
|         onComplete: OnCompleteListener = {}, | ||||
|         onNext: OnSuccessListener<T> = {} | ||||
|     onError: OnErrorListener = { it.printStackTrace() }, | ||||
|     onComplete: OnCompleteListener = {}, | ||||
|     onNext: OnSuccessListener<T> = {} | ||||
| ) = applySchedulers() | ||||
|     .subscribe(onNext, onError, onComplete) | ||||
|  | ||||
| fun <T> Single<T>.subscribeK( | ||||
|         onError: OnErrorListener = { it.printStackTrace() }, | ||||
|         onNext: OnSuccessListener<T> = {} | ||||
|     onError: OnErrorListener = { it.printStackTrace() }, | ||||
|     onSuccess: OnSuccessListener<T> = {} | ||||
| ) = applySchedulers() | ||||
|     .subscribe(onNext, onError) | ||||
|     .subscribe(onSuccess, onError) | ||||
|  | ||||
| fun <T> Maybe<T>.subscribeK( | ||||
|         onError: OnErrorListener = { it.printStackTrace() }, | ||||
|         onComplete: OnCompleteListener = {}, | ||||
|         onNext: OnSuccessListener<T> = {} | ||||
|     onError: OnErrorListener = { it.printStackTrace() }, | ||||
|     onComplete: OnCompleteListener = {}, | ||||
|     onSuccess: OnSuccessListener<T> = {} | ||||
| ) = applySchedulers() | ||||
|     .subscribe(onNext, onError, onComplete) | ||||
|     .subscribe(onSuccess, onError, onComplete) | ||||
|  | ||||
| fun <T> Flowable<T>.subscribeK( | ||||
|         onError: OnErrorListener = { it.printStackTrace() }, | ||||
|         onComplete: OnCompleteListener = {}, | ||||
|         onNext: OnSuccessListener<T> = {} | ||||
|     onError: OnErrorListener = { it.printStackTrace() }, | ||||
|     onComplete: OnCompleteListener = {}, | ||||
|     onNext: OnSuccessListener<T> = {} | ||||
| ) = applySchedulers() | ||||
|     .subscribe(onNext, onError, onComplete) | ||||
|  | ||||
| fun Completable.subscribeK( | ||||
|         onError: OnErrorListener = { it.printStackTrace() }, | ||||
|         onComplete: OnCompleteListener = {} | ||||
|     onError: OnErrorListener = { it.printStackTrace() }, | ||||
|     onComplete: OnCompleteListener = {} | ||||
| ) = applySchedulers() | ||||
|     .subscribe(onComplete, onError) | ||||
|  | ||||
| @@ -104,54 +105,54 @@ fun Completable.updateBy( | ||||
|  | ||||
|  | ||||
| fun <T> Observable<T>.doOnSubscribeUi(body: () -> Unit) = | ||||
|     doOnSubscribe { ui { body() } } | ||||
|     doOnSubscribe { UiThreadHandler.run { body() } } | ||||
|  | ||||
| fun <T> Single<T>.doOnSubscribeUi(body: () -> Unit) = | ||||
|     doOnSubscribe { ui { body() } } | ||||
|     doOnSubscribe { UiThreadHandler.run { body() } } | ||||
|  | ||||
| fun <T> Maybe<T>.doOnSubscribeUi(body: () -> Unit) = | ||||
|     doOnSubscribe { ui { body() } } | ||||
|     doOnSubscribe { UiThreadHandler.run { body() } } | ||||
|  | ||||
| fun <T> Flowable<T>.doOnSubscribeUi(body: () -> Unit) = | ||||
|     doOnSubscribe { ui { body() } } | ||||
|     doOnSubscribe { UiThreadHandler.run { body() } } | ||||
|  | ||||
| fun Completable.doOnSubscribeUi(body: () -> Unit) = | ||||
|     doOnSubscribe { ui { body() } } | ||||
|     doOnSubscribe { UiThreadHandler.run { body() } } | ||||
|  | ||||
|  | ||||
| fun <T> Observable<T>.doOnErrorUi(body: (Throwable) -> Unit) = | ||||
|     doOnError { ui { body(it) } } | ||||
|     doOnError { UiThreadHandler.run { body(it) } } | ||||
|  | ||||
| fun <T> Single<T>.doOnErrorUi(body: (Throwable) -> Unit) = | ||||
|     doOnError { ui { body(it) } } | ||||
|     doOnError { UiThreadHandler.run { body(it) } } | ||||
|  | ||||
| fun <T> Maybe<T>.doOnErrorUi(body: (Throwable) -> Unit) = | ||||
|     doOnError { ui { body(it) } } | ||||
|     doOnError { UiThreadHandler.run { body(it) } } | ||||
|  | ||||
| fun <T> Flowable<T>.doOnErrorUi(body: (Throwable) -> Unit) = | ||||
|     doOnError { ui { body(it) } } | ||||
|     doOnError { UiThreadHandler.run { body(it) } } | ||||
|  | ||||
| fun Completable.doOnErrorUi(body: (Throwable) -> Unit) = | ||||
|     doOnError { ui { body(it) } } | ||||
|     doOnError { UiThreadHandler.run { body(it) } } | ||||
|  | ||||
|  | ||||
| fun <T> Observable<T>.doOnNextUi(body: (T) -> Unit) = | ||||
|     doOnNext { ui { body(it) } } | ||||
|     doOnNext { UiThreadHandler.run { body(it) } } | ||||
|  | ||||
| fun <T> Flowable<T>.doOnNextUi(body: (T) -> Unit) = | ||||
|     doOnNext { ui { body(it) } } | ||||
|     doOnNext { UiThreadHandler.run { body(it) } } | ||||
|  | ||||
| fun <T> Single<T>.doOnSuccessUi(body: (T) -> Unit) = | ||||
|     doOnSuccess { ui { body(it) } } | ||||
|     doOnSuccess { UiThreadHandler.run { body(it) } } | ||||
|  | ||||
| fun <T> Maybe<T>.doOnSuccessUi(body: (T) -> Unit) = | ||||
|     doOnSuccess { ui { body(it) } } | ||||
|     doOnSuccess { UiThreadHandler.run { body(it) } } | ||||
|  | ||||
| fun <T> Maybe<T>.doOnCompleteUi(body: () -> Unit) = | ||||
|     doOnComplete { ui { body() } } | ||||
|     doOnComplete { UiThreadHandler.run { body() } } | ||||
|  | ||||
| fun Completable.doOnCompleteUi(body: () -> Unit) = | ||||
|     doOnComplete { ui { body() } } | ||||
|     doOnComplete { UiThreadHandler.run { body() } } | ||||
|  | ||||
|  | ||||
| fun <T, R> Observable<List<T>>.mapList( | ||||
| @@ -198,4 +199,4 @@ fun <T> ObservableField<T>.toObservable(): Observable<T> { | ||||
| fun <T : Any> T.toSingle() = Single.just(this) | ||||
|  | ||||
| fun <T1, T2, R> zip(t1: Single<T1>, t2: Single<T2>, zipper: (T1, T2) -> R) = | ||||
|         Single.zip(t1, t2, BiFunction<T1, T2, R> { rt1, rt2 -> zipper(rt1, rt2) }) | ||||
|     Single.zip(t1, t2, BiFunction<T1, T2, R> { rt1, rt2 -> zipper(rt1, rt2) }) | ||||
|   | ||||
| @@ -26,6 +26,7 @@ import androidx.annotation.ColorRes | ||||
| import androidx.annotation.DrawableRes | ||||
| import androidx.appcompat.content.res.AppCompatResources | ||||
| import androidx.core.content.ContextCompat | ||||
| import androidx.core.net.toFile | ||||
| import androidx.core.net.toUri | ||||
| import com.topjohnwu.magisk.Const | ||||
| import com.topjohnwu.magisk.FileProvider | ||||
| @@ -306,3 +307,5 @@ fun Context.unwrap() : Context { | ||||
|     } | ||||
|     return context | ||||
| } | ||||
|  | ||||
| fun Uri.writeTo(file: File) = toFile().copyTo(file) | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| package com.topjohnwu.magisk.extensions | ||||
|  | ||||
| import android.net.Uri | ||||
| import android.os.Build | ||||
| import androidx.core.net.toFile | ||||
| import java.io.File | ||||
| import java.io.InputStream | ||||
| import java.io.OutputStream | ||||
| import java.lang.reflect.Field | ||||
| import java.lang.reflect.Method | ||||
| import java.util.* | ||||
| import java.util.zip.ZipEntry | ||||
| import java.util.zip.ZipInputStream | ||||
| @@ -19,8 +19,6 @@ fun ZipInputStream.forEach(callback: (ZipEntry) -> Unit) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| fun Uri.writeTo(file: File) = toFile().copyTo(file) | ||||
|  | ||||
| fun InputStream.writeTo(file: File) = | ||||
|         withStreams(this, file.outputStream()) { reader, writer -> reader.copyTo(writer) } | ||||
|  | ||||
| @@ -100,4 +98,33 @@ fun Locale.toLangTag(): String { | ||||
|             tag.append('-').append(variant) | ||||
|         return tag.toString() | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | ||||
| // Reflection hacks | ||||
|  | ||||
| private val loadClass = ClassLoader::class.java.getMethod("loadClass", String::class.java) | ||||
| private val getDeclaredMethod = Class::class.java.getMethod("getDeclaredMethod", | ||||
|     String::class.java, arrayOf<Class<*>>()::class.java) | ||||
| private val getDeclaredField = Class::class.java.getMethod("getDeclaredField", String::class.java) | ||||
|  | ||||
| fun ClassLoader.forceLoadClass(name: String) = | ||||
|     runCatching { loadClass.invoke(this, name) }.getOrNull() as Class<*>? | ||||
|  | ||||
| fun Class<*>.forceGetDeclaredMethod(name: String, vararg types: Class<*>) = | ||||
|     (runCatching { getDeclaredMethod.invoke(this, name, types) }.getOrNull() as Method?)?.also { | ||||
|         it.isAccessible = true | ||||
|     } | ||||
|  | ||||
| fun Class<*>.forceGetDeclaredField(name: String) = | ||||
|     (runCatching { getDeclaredField.invoke(this, name) }.getOrNull() as Field?)?.also { | ||||
|         it.isAccessible = true | ||||
|     } | ||||
|  | ||||
| inline fun <reified T> T.forceGetClass(name: String) = | ||||
|     T::class.java.classLoader?.forceLoadClass(name) | ||||
|  | ||||
| fun Class<*>.forceGetField(name: String): Field? = | ||||
|     forceGetDeclaredField(name) ?: superclass?.forceGetField(name) | ||||
|  | ||||
| fun Class<*>.forceGetMethod(name: String, vararg types: Class<*>): Method? = | ||||
|     forceGetDeclaredMethod(name, *types) ?: superclass?.forceGetMethod(name, *types) | ||||
|   | ||||
| @@ -140,7 +140,7 @@ open class DownloadService : RemoteFileService() { | ||||
|         inline operator fun invoke(context: Context, argBuilder: Builder.() -> Unit) { | ||||
|             val app = context.applicationContext | ||||
|             val builder = Builder().apply(argBuilder) | ||||
|             val intent = app.intent(DownloadService::class.java).putExtra(ARG_URL, builder.subject) | ||||
|             val intent = app.intent<DownloadService>().putExtra(ARG_URL, builder.subject) | ||||
|  | ||||
|             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||||
|                 app.startForegroundService(intent) | ||||
|   | ||||
| @@ -38,7 +38,7 @@ private fun RemoteFileService.upgrade(apk: File, id: Int) { | ||||
|             patch(apk, id) | ||||
|         } else { | ||||
|             // Simply relaunch the app | ||||
|             ProcessPhoenix.triggerRebirth(this) | ||||
|             ProcessPhoenix.triggerRebirth(this, intent<ProcessPhoenix>()) | ||||
|         } | ||||
|     } else { | ||||
|         patch(apk, id) | ||||
|   | ||||
| @@ -1,10 +1,14 @@ | ||||
| package com.topjohnwu.magisk.model.entity | ||||
|  | ||||
| import androidx.room.Entity | ||||
| import androidx.room.Ignore | ||||
| import androidx.room.PrimaryKey | ||||
| import com.topjohnwu.magisk.extensions.now | ||||
| import com.topjohnwu.magisk.extensions.timeFormatTime | ||||
| import com.topjohnwu.magisk.extensions.toTime | ||||
| import com.topjohnwu.magisk.model.entity.MagiskPolicy.Companion.ALLOW | ||||
| import java.util.* | ||||
|  | ||||
| @Entity(tableName = "logs") | ||||
| data class MagiskLog( | ||||
|     val fromUid: Int, | ||||
|     val toUid: Int, | ||||
| @@ -13,9 +17,10 @@ data class MagiskLog( | ||||
|     val appName: String, | ||||
|     val command: String, | ||||
|     val action: Boolean, | ||||
|     val date: Date | ||||
|     val time: Long = -1 | ||||
| ) { | ||||
|     val timeString = date.time.toTime(timeFormatTime) | ||||
|     @PrimaryKey(autoGenerate = true) var id: Int = 0 | ||||
|     @Ignore val timeString = time.toTime(timeFormatTime) | ||||
| } | ||||
|  | ||||
| data class WrappedMagiskLog( | ||||
| @@ -23,35 +28,8 @@ data class WrappedMagiskLog( | ||||
|     val items: List<MagiskLog> | ||||
| ) | ||||
|  | ||||
| fun Map<String, String>.toLog(): MagiskLog { | ||||
|     return MagiskLog( | ||||
|         fromUid = get("from_uid")?.toIntOrNull() ?: -1, | ||||
|         toUid = get("to_uid")?.toIntOrNull() ?: -1, | ||||
|         fromPid = get("from_pid")?.toIntOrNull() ?: -1, | ||||
|         packageName = get("package_name").orEmpty(), | ||||
|         appName = get("app_name").orEmpty(), | ||||
|         command = get("command").orEmpty(), | ||||
|         action = get("action")?.toIntOrNull() != 0, | ||||
|         date = get("time")?.toLongOrNull()?.toDate() ?: Date() | ||||
|     ) | ||||
| } | ||||
|  | ||||
| fun Long.toDate() = Date(this) | ||||
|  | ||||
| fun MagiskLog.toMap() = mapOf( | ||||
|     "from_uid" to fromUid, | ||||
|     "to_uid" to toUid, | ||||
|     "from_pid" to fromPid, | ||||
|     "package_name" to packageName, | ||||
|     "app_name" to appName, | ||||
|     "command" to command, | ||||
|     "action" to action, | ||||
|     "time" to date.time | ||||
| ) | ||||
|  | ||||
| fun MagiskPolicy.toLog( | ||||
|     toUid: Int, | ||||
|     fromPid: Int, | ||||
|     command: String, | ||||
|     date: Date | ||||
| ) = MagiskLog(uid, toUid, fromPid, packageName, appName, command, policy == ALLOW, date) | ||||
|     command: String | ||||
| ) = MagiskLog(uid, toUid, fromPid, packageName, appName, command, policy == ALLOW, now) | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user