Commit 4f0e1c6c authored by Viktor De Pasquale's avatar Viktor De Pasquale

Merge remote-tracking branch 'john/master' into feature/redesign

# Conflicts:
#	app/build.gradle
#	app/src/main/java/com/topjohnwu/magisk/Hacks.kt
#	app/src/main/java/com/topjohnwu/magisk/data/database/RepoDatabase.kt
#	app/src/main/java/com/topjohnwu/magisk/data/repository/LogRepository.kt
#	app/src/main/java/com/topjohnwu/magisk/di/DatabaseModule.kt
#	app/src/main/java/com/topjohnwu/magisk/extensions/RxJava.kt
#	app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt
#	app/src/main/java/com/topjohnwu/magisk/extensions/XJava.kt
#	app/src/main/java/com/topjohnwu/magisk/model/download/RemoteFileService.kt
#	app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/LogRvItem.kt
#	app/src/main/java/com/topjohnwu/magisk/model/events/ViewEvents.kt
#	app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt
#	app/src/main/res/xml/app_settings.xml
parents 550f6aff d3b5cf82
...@@ -35,13 +35,12 @@ android { ...@@ -35,13 +35,12 @@ android {
} }
packagingOptions { packagingOptions {
exclude '/META-INF/*.version' exclude '/META-INF/**'
exclude '/META-INF/*.kotlin_module'
exclude '/META-INF/rxkotlin.properties'
exclude '/androidsupportmultidexversion.txt' exclude '/androidsupportmultidexversion.txt'
exclude '/org/bouncycastle/**' exclude '/org/bouncycastle/**'
exclude '/kotlin/**' exclude '/kotlin/**'
exclude '/kotlinx/**' exclude '/kotlinx/**'
exclude '/okhttp3/**'
} }
kotlinOptions { kotlinOptions {
...@@ -85,7 +84,7 @@ dependencies { ...@@ -85,7 +84,7 @@ dependencies {
implementation "com.github.topjohnwu.libsu:core:${vLibsu}" implementation "com.github.topjohnwu.libsu:core:${vLibsu}"
implementation "com.github.topjohnwu.libsu:io:${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-core:${vKoin}"
implementation "org.koin:koin-android:${vKoin}" implementation "org.koin:koin-android:${vKoin}"
implementation "org.koin:koin-androidx-viewmodel:${vKoin}" implementation "org.koin:koin-androidx-viewmodel:${vKoin}"
...@@ -100,10 +99,10 @@ dependencies { ...@@ -100,10 +99,10 @@ dependencies {
implementation "com.squareup.okhttp3:okhttp:${vOkHttp}" implementation "com.squareup.okhttp3:okhttp:${vOkHttp}"
implementation "com.squareup.okhttp3:logging-interceptor:${vOkHttp}" implementation "com.squareup.okhttp3:logging-interceptor:${vOkHttp}"
def vMoshi = "1.8.0" def vMoshi = '1.9.1'
implementation "com.squareup.moshi:moshi:${vMoshi}" implementation "com.squareup.moshi:moshi:${vMoshi}"
def vKotshi = "2.0.1" def vKotshi = '2.0.2'
implementation "se.ansman.kotshi:api:${vKotshi}" implementation "se.ansman.kotshi:api:${vKotshi}"
kapt "se.ansman.kotshi:compiler:${vKotshi}" kapt "se.ansman.kotshi:compiler:${vKotshi}"
...@@ -112,20 +111,22 @@ dependencies { ...@@ -112,20 +111,22 @@ dependencies {
replacedBy('com.github.topjohnwu:room-runtime') replacedBy('com.github.topjohnwu:room-runtime')
} }
} }
def vRoom = "2.2.1" def vRoom = '2.2.1'
implementation "com.github.topjohnwu:room-runtime:${vRoom}" implementation "com.github.topjohnwu:room-runtime:${vRoom}"
implementation "androidx.room:room-rxjava2:${vRoom}"
kapt "androidx.room:room-compiler:${vRoom}" kapt "androidx.room:room-compiler:${vRoom}"
def vNav = "2.1.0" def vNav = '2.1.0'
implementation "androidx.navigation:navigation-fragment-ktx:$vNav" implementation "androidx.navigation:navigation-fragment-ktx:${vNav}"
implementation "androidx.navigation:navigation-ui-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.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03'
implementation 'androidx.browser:browser:1.0.0' implementation 'androidx.browser:browser:1.0.0'
implementation 'androidx.preference:preference:1.1.0' implementation 'androidx.preference:preference:1.1.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0-rc01' implementation 'androidx.recyclerview:recyclerview:1.1.0-rc01'
implementation 'androidx.fragment:fragment-ktx:1.2.0-rc01' implementation 'androidx.fragment:fragment-ktx:1.2.0-rc02'
implementation 'androidx.work:work-runtime:2.2.0' implementation 'androidx.work:work-runtime:2.2.0'
implementation 'androidx.transition:transition:1.2.0' implementation 'androidx.transition:transition:1.2.0'
implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.multidex:multidex:2.0.1'
......
<?xml version="1.0" encoding="utf-8"?> <?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" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="com.topjohnwu.magisk"> package="com.topjohnwu.magisk">
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.VIBRATE" /> <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.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<application <application
android:name="a.e" android:name="a.e"
android:appComponentFactory="a.a"
android:allowBackup="true" android:allowBackup="true"
android:usesCleartextTraffic="true" tools:ignore="UnusedAttribute,GoogleAppIndexingWarning">
tools:ignore="UnusedAttribute,GoogleAppIndexingWarning"
tools:replace="android:appComponentFactory">
<!-- Splash --> <!-- Splash -->
<activity <activity
android:name="a.c" android:name="a.c"
android:configChanges="orientation|screenSize"
android:theme="@style/SplashTheme"> android:theme="@style/SplashTheme">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
...@@ -48,11 +24,7 @@ ...@@ -48,11 +24,7 @@
</activity> </activity>
<!-- Main --> <!-- Main -->
<activity android:name="a.b" />
<activity
android:name="a.b"
android:configChanges="orientation|screenSize"
android:exported="true" />
<activity <activity
android:name="a.i" android:name="a.i"
...@@ -69,29 +41,28 @@ ...@@ -69,29 +41,28 @@
</activity-alias> </activity-alias>
<!-- Flashing --> <!-- Flashing -->
<activity android:name="a.f" />
<activity
android:name="a.f"
android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="nosensor" />
<!-- Superuser --> <!-- Superuser -->
<activity <activity
android:name="a.m" android:name="a.m"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:directBootAware="true" android:directBootAware="true"
android:excludeFromRecents="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 -->
<receiver <receiver
android:name="a.h" android:name="a.h"
android:directBootAware="true"> android:directBootAware="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.REBOOT" /> <action android:name="android.intent.action.REBOOT" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.LOCALE_CHANGED" /> <action android:name="android.intent.action.LOCALE_CHANGED" />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
...@@ -103,10 +74,7 @@ ...@@ -103,10 +74,7 @@
</receiver> </receiver>
<!-- DownloadService --> <!-- DownloadService -->
<service android:name="a.j" />
<service
android:name="a.j"
android:exported="false" />
<!-- Hardcode GMS version --> <!-- Hardcode GMS version -->
<meta-data <meta-data
...@@ -119,6 +87,16 @@ ...@@ -119,6 +87,16 @@
android:authorities="${applicationId}.workmanager-init" android:authorities="${applicationId}.workmanager-init"
tools:node="remove" /> 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> </application>
</manifest> </manifest>
package a; package a;
import androidx.core.app.AppComponentFactory;
import com.topjohnwu.magisk.utils.PatchAPK; import com.topjohnwu.magisk.utils.PatchAPK;
import com.topjohnwu.signing.BootSigner; import com.topjohnwu.signing.BootSigner;
public class a extends AppComponentFactory { public class a {
@Deprecated @Deprecated
public static boolean patchAPK(String in, String out, String pkg) { public static boolean patchAPK(String in, String out, String pkg) {
......
...@@ -11,11 +11,15 @@ import androidx.work.impl.WorkDatabase ...@@ -11,11 +11,15 @@ import androidx.work.impl.WorkDatabase
import androidx.work.impl.WorkDatabase_Impl import androidx.work.impl.WorkDatabase_Impl
import com.topjohnwu.magisk.data.database.RepoDatabase import com.topjohnwu.magisk.data.database.RepoDatabase
import com.topjohnwu.magisk.data.database.RepoDatabase_Impl 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.ActivityTracker
import com.topjohnwu.magisk.di.koinModules import com.topjohnwu.magisk.di.koinModules
import com.topjohnwu.magisk.extensions.get import com.topjohnwu.magisk.extensions.get
import com.topjohnwu.magisk.extensions.unwrap import com.topjohnwu.magisk.extensions.unwrap
import com.topjohnwu.magisk.utils.RootInit import com.topjohnwu.magisk.utils.RootInit
import com.topjohnwu.magisk.utils.SuHandler
import com.topjohnwu.magisk.utils.updateConfig
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin import org.koin.core.context.startKoin
...@@ -33,10 +37,12 @@ open class App() : Application() { ...@@ -33,10 +37,12 @@ open class App() : Application() {
Shell.Config.verboseLogging(BuildConfig.DEBUG) Shell.Config.verboseLogging(BuildConfig.DEBUG)
Shell.Config.addInitializers(RootInit::class.java) Shell.Config.addInitializers(RootInit::class.java)
Shell.Config.setTimeout(2) Shell.Config.setTimeout(2)
FileProvider.callHandler = SuHandler
Room.setFactory { Room.setFactory {
when (it) { when (it) {
WorkDatabase::class.java -> WorkDatabase_Impl() WorkDatabase::class.java -> WorkDatabase_Impl()
RepoDatabase::class.java -> RepoDatabase_Impl() RepoDatabase::class.java -> RepoDatabase_Impl()
SuLogDatabase::class.java -> SuLogDatabase_Impl()
else -> null else -> null
} }
} }
...@@ -58,15 +64,15 @@ open class App() : Application() { ...@@ -58,15 +64,15 @@ open class App() : Application() {
app = this app = this
impl = base impl = base
} }
ResourceMgr.init(impl) val wrapped = impl.wrap()
super.attachBaseContext(impl.wrap()) super.attachBaseContext(wrapped)
// Normal startup // Normal startup
startKoin { startKoin {
androidContext(baseContext) androidContext(wrapped)
modules(koinModules) modules(koinModules)
} }
ResourceMgr.reload() ResourceMgr.init(impl)
app.registerActivityLifecycleCallbacks(get<ActivityTracker>()) app.registerActivityLifecycleCallbacks(get<ActivityTracker>())
WorkManager.initialize(impl.wrapJob(), androidx.work.Configuration.Builder().build()) WorkManager.initialize(impl.wrapJob(), androidx.work.Configuration.Builder().build())
} }
...@@ -77,7 +83,7 @@ open class App() : Application() { ...@@ -77,7 +83,7 @@ open class App() : Application() {
} }
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
ResourceMgr.reload(newConfig) resources.updateConfig(newConfig)
if (!isRunningAsStub) if (!isRunningAsStub)
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)
} }
......
...@@ -12,9 +12,8 @@ import com.topjohnwu.magisk.data.repository.DBConfig ...@@ -12,9 +12,8 @@ import com.topjohnwu.magisk.data.repository.DBConfig
import com.topjohnwu.magisk.di.Protected import com.topjohnwu.magisk.di.Protected
import com.topjohnwu.magisk.extensions.get import com.topjohnwu.magisk.extensions.get
import com.topjohnwu.magisk.extensions.inject import com.topjohnwu.magisk.extensions.inject
import com.topjohnwu.magisk.extensions.packageName
import com.topjohnwu.magisk.model.preference.PreferenceModel 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.magisk.utils.Utils
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.io.SuFile import com.topjohnwu.superuser.io.SuFile
...@@ -33,7 +32,7 @@ object Config : PreferenceModel, DBConfig { ...@@ -33,7 +32,7 @@ object Config : PreferenceModel, DBConfig {
const val ROOT_ACCESS = "root_access" const val ROOT_ACCESS = "root_access"
const val SU_MULTIUSER_MODE = "multiuser_mode" const val SU_MULTIUSER_MODE = "multiuser_mode"
const val SU_MNT_NS = "mnt_ns" 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 SU_MANAGER = "requester"
const val KEYSTORE = "keystore" const val KEYSTORE = "keystore"
...@@ -139,7 +138,7 @@ object Config : PreferenceModel, DBConfig { ...@@ -139,7 +138,7 @@ object Config : PreferenceModel, DBConfig {
var rootMode by dbSettings(Key.ROOT_ACCESS, Value.ROOT_ACCESS_APPS_AND_ADB) 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 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 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 suManager by dbStrings(Key.SU_MANAGER, "", true)
var keyStoreRaw by dbStrings(Key.KEYSTORE, "", true) var keyStoreRaw by dbStrings(Key.KEYSTORE, "", true)
...@@ -147,9 +146,18 @@ object Config : PreferenceModel, DBConfig { ...@@ -147,9 +146,18 @@ object Config : PreferenceModel, DBConfig {
val downloadDirectory get() = val downloadDirectory get() =
Utils.ensureDownloadPath(downloadPath) ?: get<Context>().getExternalFilesDir(null)!! 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) parsePrefs(this)
// Legacy stuff
remove(SU_FINGERPRINT)
// Get actual state // Get actual state
putBoolean(Key.COREONLY, Const.MAGISK_DISABLE_FILE.exists()) putBoolean(Key.COREONLY, Const.MAGISK_DISABLE_FILE.exists())
...@@ -157,7 +165,7 @@ object Config : PreferenceModel, DBConfig { ...@@ -157,7 +165,7 @@ object Config : PreferenceModel, DBConfig {
putString(Key.ROOT_ACCESS, rootMode.toString()) putString(Key.ROOT_ACCESS, rootMode.toString())
putString(Key.SU_MNT_NS, suMntNamespaceMode.toString()) putString(Key.SU_MNT_NS, suMntNamespaceMode.toString())
putString(Key.SU_MULTIUSER_MODE, suMultiuserMode.toString()) putString(Key.SU_MULTIUSER_MODE, suMultiuserMode.toString())
putBoolean(Key.SU_FINGERPRINT, FingerprintHelper.useFingerprint()) putBoolean(Key.SU_BIOMETRIC, BiometricHelper.isEnabled)
}.also { }.also {
if (!prefs.contains(Key.UPDATE_CHANNEL)) if (!prefs.contains(Key.UPDATE_CHANNEL))
prefs.edit().putString(Key.UPDATE_CHANNEL, defaultChannel.toString()).apply() prefs.edit().putString(Key.UPDATE_CHANNEL, defaultChannel.toString()).apply()
...@@ -166,7 +174,7 @@ object Config : PreferenceModel, DBConfig { ...@@ -166,7 +174,7 @@ object Config : PreferenceModel, DBConfig {
private fun parsePrefs(editor: SharedPreferences.Editor) = editor.apply { private fun parsePrefs(editor: SharedPreferences.Editor) = editor.apply {
val config = SuFile.open("/data/adb", Const.MANAGER_CONFIGS) val config = SuFile.open("/data/adb", Const.MANAGER_CONFIGS)
if (config.exists()) runCatching { if (config.exists()) runCatching {
val input = SuFileInputStream(config).buffered() val input = SuFileInputStream(config)
val parser = Xml.newPullParser() val parser = Xml.newPullParser()
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false) parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
parser.setInput(input, "UTF-8") parser.setInput(input, "UTF-8")
...@@ -217,9 +225,10 @@ object Config : PreferenceModel, DBConfig { ...@@ -217,9 +225,10 @@ object Config : PreferenceModel, DBConfig {
fun export() { fun export() {
// Flush prefs to disk // Flush prefs to disk
prefs.edit().commit() prefs.edit().commit()
val context = get<Context>(Protected)
val xml = File( val xml = File(
"${get<Context>(Protected).filesDir.parent}/shared_prefs", "${context.filesDir.parent}/shared_prefs",
"${packageName}_preferences.xml" "${context.packageName}_preferences.xml"
) )
Shell.su("cat $xml > /data/adb/${Const.MANAGER_CONFIGS}").exec() Shell.su("cat $xml > /data/adb/${Const.MANAGER_CONFIGS}").exec()
} }
......
...@@ -23,8 +23,10 @@ object Const { ...@@ -23,8 +23,10 @@ object Const {
val USER_ID = Process.myUid() / 100000 val USER_ID = Process.myUid() / 100000
object Version { object Version {
const val MIN_SUPPORT = 18000 const val MIN_VERSION = "v18.0"
const val CONNECT_MODE = 20002 const val MIN_VERCODE = 18000
const val CONNECT_MODE = 20100
const val PROVIDER_CONNECT = 20102
} }
object ID { object ID {
......
...@@ -14,8 +14,7 @@ import android.content.res.AssetManager ...@@ -14,8 +14,7 @@ import android.content.res.AssetManager
import android.content.res.Configuration import android.content.res.Configuration
import android.content.res.Resources import android.content.res.Resources
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.annotation.StringRes import com.topjohnwu.magisk.extensions.forceGetDeclaredField
import com.topjohnwu.magisk.extensions.langTagToLocale
import com.topjohnwu.magisk.model.download.DownloadService import com.topjohnwu.magisk.model.download.DownloadService
import com.topjohnwu.magisk.model.receiver.GeneralReceiver import com.topjohnwu.magisk.model.receiver.GeneralReceiver
import com.topjohnwu.magisk.model.update.UpdateCheckService import com.topjohnwu.magisk.model.update.UpdateCheckService
...@@ -23,6 +22,8 @@ import com.topjohnwu.magisk.ui.MainActivity ...@@ -23,6 +22,8 @@ import com.topjohnwu.magisk.ui.MainActivity
import com.topjohnwu.magisk.ui.SplashActivity import com.topjohnwu.magisk.ui.SplashActivity
import com.topjohnwu.magisk.ui.flash.FlashActivity import com.topjohnwu.magisk.ui.flash.FlashActivity
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
import com.topjohnwu.magisk.utils.refreshLocale
import com.topjohnwu.magisk.utils.updateConfig
import com.topjohnwu.magisk.utils.currentLocale import com.topjohnwu.magisk.utils.currentLocale
import com.topjohnwu.magisk.utils.defaultLocale import com.topjohnwu.magisk.utils.defaultLocale
import java.util.* import java.util.*
...@@ -52,49 +53,22 @@ fun Context.wrapJob(): Context = object : GlobalResContext(this) { ...@@ -52,49 +53,22 @@ fun Context.wrapJob(): Context = object : GlobalResContext(this) {
} }
} }
// Override locale and inject resources from dynamic APK fun Class<*>.cmp(pkg: String): ComponentName {
private fun Resources.patch(config: Configuration = Configuration(configuration)): Resources {
config.setLocale(currentLocale)
updateConfiguration(config, displayMetrics)
if (isRunningAsStub)
assets.addAssetPath(ResourceMgr.resApk)
return this
}
fun Class<*>.cmp(pkg: String = BuildConfig.APPLICATION_ID): ComponentName {
val name = ClassMap[this].name 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 { inline fun <reified T> Context.intent() = Intent().setComponent(T::class.java.cmp(packageName))
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)
}
fun resolveRes(idx: Int): Int {
return Info.stub?.resourceMap?.get(idx) ?: when (idx) {
DynAPK.NOTIFICATION -> R.drawable.ic_magisk_outline
DynAPK.DOWNLOAD -> R.drawable.sc_cloud_download
DynAPK.SUPERUSER -> R.drawable.sc_superuser
DynAPK.MODULES -> R.drawable.sc_extension
DynAPK.MAGISKHIDE -> R.drawable.sc_magiskhide
else -> -1
}
}
private open class GlobalResContext(base: Context) : ContextWrapper(base) { private open class GlobalResContext(base: Context) : ContextWrapper(base) {
open val mRes: Resources get() = ResourceMgr.resource open val mRes: Resources get() = ResourceMgr.resource
private val loader by lazy { javaClass.classLoader!! }
override fun getResources(): Resources { override fun getResources(): Resources {
return mRes return mRes
} }
override fun getClassLoader(): ClassLoader { override fun getClassLoader(): ClassLoader {
return loader return javaClass.classLoader!!
} }
override fun createConfigurationContext(config: Configuration): Context { override fun createConfigurationContext(config: Configuration): Context {
...@@ -104,38 +78,31 @@ private open class GlobalResContext(base: Context) : ContextWrapper(base) { ...@@ -104,38 +78,31 @@ private open class GlobalResContext(base: Context) : ContextWrapper(base) {
private class ResContext(base: Context) : GlobalResContext(base) { private class ResContext(base: Context) : GlobalResContext(base) {
override val mRes by lazy { base.resources.patch() } override val mRes by lazy { base.resources.patch() }
private fun Resources.patch(): Resources {
updateConfig()
if (isRunningAsStub)
assets.addAssetPath(ResourceMgr.resApk)
return this
}
} }
object ResourceMgr { object ResourceMgr {
internal lateinit var resource: Resources lateinit var resource: Resources
internal lateinit var resApk: String lateinit var resApk: String
fun init(context: Context) { fun init(context: Context) {
resource = context.resources resource = context.resources
if (isRunningAsStub) refreshLocale()
if (isRunningAsStub) {
resApk = DynAPK.current(context).path resApk = DynAPK.current(context).path
resource.assets.addAssetPath(resApk)
} }
fun reload(config: Configuration = Configuration(resource.configuration)) {
val localeConfig = Config.locale
currentLocale = when {
localeConfig.isEmpty() -> defaultLocale
else -> localeConfig.langTagToLocale()
}
Locale.setDefault(currentLocale)
resource.patch(config)
} }
fun getString(locale: Locale, @StringRes id: Int): String {
val config = Configuration()
config.setLocale(locale)
return Resources(resource.assets, resource.displayMetrics, config).getString(id)
}
} }
@RequiresApi(api = 28) @RequiresApi(28)
private class JobSchedulerWrapper(private val base: JobScheduler) : JobScheduler() { private class JobSchedulerWrapper(private val base: JobScheduler) : JobScheduler() {
override fun schedule(job: JobInfo): Int { override fun schedule(job: JobInfo): Int {
...@@ -163,49 +130,15 @@ private class JobSchedulerWrapper(private val base: JobScheduler) : JobScheduler ...@@ -163,49 +130,15 @@ private class JobSchedulerWrapper(private val base: JobScheduler) : JobScheduler
} }
private fun JobInfo.patch(): JobInfo { 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 name = service.className
val component = ComponentName( val component = ComponentName(
service.packageName, service.packageName,
Info.stub!!.componentMap[name] ?: name Info.stub!!.classToComponent[name] ?: name
) )
// Clone the JobInfo except component javaClass.forceGetDeclaredField("service")?.set(this, component)
val builder = JobInfo.Builder(id, component) return this
.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()
} }
} }
...@@ -220,8 +153,9 @@ object ClassMap { ...@@ -220,8 +153,9 @@ object ClassMap {
GeneralReceiver::class.java to a.h::class.java, GeneralReceiver::class.java to a.h::class.java,
DownloadService::class.java to a.j::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,
RedesignActivity::class.java to a.i::class.java RedesignActivity::class.java to a.i::class.java
) )
operator fun get(c: Class<*>) = map.getOrElse(c) { throw IllegalArgumentException() } operator fun get(c: Class<*>) = map.getOrElse(c) { c }
} }
...@@ -40,24 +40,21 @@ object Info { ...@@ -40,24 +40,21 @@ object Info {
val str = ShellUtils.fastCmd("magisk -v").split(":".toRegex())[0] val str = ShellUtils.fastCmd("magisk -v").split(":".toRegex())[0]
val code = ShellUtils.fastCmd("magisk -V").toInt() val code = ShellUtils.fastCmd("magisk -V").toInt()
val hide = Shell.su("magiskhide --status").exec().isSuccess val hide = Shell.su("magiskhide --status").exec().isSuccess
var mode = -1 Env(str, code, hide)
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)
}.getOrElse { Env() } }.getOrElse { Env() }
class Env( class Env(
val magiskVersionCode: Int = -1,
val magiskVersionString: String = "", val magiskVersionString: String = "",
hide: Boolean = false, code: Int = -1,
var connectionMode: Int = -1 hide: Boolean = false
) { ) {
val magiskHide get() = Config.magiskHide 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 { init {
Config.magiskHide = hide Config.magiskHide = hide
......
package com.topjohnwu.magisk.base.viewmodel 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.extensions.doOnSubscribeUi
import com.topjohnwu.magisk.model.events.BackPressEvent import com.topjohnwu.magisk.model.events.BackPressEvent
import com.topjohnwu.magisk.model.events.PermissionEvent import com.topjohnwu.magisk.model.events.PermissionEvent
...@@ -17,7 +17,7 @@ abstract class BaseViewModel( ...@@ -17,7 +17,7 @@ abstract class BaseViewModel(
val isConnected = Observer(gIsConnected) { gIsConnected.value } val isConnected = Observer(gIsConnected) { gIsConnected.value }
fun withView(action: Activity.() -> Unit) { fun withView(action: BaseActivity<*, *>.() -> Unit) {
ViewActionEvent(action).publish() ViewActionEvent(action).publish()
} }
......
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()
}
\ No newline at end of file
...@@ -3,7 +3,10 @@ package com.topjohnwu.magisk.data.database ...@@ -3,7 +3,10 @@ package com.topjohnwu.magisk.data.database
import android.content.Context import android.content.Context
import android.content.pm.PackageManager import android.content.pm.PackageManager
import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.data.database.base.* import com.topjohnwu.magisk.data.database.magiskdb.Delete
import com.topjohnwu.magisk.data.database.magiskdb.BaseDao
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.extensions.now
import com.topjohnwu.magisk.model.entity.MagiskPolicy import com.topjohnwu.magisk.model.entity.MagiskPolicy
import com.topjohnwu.magisk.model.entity.toMap import com.topjohnwu.magisk.model.entity.toMap
...@@ -16,7 +19,7 @@ class PolicyDao( ...@@ -16,7 +19,7 @@ class PolicyDao(
private val context: Context private val context: Context
) : BaseDao() { ) : BaseDao() {
override val table: String = DatabaseDefinition.Table.POLICY override val table: String = Table.POLICY
fun deleteOutdated( fun deleteOutdated(
nowSeconds: Long = TimeUnit.MILLISECONDS.toSeconds(now) nowSeconds: Long = TimeUnit.MILLISECONDS.toSeconds(now)
......
...@@ -4,8 +4,14 @@ import androidx.room.* ...@@ -4,8 +4,14 @@ import androidx.room.*
import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.model.entity.module.Repo 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 @Dao
abstract class RepoDao { abstract class RepoDao(private val db: RepoDatabase) {
val repoIDList get() = getRepoID().map { it.id } val repoIDList get() = getRepoID().map { it.id }
...@@ -15,13 +21,10 @@ abstract class RepoDao { ...@@ -15,13 +21,10 @@ abstract class RepoDao {
} }
var etagKey: String var etagKey: String
set(etag) = addEtagRaw(RepoEtag(0, etag)) set(value) = addEtagRaw(RepoEtag(0, value))
get() = etagRaw()?.key.orEmpty() get() = etagRaw()?.key.orEmpty()
fun clear() { fun clear() = db.clearAllTables()
clearRepos()
clearEtag()
}
@Query("SELECT * FROM repos ORDER BY last_update DESC") @Query("SELECT * FROM repos ORDER BY last_update DESC")
protected abstract fun getReposDateOrder(): List<Repo> protected abstract fun getReposDateOrder(): List<Repo>
...@@ -52,12 +55,6 @@ abstract class RepoDao { ...@@ -52,12 +55,6 @@ abstract class RepoDao {
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
protected abstract fun addEtagRaw(etag: RepoEtag) 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( data class RepoID(
......
package com.topjohnwu.magisk.data.database package com.topjohnwu.magisk.data.database
import com.topjohnwu.magisk.data.database.base.* import com.topjohnwu.magisk.data.database.magiskdb.Delete
import com.topjohnwu.magisk.data.database.magiskdb.BaseDao
import com.topjohnwu.magisk.data.database.magiskdb.Replace
import com.topjohnwu.magisk.data.database.magiskdb.Select
class SettingsDao : BaseDao() { class SettingsDao : BaseDao() {
override val table = DatabaseDefinition.Table.SETTINGS override val table = Table.SETTINGS
fun delete(key: String) = query<Delete> { fun delete(key: String) = query<Delete> {
condition { equals("key", key) } condition { equals("key", key) }
......
package com.topjohnwu.magisk.data.database package com.topjohnwu.magisk.data.database
import com.topjohnwu.magisk.data.database.base.* import com.topjohnwu.magisk.data.database.magiskdb.Delete
import com.topjohnwu.magisk.data.database.magiskdb.BaseDao
import com.topjohnwu.magisk.data.database.magiskdb.Replace
import com.topjohnwu.magisk.data.database.magiskdb.Select
class StringDao : BaseDao() { class StringDao : BaseDao() {
override val table = DatabaseDefinition.Table.STRINGS override val table = Table.STRINGS
fun delete(key: String) = query<Delete> { fun delete(key: String) = query<Delete> {
condition { equals("key", key) } condition { equals("key", key) }
......
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
}
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()
}
\ No newline at end of file
package com.topjohnwu.magisk.data.database.base
inline class MagiskQuery(private val _query: String) {
val query get() = "magisk --sqlite '$_query'"
}
\ No newline at end of file
package com.topjohnwu.magisk.data.database.base package com.topjohnwu.magisk.data.database.magiskdb
import androidx.annotation.AnyThread import androidx.annotation.StringDef
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import io.reactivex.Single import io.reactivex.Single
object DatabaseDefinition { abstract class BaseDao {
object Table { object Table {
const val POLICY = "policies" const val POLICY = "policies"
...@@ -13,15 +13,29 @@ object DatabaseDefinition { ...@@ -13,15 +13,29 @@ object DatabaseDefinition {
const val STRINGS = "strings" 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()
} }
@AnyThread fun Query.query() = query.su()
fun MagiskQuery.query() = query.su()
fun String.suRaw() = Single.fromCallable { Shell.su(this).exec().out } private fun String.suRaw() = Single.fromCallable { Shell.su(this).exec().out }
fun String.su() = suRaw().map { it.toMap() } private fun String.su() = suRaw().map { it.toMap() }
fun List<String>.toMap() = map { it.split(Regex("\\|")) } private fun List<String>.toMap() = map { it.split(Regex("\\|")) }
.map { it.toMapInternal() } .map { it.toMapInternal() }
private fun List<String>.toMapInternal() = map { it.split("=", limit = 2) } private fun List<String>.toMapInternal() = map { it.split("=", limit = 2) }
......
package com.topjohnwu.magisk.data.database.base package com.topjohnwu.magisk.data.database.magiskdb
import androidx.annotation.StringDef 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'"
interface Builder {
val requestType: String val requestType: String
var table: 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)
}
} }
} }
class Delete : MagiskQueryBuilder { class Delete : Query.Builder {
override val requestType: String = "DELETE FROM" override val requestType: String = "DELETE FROM"
override var table = "" override var table = ""
...@@ -36,7 +26,7 @@ class Delete : MagiskQueryBuilder { ...@@ -36,7 +26,7 @@ class Delete : MagiskQueryBuilder {
} }
} }
class Select : MagiskQueryBuilder { class Select : Query.Builder {
override val requestType: String get() = "SELECT $fields FROM" override val requestType: String get() = "SELECT $fields FROM"
override lateinit var table: String override lateinit var table: String
...@@ -69,7 +59,7 @@ class Replace : Insert() { ...@@ -69,7 +59,7 @@ class Replace : Insert() {
override val requestType: String = "REPLACE INTO" override val requestType: String = "REPLACE INTO"
} }
open class Insert : MagiskQueryBuilder { open class Insert : Query.Builder {
override val requestType: String = "INSERT INTO" override val requestType: String = "INSERT INTO"
override lateinit var table: String override lateinit var table: String
...@@ -137,19 +127,11 @@ class Condition { ...@@ -137,19 +127,11 @@ class Condition {
} }
} }
class Order { object Order {
@set:OrderStrict
var order = DESC
var field = ""
companion object {
const val ASC = "ASC" const val ASC = "ASC"
const val DESC = "DESC" const val DESC = "DESC"
}
} }
@StringDef(ASC, DESC) @StringDef(Order.ASC, Order.DESC)
@Retention(AnnotationRetention.SOURCE) @Retention(AnnotationRetention.SOURCE)
annotation class OrderStrict annotation class OrderStrict
...@@ -59,8 +59,8 @@ class DBBoolSettings( ...@@ -59,8 +59,8 @@ class DBBoolSettings(
val base = DBSettingsValue(name, if (default) 1 else 0) val base = DBSettingsValue(name, if (default) 1 else 0)
override fun getValue(thisRef: DBConfig, property: KProperty<*>): Boolean override fun getValue(thisRef: DBConfig, property: KProperty<*>): Boolean =
= base.getValue(thisRef, property) != 0 base.getValue(thisRef, property) != 0
override fun setValue(thisRef: DBConfig, property: KProperty<*>, value: Boolean) = 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)
......
package com.topjohnwu.magisk.data.repository package com.topjohnwu.magisk.data.repository
import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.data.database.LogDao import com.topjohnwu.magisk.data.database.SuLogDao
import com.topjohnwu.magisk.data.database.base.suRaw
import com.topjohnwu.magisk.extensions.toSingle
import com.topjohnwu.magisk.model.entity.MagiskLog import com.topjohnwu.magisk.model.entity.MagiskLog
import com.topjohnwu.magisk.model.entity.WrappedMagiskLog import com.topjohnwu.magisk.model.entity.WrappedMagiskLog
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import io.reactivex.Completable
import io.reactivex.Single
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class LogRepository( class LogRepository(
private val logDao: LogDao private val logDao: SuLogDao
) { ) {
fun fetchLogsNowrap() = logDao.fetchAll() fun fetchLogsNowrap() = logDao.fetchAll()
.map { it.sortedByDescending { it.date.time } }
fun fetchLogs() = fetchLogsNowrap() fun fetchLogs() = fetchLogsNowrap().map { it.wrap() }
.map { it.wrap() }
fun fetchMagiskLogs() = "tail -n 5000 ${Const.MAGISK_LOG}".suRaw() fun fetchMagiskLogs() = Single.fromCallable {
.filter { it.isNotEmpty() } Shell.su("tail -n 5000 ${Const.MAGISK_LOG}").exec().out
}.flattenAsFlowable { it }.filter { it.isNotEmpty() }
fun clearLogs() = logDao.deleteAll() fun clearLogs() = logDao.deleteAll()
fun clearOutdated() = logDao.deleteOutdated()
fun clearMagiskLogs() = Shell.su("echo -n > " + Const.MAGISK_LOG) fun clearMagiskLogs() = Completable.fromAction {
.toSingle() Shell.su("echo -n > ${Const.MAGISK_LOG}").exec()
.map { it.exec() } }
fun put(log: MagiskLog) = logDao.put(log) fun insert(log: MagiskLog) = logDao.insert(log)
private fun List<MagiskLog>.wrap(): List<WrappedMagiskLog> { private fun List<MagiskLog>.wrap(): List<WrappedMagiskLog> {
val day = TimeUnit.DAYS.toMillis(1) val day = TimeUnit.DAYS.toMillis(1)
return groupBy { it.date.time / day } return groupBy { it.time / day }
.map { WrappedMagiskLog(it.key * day, it.value) } .map { WrappedMagiskLog(it.key * day, it.value) }
} }
......
...@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.data.repository ...@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.data.repository
import android.content.pm.PackageManager import android.content.pm.PackageManager
import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.Info
import com.topjohnwu.magisk.data.database.base.su
import com.topjohnwu.magisk.data.network.GithubRawServices import com.topjohnwu.magisk.data.network.GithubRawServices
import com.topjohnwu.magisk.extensions.getLabel import com.topjohnwu.magisk.extensions.getLabel
import com.topjohnwu.magisk.extensions.packageName import com.topjohnwu.magisk.extensions.packageName
...@@ -57,7 +56,7 @@ class MagiskRepository( ...@@ -57,7 +56,7 @@ class MagiskRepository(
.toList() .toList()
fun toggleHide(isEnabled: Boolean, packageName: String, process: String) = 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" private val Boolean.state get() = if (this) "add" else "rm"
......
...@@ -8,7 +8,6 @@ import org.koin.dsl.module ...@@ -8,7 +8,6 @@ import org.koin.dsl.module
val databaseModule = module { val databaseModule = module {
single { LogDao() }
single { PolicyDao(get()) } single { PolicyDao(get()) }
single { SettingsDao() } single { SettingsDao() }
single { StringDao() } single { StringDao() }
...@@ -16,6 +15,7 @@ val databaseModule = module { ...@@ -16,6 +15,7 @@ val databaseModule = module {
single { get<RepoDatabase>().repoDao() } single { get<RepoDatabase>().repoDao() }
single { get<RepoDatabase>().repoByNameDao() } single { get<RepoDatabase>().repoByNameDao() }
single { get<RepoDatabase>().repoByUpdatedDao() } single { get<RepoDatabase>().repoByUpdatedDao() }
single { createSuLogDatabase(get(Protected)).suLogDao() }
single { RepoUpdater(get(), get()) } single { RepoUpdater(get(), get()) }
} }
...@@ -23,3 +23,8 @@ fun createRepoDatabase(context: Context) = ...@@ -23,3 +23,8 @@ fun createRepoDatabase(context: Context) =
Room.databaseBuilder(context, RepoDatabase::class.java, "repo.db") Room.databaseBuilder(context, RepoDatabase::class.java, "repo.db")
.fallbackToDestructiveMigration() .fallbackToDestructiveMigration()
.build() .build()
fun createSuLogDatabase(context: Context) =
Room.databaseBuilder(context, SuLogDatabase::class.java, "sulogs.db")
.fallbackToDestructiveMigration()
.build()
package com.topjohnwu.magisk.extensions
import android.os.Handler
import android.os.Looper
fun ui(body: () -> Unit) = Handler(Looper.getMainLooper()).post(body)
\ No newline at end of file
...@@ -2,6 +2,7 @@ package com.topjohnwu.magisk.extensions ...@@ -2,6 +2,7 @@ package com.topjohnwu.magisk.extensions
import androidx.databinding.ObservableField import androidx.databinding.ObservableField
import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.magisk.utils.KObservableField
import com.topjohnwu.superuser.internal.UiThreadHandler
import io.reactivex.* import io.reactivex.*
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposables import io.reactivex.disposables.Disposables
...@@ -53,14 +54,14 @@ fun <T> Single<T>.subscribeK( ...@@ -53,14 +54,14 @@ fun <T> Single<T>.subscribeK(
onError: OnErrorListener = { it.printStackTrace() }, onError: OnErrorListener = { it.printStackTrace() },
onNext: OnSuccessListener<T> = {} onNext: OnSuccessListener<T> = {}
) = applySchedulers() ) = applySchedulers()
.subscribe(onNext, onError) .subscribe(onSuccess, onError)
fun <T> Maybe<T>.subscribeK( fun <T> Maybe<T>.subscribeK(
onError: OnErrorListener = { it.printStackTrace() }, onError: OnErrorListener = { it.printStackTrace() },
onComplete: OnCompleteListener = {}, onComplete: OnCompleteListener = {},
onNext: OnSuccessListener<T> = {} onSuccess: OnSuccessListener<T> = {}
) = applySchedulers() ) = applySchedulers()
.subscribe(onNext, onError, onComplete) .subscribe(onSuccess, onError, onComplete)
fun <T> Flowable<T>.subscribeK( fun <T> Flowable<T>.subscribeK(
onError: OnErrorListener = { it.printStackTrace() }, onError: OnErrorListener = { it.printStackTrace() },
...@@ -104,54 +105,54 @@ fun Completable.updateBy( ...@@ -104,54 +105,54 @@ fun Completable.updateBy(
fun <T> Observable<T>.doOnSubscribeUi(body: () -> Unit) = fun <T> Observable<T>.doOnSubscribeUi(body: () -> Unit) =
doOnSubscribe { ui { body() } } doOnSubscribe { UiThreadHandler.run { body() } }
fun <T> Single<T>.doOnSubscribeUi(body: () -> Unit) = fun <T> Single<T>.doOnSubscribeUi(body: () -> Unit) =
doOnSubscribe { ui { body() } } doOnSubscribe { UiThreadHandler.run { body() } }
fun <T> Maybe<T>.doOnSubscribeUi(body: () -> Unit) = fun <T> Maybe<T>.doOnSubscribeUi(body: () -> Unit) =
doOnSubscribe { ui { body() } } doOnSubscribe { UiThreadHandler.run { body() } }
fun <T> Flowable<T>.doOnSubscribeUi(body: () -> Unit) = fun <T> Flowable<T>.doOnSubscribeUi(body: () -> Unit) =
doOnSubscribe { ui { body() } } doOnSubscribe { UiThreadHandler.run { body() } }
fun Completable.doOnSubscribeUi(body: () -> Unit) = fun Completable.doOnSubscribeUi(body: () -> Unit) =
doOnSubscribe { ui { body() } } doOnSubscribe { UiThreadHandler.run { body() } }
fun <T> Observable<T>.doOnErrorUi(body: (Throwable) -> Unit) = 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) = 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) = 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) = fun <T> Flowable<T>.doOnErrorUi(body: (Throwable) -> Unit) =
doOnError { ui { body(it) } } doOnError { UiThreadHandler.run { body(it) } }
fun Completable.doOnErrorUi(body: (Throwable) -> Unit) = fun Completable.doOnErrorUi(body: (Throwable) -> Unit) =
doOnError { ui { body(it) } } doOnError { UiThreadHandler.run { body(it) } }
fun <T> Observable<T>.doOnNextUi(body: (T) -> Unit) = 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) = 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) = 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) = fun <T> Maybe<T>.doOnSuccessUi(body: (T) -> Unit) =
doOnSuccess { ui { body(it) } } doOnSuccess { UiThreadHandler.run { body(it) } }
fun <T> Maybe<T>.doOnCompleteUi(body: () -> Unit) = fun <T> Maybe<T>.doOnCompleteUi(body: () -> Unit) =
doOnComplete { ui { body() } } doOnComplete { UiThreadHandler.run { body() } }
fun Completable.doOnCompleteUi(body: () -> Unit) = fun Completable.doOnCompleteUi(body: () -> Unit) =
doOnComplete { ui { body() } } doOnComplete { UiThreadHandler.run { body() } }
fun <T, R> Observable<List<T>>.mapList( fun <T, R> Observable<List<T>>.mapList(
......
...@@ -12,13 +12,21 @@ import android.content.pm.PackageManager.* ...@@ -12,13 +12,21 @@ import android.content.pm.PackageManager.*
import android.content.res.Configuration import android.content.res.Configuration
import android.content.res.Resources import android.content.res.Resources
import android.database.Cursor import android.database.Cursor
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.AdaptiveIconDrawable
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.LayerDrawable
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Build.VERSION.SDK_INT
import android.provider.OpenableColumns import android.provider.OpenableColumns
import android.view.View import android.view.View
import androidx.annotation.ColorRes import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.net.toFile
import androidx.core.net.toUri import androidx.core.net.toUri
import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.FileProvider import com.topjohnwu.magisk.FileProvider
...@@ -100,6 +108,23 @@ fun Context.rawResource(id: Int) = resources.openRawResource(id) ...@@ -100,6 +108,23 @@ fun Context.rawResource(id: Int) = resources.openRawResource(id)
fun Context.readUri(uri: Uri) = fun Context.readUri(uri: Uri) =
contentResolver.openInputStream(uri) ?: throw FileNotFoundException() contentResolver.openInputStream(uri) ?: throw FileNotFoundException()
fun Context.getBitmap(id: Int): Bitmap {
var drawable = AppCompatResources.getDrawable(this, id)!!
if (drawable is BitmapDrawable)
return drawable.bitmap
if (SDK_INT >= 26 && drawable is AdaptiveIconDrawable) {
drawable = LayerDrawable(arrayOf(drawable.background, drawable.foreground))
}
val bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth, drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
return bitmap
}
fun Intent.startActivity(context: Context) = context.startActivity(this) fun Intent.startActivity(context: Context) = context.startActivity(this)
fun Intent.startActivityWithRoot() { fun Intent.startActivityWithRoot() {
...@@ -286,6 +311,8 @@ fun Context.unwrap(): Context { ...@@ -286,6 +311,8 @@ fun Context.unwrap(): Context {
return context return context
} }
fun Uri.writeTo(file: File) = toFile().copyTo(file)
fun Context.hasPermissions(vararg permissions: String) = permissions.all { fun Context.hasPermissions(vararg permissions: String) = permissions.all {
ContextCompat.checkSelfPermission(this, it) == PERMISSION_GRANTED ContextCompat.checkSelfPermission(this, it) == PERMISSION_GRANTED
} }
......
package com.topjohnwu.magisk.extensions package com.topjohnwu.magisk.extensions
import android.net.Uri
import android.os.Build import android.os.Build
import androidx.core.net.toFile import androidx.core.net.toFile
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
...@@ -21,8 +22,6 @@ fun ZipInputStream.forEach(callback: (ZipEntry) -> Unit) { ...@@ -21,8 +22,6 @@ fun ZipInputStream.forEach(callback: (ZipEntry) -> Unit) {
} }
} }
fun Uri.writeTo(file: File) = toFile().copyTo(file)
fun InputStream.writeTo(file: File) = fun InputStream.writeTo(file: File) =
withStreams(this, file.outputStream()) { reader, writer -> reader.copyTo(writer) } withStreams(this, file.outputStream()) { reader, writer -> reader.copyTo(writer) }
...@@ -106,3 +105,32 @@ fun Locale.toLangTag(): String { ...@@ -106,3 +105,32 @@ fun Locale.toLangTag(): String {
fun SimpleDateFormat.parseOrNull(date: String) = fun SimpleDateFormat.parseOrNull(date: String) =
runCatching { parse(date) }.onFailure { Timber.e(it) }.getOrNull() runCatching { parse(date) }.onFailure { Timber.e(it) }.getOrNull()
// 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)
package com.topjohnwu.magisk.model.download package com.topjohnwu.magisk.model.download
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Notification
import android.app.PendingIntent import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import androidx.core.app.NotificationCompat
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.extensions.chooser import com.topjohnwu.magisk.extensions.chooser
import com.topjohnwu.magisk.extensions.exists import com.topjohnwu.magisk.extensions.exists
...@@ -69,14 +69,14 @@ open class DownloadService : RemoteFileService() { ...@@ -69,14 +69,14 @@ open class DownloadService : RemoteFileService() {
// --- // ---
override fun NotificationCompat.Builder.addActions(subject: DownloadSubject) override fun Notification.Builder.addActions(subject: DownloadSubject)
= when (subject) { = when (subject) {
is Magisk -> addActionsInternal(subject) is Magisk -> addActionsInternal(subject)
is Module -> addActionsInternal(subject) is Module -> addActionsInternal(subject)
is Manager -> addActionsInternal(subject) is Manager -> addActionsInternal(subject)
} }
private fun NotificationCompat.Builder.addActionsInternal(subject: Magisk) private fun Notification.Builder.addActionsInternal(subject: Magisk)
= when (val conf = subject.configuration) { = when (val conf = subject.configuration) {
Download -> this.apply { Download -> this.apply {
fileIntent(subject.file.parentFile!!) fileIntent(subject.file.parentFile!!)
...@@ -92,7 +92,7 @@ open class DownloadService : RemoteFileService() { ...@@ -92,7 +92,7 @@ open class DownloadService : RemoteFileService() {
else -> this else -> this
} }
private fun NotificationCompat.Builder.addActionsInternal(subject: Module) private fun Notification.Builder.addActionsInternal(subject: Module)
= when (subject.configuration) { = when (subject.configuration) {
Download -> this.apply { Download -> this.apply {
fileIntent(subject.file.parentFile!!) fileIntent(subject.file.parentFile!!)
...@@ -106,19 +106,19 @@ open class DownloadService : RemoteFileService() { ...@@ -106,19 +106,19 @@ open class DownloadService : RemoteFileService() {
else -> this else -> this
} }
private fun NotificationCompat.Builder.addActionsInternal(subject: Manager) private fun Notification.Builder.addActionsInternal(subject: Manager)
= when (subject.configuration) { = when (subject.configuration) {
APK.Upgrade -> setContentIntent(APKInstall.installIntent(context, subject.file)) APK.Upgrade -> setContentIntent(APKInstall.installIntent(context, subject.file))
else -> this else -> this
} }
@Suppress("ReplaceSingleLineLet") @Suppress("ReplaceSingleLineLet")
private fun NotificationCompat.Builder.setContentIntent(intent: Intent) = private fun Notification.Builder.setContentIntent(intent: Intent) =
PendingIntent.getActivity(context, nextInt(), intent, PendingIntent.FLAG_ONE_SHOT) PendingIntent.getActivity(context, nextInt(), intent, PendingIntent.FLAG_ONE_SHOT)
.let { setContentIntent(it) } .let { setContentIntent(it) }
@Suppress("ReplaceSingleLineLet") @Suppress("ReplaceSingleLineLet")
private fun NotificationCompat.Builder.addAction(icon: Int, title: Int, intent: Intent) = private fun Notification.Builder.addAction(icon: Int, title: Int, intent: Intent) =
PendingIntent.getActivity(context, nextInt(), intent, PendingIntent.FLAG_ONE_SHOT) PendingIntent.getActivity(context, nextInt(), intent, PendingIntent.FLAG_ONE_SHOT)
.let { addAction(icon, getString(title), it) } .let { addAction(icon, getString(title), it) }
...@@ -140,7 +140,7 @@ open class DownloadService : RemoteFileService() { ...@@ -140,7 +140,7 @@ open class DownloadService : RemoteFileService() {
inline operator fun invoke(context: Context, argBuilder: Builder.() -> Unit) { inline operator fun invoke(context: Context, argBuilder: Builder.() -> Unit) {
val app = context.applicationContext val app = context.applicationContext
val builder = Builder().apply(argBuilder) 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) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
app.startForegroundService(intent) app.startForegroundService(intent)
......
...@@ -38,7 +38,7 @@ private fun RemoteFileService.upgrade(apk: File, id: Int) { ...@@ -38,7 +38,7 @@ private fun RemoteFileService.upgrade(apk: File, id: Int) {
patch(apk, id) patch(apk, id)
} else { } else {
// Simply relaunch the app // Simply relaunch the app
ProcessPhoenix.triggerRebirth(this) ProcessPhoenix.triggerRebirth(this, intent<ProcessPhoenix>())
} }
} else { } else {
patch(apk, id) patch(apk, id)
......
...@@ -3,22 +3,20 @@ package com.topjohnwu.magisk.model.download ...@@ -3,22 +3,20 @@ package com.topjohnwu.magisk.model.download
import android.app.Notification import android.app.Notification
import android.content.Intent import android.content.Intent
import android.os.IBinder import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.topjohnwu.magisk.base.BaseService import com.topjohnwu.magisk.base.BaseService
import com.topjohnwu.magisk.view.Notifications
import org.koin.core.KoinComponent import org.koin.core.KoinComponent
import java.util.* import java.util.*
import kotlin.random.Random.Default.nextInt import kotlin.random.Random.Default.nextInt
abstract class NotificationService : BaseService(), KoinComponent { abstract class NotificationService : BaseService(), KoinComponent {
abstract val defaultNotification: NotificationCompat.Builder abstract val defaultNotification: Notification.Builder
private val manager by lazy { NotificationManagerCompat.from(this) }
private val hasNotifications get() = notifications.isNotEmpty() private val hasNotifications get() = notifications.isNotEmpty()
private val notifications = private val notifications =
Collections.synchronizedMap(mutableMapOf<Int, NotificationCompat.Builder>()) Collections.synchronizedMap(mutableMapOf<Int, Notification.Builder>())
override fun onTaskRemoved(rootIntent: Intent?) { override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent) super.onTaskRemoved(rootIntent)
...@@ -30,7 +28,7 @@ abstract class NotificationService : BaseService(), KoinComponent { ...@@ -30,7 +28,7 @@ abstract class NotificationService : BaseService(), KoinComponent {
fun update( fun update(
id: Int, id: Int,
body: (NotificationCompat.Builder) -> Unit = {} body: (Notification.Builder) -> Unit = {}
) { ) {
val notification = notifications.getOrPut(id) { defaultNotification } val notification = notifications.getOrPut(id) { defaultNotification }
...@@ -43,7 +41,7 @@ abstract class NotificationService : BaseService(), KoinComponent { ...@@ -43,7 +41,7 @@ abstract class NotificationService : BaseService(), KoinComponent {
protected fun finishNotify( protected fun finishNotify(
id: Int, id: Int,
editBody: (NotificationCompat.Builder) -> NotificationCompat.Builder? = { null } editBody: (Notification.Builder) -> Notification.Builder? = { null }
) : Int { ) : Int {
val currentNotification = remove(id)?.run(editBody) val currentNotification = remove(id)?.run(editBody)
...@@ -62,11 +60,11 @@ abstract class NotificationService : BaseService(), KoinComponent { ...@@ -62,11 +60,11 @@ abstract class NotificationService : BaseService(), KoinComponent {
// --- // ---
private fun notify(id: Int, notification: Notification) { private fun notify(id: Int, notification: Notification) {
manager.notify(id, notification) Notifications.mgr.notify(id, notification)
} }
private fun cancel(id: Int) { private fun cancel(id: Int) {
manager.cancel(id) Notifications.mgr.cancel(id)
} }
protected fun remove(id: Int) = notifications.remove(id).also { protected fun remove(id: Int) = notifications.remove(id).also {
......
package com.topjohnwu.magisk.model.download package com.topjohnwu.magisk.model.download
import android.app.Activity import android.app.Activity
import android.app.Notification
import android.content.Intent import android.content.Intent
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
...@@ -27,7 +28,7 @@ abstract class RemoteFileService : NotificationService() { ...@@ -27,7 +28,7 @@ abstract class RemoteFileService : NotificationService() {
val service: GithubRawServices by inject() val service: GithubRawServices by inject()
override val defaultNotification: NotificationCompat.Builder override val defaultNotification
get() = Notifications.progress(this, "") get() = Notifications.progress(this, "")
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
...@@ -117,8 +118,8 @@ abstract class RemoteFileService : NotificationService() { ...@@ -117,8 +118,8 @@ abstract class RemoteFileService : NotificationService() {
@Throws(Throwable::class) @Throws(Throwable::class)
protected abstract fun onFinished(subject: DownloadSubject, id: Int) protected abstract fun onFinished(subject: DownloadSubject, id: Int)
protected abstract fun NotificationCompat.Builder.addActions(subject: DownloadSubject) protected abstract fun Notification.Builder.addActions(subject: DownloadSubject)
: NotificationCompat.Builder : Notification.Builder
companion object : KoinComponent { companion object : KoinComponent {
const val ARG_URL = "arg_url" const val ARG_URL = "arg_url"
......
package com.topjohnwu.magisk.model.entity 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.timeFormatTime
import com.topjohnwu.magisk.extensions.toTime import com.topjohnwu.magisk.extensions.toTime
import com.topjohnwu.magisk.model.entity.MagiskPolicy.Companion.ALLOW import com.topjohnwu.magisk.model.entity.MagiskPolicy.Companion.ALLOW
import java.util.*
@Entity(tableName = "logs")
data class MagiskLog( data class MagiskLog(
val fromUid: Int, val fromUid: Int,
val toUid: Int, val toUid: Int,
...@@ -13,9 +17,10 @@ data class MagiskLog( ...@@ -13,9 +17,10 @@ data class MagiskLog(
val appName: String, val appName: String,
val command: String, val command: String,
val action: Boolean, 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( data class WrappedMagiskLog(
...@@ -23,35 +28,8 @@ data class WrappedMagiskLog( ...@@ -23,35 +28,8 @@ data class WrappedMagiskLog(
val items: List<MagiskLog> 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( fun MagiskPolicy.toLog(
toUid: Int, toUid: Int,
fromPid: Int, fromPid: Int,
command: String, command: String
date: Date ) = MagiskLog(uid, toUid, fromPid, packageName, appName, command, policy == ALLOW, now)
) = MagiskLog(uid, toUid, fromPid, packageName, appName, command, policy == ALLOW, date)
...@@ -7,11 +7,11 @@ import com.topjohnwu.magisk.model.entity.MagiskPolicy.Companion.INTERACTIVE ...@@ -7,11 +7,11 @@ import com.topjohnwu.magisk.model.entity.MagiskPolicy.Companion.INTERACTIVE
data class MagiskPolicy( data class MagiskPolicy(
val uid: Int, var uid: Int,
val packageName: String, val packageName: String,
val appName: String, val appName: String,
val policy: Int = INTERACTIVE, var policy: Int = INTERACTIVE,
val until: Long = -1L, var until: Long = -1L,
val logging: Boolean = true, val logging: Boolean = true,
val notification: Boolean = true, val notification: Boolean = true,
val applicationInfo: ApplicationInfo val applicationInfo: ApplicationInfo
...@@ -38,7 +38,7 @@ fun MagiskPolicy.toMap() = mapOf( ...@@ -38,7 +38,7 @@ fun MagiskPolicy.toMap() = mapOf(
fun Map<String, String>.toPolicy(pm: PackageManager): MagiskPolicy { fun Map<String, String>.toPolicy(pm: PackageManager): MagiskPolicy {
val uid = get("uid")?.toIntOrNull() ?: -1 val uid = get("uid")?.toIntOrNull() ?: -1
val packageName = get("package_name").orEmpty() val packageName = get("package_name").orEmpty()
val info = pm.getApplicationInfo(packageName, 0) val info = pm.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES)
if (info.uid != uid) if (info.uid != uid)
throw PackageManager.NameNotFoundException() throw PackageManager.NameNotFoundException()
...@@ -56,14 +56,15 @@ fun Map<String, String>.toPolicy(pm: PackageManager): MagiskPolicy { ...@@ -56,14 +56,15 @@ fun Map<String, String>.toPolicy(pm: PackageManager): MagiskPolicy {
} }
@Throws(PackageManager.NameNotFoundException::class) @Throws(PackageManager.NameNotFoundException::class)
fun Int.toPolicy(pm: PackageManager): MagiskPolicy { fun Int.toPolicy(pm: PackageManager, policy: Int = INTERACTIVE): MagiskPolicy {
val pkg = pm.getPackagesForUid(this)?.firstOrNull() val pkg = pm.getPackagesForUid(this)?.firstOrNull()
?: throw PackageManager.NameNotFoundException() ?: throw PackageManager.NameNotFoundException()
val info = pm.getApplicationInfo(pkg, 0) val info = pm.getApplicationInfo(pkg, PackageManager.GET_UNINSTALLED_PACKAGES)
return MagiskPolicy( return MagiskPolicy(
uid = this, uid = info.uid,
packageName = pkg, packageName = pkg,
policy = policy,
applicationInfo = info, applicationInfo = info,
appName = info.loadLabel(pm).toString() appName = info.getLabel(pm)
) )
} }
...@@ -65,7 +65,7 @@ class Module(path: String) : BaseModule() { ...@@ -65,7 +65,7 @@ class Module(path: String) : BaseModule() {
val module = Module(Const.MAGISK_PATH + "/" + file.name) val module = Module(Const.MAGISK_PATH + "/" + file.name)
moduleList.add(module) moduleList.add(module)
} }
return moduleList.sortedBy { it.name } return moduleList.sortedBy { it.name.toLowerCase() }
} }
} }
} }
...@@ -40,8 +40,10 @@ class LogItemRvItem( ...@@ -40,8 +40,10 @@ class LogItemRvItem(
fun toggle() = isExpanded.toggle() fun toggle() = isExpanded.toggle()
override fun contentSameAs(other: LogItemRvItem): Boolean = items override fun contentSameAs(other: LogItemRvItem): Boolean {
.any { !other.items.contains(it) } if (items.size != other.items.size) return false
return items.all { it in other.items }
}
override fun itemSameAs(other: LogItemRvItem): Boolean = date == other.date override fun itemSameAs(other: LogItemRvItem): Boolean = date == other.date
} }
...@@ -53,13 +55,7 @@ class LogItemEntryRvItem(val item: MagiskLog) : ComparableRvItem<LogItemEntryRvI ...@@ -53,13 +55,7 @@ class LogItemEntryRvItem(val item: MagiskLog) : ComparableRvItem<LogItemEntryRvI
fun toggle() = isExpanded.toggle() fun toggle() = isExpanded.toggle()
override fun contentSameAs(other: LogItemEntryRvItem) = item.fromUid == other.item.fromUid && override fun contentSameAs(other: LogItemEntryRvItem) = item == other.item
item.toUid == other.item.toUid &&
item.fromPid == other.item.fromPid &&
item.packageName == other.item.packageName &&
item.command == other.item.command &&
item.action == other.item.action &&
item.date == other.item.date
override fun itemSameAs(other: LogItemEntryRvItem) = item.appName == other.item.appName override fun itemSameAs(other: LogItemEntryRvItem) = item.appName == other.item.appName
} }
......
package com.topjohnwu.magisk.model.events package com.topjohnwu.magisk.model.events
import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
...@@ -15,6 +14,7 @@ import com.topjohnwu.magisk.data.repository.MagiskRepository ...@@ -15,6 +14,7 @@ import com.topjohnwu.magisk.data.repository.MagiskRepository
import com.topjohnwu.magisk.extensions.DynamicClassLoader import com.topjohnwu.magisk.extensions.DynamicClassLoader
import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.extensions.subscribeK
import com.topjohnwu.magisk.extensions.writeTo import com.topjohnwu.magisk.extensions.writeTo
import com.topjohnwu.magisk.base.BaseActivity
import com.topjohnwu.magisk.model.entity.module.Repo import com.topjohnwu.magisk.model.entity.module.Repo
import com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder import com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder
import com.topjohnwu.magisk.utils.RxBus import com.topjohnwu.magisk.utils.RxBus
...@@ -135,7 +135,7 @@ class UpdateSafetyNetEvent : ViewEvent(), ContextExecutor, KoinComponent, Safety ...@@ -135,7 +135,7 @@ class UpdateSafetyNetEvent : ViewEvent(), ContextExecutor, KoinComponent, Safety
} }
} }
class ViewActionEvent(val action: Activity.() -> Unit) : ViewEvent(), ActivityExecutor { class ViewActionEvent(val action: BaseActivity<*, *>.() -> Unit) : ViewEvent(), ActivityExecutor {
override fun invoke(activity: AppCompatActivity) = activity.run(action) override fun invoke(activity: AppCompatActivity) = activity.run(action)
} }
......
...@@ -2,37 +2,25 @@ package com.topjohnwu.magisk.model.receiver ...@@ -2,37 +2,25 @@ package com.topjohnwu.magisk.model.receiver
import android.content.ContextWrapper import android.content.ContextWrapper
import android.content.Intent import android.content.Intent
import android.os.Build.VERSION.SDK_INT import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.* import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.Info
import com.topjohnwu.magisk.base.BaseReceiver import com.topjohnwu.magisk.base.BaseReceiver
import com.topjohnwu.magisk.data.database.PolicyDao import com.topjohnwu.magisk.data.database.PolicyDao
import com.topjohnwu.magisk.data.database.base.su
import com.topjohnwu.magisk.extensions.reboot import com.topjohnwu.magisk.extensions.reboot
import com.topjohnwu.magisk.extensions.startActivity
import com.topjohnwu.magisk.extensions.startActivityWithRoot
import com.topjohnwu.magisk.model.download.DownloadService import com.topjohnwu.magisk.model.download.DownloadService
import com.topjohnwu.magisk.model.entity.ManagerJson import com.topjohnwu.magisk.model.entity.ManagerJson
import com.topjohnwu.magisk.model.entity.internal.Configuration import com.topjohnwu.magisk.model.entity.internal.Configuration
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject import com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity import com.topjohnwu.magisk.utils.SuHandler
import com.topjohnwu.magisk.utils.SuLogger
import com.topjohnwu.magisk.view.Notifications
import com.topjohnwu.magisk.view.Shortcuts import com.topjohnwu.magisk.view.Shortcuts
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import org.koin.core.inject import org.koin.core.inject
import timber.log.Timber
open class GeneralReceiver : BaseReceiver() { open class GeneralReceiver : BaseReceiver() {
private val policyDB: PolicyDao by inject() private val policyDB: PolicyDao by inject()
companion object {
const val REQUEST = "request"
const val LOG = "log"
const val NOTIFY = "notify"
const val TEST = "test"
}
private fun getPkg(intent: Intent): String { private fun getPkg(intent: Intent): String {
return intent.data?.encodedSchemeSpecificPart.orEmpty() return intent.data?.encodedSchemeSpecificPart.orEmpty()
} }
...@@ -40,59 +28,19 @@ open class GeneralReceiver : BaseReceiver() { ...@@ -40,59 +28,19 @@ open class GeneralReceiver : BaseReceiver() {
override fun onReceive(context: ContextWrapper, intent: Intent?) { override fun onReceive(context: ContextWrapper, intent: Intent?) {
intent ?: return intent ?: return
// Debug messages
if (BuildConfig.DEBUG) {
Timber.d(intent.action)
intent.extras?.let { bundle ->
bundle.keySet().forEach {
Timber.d("[%s]=[%s]", it, bundle[it])
}
}
}
when (intent.action ?: return) { when (intent.action ?: return) {
Intent.ACTION_REBOOT, Intent.ACTION_BOOT_COMPLETED -> { Intent.ACTION_REBOOT -> {
val action = intent.getStringExtra("action") SuHandler(context, intent.getStringExtra("action"), intent.extras)
if (action == null) {
// Actual boot completed event
Shell.su("mm_patch_dtbo").submit {
if (it.isSuccess)
Notifications.dtboPatched(context)
}
return
} }
when (action) { Intent.ACTION_PACKAGE_REPLACED -> {
REQUEST -> {
val i = context.intent(SuRequestActivity::class.java)
.setAction(action)
.putExtra("socket", intent.getStringExtra("socket"))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
if (SDK_INT >= 29) {
// Android Q does not allow starting activity from background
i.startActivityWithRoot()
} else {
i.startActivity(context)
}
}
LOG -> SuLogger.handleLogs(context, intent)
NOTIFY -> SuLogger.handleNotify(context, intent)
TEST -> {
val mode = intent.getIntExtra("mode", 1 shl 1)
if (mode > Info.env.connectionMode)
Info.env.connectionMode = mode
Shell.su("magisk --connect-mode $mode").submit()
}
}
}
Intent.ACTION_PACKAGE_REPLACED ->
// This will only work pre-O // This will only work pre-O
if (Config.suReAuth) if (Config.suReAuth)
policyDB.delete(getPkg(intent)).blockingGet() policyDB.delete(getPkg(intent)).blockingGet()
}
Intent.ACTION_PACKAGE_FULLY_REMOVED -> { Intent.ACTION_PACKAGE_FULLY_REMOVED -> {
val pkg = getPkg(intent) val pkg = getPkg(intent)
policyDB.delete(pkg).blockingGet() policyDB.delete(pkg).blockingGet()
"magiskhide --rm $pkg".su().blockingGet() Shell.su("magiskhide --rm $pkg").submit()
} }
Intent.ACTION_LOCALE_CHANGED -> Shortcuts.setup(context) Intent.ACTION_LOCALE_CHANGED -> Shortcuts.setup(context)
Const.Key.BROADCAST_MANAGER_UPDATE -> { Const.Key.BROADCAST_MANAGER_UPDATE -> {
......
...@@ -266,7 +266,7 @@ abstract class MagiskInstaller { ...@@ -266,7 +266,7 @@ abstract class MagiskInstaller {
val patched = File(installDir, "new-boot.img") val patched = File(installDir, "new-boot.img")
if (isSigned) { if (isSigned) {
console.add("- Signing boot image with test keys") console.add("- Signing boot image with verity keys")
val signed = File(installDir, "signed.img") val signed = File(installDir, "signed.img")
try { try {
withStreams(SuFileInputStream(patched), signed.outputStream().buffered()) { withStreams(SuFileInputStream(patched), signed.outputStream().buffered()) {
......
...@@ -2,11 +2,13 @@ package com.topjohnwu.magisk.ui ...@@ -2,11 +2,13 @@ package com.topjohnwu.magisk.ui
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.core.view.GravityCompat import androidx.core.view.GravityCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction import androidx.fragment.app.FragmentTransaction
import com.ncapdevi.fragnav.FragNavController import com.ncapdevi.fragnav.FragNavController
import com.ncapdevi.fragnav.FragNavTransactionOptions import com.ncapdevi.fragnav.FragNavTransactionOptions
import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.Const.Key.OPEN_SECTION import com.topjohnwu.magisk.Const.Key.OPEN_SECTION
import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.Info
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
...@@ -29,7 +31,6 @@ import com.topjohnwu.magisk.ui.module.ReposFragment ...@@ -29,7 +31,6 @@ import com.topjohnwu.magisk.ui.module.ReposFragment
import com.topjohnwu.magisk.ui.settings.SettingsFragment import com.topjohnwu.magisk.ui.settings.SettingsFragment
import com.topjohnwu.magisk.ui.superuser.SuperuserFragment import com.topjohnwu.magisk.ui.superuser.SuperuserFragment
import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.superuser.Shell
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
import timber.log.Timber import timber.log.Timber
import kotlin.reflect.KClass import kotlin.reflect.KClass
...@@ -60,12 +61,21 @@ open class MainActivity : BaseActivity<MainViewModel, ActivityMainBinding>(), Na ...@@ -60,12 +61,21 @@ open class MainActivity : BaseActivity<MainViewModel, ActivityMainBinding>(), Na
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
if (!SplashActivity.DONE) { if (!SplashActivity.DONE) {
startActivity(intent(SplashActivity::class.java)) startActivity(intent<SplashActivity>())
finish() finish()
} }
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
if (Info.env.isUnsupported && !viewModel.shownUnsupportedDialog) {
viewModel.shownUnsupportedDialog = true
AlertDialog.Builder(this)
.setTitle(R.string.unsupport_magisk_title)
.setMessage(getString(R.string.unsupport_magisk_msg, Const.Version.MIN_VERSION))
.setPositiveButton(android.R.string.ok, null)
.show()
}
navigationController.apply { navigationController.apply {
rootFragmentListener = this@MainActivity rootFragmentListener = this@MainActivity
transactionListener = this@MainActivity transactionListener = this@MainActivity
...@@ -153,16 +163,11 @@ open class MainActivity : BaseActivity<MainViewModel, ActivityMainBinding>(), Na ...@@ -153,16 +163,11 @@ open class MainActivity : BaseActivity<MainViewModel, ActivityMainBinding>(), Na
private fun checkHideSection() { private fun checkHideSection() {
val menu = binding.navView.menu val menu = binding.navView.menu
menu.findItem(R.id.magiskHideFragment).isVisible = menu.findItem(R.id.magiskHideFragment).isVisible = Info.env.isActive && Info.env.magiskHide
Shell.rootAccess() && Info.env.magiskHide menu.findItem(R.id.modulesFragment).isVisible = Info.env.isActive
menu.findItem(R.id.modulesFragment).isVisible = menu.findItem(R.id.reposFragment).isVisible = Info.isConnected.value && Info.env.isActive
Shell.rootAccess() && Info.env.magiskVersionCode >= 0 menu.findItem(R.id.logFragment).isVisible = Info.env.isActive
menu.findItem(R.id.reposFragment).isVisible = menu.findItem(R.id.superuserFragment).isVisible = Utils.showSuperUser()
(viewModel.isConnected.value && Shell.rootAccess() && Info.env.magiskVersionCode >= 0)
menu.findItem(R.id.logFragment).isVisible =
Shell.rootAccess()
menu.findItem(R.id.superuserFragment).isVisible =
Utils.showSuperUser()
} }
private fun FragNavTransactionOptions.Builder.customAnimations(options: MagiskAnimBuilder) = private fun FragNavTransactionOptions.Builder.customAnimations(options: MagiskAnimBuilder) =
......
...@@ -8,6 +8,8 @@ import com.topjohnwu.magisk.model.navigation.Navigation ...@@ -8,6 +8,8 @@ import com.topjohnwu.magisk.model.navigation.Navigation
class MainViewModel : BaseViewModel() { class MainViewModel : BaseViewModel() {
var shownUnsupportedDialog = false
fun navPressed() = Navigation.Main.OPEN_NAV.publish() fun navPressed() = Navigation.Main.OPEN_NAV.publish()
fun navigationItemPressed(item: MenuItem): Boolean { fun navigationItemPressed(item: MenuItem): Boolean {
......
...@@ -7,9 +7,13 @@ import android.text.TextUtils ...@@ -7,9 +7,13 @@ import android.text.TextUtils
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import com.topjohnwu.magisk.* import com.topjohnwu.magisk.*
import com.topjohnwu.magisk.model.navigation.Navigation import com.topjohnwu.magisk.model.navigation.Navigation
import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.intent
import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.magisk.view.Notifications import com.topjohnwu.magisk.view.Notifications
import com.topjohnwu.magisk.view.Shortcuts import com.topjohnwu.magisk.view.Shortcuts
import com.topjohnwu.magisk.wrap
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
open class SplashActivity : Activity() { open class SplashActivity : Activity() {
...@@ -20,19 +24,7 @@ open class SplashActivity : Activity() { ...@@ -20,19 +24,7 @@ open class SplashActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
Shell.getShell { initAndStart() }
Shell.getShell {
if (Info.env.magiskVersionCode > 0 && Info.env.magiskVersionCode < Const.Version.MIN_SUPPORT) {
AlertDialog.Builder(this)
.setTitle(R.string.unsupport_magisk_title)
.setMessage(R.string.unsupport_magisk_message)
.setNegativeButton(android.R.string.ok, null)
.setOnDismissListener { finish() }
.show()
} else {
initAndStart()
}
}
} }
private fun initAndStart() { private fun initAndStart() {
...@@ -41,7 +33,7 @@ open class SplashActivity : Activity() { ...@@ -41,7 +33,7 @@ open class SplashActivity : Activity() {
Config.suManager = "" Config.suManager = ""
Shell.su("pm uninstall $pkg").submit() Shell.su("pm uninstall $pkg").submit()
} }
if (TextUtils.equals(pkg, packageName)) { if (pkg == packageName) {
runCatching { runCatching {
// We are the manager, remove com.topjohnwu.magisk as it could be malware // We are the manager, remove com.topjohnwu.magisk as it could be malware
packageManager.getApplicationInfo(BuildConfig.APPLICATION_ID, 0) packageManager.getApplicationInfo(BuildConfig.APPLICATION_ID, 0)
...@@ -61,6 +53,11 @@ open class SplashActivity : Activity() { ...@@ -61,6 +53,11 @@ open class SplashActivity : Activity() {
// Setup shortcuts // Setup shortcuts
Shortcuts.setup(this) Shortcuts.setup(this)
Shell.su("mm_patch_dtbo").submit {
if (it.isSuccess)
Notifications.dtboPatched(this)
}
DONE = true DONE = true
Navigation.start(intent, this) Navigation.start(intent, this)
finish() finish()
......
...@@ -2,9 +2,9 @@ package com.topjohnwu.magisk.ui.flash ...@@ -2,9 +2,9 @@ package com.topjohnwu.magisk.ui.flash
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.ActivityInfo
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import androidx.core.app.NotificationManagerCompat
import androidx.core.net.toUri import androidx.core.net.toUri
import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
...@@ -16,6 +16,7 @@ import com.topjohnwu.magisk.model.events.BackPressEvent ...@@ -16,6 +16,7 @@ import com.topjohnwu.magisk.model.events.BackPressEvent
import com.topjohnwu.magisk.model.events.PermissionEvent import com.topjohnwu.magisk.model.events.PermissionEvent
import com.topjohnwu.magisk.model.events.SnackbarEvent import com.topjohnwu.magisk.model.events.SnackbarEvent
import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.model.events.ViewEvent
import com.topjohnwu.magisk.view.Notifications
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf import org.koin.core.parameter.parametersOf
import java.io.File import java.io.File
...@@ -32,10 +33,11 @@ open class FlashActivity : BaseActivity<FlashViewModel, ActivityFlashBinding>() ...@@ -32,10 +33,11 @@ open class FlashActivity : BaseActivity<FlashViewModel, ActivityFlashBinding>()
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val id = intent.getIntExtra(Const.Key.DISMISS_ID, -1) val id = intent.getIntExtra(Const.Key.DISMISS_ID, -1)
if (id != -1) if (id != -1)
NotificationManagerCompat.from(this).cancel(id) Notifications.mgr.cancel(id)
} }
override fun onBackPressed() { override fun onBackPressed() {
...@@ -60,7 +62,7 @@ open class FlashActivity : BaseActivity<FlashViewModel, ActivityFlashBinding>() ...@@ -60,7 +62,7 @@ open class FlashActivity : BaseActivity<FlashViewModel, ActivityFlashBinding>()
companion object { companion object {
private fun intent(context: Context) = context.intent(FlashActivity::class.java) private fun intent(context: Context) = context.intent<FlashActivity>()
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
private fun intent(context: Context, file: File) = intent(context).setData(file.toUri()) private fun intent(context: Context, file: File) = intent(context).setData(file.toUri())
......
...@@ -90,9 +90,7 @@ class HideViewModel( ...@@ -90,9 +90,7 @@ class HideViewModel(
.toList() .toList()
.map { it to items.calculateDiff(it) } .map { it to items.calculateDiff(it) }
private fun toggleItem(item: HideProcessRvItem) = magiskRepo private fun toggleItem(item: HideProcessRvItem) =
.toggleHide(item.isHidden.value, item.packageName, item.process) magiskRepo.toggleHide(item.isHidden.value, item.packageName, item.process)
.subscribeK()
.add()
} }
...@@ -77,7 +77,7 @@ class HomeViewModel( ...@@ -77,7 +77,7 @@ class HomeViewModel(
"" ""
} }
val safetyNetTitle = KObservableField(R.string.safetyNet_check_text) val safetyNetTitle = KObservableField(R.string.safetyNet_check_text.res())
val ctsState = KObservableField(SafetyNetState.IDLE) val ctsState = KObservableField(SafetyNetState.IDLE)
val basicIntegrityState = KObservableField(SafetyNetState.IDLE) val basicIntegrityState = KObservableField(SafetyNetState.IDLE)
val safetyNetState = Observer(ctsState, basicIntegrityState) { val safetyNetState = Observer(ctsState, basicIntegrityState) {
...@@ -92,7 +92,7 @@ class HomeViewModel( ...@@ -92,7 +92,7 @@ class HomeViewModel(
} }
} }
val hasRoot = KObservableField(false) val isActive = KObservableField(false)
private var shownDialog = false private var shownDialog = false
...@@ -135,7 +135,7 @@ class HomeViewModel( ...@@ -135,7 +135,7 @@ class HomeViewModel(
fun safetyNetPressed() { fun safetyNetPressed() {
ctsState.value = SafetyNetState.LOADING ctsState.value = SafetyNetState.LOADING
basicIntegrityState.value = SafetyNetState.LOADING basicIntegrityState.value = SafetyNetState.LOADING
safetyNetTitle.value = R.string.checking_safetyNet_status safetyNetTitle.value = R.string.checking_safetyNet_status.res()
UpdateSafetyNetEvent().publish() UpdateSafetyNetEvent().publish()
} }
...@@ -144,7 +144,7 @@ class HomeViewModel( ...@@ -144,7 +144,7 @@ class HomeViewModel(
response and 0x0F == 0 -> { response and 0x0F == 0 -> {
val hasCtsPassed = response and SafetyNetHelper.CTS_PASS != 0 val hasCtsPassed = response and SafetyNetHelper.CTS_PASS != 0
val hasBasicIntegrityPassed = response and SafetyNetHelper.BASIC_PASS != 0 val hasBasicIntegrityPassed = response and SafetyNetHelper.BASIC_PASS != 0
safetyNetTitle.value = R.string.safetyNet_check_success safetyNetTitle.value = R.string.safetyNet_check_success.res()
ctsState.value = if (hasCtsPassed) { ctsState.value = if (hasCtsPassed) {
SafetyNetState.PASS SafetyNetState.PASS
} else { } else {
...@@ -164,8 +164,8 @@ class HomeViewModel( ...@@ -164,8 +164,8 @@ class HomeViewModel(
ctsState.value = SafetyNetState.IDLE ctsState.value = SafetyNetState.IDLE
basicIntegrityState.value = SafetyNetState.IDLE basicIntegrityState.value = SafetyNetState.IDLE
safetyNetTitle.value = when (response) { safetyNetTitle.value = when (response) {
SafetyNetHelper.RESPONSE_ERR -> R.string.safetyNet_res_invalid SafetyNetHelper.RESPONSE_ERR -> R.string.safetyNet_res_invalid.res()
else -> R.string.safetyNet_api_error else -> R.string.safetyNet_api_error.res()
} }
} }
} }
...@@ -175,7 +175,7 @@ class HomeViewModel( ...@@ -175,7 +175,7 @@ class HomeViewModel(
if (invalidate) if (invalidate)
Info.envRef.invalidate() Info.envRef.invalidate()
hasRoot.value = Shell.rootAccess() isActive.value = Info.env.isActive
val fetchUpdate = if (isConnected.value) val fetchUpdate = if (isConnected.value)
magiskRepo.fetchUpdate().ignoreElement() magiskRepo.fetchUpdate().ignoreElement()
...@@ -192,7 +192,7 @@ class HomeViewModel( ...@@ -192,7 +192,7 @@ class HomeViewModel(
_managerState.value = MagiskState.LOADING _managerState.value = MagiskState.LOADING
ctsState.value = SafetyNetState.IDLE ctsState.value = SafetyNetState.IDLE
basicIntegrityState.value = SafetyNetState.IDLE basicIntegrityState.value = SafetyNetState.IDLE
safetyNetTitle.value = R.string.safetyNet_check_text safetyNetTitle.value = R.string.safetyNet_check_text.res()
}.subscribeK { }.subscribeK {
updateSelf() updateSelf()
ensureEnv() ensureEnv()
...@@ -215,8 +215,8 @@ class HomeViewModel( ...@@ -215,8 +215,8 @@ class HomeViewModel(
private fun updateSelf() { private fun updateSelf() {
magiskState.value = when (Info.env.magiskVersionCode) { magiskState.value = when (Info.env.magiskVersionCode) {
in Int.MIN_VALUE until 0 -> MagiskState.NOT_INSTALLED in Int.MIN_VALUE .. 0 -> MagiskState.NOT_INSTALLED
in 1 until (Info.remote.magisk.versionCode - 1) -> MagiskState.OBSOLETE in 1 until Info.remote.magisk.versionCode -> MagiskState.OBSOLETE
else -> MagiskState.UP_TO_DATE else -> MagiskState.UP_TO_DATE
} }
...@@ -224,10 +224,10 @@ class HomeViewModel( ...@@ -224,10 +224,10 @@ class HomeViewModel(
VERSION_FMT.format(Info.remote.magisk.version, Info.remote.magisk.versionCode) VERSION_FMT.format(Info.remote.magisk.version, Info.remote.magisk.versionCode)
_managerState.value = when (Info.remote.app.versionCode) { _managerState.value = when (Info.remote.app.versionCode) {
in Int.MIN_VALUE until 0 -> MagiskState.NOT_INSTALLED //wrong update channel in Int.MIN_VALUE .. 0 -> MagiskState.NOT_INSTALLED //wrong update channel
in (BuildConfig.VERSION_CODE + 1) until Int.MAX_VALUE -> MagiskState.OBSOLETE in (BuildConfig.VERSION_CODE + 1) .. Int.MAX_VALUE -> MagiskState.OBSOLETE
else -> { else -> {
if (isRunningAsStub && Info.stub!!.version < Info.remote.stub.versionCode) if (Info.stub?.version ?: Int.MAX_VALUE < Info.remote.stub.versionCode)
MagiskState.OBSOLETE MagiskState.OBSOLETE
else else
MagiskState.UP_TO_DATE MagiskState.UP_TO_DATE
......
...@@ -104,7 +104,6 @@ class LogViewModel( ...@@ -104,7 +104,6 @@ class LogViewModel(
.add() .add()
private fun clearMagiskLogs(callback: () -> Unit) = logRepo.clearMagiskLogs() private fun clearMagiskLogs(callback: () -> Unit) = logRepo.clearMagiskLogs()
.ignoreElement()
.doOnComplete(callback) .doOnComplete(callback)
.subscribeK { SnackbarEvent(R.string.logs_cleared).publish() } .subscribeK { SnackbarEvent(R.string.logs_cleared).publish() }
.add() .add()
...@@ -115,7 +114,6 @@ class LogViewModel( ...@@ -115,7 +114,6 @@ class LogViewModel(
.toList() .toList()
private fun fetchMagiskLog() = logRepo.fetchMagiskLogs() private fun fetchMagiskLog() = logRepo.fetchMagiskLogs()
.flattenAsFlowable { it }
.map { ConsoleRvItem(it) } .map { ConsoleRvItem(it) }
.toList() .toList()
......
...@@ -28,7 +28,7 @@ class ModulesFragment : BaseFragment<ModuleViewModel, FragmentModulesBinding>() ...@@ -28,7 +28,7 @@ class ModulesFragment : BaseFragment<ModuleViewModel, FragmentModulesBinding>()
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == Const.ID.FETCH_ZIP && resultCode == Activity.RESULT_OK && data != null) { if (requestCode == Const.ID.FETCH_ZIP && resultCode == Activity.RESULT_OK && data != null) {
// Get the URI of the selected file // Get the URI of the selected file
val intent = activity.intent(FlashActivity::class.java) val intent = activity.intent<FlashActivity>()
intent.setData(data.data).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP) intent.setData(data.data).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP)
startActivity(intent) startActivity(intent)
} }
......
...@@ -19,13 +19,11 @@ import com.topjohnwu.magisk.data.database.RepoDao ...@@ -19,13 +19,11 @@ import com.topjohnwu.magisk.data.database.RepoDao
import com.topjohnwu.magisk.databinding.CustomDownloadDialogBinding import com.topjohnwu.magisk.databinding.CustomDownloadDialogBinding
import com.topjohnwu.magisk.databinding.DialogCustomNameBinding import com.topjohnwu.magisk.databinding.DialogCustomNameBinding
import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.extensions.subscribeK
import com.topjohnwu.magisk.extensions.toLangTag
import com.topjohnwu.magisk.model.download.DownloadService import com.topjohnwu.magisk.model.download.DownloadService
import com.topjohnwu.magisk.model.entity.internal.Configuration import com.topjohnwu.magisk.model.entity.internal.Configuration
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject import com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import com.topjohnwu.magisk.model.observer.Observer import com.topjohnwu.magisk.model.observer.Observer
import com.topjohnwu.magisk.utils.* import com.topjohnwu.magisk.utils.*
import com.topjohnwu.magisk.view.dialogs.FingerprintAuthDialog
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import io.reactivex.Completable import io.reactivex.Completable
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
...@@ -64,8 +62,8 @@ class SettingsFragment : BasePreferenceFragment() { ...@@ -64,8 +62,8 @@ class SettingsFragment : BasePreferenceFragment() {
multiuserConfig = findPreference(Config.Key.SU_MULTIUSER_MODE)!! multiuserConfig = findPreference(Config.Key.SU_MULTIUSER_MODE)!!
nsConfig = findPreference(Config.Key.SU_MNT_NS)!! nsConfig = findPreference(Config.Key.SU_MNT_NS)!!
val reauth = findPreference<SwitchPreferenceCompat>(Config.Key.SU_REAUTH)!! val reauth = findPreference<SwitchPreferenceCompat>(Config.Key.SU_REAUTH)!!
val fingerprint = findPreference<SwitchPreferenceCompat>(Config.Key.SU_FINGERPRINT)!! val biometric = findPreference<SwitchPreferenceCompat>(Config.Key.SU_BIOMETRIC)!!
val generalCatagory = findPreference<PreferenceCategory>("general")!! val generalCategory = findPreference<PreferenceCategory>("general")!!
val magiskCategory = findPreference<PreferenceCategory>("magisk")!! val magiskCategory = findPreference<PreferenceCategory>("magisk")!!
val suCategory = findPreference<PreferenceCategory>("superuser")!! val suCategory = findPreference<PreferenceCategory>("superuser")!!
val hideManager = findPreference<Preference>("hide")!! val hideManager = findPreference<Preference>("hide")!!
...@@ -91,16 +89,16 @@ class SettingsFragment : BasePreferenceFragment() { ...@@ -91,16 +89,16 @@ class SettingsFragment : BasePreferenceFragment() {
suCategory.removePreference(reauth) suCategory.removePreference(reauth)
} }
// Disable fingerprint option if not possible // Disable biometric option if not possible
if (!FingerprintHelper.canUseFingerprint()) { if (!BiometricHelper.isSupported) {
fingerprint.isEnabled = false biometric.isEnabled = false
fingerprint.isChecked = false biometric.isChecked = false
fingerprint.setSummary(R.string.disable_fingerprint) biometric.setSummary(R.string.no_biometric)
} }
if (Const.USER_ID == 0 && Info.isConnected.value && Shell.rootAccess()) { if (Const.USER_ID == 0 && Info.isConnected.value && Info.env.isActive) {
if (activity.packageName == BuildConfig.APPLICATION_ID) { if (activity.packageName == BuildConfig.APPLICATION_ID) {
generalCatagory.removePreference(restoreManager) generalCategory.removePreference(restoreManager)
hideManager.setOnPreferenceClickListener { hideManager.setOnPreferenceClickListener {
showManagerNameDialog { showManagerNameDialog {
PatchAPK.hideManager(requireContext(), it) PatchAPK.hideManager(requireContext(), it)
...@@ -108,7 +106,7 @@ class SettingsFragment : BasePreferenceFragment() { ...@@ -108,7 +106,7 @@ class SettingsFragment : BasePreferenceFragment() {
true true
} }
} else { } else {
generalCatagory.removePreference(hideManager) generalCategory.removePreference(hideManager)
restoreManager.setOnPreferenceClickListener { restoreManager.setOnPreferenceClickListener {
DownloadService(requireContext()) { DownloadService(requireContext()) {
subject = DownloadSubject.Manager(Configuration.APK.Restore) subject = DownloadSubject.Manager(Configuration.APK.Restore)
...@@ -118,25 +116,32 @@ class SettingsFragment : BasePreferenceFragment() { ...@@ -118,25 +116,32 @@ class SettingsFragment : BasePreferenceFragment() {
} }
} else { } else {
// Remove if not primary user, no connection, or no root // Remove if not primary user, no connection, or no root
generalCatagory.removePreference(restoreManager) generalCategory.removePreference(restoreManager)
generalCatagory.removePreference(hideManager) generalCategory.removePreference(hideManager)
} }
if (!Utils.showSuperUser()) { if (!Utils.showSuperUser()) {
preferenceScreen.removePreference(suCategory) preferenceScreen.removePreference(suCategory)
} }
if (!Shell.rootAccess()) { if (!Info.env.isActive) {
preferenceScreen.removePreference(magiskCategory) preferenceScreen.removePreference(magiskCategory)
generalCatagory.removePreference(hideManager) generalCategory.removePreference(hideManager)
} }
findPreference<Preference>("clear")?.setOnPreferenceClickListener { findPreference<Preference>("clear")?.also {
if (Info.env.isActive) {
it.setOnPreferenceClickListener {
Completable.fromAction { repoDB.clear() }.subscribeK { Completable.fromAction { repoDB.clear() }.subscribeK {
Utils.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT) Utils.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT)
} }
true true
} }
} else {
generalCategory.removePreference(it)
}
}
findPreference<Preference>("hosts")?.setOnPreferenceClickListener { findPreference<Preference>("hosts")?.setOnPreferenceClickListener {
Shell.su("add_hosts_module").submit { Shell.su("add_hosts_module").submit {
Utils.toast(R.string.settings_hosts_toast, Toast.LENGTH_SHORT) Utils.toast(R.string.settings_hosts_toast, Toast.LENGTH_SHORT)
...@@ -146,20 +151,21 @@ class SettingsFragment : BasePreferenceFragment() { ...@@ -146,20 +151,21 @@ class SettingsFragment : BasePreferenceFragment() {
findPreference<Preference>(Config.Key.DOWNLOAD_PATH)?.apply { findPreference<Preference>(Config.Key.DOWNLOAD_PATH)?.apply {
summary = Config.downloadPath summary = Config.downloadPath
}?.setOnPreferenceClickListener { preference -> setOnPreferenceClickListener { pref ->
activity.withExternalRW { activity.withExternalRW {
onSuccess { onSuccess {
showDownloadDialog { showDownloadDialog {
Config.downloadPath = it Config.downloadPath = it
preference.summary = it pref.summary = it
} }
} }
} }
true true
} }
}
updateChannel.setOnPreferenceChangeListener { _, value -> updateChannel.setOnPreferenceChangeListener { _, value ->
val channel = Integer.parseInt(value as String) val channel = value.toString().toInt()
val previous = Config.updateChannel val previous = Config.updateChannel
if (channel == Config.Value.CUSTOM_CHANNEL) { if (channel == Config.Value.CUSTOM_CHANNEL) {
...@@ -201,7 +207,7 @@ class SettingsFragment : BasePreferenceFragment() { ...@@ -201,7 +207,7 @@ class SettingsFragment : BasePreferenceFragment() {
Shell.su("magiskhide --disable").submit() Shell.su("magiskhide --disable").submit()
} }
Config.Key.LOCALE -> { Config.Key.LOCALE -> {
ResourceMgr.reload() refreshLocale()
activity.recreate() activity.recreate()
} }
Config.Key.CHECK_UPDATES -> Utils.scheduleUpdateCheck(activity) Config.Key.CHECK_UPDATES -> Utils.scheduleUpdateCheck(activity)
...@@ -211,13 +217,13 @@ class SettingsFragment : BasePreferenceFragment() { ...@@ -211,13 +217,13 @@ class SettingsFragment : BasePreferenceFragment() {
override fun onPreferenceTreeClick(preference: Preference): Boolean { override fun onPreferenceTreeClick(preference: Preference): Boolean {
when (preference.key) { when (preference.key) {
Config.Key.SU_FINGERPRINT -> { Config.Key.SU_BIOMETRIC -> {
val checked = (preference as SwitchPreferenceCompat).isChecked val checked = (preference as SwitchPreferenceCompat).isChecked
preference.isChecked = !checked preference.isChecked = !checked
FingerprintAuthDialog(requireActivity()) { BiometricHelper.authenticate(requireActivity()) {
preference.isChecked = checked preference.isChecked = checked
Config.suFingerprint = checked Config.suBiometric = checked
}.show() }
} }
} }
return true return true
...@@ -225,22 +231,7 @@ class SettingsFragment : BasePreferenceFragment() { ...@@ -225,22 +231,7 @@ class SettingsFragment : BasePreferenceFragment() {
private fun setLocalePreference(lp: ListPreference) { private fun setLocalePreference(lp: ListPreference) {
lp.isEnabled = false lp.isEnabled = false
availableLocales.map { availableLocales.subscribeK { (names, values) ->
val names = mutableListOf<String>()
val values = mutableListOf<String>()
names.add(
ResourceMgr.getString(defaultLocale, R.string.system_default)
)
values.add("")
it.forEach { locale ->
names.add(locale.getDisplayName(locale))
values.add(locale.toLangTag())
}
Pair(names.toTypedArray(), values.toTypedArray())
}.subscribeK { (names, values) ->
lp.isEnabled = true lp.isEnabled = true
lp.entries = names lp.entries = names
lp.entryValues = values lp.entryValues = values
......
...@@ -15,11 +15,10 @@ import com.topjohnwu.magisk.model.entity.recycler.PolicyRvItem ...@@ -15,11 +15,10 @@ import com.topjohnwu.magisk.model.entity.recycler.PolicyRvItem
import com.topjohnwu.magisk.model.events.PolicyEnableEvent import com.topjohnwu.magisk.model.events.PolicyEnableEvent
import com.topjohnwu.magisk.model.events.PolicyUpdateEvent import com.topjohnwu.magisk.model.events.PolicyUpdateEvent
import com.topjohnwu.magisk.model.events.SnackbarEvent import com.topjohnwu.magisk.model.events.SnackbarEvent
import com.topjohnwu.magisk.utils.BiometricHelper
import com.topjohnwu.magisk.utils.DiffObservableList import com.topjohnwu.magisk.utils.DiffObservableList
import com.topjohnwu.magisk.utils.FingerprintHelper
import com.topjohnwu.magisk.utils.RxBus import com.topjohnwu.magisk.utils.RxBus
import com.topjohnwu.magisk.view.dialogs.CustomAlertDialog import com.topjohnwu.magisk.view.dialogs.CustomAlertDialog
import com.topjohnwu.magisk.view.dialogs.FingerprintAuthDialog
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import me.tatarka.bindingcollectionadapter2.ItemBinding import me.tatarka.bindingcollectionadapter2.ItemBinding
...@@ -42,6 +41,11 @@ class SuperuserViewModel( ...@@ -42,6 +41,11 @@ class SuperuserViewModel(
init { init {
rxBus.register<PolicyEnableEvent>() rxBus.register<PolicyEnableEvent>()
.filter {
val isIgnored = it.item == ignoreNext
if (isIgnored) ignoreNext = null
!isIgnored
}
.subscribeK { togglePolicy(it.item, it.enable) } .subscribeK { togglePolicy(it.item, it.enable) }
.add() .add()
rxBus.register<PolicyUpdateEvent>() rxBus.register<PolicyUpdateEvent>()
...@@ -78,8 +82,8 @@ class SuperuserViewModel( ...@@ -78,8 +82,8 @@ class SuperuserViewModel(
.add() .add()
withView { withView {
if (FingerprintHelper.useFingerprint()) { if (BiometricHelper.isEnabled) {
FingerprintAuthDialog(this) { updateState() }.show() BiometricHelper.authenticate(this) { updateState() }
} else { } else {
CustomAlertDialog(this) CustomAlertDialog(this)
.setTitle(R.string.su_revoke_title) .setTitle(R.string.su_revoke_title)
...@@ -126,12 +130,12 @@ class SuperuserViewModel( ...@@ -126,12 +130,12 @@ class SuperuserViewModel(
.add() .add()
} }
if (FingerprintHelper.useFingerprint()) { if (BiometricHelper.isEnabled) {
withView { withView {
FingerprintAuthDialog(this, { updateState() }, { BiometricHelper.authenticate(this, onError = {
ignoreNext = item ignoreNext = item
item.isEnabled.toggle() item.isEnabled.toggle()
}).show() }) { updateState() }
} }
} else { } else {
updateState() updateState()
......
package com.topjohnwu.magisk.ui.surequest package com.topjohnwu.magisk.ui.surequest
import android.content.Intent
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
...@@ -7,11 +8,11 @@ import android.view.Window ...@@ -7,11 +8,11 @@ import android.view.Window
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.base.BaseActivity import com.topjohnwu.magisk.base.BaseActivity
import com.topjohnwu.magisk.databinding.ActivityRequestBinding import com.topjohnwu.magisk.databinding.ActivityRequestBinding
import com.topjohnwu.magisk.model.entity.MagiskPolicy
import com.topjohnwu.magisk.model.events.DieEvent import com.topjohnwu.magisk.model.events.DieEvent
import com.topjohnwu.magisk.model.events.ViewActionEvent
import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.model.events.ViewEvent
import com.topjohnwu.magisk.model.receiver.GeneralReceiver import com.topjohnwu.magisk.utils.SuHandler
import com.topjohnwu.magisk.utils.SuLogger import com.topjohnwu.magisk.utils.SuHandler.REQUEST
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
open class SuRequestActivity : BaseActivity<SuRequestViewModel, ActivityRequestBinding>() { open class SuRequestActivity : BaseActivity<SuRequestViewModel, ActivityRequestBinding>() {
...@@ -21,7 +22,7 @@ open class SuRequestActivity : BaseActivity<SuRequestViewModel, ActivityRequestB ...@@ -21,7 +22,7 @@ open class SuRequestActivity : BaseActivity<SuRequestViewModel, ActivityRequestB
override val viewModel: SuRequestViewModel by viewModel() override val viewModel: SuRequestViewModel by viewModel()
override fun onBackPressed() { override fun onBackPressed() {
viewModel.handler?.handleAction(MagiskPolicy.DENY, -1) viewModel.denyPressed()
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
...@@ -29,24 +30,34 @@ open class SuRequestActivity : BaseActivity<SuRequestViewModel, ActivityRequestB ...@@ -29,24 +30,34 @@ open class SuRequestActivity : BaseActivity<SuRequestViewModel, ActivityRequestB
lockOrientation() lockOrientation()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val intent = intent fun showRequest() {
when (intent?.action) {
GeneralReceiver.REQUEST -> {
if (!viewModel.handleRequest(intent)) if (!viewModel.handleRequest(intent))
finish() finish()
return
}
GeneralReceiver.LOG -> SuLogger.handleLogs(this, intent)
GeneralReceiver.NOTIFY -> SuLogger.handleNotify(this, intent)
} }
fun runHandler(action: String?) {
SuHandler(this, action, intent.extras)
finish() finish()
} }
if (intent.action == Intent.ACTION_VIEW) {
val action = intent.getStringExtra("action")
if (action == REQUEST) {
showRequest()
} else {
runHandler(action)
}
} else if (intent.action == REQUEST) {
showRequest()
} else {
runHandler(intent.action)
}
}
override fun onEventDispatched(event: ViewEvent) { override fun onEventDispatched(event: ViewEvent) {
super.onEventDispatched(event) super.onEventDispatched(event)
when (event) { when (event) {
is ViewActionEvent -> event.action(this)
is DieEvent -> finish() is DieEvent -> finish()
} }
} }
......
package com.topjohnwu.magisk.ui.surequest package com.topjohnwu.magisk.ui.surequest
import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Resources import android.content.res.Resources
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.hardware.fingerprint.FingerprintManager
import android.os.CountDownTimer import android.os.CountDownTimer
import android.text.TextUtils
import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.base.viewmodel.BaseViewModel import com.topjohnwu.magisk.base.viewmodel.BaseViewModel
import com.topjohnwu.magisk.data.database.PolicyDao import com.topjohnwu.magisk.data.database.PolicyDao
import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.databinding.ComparableRvItem
import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback
import com.topjohnwu.magisk.extensions.now import com.topjohnwu.magisk.extensions.now
import com.topjohnwu.magisk.model.entity.MagiskPolicy import com.topjohnwu.magisk.model.entity.MagiskPolicy
import com.topjohnwu.magisk.model.entity.recycler.SpinnerRvItem import com.topjohnwu.magisk.model.entity.recycler.SpinnerRvItem
import com.topjohnwu.magisk.model.entity.toPolicy import com.topjohnwu.magisk.model.entity.toPolicy
import com.topjohnwu.magisk.model.events.DieEvent import com.topjohnwu.magisk.model.events.DieEvent
import com.topjohnwu.magisk.utils.BiometricHelper
import com.topjohnwu.magisk.utils.DiffObservableList import com.topjohnwu.magisk.utils.DiffObservableList
import com.topjohnwu.magisk.utils.FingerprintHelper
import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.magisk.utils.KObservableField
import com.topjohnwu.magisk.utils.SuConnector import com.topjohnwu.magisk.utils.SuConnector
import me.tatarka.bindingcollectionadapter2.BindingListViewAdapter import me.tatarka.bindingcollectionadapter2.BindingListViewAdapter
...@@ -45,7 +42,6 @@ class SuRequestViewModel( ...@@ -45,7 +42,6 @@ class SuRequestViewModel(
val denyText = KObservableField(resources.getString(R.string.deny)) val denyText = KObservableField(resources.getString(R.string.deny))
val warningText = KObservableField<CharSequence>(resources.getString(R.string.su_warning)) val warningText = KObservableField<CharSequence>(resources.getString(R.string.su_warning))
val canUseFingerprint = KObservableField(FingerprintHelper.useFingerprint())
val selectedItemPosition = KObservableField(0) val selectedItemPosition = KObservableField(0)
private val items = DiffObservableList(ComparableRvItem.callback) private val items = DiffObservableList(ComparableRvItem.callback)
...@@ -58,48 +54,33 @@ class SuRequestViewModel( ...@@ -58,48 +54,33 @@ class SuRequestViewModel(
setItems(items) setItems(items)
} }
private val cancelTasks = mutableListOf<() -> Unit>()
var handler: ActionHandler? = null private lateinit var timer: CountDownTimer
private var timer: CountDownTimer? = null private lateinit var policy: MagiskPolicy
private var policy: MagiskPolicy? = null private lateinit var connector: SuConnector
set(value) {
field = value
updatePolicy(value)
}
init {
resources.getStringArray(R.array.allow_timeout)
.map { SpinnerRvItem(it) }
.let { items.update(it) }
selectedItemPosition.addOnPropertyChangedCallback {
Timber.e("Changed position to $it")
}
}
private fun updatePolicy(policy: MagiskPolicy?) {
policy ?: return
icon.value = policy.applicationInfo.loadIcon(packageManager)
title.value = policy.appName
packageName.value = policy.packageName
selectedItemPosition.value = timeoutPrefs.getInt(policy.packageName, 0)
}
private fun cancelTimer() { private fun cancelTimer() {
timer?.cancel() timer.cancel()
denyText.value = resources.getString(R.string.deny) denyText.value = resources.getString(R.string.deny)
} }
fun grantPressed() { fun grantPressed() {
handler?.handleAction(MagiskPolicy.ALLOW) cancelTimer()
timer?.cancel() if (BiometricHelper.isEnabled) {
withView {
BiometricHelper.authenticate(this) {
handleAction(MagiskPolicy.ALLOW)
}
}
} else {
handleAction(MagiskPolicy.ALLOW)
}
} }
fun denyPressed() { fun denyPressed() {
handler?.handleAction(MagiskPolicy.DENY) handleAction(MagiskPolicy.DENY)
timer?.cancel() timer.cancel()
} }
fun spinnerTouched(): Boolean { fun spinnerTouched(): Boolean {
...@@ -110,75 +91,27 @@ class SuRequestViewModel( ...@@ -110,75 +91,27 @@ class SuRequestViewModel(
fun handleRequest(intent: Intent): Boolean { fun handleRequest(intent: Intent): Boolean {
val socketName = intent.getStringExtra("socket") ?: return false val socketName = intent.getStringExtra("socket") ?: return false
val connector: SuConnector
try { try {
connector = object : SuConnector(socketName) { connector = Connector(socketName)
@Throws(IOException::class) val map = connector.readRequest()
override fun onResponse() { val uid = map["uid"]?.toIntOrNull() ?: return false
out.writeInt(policy?.policy ?: return) policy = uid.toPolicy(packageManager)
} } catch (e: Exception) {
} Timber.e(e)
val bundle = connector.readSocketInput()
val uid = bundle.getString("uid")?.toIntOrNull() ?: return false
policyDB.deleteOutdated().blockingGet() // wrong!
policy = runCatching { policyDB.fetch(uid).blockingGet() }
.getOrDefault(uid.toPolicy(packageManager))
} catch (e: IOException) {
e.printStackTrace()
return false
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
return false return false
} }
handler = object : ActionHandler() {
override fun handleAction() {
connector.response()
done()
}
@SuppressLint("ApplySharedPref")
override fun handleAction(action: Int) {
val pos = selectedItemPosition.value
timeoutPrefs.edit().putInt(policy?.packageName, pos).commit()
handleAction(action, Config.Value.TIMEOUT_LIST[pos])
}
override fun handleAction(action: Int, time: Int) {
val until = if (time >= 0) {
if (time == 0) {
0
} else {
MILLISECONDS.toSeconds(now) + MINUTES.toSeconds(time.toLong())
}
} else {
policy?.until ?: 0
}
policy = policy?.copy(policy = action, until = until)?.apply {
policyDB.update(this).blockingGet()
}
handleAction()
}
}
// Never allow com.topjohnwu.magisk (could be malware) // Never allow com.topjohnwu.magisk (could be malware)
if (TextUtils.equals(policy?.packageName, BuildConfig.APPLICATION_ID)) if (policy.packageName == BuildConfig.APPLICATION_ID)
return false return false
// If not interactive, response directly
if (policy?.policy != MagiskPolicy.INTERACTIVE) {
handler?.handleAction()
return true
}
when (Config.suAutoReponse) { when (Config.suAutoReponse) {
Config.Value.SU_AUTO_DENY -> { Config.Value.SU_AUTO_DENY -> {
handler?.handleAction(MagiskPolicy.DENY, 0) handleAction(MagiskPolicy.DENY, 0)
return true return true
} }
Config.Value.SU_AUTO_ALLOW -> { Config.Value.SU_AUTO_ALLOW -> {
handler?.handleAction(MagiskPolicy.ALLOW, 0) handleAction(MagiskPolicy.ALLOW, 0)
return true return true
} }
} }
...@@ -187,77 +120,64 @@ class SuRequestViewModel( ...@@ -187,77 +120,64 @@ class SuRequestViewModel(
return true return true
} }
@SuppressLint("ClickableViewAccessibility")
private fun showUI() { private fun showUI() {
resources.getStringArray(R.array.allow_timeout)
.map { SpinnerRvItem(it) }
.let { items.update(it) }
icon.value = policy.applicationInfo.loadIcon(packageManager)
title.value = policy.appName
packageName.value = policy.packageName
selectedItemPosition.value = timeoutPrefs.getInt(policy.packageName, 0)
val millis = SECONDS.toMillis(Config.suDefaultTimeout.toLong()) val millis = SECONDS.toMillis(Config.suDefaultTimeout.toLong())
timer = object : CountDownTimer(millis, 1000) { timer = object : CountDownTimer(millis, 1000) {
override fun onTick(remains: Long) { override fun onTick(remains: Long) {
denyText.value = "%s (%d)" denyText.value = "${resources.getString(R.string.deny)} (${remains / 1000})"
.format(resources.getString(R.string.deny), remains / 1000)
} }
override fun onFinish() { override fun onFinish() {
denyText.value = resources.getString(R.string.deny) denyText.value = resources.getString(R.string.deny)
handler?.handleAction(MagiskPolicy.DENY) handleAction(MagiskPolicy.DENY)
}
}
timer?.start()
handler?.addCancel(Runnable { cancelTimer() })
val useFP = canUseFingerprint.value
if (useFP)
try {
val helper = SuFingerprint()
helper.authenticate()
handler?.addCancel(Runnable { helper.cancel() })
} catch (e: Exception) {
e.printStackTrace()
}
} }
private inner class SuFingerprint @Throws(Exception::class)
internal constructor() : FingerprintHelper() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
warningText.value = errString
} }
timer.start()
override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence) { cancelTasks.add { cancelTimer() }
warningText.value = helpString
} }
override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult) { private fun handleAction() {
handler?.handleAction(MagiskPolicy.ALLOW) connector.response()
cancelTasks.forEach { it() }
DieEvent().publish()
} }
override fun onAuthenticationFailed() { private fun handleAction(action: Int) {
warningText.value = resources.getString(R.string.auth_fail) val pos = selectedItemPosition.value
} timeoutPrefs.edit().putInt(policy.packageName, pos).apply()
handleAction(action, Config.Value.TIMEOUT_LIST[pos])
} }
open inner class ActionHandler { private fun handleAction(action: Int, time: Int) {
private val cancelTasks = mutableListOf<Runnable>() val until = if (time > 0)
MILLISECONDS.toSeconds(now) + MINUTES.toSeconds(time.toLong())
internal open fun handleAction() { else
done() time.toLong()
}
internal open fun handleAction(action: Int) { policy.policy = action
done() policy.until = until
} policy.uid = policy.uid % 100000 + Const.USER_ID * 100000
internal open fun handleAction(action: Int, time: Int) { if (until >= 0)
done() policyDB.update(policy).blockingAwait()
}
internal fun addCancel(r: Runnable) { handleAction()
cancelTasks.add(r)
} }
internal fun done() { private inner class Connector @Throws(Exception::class)
cancelTasks.forEach { it.run() } internal constructor(name: String) : SuConnector(name) {
DieEvent().publish() @Throws(IOException::class)
override fun onResponse() {
out.writeInt(policy.policy)
} }
} }
......
package com.topjohnwu.magisk.utils
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.R
import org.koin.core.KoinComponent
import org.koin.core.get
object BiometricHelper: KoinComponent {
private val mgr by lazy { BiometricManager.from(get()) }
val isSupported get() = when (mgr.canAuthenticate()) {
BiometricManager.BIOMETRIC_SUCCESS -> true
else -> false
}
val isEnabled: Boolean get() {
val enabled = Config.suBiometric
if (enabled && !isSupported) {
Config.suBiometric = false
return false
}
return enabled
}
fun authenticate(
activity: FragmentActivity,
onError: () -> Unit = {},
onSuccess: () -> Unit): BiometricPrompt {
val prompt = BiometricPrompt(activity,
ContextCompat.getMainExecutor(activity),
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
onError()
}
override fun onAuthenticationFailed() {
onError()
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
onSuccess()
}
}
)
val info = BiometricPrompt.PromptInfo.Builder()
.setConfirmationRequired(true)
.setDeviceCredentialAllowed(false)
.setTitle(activity.getString(R.string.authenticate))
.setNegativeButtonText(activity.getString(android.R.string.cancel))
.build()
prompt.authenticate(info)
return prompt
}
}
package com.topjohnwu.magisk.utils
import android.annotation.TargetApi
import android.app.KeyguardManager
import android.content.Context
import android.hardware.fingerprint.FingerprintManager
import android.os.Build
import android.os.CancellationSignal
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.extensions.get
import com.topjohnwu.magisk.extensions.inject
import java.security.KeyStore
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey
@TargetApi(Build.VERSION_CODES.M)
abstract class FingerprintHelper @Throws(Exception::class)
protected constructor() {
private val manager: FingerprintManager?
private val cipher: Cipher
private var cancel: CancellationSignal? = null
private val context: Context by inject()
init {
val keyStore = KeyStore.getInstance("AndroidKeyStore")
manager = context.getSystemService(FingerprintManager::class.java)
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7)
keyStore.load(null)
var key = keyStore.getKey(SU_KEYSTORE_KEY, null) as SecretKey? ?: generateKey()
runCatching {
cipher.init(Cipher.ENCRYPT_MODE, key)
}.onFailure {
// Only happens on Marshmallow
key = generateKey()
cipher.init(Cipher.ENCRYPT_MODE, key)
}
}
abstract fun onAuthenticationError(errorCode: Int, errString: CharSequence)
abstract fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence)
abstract fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult)
abstract fun onAuthenticationFailed()
fun authenticate() {
cancel = CancellationSignal()
val cryptoObject = FingerprintManager.CryptoObject(cipher)
manager!!.authenticate(cryptoObject, cancel, 0, Callback(), null)
}
fun cancel() {
if (cancel != null)
cancel!!.cancel()
}
@Throws(Exception::class)
private fun generateKey(): SecretKey {
val keygen = KeyGenerator
.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
val builder = KeyGenParameterSpec.Builder(
SU_KEYSTORE_KEY,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
builder.setInvalidatedByBiometricEnrollment(false)
}
keygen.init(builder.build())
return keygen.generateKey()
}
private inner class Callback : FingerprintManager.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
this@FingerprintHelper.onAuthenticationError(errorCode, errString)
}
override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence) {
this@FingerprintHelper.onAuthenticationHelp(helpCode, helpString)
}
override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult) {
this@FingerprintHelper.onAuthenticationSucceeded(result)
}
override fun onAuthenticationFailed() {
this@FingerprintHelper.onAuthenticationFailed()
}
}
companion object {
private const val SU_KEYSTORE_KEY = "su_key"
fun useFingerprint(): Boolean {
var fp = Config.suFingerprint
if (fp && !canUseFingerprint()) {
Config.suFingerprint = false
fp = false
}
return fp
}
fun canUseFingerprint(context: Context = get()): Boolean {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
return false
val km = context.getSystemService(KeyguardManager::class.java)
val fm = context.getSystemService(FingerprintManager::class.java)
return km?.isKeyguardSecure ?: false &&
fm != null && fm.isHardwareDetected && fm.hasEnrolledFingerprints()
}
}
}
...@@ -5,6 +5,7 @@ import android.util.Base64 ...@@ -5,6 +5,7 @@ import android.util.Base64
import android.util.Base64OutputStream import android.util.Base64OutputStream
import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.di.koinModules import com.topjohnwu.magisk.di.koinModules
import com.topjohnwu.magisk.utils.PatchAPK.ALPHANUM
import com.topjohnwu.signing.CryptoUtils.readCertificate import com.topjohnwu.signing.CryptoUtils.readCertificate
import com.topjohnwu.signing.CryptoUtils.readPrivateKey import com.topjohnwu.signing.CryptoUtils.readPrivateKey
import com.topjohnwu.superuser.internal.InternalUtils import com.topjohnwu.superuser.internal.InternalUtils
...@@ -38,7 +39,6 @@ class Keygen: CertKeyProvider { ...@@ -38,7 +39,6 @@ class Keygen: CertKeyProvider {
private const val ALIAS = "magisk" private const val ALIAS = "magisk"
private val PASSWORD get() = "magisk".toCharArray() private val PASSWORD get() = "magisk".toCharArray()
private const val TESTKEY_CERT = "61ed377e85d386a8dfee6b864bd85b0bfaa5af81" private const val TESTKEY_CERT = "61ed377e85d386a8dfee6b864bd85b0bfaa5af81"
private const val DNAME = "C=US,ST=California,L=Mountain View,O=Google Inc.,OU=Android,CN=Android"
private const val BASE64_FLAG = Base64.NO_PADDING or Base64.NO_WRAP private const val BASE64_FLAG = Base64.NO_PADDING or Base64.NO_WRAP
} }
...@@ -88,6 +88,17 @@ class Keygen: CertKeyProvider { ...@@ -88,6 +88,17 @@ class Keygen: CertKeyProvider {
} }
} }
private fun randomString(): String {
val rand = kotlin.random.Random.Default
val len = rand.nextInt(5, 10)
val sb = StringBuilder(len)
for (i in 0..len) {
val idx = rand.nextInt(ALPHANUM.length)
sb.append(ALPHANUM[idx])
}
return sb.toString()
}
private fun init(): KeyStore { private fun init(): KeyStore {
GlobalContext.getOrNull() ?: { GlobalContext.getOrNull() ?: {
// Invoked externally, do some basic initialization // Invoked externally, do some basic initialization
...@@ -113,7 +124,7 @@ class Keygen: CertKeyProvider { ...@@ -113,7 +124,7 @@ class Keygen: CertKeyProvider {
// Generate new private key and certificate // Generate new private key and certificate
val kp = KeyPairGenerator.getInstance("RSA").apply { initialize(4096) }.genKeyPair() val kp = KeyPairGenerator.getInstance("RSA").apply { initialize(4096) }.genKeyPair()
val dname = X500Name(DNAME) val dname = X500Name("CN=${randomString()}")
val builder = JcaX509v3CertificateBuilder(dname, BigInteger(160, Random()), val builder = JcaX509v3CertificateBuilder(dname, BigInteger(160, Random()),
start.time, end.time, dname, kp.public) start.time, end.time, dname, kp.public)
val signer = JcaContentSignerBuilder("SHA256WithRSA").build(kp.private) val signer = JcaContentSignerBuilder("SHA256WithRSA").build(kp.private)
......
@file:Suppress("DEPRECATION")
package com.topjohnwu.magisk.utils package com.topjohnwu.magisk.utils
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.res.Configuration import android.content.res.Configuration
import android.content.res.Resources import android.content.res.Resources
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.ResourceMgr import com.topjohnwu.magisk.ResourceMgr
import com.topjohnwu.magisk.extensions.langTagToLocale import com.topjohnwu.magisk.extensions.langTagToLocale
import com.topjohnwu.magisk.extensions.toLangTag
import io.reactivex.Single import io.reactivex.Single
import java.util.* import java.util.*
import kotlin.Comparator import kotlin.Comparator
import kotlin.collections.ArrayList
var currentLocale: Locale = Locale.getDefault() var currentLocale: Locale = Locale.getDefault()
@SuppressLint("ConstantLocale") @SuppressLint("ConstantLocale")
val defaultLocale: Locale = Locale.getDefault() val defaultLocale: Locale = Locale.getDefault()
@Suppress("DEPRECATION")
val availableLocales = Single.fromCallable { val availableLocales = Single.fromCallable {
val compareId = R.string.app_changelog val compareId = R.string.app_changelog
mutableListOf<Locale>().apply { val config = ResourceMgr.resource.configuration
val metrics = ResourceMgr.resource.displayMetrics
val res = Resources(ResourceMgr.resource.assets, metrics, config)
val locales = mutableListOf<Locale>().apply {
// Add default locale // Add default locale
add(Locale.ENGLISH) add(Locale.ENGLISH)
...@@ -26,10 +34,6 @@ val availableLocales = Single.fromCallable { ...@@ -26,10 +34,6 @@ val availableLocales = Single.fromCallable {
add(Locale.TAIWAN) add(Locale.TAIWAN)
add(Locale("pt", "BR")) add(Locale("pt", "BR"))
val config = Configuration()
val metrics = ResourceMgr.resource.displayMetrics
val res = Resources(ResourceMgr.resource.assets, metrics, config)
// Other locales // Other locales
val otherLocales = ResourceMgr.resource.assets.locales val otherLocales = ResourceMgr.resource.assets.locales
.map { it.langTagToLocale() } .map { it.langTagToLocale() }
...@@ -39,11 +43,47 @@ val availableLocales = Single.fromCallable { ...@@ -39,11 +43,47 @@ val availableLocales = Single.fromCallable {
res.getString(compareId) res.getString(compareId)
} }
listOf("", "").toTypedArray()
addAll(otherLocales) addAll(otherLocales)
}.sortedWith(Comparator { a, b -> }.sortedWith(Comparator { a, b ->
a.getDisplayName(a).toLowerCase(a) a.getDisplayName(a).toLowerCase(a)
.compareTo(b.getDisplayName(b).toLowerCase(b)) .compareTo(b.getDisplayName(b).toLowerCase(b))
}) })
config.setLocale(defaultLocale)
res.updateConfiguration(config, metrics)
val defName = res.getString(R.string.system_default)
// Restore back to current locale
config.setLocale(currentLocale)
res.updateConfiguration(config, metrics)
Pair(locales, defName)
}.map { (locales, defName) ->
val names = ArrayList<String>(locales.size + 1)
val values = ArrayList<String>(locales.size + 1)
names.add(defName)
values.add("")
locales.forEach { locale ->
names.add(locale.getDisplayName(locale))
values.add(locale.toLangTag())
}
Pair(names.toTypedArray(), values.toTypedArray())
}.cache()!! }.cache()!!
fun Resources.updateConfig(config: Configuration = configuration) {
config.setLocale(currentLocale)
updateConfiguration(config, displayMetrics)
}
fun refreshLocale() {
val localeConfig = Config.locale
currentLocale = when {
localeConfig.isEmpty() -> defaultLocale
else -> localeConfig.langTagToLocale()
}
Locale.setDefault(currentLocale)
ResourceMgr.resource.updateConfig()
}
...@@ -27,7 +27,7 @@ object PatchAPK { ...@@ -27,7 +27,7 @@ object PatchAPK {
private val UPPERALPHA = LOWERALPHA.toUpperCase() private val UPPERALPHA = LOWERALPHA.toUpperCase()
private val ALPHA = LOWERALPHA + UPPERALPHA private val ALPHA = LOWERALPHA + UPPERALPHA
private const val DIGITS = "0123456789" private const val DIGITS = "0123456789"
private val ALPHANUM = ALPHA + DIGITS val ALPHANUM = ALPHA + DIGITS
private val ALPHANUMDOTS = "$ALPHANUM............" private val ALPHANUMDOTS = "$ALPHANUM............"
private fun genPackageName(prefix: String, length: Int): String { private fun genPackageName(prefix: String, length: Int): String {
...@@ -77,8 +77,9 @@ object PatchAPK { ...@@ -77,8 +77,9 @@ object PatchAPK {
} }
private fun patchAndHide(context: Context, label: String): Boolean { private fun patchAndHide(context: Context, label: String): Boolean {
val src = if (!isRunningAsStub && SDK_INT >= 28 &&
Info.env.magiskVersionCode >= Const.Version.PROVIDER_CONNECT) {
// If not running as stub, and we are compatible with stub, use stub // If not running as stub, and we are compatible with stub, use stub
val src = if (!isRunningAsStub && SDK_INT >= 28 && Info.env.connectionMode == 3) {
val stub = File(context.cacheDir, "stub.apk") val stub = File(context.cacheDir, "stub.apk")
val svc = get<GithubRawServices>() val svc = get<GithubRawServices>()
runCatching { runCatching {
...@@ -118,7 +119,7 @@ object PatchAPK { ...@@ -118,7 +119,7 @@ object PatchAPK {
@JvmOverloads @JvmOverloads
fun patch(apk: String, out: String, pkg: String, label: String = "Manager"): Boolean { fun patch(apk: String, out: String, pkg: String, label: String = "Manager"): Boolean {
try { try {
val jar = JarMap(apk) val jar = JarMap.open(apk)
val je = jar.getJarEntry(Const.ANDROID_MANIFEST) val je = jar.getJarEntry(Const.ANDROID_MANIFEST)
val xml = jar.getRawData(je) val xml = jar.getRawData(je)
......
...@@ -2,11 +2,9 @@ package com.topjohnwu.magisk.utils ...@@ -2,11 +2,9 @@ package com.topjohnwu.magisk.utils
import android.net.LocalSocket import android.net.LocalSocket
import android.net.LocalSocketAddress import android.net.LocalSocketAddress
import android.os.Bundle import androidx.collection.ArrayMap
import android.text.TextUtils
import timber.log.Timber import timber.log.Timber
import java.io.* import java.io.*
import java.nio.charset.Charset
abstract class SuConnector @Throws(IOException::class) abstract class SuConnector @Throws(IOException::class)
protected constructor(name: String) { protected constructor(name: String) {
...@@ -21,24 +19,23 @@ protected constructor(name: String) { ...@@ -21,24 +19,23 @@ protected constructor(name: String) {
input = DataInputStream(BufferedInputStream(socket.inputStream)) input = DataInputStream(BufferedInputStream(socket.inputStream))
} }
@Throws(IOException::class)
private fun readString(): String { private fun readString(): String {
val len = input.readInt() val len = input.readInt()
val buf = ByteArray(len) val buf = ByteArray(len)
input.readFully(buf) input.readFully(buf)
return String(buf, Charset.forName("UTF-8")) return String(buf, Charsets.UTF_8)
} }
@Throws(IOException::class) @Throws(IOException::class)
fun readSocketInput(): Bundle { fun readRequest(): Map<String, String> {
val bundle = Bundle() val ret = ArrayMap<String, String>()
while (true) { while (true) {
val name = readString() val name = readString()
if (TextUtils.equals(name, "eof")) if (name == "eof")
break break
bundle.putString(name, readString()) ret[name] = readString()
} }
return bundle return ret
} }
fun response() { fun response() {
......
package com.topjohnwu.magisk.utils
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.os.Process
import android.widget.Toast
import com.topjohnwu.magisk.*
import com.topjohnwu.magisk.data.repository.LogRepository
import com.topjohnwu.magisk.extensions.get
import com.topjohnwu.magisk.extensions.startActivity
import com.topjohnwu.magisk.extensions.startActivityWithRoot
import com.topjohnwu.magisk.extensions.subscribeK
import com.topjohnwu.magisk.model.entity.MagiskPolicy
import com.topjohnwu.magisk.model.entity.toLog
import com.topjohnwu.magisk.model.entity.toPolicy
import com.topjohnwu.magisk.ui.surequest.SuRequestActivity
import com.topjohnwu.superuser.Shell
import timber.log.Timber
object SuHandler : ProviderCallHandler {
const val REQUEST = "request"
const val LOG = "log"
const val NOTIFY = "notify"
const val TEST = "test"
override fun call(context: Context, method: String, arg: String?, extras: Bundle?): Bundle? {
invoke(context.wrap(), method, extras)
return null
}
operator fun invoke(context: Context, action: String?, data: Bundle?) {
data ?: return
// Debug messages
if (BuildConfig.DEBUG) {
Timber.d(action)
data.let { bundle ->
bundle.keySet().forEach {
Timber.d("[%s]=[%s]", it, bundle[it])
}
}
}
when (action) {
REQUEST -> {
val intent = context.intent<SuRequestActivity>()
.setAction(action)
.putExtras(data)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
if (Build.VERSION.SDK_INT >= 29) {
// Android Q does not allow starting activity from background
intent.startActivityWithRoot()
} else {
intent.startActivity(context)
}
}
LOG -> handleLogs(context, data)
NOTIFY -> handleNotify(context, data)
TEST -> {
val mode = data.getInt("mode", 2)
Shell.su(
"magisk --connect-mode $mode",
"magisk --use-broadcast"
).submit()
}
}
}
private fun Any?.toInt(): Int? {
return when (this) {
is Int -> this
is Long -> this.toInt()
else -> null
}
}
private fun handleLogs(context: Context, data: Bundle) {
val fromUid = data["from.uid"].toInt() ?: return
if (fromUid == Process.myUid())
return
val pm = context.packageManager
val notify = data.getBoolean("notify", true)
val allow = data["policy"].toInt() ?: return
val policy = runCatching { fromUid.toPolicy(pm, allow) }.getOrElse { return }
if (notify)
notify(context, policy)
val toUid = data["to.uid"].toInt() ?: return
val pid = data["pid"].toInt() ?: return
val command = data.getString("command") ?: return
val log = policy.toLog(
toUid = toUid,
fromPid = pid,
command = command
)
val logRepo = get<LogRepository>()
logRepo.insert(log).subscribeK(onError = { Timber.e(it) })
}
private fun handleNotify(context: Context, data: Bundle) {
val fromUid = data["from.uid"].toInt() ?: return
if (fromUid == Process.myUid())
return
val pm = context.packageManager
val allow = data["policy"].toInt() ?: return
runCatching {
val policy = fromUid.toPolicy(pm, allow)
if (policy.policy >= 0)
notify(context, policy)
}
}
private fun notify(context: Context, policy: MagiskPolicy) {
if (policy.notification && Config.suNotification == Config.Value.NOTIFICATION_TOAST) {
val resId = if (policy.policy == MagiskPolicy.ALLOW)
R.string.su_allow_toast
else
R.string.su_deny_toast
Utils.toast(context.getString(resId, policy.appName), Toast.LENGTH_SHORT)
}
}
}
package com.topjohnwu.magisk.utils
import android.content.Context
import android.content.Intent
import android.os.Process
import android.widget.Toast
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.data.database.PolicyDao
import com.topjohnwu.magisk.data.repository.LogRepository
import com.topjohnwu.magisk.extensions.get
import com.topjohnwu.magisk.model.entity.MagiskPolicy
import com.topjohnwu.magisk.model.entity.toLog
import com.topjohnwu.magisk.model.entity.toPolicy
import java.util.*
object SuLogger {
fun handleLogs(context: Context, intent: Intent) {
val fromUid = intent.getIntExtra("from.uid", -1)
if (fromUid < 0) return
if (fromUid == Process.myUid()) return
val pm = context.packageManager
val notify: Boolean
val data = intent.extras
val policy: MagiskPolicy = if (data!!.containsKey("notify")) {
notify = data.getBoolean("notify")
runCatching {
fromUid.toPolicy(pm)
}.getOrElse { return }
} else {
// Doesn't report whether notify or not, check database ourselves
val policyDB = get<PolicyDao>()
val policy = policyDB.fetch(fromUid).blockingGet() ?: return
notify = policy.notification
policy
}.copy(policy = data.getInt("policy", -1))
if (policy.policy < 0)
return
if (notify)
handleNotify(context, policy)
val toUid = intent.getIntExtra("to.uid", -1)
if (toUid < 0) return
val pid = intent.getIntExtra("pid", -1)
if (pid < 0) return
val command = intent.getStringExtra("command") ?: return
val log = policy.toLog(
toUid = toUid,
fromPid = pid,
command = command,
date = Date()
)
val logRepo = get<LogRepository>()
logRepo.put(log).blockingGet()?.printStackTrace()
}
private fun handleNotify(context: Context, policy: MagiskPolicy) {
if (policy.notification && Config.suNotification == Config.Value.NOTIFICATION_TOAST) {
Utils.toast(
context.getString(
if (policy.policy == MagiskPolicy.ALLOW)
R.string.su_allow_toast
else
R.string.su_deny_toast, policy.appName
),
Toast.LENGTH_SHORT
)
}
}
fun handleNotify(context: Context, intent: Intent) {
val fromUid = intent.getIntExtra("from.uid", -1)
if (fromUid < 0) return
if (fromUid == Process.myUid()) return
runCatching {
val pm = context.packageManager
val policy = fromUid.toPolicy(pm)
.copy(policy = intent.getIntExtra("policy", -1))
if (policy.policy >= 0)
handleNotify(context, policy)
}
}
}
...@@ -11,7 +11,6 @@ import com.topjohnwu.magisk.* ...@@ -11,7 +11,6 @@ import com.topjohnwu.magisk.*
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.extensions.get import com.topjohnwu.magisk.extensions.get
import com.topjohnwu.magisk.model.update.UpdateCheckService import com.topjohnwu.magisk.model.update.UpdateCheckService
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.internal.UiThreadHandler import com.topjohnwu.superuser.internal.UiThreadHandler
import java.io.File import java.io.File
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
...@@ -34,7 +33,7 @@ object Utils { ...@@ -34,7 +33,7 @@ object Utils {
} }
fun showSuperUser(): Boolean { fun showSuperUser(): Boolean {
return Shell.rootAccess() && (Const.USER_ID == 0 return Info.env.isActive && (Const.USER_ID == 0
|| Config.suMultiuserMode != Config.Value.MULTIUSER_MODE_OWNER_MANAGED) || Config.suMultiuserMode != Config.Value.MULTIUSER_MODE_OWNER_MANAGED)
} }
......
package com.topjohnwu.magisk.view package com.topjohnwu.magisk.view
import android.app.Notification
import android.app.NotificationChannel import android.app.NotificationChannel
import android.app.NotificationManager import android.app.NotificationManager
import android.app.PendingIntent import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.os.Build import android.os.Build.VERSION.SDK_INT
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.TaskStackBuilder import androidx.core.app.TaskStackBuilder
import androidx.core.content.getSystemService
import androidx.core.graphics.drawable.toIcon
import com.topjohnwu.magisk.* import com.topjohnwu.magisk.*
import com.topjohnwu.magisk.Const.ID.PROGRESS_NOTIFICATION_CHANNEL
import com.topjohnwu.magisk.Const.ID.UPDATE_NOTIFICATION_CHANNEL
import com.topjohnwu.magisk.extensions.get import com.topjohnwu.magisk.extensions.get
import com.topjohnwu.magisk.extensions.getBitmap
import com.topjohnwu.magisk.model.receiver.GeneralReceiver import com.topjohnwu.magisk.model.receiver.GeneralReceiver
import com.topjohnwu.magisk.ui.SplashActivity import com.topjohnwu.magisk.ui.SplashActivity
object Notifications { object Notifications {
val mgr by lazy { NotificationManagerCompat.from(get()) } val mgr by lazy { get<Context>().getSystemService<NotificationManager>()!! }
private val icon by lazy { resolveRes(DynAPK.NOTIFICATION) }
fun setup(context: Context) { fun setup(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (SDK_INT >= 26) {
mgr.deleteNotificationChannel("magisk_notification") var channel = NotificationChannel(UPDATE_NOTIFICATION_CHANNEL,
var channel = NotificationChannel(Const.ID.UPDATE_NOTIFICATION_CHANNEL,
context.getString(R.string.update_channel), NotificationManager.IMPORTANCE_DEFAULT) context.getString(R.string.update_channel), NotificationManager.IMPORTANCE_DEFAULT)
mgr.createNotificationChannel(channel) mgr.createNotificationChannel(channel)
channel = NotificationChannel(Const.ID.PROGRESS_NOTIFICATION_CHANNEL, channel = NotificationChannel(PROGRESS_NOTIFICATION_CHANNEL,
context.getString(R.string.progress_channel), NotificationManager.IMPORTANCE_LOW) context.getString(R.string.progress_channel), NotificationManager.IMPORTANCE_LOW)
mgr.createNotificationChannel(channel) mgr.createNotificationChannel(channel)
} }
} }
private fun updateBuilder(context: Context): Notification.Builder {
return Notification.Builder(context).apply {
val bitmap = context.getBitmap(R.drawable.ic_magisk_outline)
setLargeIcon(bitmap)
if (SDK_INT >= 26) {
setSmallIcon(bitmap.toIcon())
setChannelId(UPDATE_NOTIFICATION_CHANNEL)
} else {
setSmallIcon(R.drawable.ic_magisk_outline)
setVibrate(longArrayOf(0, 100, 100, 100))
}
}
}
fun magiskUpdate(context: Context) { fun magiskUpdate(context: Context) {
val intent = context.intent(SplashActivity::class.java) val intent = context.intent<SplashActivity>()
.putExtra(Const.Key.OPEN_SECTION, "magisk") .putExtra(Const.Key.OPEN_SECTION, "magisk")
val stackBuilder = TaskStackBuilder.create(context) val stackBuilder = TaskStackBuilder.create(context)
stackBuilder.addParentStack(SplashActivity::class.java.cmp(context.packageName)) stackBuilder.addParentStack(SplashActivity::class.java.cmp(context.packageName))
...@@ -39,11 +55,9 @@ object Notifications { ...@@ -39,11 +55,9 @@ object Notifications {
val pendingIntent = stackBuilder.getPendingIntent(Const.ID.MAGISK_UPDATE_NOTIFICATION_ID, val pendingIntent = stackBuilder.getPendingIntent(Const.ID.MAGISK_UPDATE_NOTIFICATION_ID,
PendingIntent.FLAG_UPDATE_CURRENT) PendingIntent.FLAG_UPDATE_CURRENT)
val builder = NotificationCompat.Builder(context, Const.ID.UPDATE_NOTIFICATION_CHANNEL) val builder = updateBuilder(context)
builder.setSmallIcon(icon)
.setContentTitle(context.getString(R.string.magisk_update_title)) .setContentTitle(context.getString(R.string.magisk_update_title))
.setContentText(context.getString(R.string.manager_download_install)) .setContentText(context.getString(R.string.manager_download_install))
.setVibrate(longArrayOf(0, 100, 100, 100))
.setAutoCancel(true) .setAutoCancel(true)
.setContentIntent(pendingIntent) .setContentIntent(pendingIntent)
...@@ -51,18 +65,16 @@ object Notifications { ...@@ -51,18 +65,16 @@ object Notifications {
} }
fun managerUpdate(context: Context) { fun managerUpdate(context: Context) {
val intent = context.intent(GeneralReceiver::class.java) val intent = context.intent<GeneralReceiver>()
.setAction(Const.Key.BROADCAST_MANAGER_UPDATE) .setAction(Const.Key.BROADCAST_MANAGER_UPDATE)
.putExtra(Const.Key.INTENT_SET_APP, Info.remote.app) .putExtra(Const.Key.INTENT_SET_APP, Info.remote.app)
val pendingIntent = PendingIntent.getBroadcast(context, val pendingIntent = PendingIntent.getBroadcast(context,
Const.ID.APK_UPDATE_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT) Const.ID.APK_UPDATE_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT)
val builder = NotificationCompat.Builder(context, Const.ID.UPDATE_NOTIFICATION_CHANNEL) val builder = updateBuilder(context)
builder.setSmallIcon(icon)
.setContentTitle(context.getString(R.string.manager_update_title)) .setContentTitle(context.getString(R.string.manager_update_title))
.setContentText(context.getString(R.string.manager_download_install)) .setContentText(context.getString(R.string.manager_download_install))
.setVibrate(longArrayOf(0, 100, 100, 100))
.setAutoCancel(true) .setAutoCancel(true)
.setContentIntent(pendingIntent) .setContentIntent(pendingIntent)
...@@ -70,25 +82,36 @@ object Notifications { ...@@ -70,25 +82,36 @@ object Notifications {
} }
fun dtboPatched(context: Context) { fun dtboPatched(context: Context) {
val intent = context.intent(GeneralReceiver::class.java) val intent = context.intent<GeneralReceiver>()
.setAction(Const.Key.BROADCAST_REBOOT) .setAction(Const.Key.BROADCAST_REBOOT)
val pendingIntent = PendingIntent.getBroadcast(context, val pendingIntent = PendingIntent.getBroadcast(context,
Const.ID.DTBO_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT) Const.ID.DTBO_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT)
val builder = NotificationCompat.Builder(context, Const.ID.UPDATE_NOTIFICATION_CHANNEL) val builder = updateBuilder(context)
builder.setSmallIcon(icon)
.setContentTitle(context.getString(R.string.dtbo_patched_title)) .setContentTitle(context.getString(R.string.dtbo_patched_title))
.setContentText(context.getString(R.string.dtbo_patched_reboot)) .setContentText(context.getString(R.string.dtbo_patched_reboot))
.setVibrate(longArrayOf(0, 100, 100, 100))
.addAction(R.drawable.ic_refresh, context.getString(R.string.reboot), pendingIntent) if (SDK_INT >= 23) {
val action = Notification.Action.Builder(
context.getBitmap(R.drawable.ic_refresh).toIcon(),
context.getString(R.string.reboot), pendingIntent).build()
builder.addAction(action)
} else {
builder.addAction(
R.drawable.ic_refresh,
context.getString(R.string.reboot), pendingIntent)
}
mgr.notify(Const.ID.DTBO_NOTIFICATION_ID, builder.build()) mgr.notify(Const.ID.DTBO_NOTIFICATION_ID, builder.build())
} }
fun progress(context: Context, title: CharSequence): NotificationCompat.Builder { fun progress(context: Context, title: CharSequence): Notification.Builder {
val builder = NotificationCompat.Builder(context, Const.ID.PROGRESS_NOTIFICATION_CHANNEL) val builder = if (SDK_INT >= 26) {
builder.setPriority(NotificationCompat.PRIORITY_LOW) Notification.Builder(context, PROGRESS_NOTIFICATION_CHANNEL)
.setSmallIcon(android.R.drawable.stat_sys_download) } else {
Notification.Builder(context).setPriority(Notification.PRIORITY_LOW)
}
builder.setSmallIcon(android.R.drawable.stat_sys_download)
.setContentTitle(title) .setContentTitle(title)
.setProgress(0, 0, true) .setProgress(0, 0, true)
.setOngoing(true) .setOngoing(true)
......
...@@ -7,16 +7,19 @@ import android.content.pm.ShortcutManager ...@@ -7,16 +7,19 @@ import android.content.pm.ShortcutManager
import android.graphics.drawable.Icon import android.graphics.drawable.Icon
import android.os.Build import android.os.Build
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.content.getSystemService
import androidx.core.graphics.drawable.toAdaptiveIcon
import androidx.core.graphics.drawable.toIcon
import com.topjohnwu.magisk.* import com.topjohnwu.magisk.*
import com.topjohnwu.magisk.extensions.getBitmap
import com.topjohnwu.magisk.ui.SplashActivity import com.topjohnwu.magisk.ui.SplashActivity
import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.superuser.Shell
object Shortcuts { object Shortcuts {
fun setup(context: Context) { fun setup(context: Context) {
if (Build.VERSION.SDK_INT >= 25) { if (Build.VERSION.SDK_INT >= 25) {
val manager = context.getSystemService(ShortcutManager::class.java) val manager = context.getSystemService<ShortcutManager>()
manager?.dynamicShortcuts = getShortCuts(context) manager?.dynamicShortcuts = getShortCuts(context)
} }
} }
...@@ -24,49 +27,72 @@ object Shortcuts { ...@@ -24,49 +27,72 @@ object Shortcuts {
@RequiresApi(api = 25) @RequiresApi(api = 25)
private fun getShortCuts(context: Context): List<ShortcutInfo> { private fun getShortCuts(context: Context): List<ShortcutInfo> {
val shortCuts = mutableListOf<ShortcutInfo>() val shortCuts = mutableListOf<ShortcutInfo>()
val root = Shell.rootAccess() val intent = context.intent<SplashActivity>()
val intent = context.intent(SplashActivity::class.java)
fun getIcon(id: Int): Icon {
return if (Build.VERSION.SDK_INT >= 26)
context.getBitmap(id).toAdaptiveIcon()
else
context.getBitmap(id).toIcon()
}
if (Utils.showSuperUser()) { if (Utils.showSuperUser()) {
shortCuts.add(ShortcutInfo.Builder(context, "superuser") shortCuts.add(
ShortcutInfo.Builder(context, "superuser")
.setShortLabel(context.getString(R.string.superuser)) .setShortLabel(context.getString(R.string.superuser))
.setIntent(Intent(intent) .setIntent(
Intent(intent)
.putExtra(Const.Key.OPEN_SECTION, "superuser") .putExtra(Const.Key.OPEN_SECTION, "superuser")
.setAction(Intent.ACTION_VIEW) .setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
.setIcon(Icon.createWithResource(context, resolveRes(DynAPK.SUPERUSER))) )
.setIcon(getIcon(R.drawable.sc_superuser))
.setRank(0) .setRank(0)
.build()) .build()
)
} }
if (root && Info.env.magiskHide) { if (Info.env.magiskHide) {
shortCuts.add(ShortcutInfo.Builder(context, "magiskhide") shortCuts.add(
ShortcutInfo.Builder(context, "magiskhide")
.setShortLabel(context.getString(R.string.magiskhide)) .setShortLabel(context.getString(R.string.magiskhide))
.setIntent(Intent(intent) .setIntent(
Intent(intent)
.putExtra(Const.Key.OPEN_SECTION, "magiskhide") .putExtra(Const.Key.OPEN_SECTION, "magiskhide")
.setAction(Intent.ACTION_VIEW) .setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
.setIcon(Icon.createWithResource(context, resolveRes(DynAPK.MAGISKHIDE))) )
.setIcon(getIcon(R.drawable.sc_magiskhide))
.setRank(1) .setRank(1)
.build()) .build()
)
} }
if (!Config.coreOnly && root && Info.env.magiskVersionCode >= 0) { if (!Config.coreOnly && Info.env.isActive) {
shortCuts.add(ShortcutInfo.Builder(context, "modules") shortCuts.add(
ShortcutInfo.Builder(context, "modules")
.setShortLabel(context.getString(R.string.modules)) .setShortLabel(context.getString(R.string.modules))
.setIntent(Intent(intent) .setIntent(
Intent(intent)
.putExtra(Const.Key.OPEN_SECTION, "modules") .putExtra(Const.Key.OPEN_SECTION, "modules")
.setAction(Intent.ACTION_VIEW) .setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
.setIcon(Icon.createWithResource(context, resolveRes(DynAPK.MODULES))) )
.setIcon(getIcon(R.drawable.sc_extension))
.setRank(3) .setRank(3)
.build()) .build()
shortCuts.add(ShortcutInfo.Builder(context, "downloads") )
shortCuts.add(
ShortcutInfo.Builder(context, "downloads")
.setShortLabel(context.getString(R.string.downloads)) .setShortLabel(context.getString(R.string.downloads))
.setIntent(Intent(intent) .setIntent(
Intent(intent)
.putExtra(Const.Key.OPEN_SECTION, "downloads") .putExtra(Const.Key.OPEN_SECTION, "downloads")
.setAction(Intent.ACTION_VIEW) .setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
.setIcon(Icon.createWithResource(context, resolveRes(DynAPK.DOWNLOAD))) )
.setIcon(getIcon(R.drawable.sc_cloud_download))
.setRank(2) .setRank(2)
.build()) .build()
)
} }
return shortCuts return shortCuts
} }
......
package com.topjohnwu.magisk.view.dialogs
import android.annotation.TargetApi
import android.app.Activity
import android.graphics.Color
import android.hardware.fingerprint.FingerprintManager
import android.os.Build
import android.view.Gravity
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.utils.FingerprintHelper
import com.topjohnwu.magisk.utils.Utils
@TargetApi(Build.VERSION_CODES.M)
class FingerprintAuthDialog(activity: Activity, private val callback: () -> Unit)
: CustomAlertDialog(activity) {
private var failureCallback: (() -> Unit)? = null
private var helper: DialogFingerprintHelper? = null
init {
val fingerprint = ContextCompat.getDrawable(activity, R.drawable.ic_fingerprint)
fingerprint?.setBounds(0, 0, Utils.dpInPx(50), Utils.dpInPx(50))
val theme = activity.theme
val ta = theme.obtainStyledAttributes(intArrayOf(R.attr.imageColorTint))
fingerprint?.setTint(ta.getColor(0, Color.GRAY))
ta.recycle()
binding.message.setCompoundDrawables(null, null, null, fingerprint)
binding.message.compoundDrawablePadding = Utils.dpInPx(20)
binding.message.gravity = Gravity.CENTER
setMessage(R.string.auth_fingerprint)
setNegativeButton(android.R.string.cancel) { _, _ ->
helper?.cancel()
failureCallback?.invoke()
}
setOnCancelListener {
helper?.cancel()
failureCallback?.invoke()
}
runCatching {
helper = DialogFingerprintHelper()
}
}
constructor(activity: Activity, onSuccess: () -> Unit, onFailure: () -> Unit)
: this(activity, onSuccess) {
failureCallback = onFailure
}
override fun show(): AlertDialog {
return create().apply {
if (helper == null) {
dismiss()
Utils.toast(R.string.auth_fail, Toast.LENGTH_SHORT)
} else {
helper?.authenticate()
show()
}
}
}
internal inner class DialogFingerprintHelper @Throws(Exception::class)
constructor() : FingerprintHelper() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
binding.message.setTextColor(Color.RED)
binding.message.text = errString
}
override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence) {
binding.message.setTextColor(Color.RED)
binding.message.text = helpString
}
override fun onAuthenticationFailed() {
binding.message.setTextColor(Color.RED)
binding.message.setText(R.string.auth_fail)
}
override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult) {
dismiss()
callback()
}
}
}
...@@ -129,22 +129,12 @@ ...@@ -129,22 +129,12 @@
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/grant_btn" android:id="@+id/grant_btn"
style="@style/Widget.Button.Text" style="@style/Widget.Button.Text"
gone="@{viewModel.canUseFingerprint}"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:onClick="@{() -> viewModel.grantPressed()}" android:onClick="@{() -> viewModel.grantPressed()}"
android:text="@string/grant" /> android:text="@string/grant" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/fingerprint"
gone="@{!viewModel.canUseFingerprint}"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="7dp"
android:tint="?attr/colorAccent"
app:srcCompat="@drawable/ic_fingerprint" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
......
...@@ -15,8 +15,6 @@ ...@@ -15,8 +15,6 @@
<import type="com.topjohnwu.magisk.R" /> <import type="com.topjohnwu.magisk.R" />
<import type="com.topjohnwu.magisk.extensions.XStringKt" />
<variable <variable
name="viewModel" name="viewModel"
type="com.topjohnwu.magisk.ui.home.HomeViewModel" /> type="com.topjohnwu.magisk.ui.home.HomeViewModel" />
...@@ -203,7 +201,7 @@ ...@@ -203,7 +201,7 @@
<View <View
style="@style/Widget.Divider.Horizontal" style="@style/Widget.Divider.Horizontal"
gone="@{!viewModel.hasRoot || !viewModel.isConnected}" gone="@{!viewModel.isActive || !viewModel.isConnected}"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_margin="@dimen/margin_generic" /> android:layout_margin="@dimen/margin_generic" />
...@@ -239,7 +237,7 @@ ...@@ -239,7 +237,7 @@
android:layout_marginRight="@dimen/margin_generic" android:layout_marginRight="@dimen/margin_generic"
android:gravity="center" android:gravity="center"
android:maxLines="1" android:maxLines="1"
android:text="@{XStringKt.res(viewModel.safetyNetTitle)}" android:text="@{viewModel.safetyNetTitle}"
android:textStyle="bold" android:textStyle="bold"
app:autoSizeMaxTextSize="14sp" app:autoSizeMaxTextSize="14sp"
app:autoSizeTextType="uniform" app:autoSizeTextType="uniform"
...@@ -484,13 +482,13 @@ ...@@ -484,13 +482,13 @@
<View <View
style="@style/Widget.Divider.Horizontal" style="@style/Widget.Divider.Horizontal"
gone="@{!viewModel.hasRoot}" gone="@{!viewModel.isActive}"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_margin="@dimen/margin_generic" /> android:layout_margin="@dimen/margin_generic" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
style="@style/Widget.Button.Text" style="@style/Widget.Button.Text"
gone="@{!viewModel.hasRoot}" gone="@{!viewModel.isActive}"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.uninstallPressed()}" android:onClick="@{() -> viewModel.uninstallPressed()}"
......
# v7.3.5 # v7.4.0
- Sort installed modules by name - Hide Magisk Manager with stub APKs on Android 9.0+. Not all devices will be supported, please refer to Magisk v20.1 release notes.
- Better pre-5.0 support - Allow customizing app name when hiding Magisk Manager
- Fix potential issues when patching tar files - Generate random keys to sign the hidden Magisk Manager to prevent signature detections
- Fix fingerprint UI infinite loop
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
<string name="settings">الإعدادات</string> <string name="settings">الإعدادات</string>
<string name="install">التثبيت</string> <string name="install">التثبيت</string>
<string name="unsupport_magisk_title">إصدار Magisk غير مدعوم</string> <string name="unsupport_magisk_title">إصدار Magisk غير مدعوم</string>
<string name="unsupport_magisk_message">لا يدعم هذا الإصدار من Magisk Manager إصدارا لـ Magisk vأقل من 18\n\n بإمكانك تحديث Magisk يدويا أو تثبيت إصدار أدنى.</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magisk غير مثبت</string> <string name="magisk_version_error">Magisk غير مثبت</string>
...@@ -164,9 +163,6 @@ ...@@ -164,9 +163,6 @@
<string name="request_timeout_summary">%1$d ثانية</string> <string name="request_timeout_summary">%1$d ثانية</string>
<string name="settings_su_reauth_title">إعادة المصادقة بعد الترقية</string> <string name="settings_su_reauth_title">إعادة المصادقة بعد الترقية</string>
<string name="settings_su_reauth_summary">أعد المصادقة على صلاحيات المستخدم المتميز بعد إجراء ترقيات للتطبيق</string> <string name="settings_su_reauth_summary">أعد المصادقة على صلاحيات المستخدم المتميز بعد إجراء ترقيات للتطبيق</string>
<string name="settings_su_fingerprint_summary">استخدام قارئ بصمات الأصابع للسماح بطلبات المستخدم المتميز</string>
<string name="settings_su_fingerprint_title">تمكين مصادقة البصمة</string>
<string name="auth_fingerprint">مصادقة البصمة</string>
<string name="multiuser_mode">نمط تعدد المستخدمين</string> <string name="multiuser_mode">نمط تعدد المستخدمين</string>
<string name="settings_owner_only">مالك الجهاز فقط</string> <string name="settings_owner_only">مالك الجهاز فقط</string>
...@@ -183,7 +179,6 @@ ...@@ -183,7 +179,6 @@
<string name="global_summary">تستخدم كافة جلسات العمل للروت مساحة الاسم ذات التركيب العامة</string> <string name="global_summary">تستخدم كافة جلسات العمل للروت مساحة الاسم ذات التركيب العامة</string>
<string name="requester_summary">سترث جلسات العمل للروت مساحة الأسماء لطالبيها</string> <string name="requester_summary">سترث جلسات العمل للروت مساحة الأسماء لطالبيها</string>
<string name="isolate_summary">سيكون لكل جلسة عمل للروت مساحة اسم معزولة خاصة بها </string> <string name="isolate_summary">سيكون لكل جلسة عمل للروت مساحة اسم معزولة خاصة بها </string>
<string name="disable_fingerprint">لم تُعين بصمات الأصابع أو لا يوجد قارئ بصمات</string>
<string name="settings_download_path_error">خطأ عند إنشاء مجلد. عليه أن يكون سهلا الوصول إليه من خلال مجلد التخزين للروت و ألا يكون ملفا.</string> <string name="settings_download_path_error">خطأ عند إنشاء مجلد. عليه أن يكون سهلا الوصول إليه من خلال مجلد التخزين للروت و ألا يكون ملفا.</string>
<!--Superuser--> <!--Superuser-->
...@@ -211,7 +206,6 @@ ...@@ -211,7 +206,6 @@
<string name="su_revoke_msg">تأكيد لسحب صلاحيات %1$s ?</string> <string name="su_revoke_msg">تأكيد لسحب صلاحيات %1$s ?</string>
<string name="toast">ملاحظة منبثقة</string> <string name="toast">ملاحظة منبثقة</string>
<string name="none">بدون</string> <string name="none">بدون</string>
<string name="auth_fail">فشل المصادقة</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
<string name="settings">Tənzimləmələr</string> <string name="settings">Tənzimləmələr</string>
<string name="install">Quraşdır</string> <string name="install">Quraşdır</string>
<string name="unsupport_magisk_title">Dəstəklənməyən Magisk Versiyası</string> <string name="unsupport_magisk_title">Dəstəklənməyən Magisk Versiyası</string>
<string name="unsupport_magisk_message">Magisk Manager\'in bu versiyası Magisk\'in v18.0 versiyasndan aşağısını dəstəkləmir.\n\nMagisk\'i əllə yüksəldə, yaxud tətbiqi əvvəlki versiyalarına qaytara bilərsiniz.</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magisk yüklənməyib.</string> <string name="magisk_version_error">Magisk yüklənməyib.</string>
...@@ -152,9 +151,6 @@ ...@@ -152,9 +151,6 @@
<string name="request_timeout_summary">%1$d saniyə</string> <string name="request_timeout_summary">%1$d saniyə</string>
<string name="settings_su_reauth_title">Yüksəltmədən sonra Yenidən İdentifikasiya et</string> <string name="settings_su_reauth_title">Yüksəltmədən sonra Yenidən İdentifikasiya et</string>
<string name="settings_su_reauth_summary">Tətbiq yeniləmələridən sonra superuser icazələrini yenidən identifikasiya et</string> <string name="settings_su_reauth_summary">Tətbiq yeniləmələridən sonra superuser icazələrini yenidən identifikasiya et</string>
<string name="settings_su_fingerprint_title">Barmaq İzi İdentifikasiyasını Aç</string>
<string name="settings_su_fingerprint_summary">Barmaq izi oxuyucunu superuser icazələri üçün işlət</string>
<string name="auth_fingerprint">Barmaq izini İdentifikasiya et</string>
<string name="multiuser_mode">Çox-istifadəçi modu</string> <string name="multiuser_mode">Çox-istifadəçi modu</string>
<string name="settings_owner_only">Yalnız cihaz sahibi</string> <string name="settings_owner_only">Yalnız cihaz sahibi</string>
<string name="settings_owner_manage">Cihaz sahibinin idarəçiliyində</string> <string name="settings_owner_manage">Cihaz sahibinin idarəçiliyində</string>
...@@ -169,7 +165,6 @@ ...@@ -169,7 +165,6 @@
<string name="global_summary">Bütün root sessyaları qlobal qoşma namespace\'dən istifadə edir.</string> <string name="global_summary">Bütün root sessyaları qlobal qoşma namespace\'dən istifadə edir.</string>
<string name="requester_summary">Root sessyaları soruşulan namespace\'ləri birindən digərinə keçirəcək.</string> <string name="requester_summary">Root sessyaları soruşulan namespace\'ləri birindən digərinə keçirəcək.</string>
<string name="isolate_summary">Hər bir root sessyasının ayrılmış namespace\'i olacaq.</string> <string name="isolate_summary">Hər bir root sessyasının ayrılmış namespace\'i olacaq.</string>
<string name="disable_fingerprint">Barmaq izi təyin edilməyib ya da dəstəklənmir.</string>
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">Superuser Tələbi</string> <string name="su_request_title">Superuser Tələbi</string>
...@@ -196,7 +191,6 @@ ...@@ -196,7 +191,6 @@
<string name="su_revoke_msg">%1$s üçün haqları ləğv etməyi təsdiq edirsiniz?</string> <string name="su_revoke_msg">%1$s üçün haqları ləğv etməyi təsdiq edirsiniz?</string>
<string name="toast">Tost</string> <string name="toast">Tost</string>
<string name="none">Heçnə</string> <string name="none">Heçnə</string>
<string name="auth_fail">İdentifikasiya xətası</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
......
...@@ -142,9 +142,6 @@ ...@@ -142,9 +142,6 @@
<string name="request_timeout_summary">%1$d секунди</string> <string name="request_timeout_summary">%1$d секунди</string>
<string name="settings_su_reauth_title">Повторно запитване след актуализация</string> <string name="settings_su_reauth_title">Повторно запитване след актуализация</string>
<string name="settings_su_reauth_summary">Повторно запитване за Superuser достъп след актуализация на приложенията.</string> <string name="settings_su_reauth_summary">Повторно запитване за Superuser достъп след актуализация на приложенията.</string>
<string name="settings_su_fingerprint_title">Superuser права само с пръстов отпечатък</string>
<string name="settings_su_fingerprint_summary">Използване на сензора за пръстови отпечатъци за разрешаване на Superuser достъп.</string>
<string name="auth_fingerprint">Удостоверете с пръстов отпечатък.</string>
<string name="multiuser_mode">Потребителски достъп</string> <string name="multiuser_mode">Потребителски достъп</string>
<string name="settings_owner_only">Само собственик</string> <string name="settings_owner_only">Само собственик</string>
...@@ -161,7 +158,6 @@ ...@@ -161,7 +158,6 @@
<string name="global_summary">Всички сесии с руут достъп използват глобалното именно пространство.</string> <string name="global_summary">Всички сесии с руут достъп използват глобалното именно пространство.</string>
<string name="requester_summary">Всички сесии с руут достъп наследяват именното пространство на запитващото приложение.</string> <string name="requester_summary">Всички сесии с руут достъп наследяват именното пространство на запитващото приложение.</string>
<string name="isolate_summary">Всички сесии с руут достъп имат собствени именни пространства.</string> <string name="isolate_summary">Всички сесии с руут достъп имат собствени именни пространства.</string>
<string name="disable_fingerprint">Не са добавени пръстови отпечатъци или устройството не поддържа тази функция.</string>
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">Запитване за Superuser достъп</string> <string name="su_request_title">Запитване за Superuser достъп</string>
...@@ -188,7 +184,6 @@ ...@@ -188,7 +184,6 @@
<string name="su_revoke_msg">Потвърждавате ли анулирането на настройките за достъп на %1$s?</string> <string name="su_revoke_msg">Потвърждавате ли анулирането на настройките за достъп на %1$s?</string>
<string name="toast">Toast</string> <string name="toast">Toast</string>
<string name="none">Без</string> <string name="none">Без</string>
<string name="auth_fail">Неуспешна заверка.</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="target_uid">Целеви UID: %1$d</string> <string name="target_uid">Целеви UID: %1$d</string>
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
<string name="settings">Configuració</string> <string name="settings">Configuració</string>
<string name="install">Instal·lar</string> <string name="install">Instal·lar</string>
<string name="unsupport_magisk_title">Versió de Magisk incompatible</string> <string name="unsupport_magisk_title">Versió de Magisk incompatible</string>
<string name="unsupport_magisk_message">Aquesta versió de Magisk Manager no suporta una versió inferior a la 18.0.\n\nPots actualitzar Magisk manualment o pots instalar una versió anterior de la app.</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magisk no està instal·lat</string> <string name="magisk_version_error">Magisk no està instal·lat</string>
...@@ -167,9 +166,6 @@ ...@@ -167,9 +166,6 @@
<string name="request_timeout_summary">%1$d segons</string> <string name="request_timeout_summary">%1$d segons</string>
<string name="settings_su_reauth_title">Demanar després d\'una actualització</string> <string name="settings_su_reauth_title">Demanar després d\'una actualització</string>
<string name="settings_su_reauth_summary">Demanar permisos de superusuari novament si una aplicació és actualitzada o reinstal·lada</string> <string name="settings_su_reauth_summary">Demanar permisos de superusuari novament si una aplicació és actualitzada o reinstal·lada</string>
<string name="settings_su_fingerprint_title">Autenticació per Empremta Dactilar</string>
<string name="settings_su_fingerprint_summary">Utilitza el sensor d\'Empremta Dactilar per permetre les sol·licituds de superusuari</string>
<string name="auth_fingerprint">Autenticar Emprempta Digital</string>
<string name="multiuser_mode">Mode Multiusuari</string> <string name="multiuser_mode">Mode Multiusuari</string>
<string name="settings_owner_only">Només Administrador del Dispositiu</string> <string name="settings_owner_only">Només Administrador del Dispositiu</string>
...@@ -186,7 +182,6 @@ ...@@ -186,7 +182,6 @@
<string name="global_summary">Totes les sessions d\'arrel utilitzen el suport Namespace Global</string> <string name="global_summary">Totes les sessions d\'arrel utilitzen el suport Namespace Global</string>
<string name="requester_summary">Les sessions d\'arrel heretaran les peticiones Namespace</string> <string name="requester_summary">Les sessions d\'arrel heretaran les peticiones Namespace</string>
<string name="isolate_summary">Totes les sessions d\'arrel tindran la seva pròpia Namespace</string> <string name="isolate_summary">Totes les sessions d\'arrel tindran la seva pròpia Namespace</string>
<string name="disable_fingerprint">No s\'han establert empremtes dactilars o no existeix el suport del dispositiu</string>
<string name="settings_download_path_error">Error al crear la carpeta. El directori ha de ser accesible desde el directori arrel i no pot ser un arxiu.</string> <string name="settings_download_path_error">Error al crear la carpeta. El directori ha de ser accesible desde el directori arrel i no pot ser un arxiu.</string>
<!--Superuser--> <!--Superuser-->
...@@ -214,7 +209,6 @@ ...@@ -214,7 +209,6 @@
<string name="su_revoke_msg">Confirma per revocar drets de %1$s</string> <string name="su_revoke_msg">Confirma per revocar drets de %1$s</string>
<string name="toast">Avís</string> <string name="toast">Avís</string>
<string name="none">Cap</string> <string name="none">Cap</string>
<string name="auth_fail">Autenticació fallida</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
<string name="settings">Nastavení</string> <string name="settings">Nastavení</string>
<string name="install">Instalovat</string> <string name="install">Instalovat</string>
<string name="unsupport_magisk_title">Nepodporovaná verze Magisk</string> <string name="unsupport_magisk_title">Nepodporovaná verze Magisk</string>
<string name="unsupport_magisk_message">Tato verze Magisk Managera nepodporuje verzi Magisk nižší než v18.0.\n\nMůžete buď ručně aktualizovat Magisk, nebo aplikaci downgradovat na starší verzi.</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magisk není nainstalován.</string> <string name="magisk_version_error">Magisk není nainstalován.</string>
...@@ -155,9 +154,6 @@ ...@@ -155,9 +154,6 @@
<string name="request_timeout_summary">%1$d sekund</string> <string name="request_timeout_summary">%1$d sekund</string>
<string name="settings_su_reauth_title">Opětovné ověření po aktualizaci</string> <string name="settings_su_reauth_title">Opětovné ověření po aktualizaci</string>
<string name="settings_su_reauth_summary">Opětovné ověření oprávnění Superuser po aktualizaci aplikace</string> <string name="settings_su_reauth_summary">Opětovné ověření oprávnění Superuser po aktualizaci aplikace</string>
<string name="settings_su_fingerprint_title">Povolit ověřování otisky prstů</string>
<string name="settings_su_fingerprint_summary">Chcete-li povolit požadavky Superuser, použijte snímač otisků prstů</string>
<string name="auth_fingerprint">Ověřování otisky prstů</string>
<string name="multiuser_mode">Režim více uživatelů</string> <string name="multiuser_mode">Režim více uživatelů</string>
<string name="settings_owner_only">Pouze vlastník zařízení</string> <string name="settings_owner_only">Pouze vlastník zařízení</string>
...@@ -174,7 +170,6 @@ ...@@ -174,7 +170,6 @@
<string name="global_summary">Všechny relace root používají globální připojení jmenného prostoru.</string> <string name="global_summary">Všechny relace root používají globální připojení jmenného prostoru.</string>
<string name="requester_summary">Kořenové relace dědí jmenný prostor žadatele.</string> <string name="requester_summary">Kořenové relace dědí jmenný prostor žadatele.</string>
<string name="isolate_summary">Každá relace root bude mít svůj vlastní izolovaný jmenný prostor.</string> <string name="isolate_summary">Každá relace root bude mít svůj vlastní izolovaný jmenný prostor.</string>
<string name="disable_fingerprint">Nebyly nastaveny žádné otisky prstů ani žádná podpora zařízení.</string>
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">Požadavek Superuser</string> <string name="su_request_title">Požadavek Superuser</string>
...@@ -201,7 +196,6 @@ ...@@ -201,7 +196,6 @@
<string name="su_revoke_msg">Smazat záznam ohledně oprávnění pro %1$s?</string> <string name="su_revoke_msg">Smazat záznam ohledně oprávnění pro %1$s?</string>
<string name="toast">Informační text</string> <string name="toast">Informační text</string>
<string name="none">Žádný</string> <string name="none">Žádný</string>
<string name="auth_fail">Ověření se nezdařilo</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
<string name="settings">Einstellungen</string> <string name="settings">Einstellungen</string>
<string name="install">Installieren</string> <string name="install">Installieren</string>
<string name="unsupport_magisk_title">Nicht unterstützte Magisk Version</string> <string name="unsupport_magisk_title">Nicht unterstützte Magisk Version</string>
<string name="unsupport_magisk_message">Diese Version von Magisk Manager unterstützt keine Magisk-Version kleiner als 18.0. \n\nSie können entweder Magisk manuell aktualisieren oder die App auf eine ältere Version herabstufen.</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magisk ist nicht installiert</string> <string name="magisk_version_error">Magisk ist nicht installiert</string>
...@@ -157,9 +156,6 @@ ...@@ -157,9 +156,6 @@
<string name="request_timeout_summary">%1$d Sekunden</string> <string name="request_timeout_summary">%1$d Sekunden</string>
<string name="settings_su_reauth_title">Nach Aktualisierung erneut authentifizieren</string> <string name="settings_su_reauth_title">Nach Aktualisierung erneut authentifizieren</string>
<string name="settings_su_reauth_summary">Superuser-Zugriff nach App-Aktualisierung erneut abfragen</string> <string name="settings_su_reauth_summary">Superuser-Zugriff nach App-Aktualisierung erneut abfragen</string>
<string name="settings_su_fingerprint_title">Aktiviere Authentifizierung durch Fingerabdruck</string>
<string name="settings_su_fingerprint_summary">Fingerabdrucksensor benutzen um Superuser-Anfragen zu erlauben</string>
<string name="auth_fingerprint">Authentifiziere Fingerabdruck</string>
<string name="multiuser_mode">Mehrbenutzermodus</string> <string name="multiuser_mode">Mehrbenutzermodus</string>
<string name="settings_owner_only">Nur der Gerätebesitzer</string> <string name="settings_owner_only">Nur der Gerätebesitzer</string>
...@@ -176,7 +172,6 @@ ...@@ -176,7 +172,6 @@
<string name="global_summary">Alle Root-Sitzungen benutzen den global angelegten Namespace</string> <string name="global_summary">Alle Root-Sitzungen benutzen den global angelegten Namespace</string>
<string name="requester_summary">Root-Sitzungen erben den Namespace des Abfragenden</string> <string name="requester_summary">Root-Sitzungen erben den Namespace des Abfragenden</string>
<string name="isolate_summary">Jede Root-Sitzung hat ihren isolierten Namespace</string> <string name="isolate_summary">Jede Root-Sitzung hat ihren isolierten Namespace</string>
<string name="disable_fingerprint">Keine Fingerabdrücke gespeichert oder keine Geräteunterstützung</string>
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">Superuser-Anfrage</string> <string name="su_request_title">Superuser-Anfrage</string>
...@@ -203,7 +198,6 @@ ...@@ -203,7 +198,6 @@
<string name="su_revoke_msg">Möchtest du die Rechte für %1$s entziehen?</string> <string name="su_revoke_msg">Möchtest du die Rechte für %1$s entziehen?</string>
<string name="toast">Popup</string> <string name="toast">Popup</string>
<string name="none">Keine</string> <string name="none">Keine</string>
<string name="auth_fail">Authentifizierung fehlgeschlagen</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
<string name="settings">Ajustes</string> <string name="settings">Ajustes</string>
<string name="install">Instalar</string> <string name="install">Instalar</string>
<string name="unsupport_magisk_title">Versión de Magisk no soportada</string> <string name="unsupport_magisk_title">Versión de Magisk no soportada</string>
<string name="unsupport_magisk_message">Esta versión de Magisk Manager no admite una versión de Magisk inferior a la v18.0.\n\nPuede actualizar Magisk de forma manual o instalar una versión anterior de la aplicación.</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magisk no está instalado</string> <string name="magisk_version_error">Magisk no está instalado</string>
...@@ -114,6 +113,7 @@ ...@@ -114,6 +113,7 @@
<string name="env_fix_title">Se Requiere una Instalación Adicional</string> <string name="env_fix_title">Se Requiere una Instalación Adicional</string>
<string name="env_fix_msg">Su dispositivo requiere una instalación adicional para que Magisk funcione correctamente. Se descargará el zip de instalación de Magisk, desea continuar ahora?</string> <string name="env_fix_msg">Su dispositivo requiere una instalación adicional para que Magisk funcione correctamente. Se descargará el zip de instalación de Magisk, desea continuar ahora?</string>
<string name="setup_msg">Ejecutando Configuración de Entorno</string> <string name="setup_msg">Ejecutando Configuración de Entorno</string>
<string name="authenticate">Autenticar</string>
<!--Settings Activity --> <!--Settings Activity -->
<string name="settings_general_category">General</string> <string name="settings_general_category">General</string>
...@@ -160,9 +160,9 @@ ...@@ -160,9 +160,9 @@
<string name="request_timeout_summary">%1$d segundos</string> <string name="request_timeout_summary">%1$d segundos</string>
<string name="settings_su_reauth_title">Re-autenticación</string> <string name="settings_su_reauth_title">Re-autenticación</string>
<string name="settings_su_reauth_summary">Pedir permisos de superusuario nuevamente si una aplicación es actualizada o reinstalada</string> <string name="settings_su_reauth_summary">Pedir permisos de superusuario nuevamente si una aplicación es actualizada o reinstalada</string>
<string name="settings_su_fingerprint_title">Autenticación por Huella Dactilar</string> <string name="settings_su_biometric_title">Habilitar autenticación biométrica</string>
<string name="settings_su_fingerprint_summary">Utilizar el sensor de Huella Dactilar para permitir las solicitudes de superusuario</string> <string name="settings_su_biometric_summary">Usar autenticación biométrica para permitir solicitudes de superusuario</string>
<string name="auth_fingerprint">Autenticar Huella Dactilar</string> <string name="no_biometric">Dispositivo no compatible o las configuraciones biométricas no están habilitadas</string>
<string name="multiuser_mode">Modo MultiUsuario</string> <string name="multiuser_mode">Modo MultiUsuario</string>
<string name="settings_owner_only">Sólo Administrador del Dispositivo</string> <string name="settings_owner_only">Sólo Administrador del Dispositivo</string>
...@@ -179,7 +179,6 @@ ...@@ -179,7 +179,6 @@
<string name="global_summary">Todas las sesiones de root utilizan el soporte Global Namespace</string> <string name="global_summary">Todas las sesiones de root utilizan el soporte Global Namespace</string>
<string name="requester_summary">Las sesiones de root heredarán las peticiones Namespace</string> <string name="requester_summary">Las sesiones de root heredarán las peticiones Namespace</string>
<string name="isolate_summary">Cada sesión root tendrá su propia Namespace</string> <string name="isolate_summary">Cada sesión root tendrá su propia Namespace</string>
<string name="disable_fingerprint">No se establecieron huellas dactilares o no existe soporte del dispositivo</string>
<string name="settings_download_path_error">Error al crear la carpeta. Debe ser accesible desde el directorio raíz de almacenamiento y no debe ser un archivo.</string> <string name="settings_download_path_error">Error al crear la carpeta. Debe ser accesible desde el directorio raíz de almacenamiento y no debe ser un archivo.</string>
<!--Superuser--> <!--Superuser-->
...@@ -207,7 +206,6 @@ ...@@ -207,7 +206,6 @@
<string name="su_revoke_msg">¿Confirmar para revocar derechos de %1$s?</string> <string name="su_revoke_msg">¿Confirmar para revocar derechos de %1$s?</string>
<string name="toast">Aviso</string> <string name="toast">Aviso</string>
<string name="none">Nada</string> <string name="none">Nada</string>
<string name="auth_fail">Autenticación fallida</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
<string name="settings">Seaded</string> <string name="settings">Seaded</string>
<string name="install">Installi</string> <string name="install">Installi</string>
<string name="unsupport_magisk_title">Mittetoetatud Magisk\'i versioon</string> <string name="unsupport_magisk_title">Mittetoetatud Magisk\'i versioon</string>
<string name="unsupport_magisk_message">See Magisk Manager\'i versioon ei toeta Magisk\'ist vanemat versiooni kui v18.0.\n\nSa võid kas Magisk\'i käsitsi täiendada või alandad rakenduse vanemale versioonile.</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magisk pole installitud</string> <string name="magisk_version_error">Magisk pole installitud</string>
...@@ -163,9 +162,6 @@ ...@@ -163,9 +162,6 @@
<string name="request_timeout_summary">%1$d sekundit</string> <string name="request_timeout_summary">%1$d sekundit</string>
<string name="settings_su_reauth_title">Taas-autendi peale täiendust</string> <string name="settings_su_reauth_title">Taas-autendi peale täiendust</string>
<string name="settings_su_reauth_summary">Taas-autendi superkasutaja õigused peale rakenduse täiendamist</string> <string name="settings_su_reauth_summary">Taas-autendi superkasutaja õigused peale rakenduse täiendamist</string>
<string name="settings_su_fingerprint_title">Luba sõrmejäljega autentimine</string>
<string name="settings_su_fingerprint_summary">Kasuta sõrmejäljelugejat superkasutaja taotluste lubamiseks</string>
<string name="auth_fingerprint">Autendi sõrmejälg</string>
<string name="multiuser_mode">Mitmikkasutaja režiim</string> <string name="multiuser_mode">Mitmikkasutaja režiim</string>
<string name="settings_owner_only">Ainult seadme omanik</string> <string name="settings_owner_only">Ainult seadme omanik</string>
...@@ -182,7 +178,6 @@ ...@@ -182,7 +178,6 @@
<string name="global_summary">Kõik juurkasutaja sessioonid kasutavad globaalset monteerimise nimeruumi.</string> <string name="global_summary">Kõik juurkasutaja sessioonid kasutavad globaalset monteerimise nimeruumi.</string>
<string name="requester_summary">Juurkasutaja sessioonid võtavad üle selle taotleja nimeruumi.</string> <string name="requester_summary">Juurkasutaja sessioonid võtavad üle selle taotleja nimeruumi.</string>
<string name="isolate_summary">Iga juurkasutaja sessioon saab oma isoleeritud nimeruumi.</string> <string name="isolate_summary">Iga juurkasutaja sessioon saab oma isoleeritud nimeruumi.</string>
<string name="disable_fingerprint">Sõrmejälgi pole määratud või seade pole toetatud.</string>
<string name="settings_download_path_error">Faili loomisel esines viga. See peab olema ligipääsetav mäluruumi juurkaustast ning ei tohi olla fail.</string> <string name="settings_download_path_error">Faili loomisel esines viga. See peab olema ligipääsetav mäluruumi juurkaustast ning ei tohi olla fail.</string>
<!--Superuser--> <!--Superuser-->
...@@ -210,7 +205,6 @@ ...@@ -210,7 +205,6 @@
<string name="su_revoke_msg">Kinnitad rakenduse %1$s õiguste eemaldamise?</string> <string name="su_revoke_msg">Kinnitad rakenduse %1$s õiguste eemaldamise?</string>
<string name="toast">Hüpik</string> <string name="toast">Hüpik</string>
<string name="none">Puudub</string> <string name="none">Puudub</string>
<string name="auth_fail">Autentimine ebaõnnestus</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
<string name="settings">Paramètres</string> <string name="settings">Paramètres</string>
<string name="install">Installer</string> <string name="install">Installer</string>
<string name="unsupport_magisk_title">Version de Magisk non prise en charge</string> <string name="unsupport_magisk_title">Version de Magisk non prise en charge</string>
<string name="unsupport_magisk_message">Cette version de Magisk Manager ne prend pas en charge les versions de Magisk inférieures à v18.0.\n\nSi vous continuez, toutes les fonctionnalités de Magisk ne seront pas disponibles et vous ne pourrez que mettre à jour Magisk.</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magisk n’est pas installé.</string> <string name="magisk_version_error">Magisk n’est pas installé.</string>
...@@ -145,7 +144,10 @@ ...@@ -145,7 +144,10 @@
<string name="settings_hosts_title">Fichier hosts sans système</string> <string name="settings_hosts_title">Fichier hosts sans système</string>
<string name="settings_hosts_summary">Prise en charge du fichier hosts sans système pour les applications de type AdBlock.</string> <string name="settings_hosts_summary">Prise en charge du fichier hosts sans système pour les applications de type AdBlock.</string>
<string name="settings_hosts_toast">Ajout d’un module hosts sans système</string> <string name="settings_hosts_toast">Ajout d’un module hosts sans système</string>
<string name="settings_app_name">Taper le nom de l\'application désirée</string>
<string name="settings_app_name_hint">Nouveau nom</string>
<string name="settings_app_name_helper">L\'application sera réempacter sous ce nom</string>
<string name="settings_app_name_error">Format invalide</string>
<string name="settings_su_app_adb">Applications et ADB</string> <string name="settings_su_app_adb">Applications et ADB</string>
<string name="settings_su_app">Applications uniquement</string> <string name="settings_su_app">Applications uniquement</string>
<string name="settings_su_adb">ADB uniquement</string> <string name="settings_su_adb">ADB uniquement</string>
...@@ -163,9 +165,6 @@ ...@@ -163,9 +165,6 @@
<string name="request_timeout_summary">%1$d secondes</string> <string name="request_timeout_summary">%1$d secondes</string>
<string name="settings_su_reauth_title">Authentifier à nouveau après la mise à niveau</string> <string name="settings_su_reauth_title">Authentifier à nouveau après la mise à niveau</string>
<string name="settings_su_reauth_summary">Authentifier à nouveau les autorisations super‐utilisateur après une mise à jour de l’application</string> <string name="settings_su_reauth_summary">Authentifier à nouveau les autorisations super‐utilisateur après une mise à jour de l’application</string>
<string name="settings_su_fingerprint_title">Activer l’authentification par empreinte digitale</string>
<string name="settings_su_fingerprint_summary">Utiliser le lecteur d’empreintes digitales pour autoriser les demandes super‐utilisateur</string>
<string name="auth_fingerprint">Authentifier l’empreinte digitale</string>
<string name="multiuser_mode">Mode multi‐utilisateur</string> <string name="multiuser_mode">Mode multi‐utilisateur</string>
<string name="settings_owner_only">Propriétaire de l’appareil uniquement</string> <string name="settings_owner_only">Propriétaire de l’appareil uniquement</string>
...@@ -182,7 +181,6 @@ ...@@ -182,7 +181,6 @@
<string name="global_summary">Toutes les sessions super‐utilisateur utilisent l’espace de noms global du montage.</string> <string name="global_summary">Toutes les sessions super‐utilisateur utilisent l’espace de noms global du montage.</string>
<string name="requester_summary">Les sessions super‐utilisateur hériteront de l’espace de noms de leur demandeur.</string> <string name="requester_summary">Les sessions super‐utilisateur hériteront de l’espace de noms de leur demandeur.</string>
<string name="isolate_summary">Chaque session super‐utilisateur aura son propre espace de noms isolé.</string> <string name="isolate_summary">Chaque session super‐utilisateur aura son propre espace de noms isolé.</string>
<string name="disable_fingerprint">Aucune empreinte digitale n’a été définie ou le lecteur d’empreinte n’est pas pris en charge.</string>
<string name="settings_download_path_error">Erreur lors de la création du dossier. Il doit être accessible depuis le répertoire racine du stockage et ne doit pas être un fichier.</string> <string name="settings_download_path_error">Erreur lors de la création du dossier. Il doit être accessible depuis le répertoire racine du stockage et ne doit pas être un fichier.</string>
<!--Superuser--> <!--Superuser-->
...@@ -210,7 +208,6 @@ ...@@ -210,7 +208,6 @@
<string name="su_revoke_msg">Confirmez‐vous l’annulation des droits pour %1$s ?</string> <string name="su_revoke_msg">Confirmez‐vous l’annulation des droits pour %1$s ?</string>
<string name="toast">Toast</string> <string name="toast">Toast</string>
<string name="none">Aucun</string> <string name="none">Aucun</string>
<string name="auth_fail">Échec de l’authentification</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID : %1$d</string> <string name="pid">PID : %1$d</string>
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
<string name="settings">सेटिंग्स</string> <string name="settings">सेटिंग्स</string>
<string name="install">स्थापित करें</string> <string name="install">स्थापित करें</string>
<string name="unsupport_magisk_title">असमर्थित Magisk संस्करण</string> <string name="unsupport_magisk_title">असमर्थित Magisk संस्करण</string>
<string name="unsupport_magisk_message">Magisk Manager का यह संस्करण Magisk के v18.0 संस्करण से कम का समर्थन नहीं करता है.\n\nआप या तो खुद से Magisk को अपग्रेड करें, या फिर एप्लीकेशन को पुराने संस्करण पे डाउनग्रेड करें .</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magisk स्थापित नहीं है</string> <string name="magisk_version_error">Magisk स्थापित नहीं है</string>
...@@ -163,9 +162,6 @@ ...@@ -163,9 +162,6 @@
<string name="request_timeout_summary">%1$d सेकंड्‌स</string> <string name="request_timeout_summary">%1$d सेकंड्‌स</string>
<string name="settings_su_reauth_title">अपग्रेड के बाद फिर से प्रमाणित करें</string> <string name="settings_su_reauth_title">अपग्रेड के बाद फिर से प्रमाणित करें</string>
<string name="settings_su_reauth_summary">एप्लीकेशन अपग्रेड होने के बाद उत्तम उपयोगकर्ता की अनुमतियों को फिर से प्रमाणित करें</string> <string name="settings_su_reauth_summary">एप्लीकेशन अपग्रेड होने के बाद उत्तम उपयोगकर्ता की अनुमतियों को फिर से प्रमाणित करें</string>
<string name="settings_su_fingerprint_title">फिंगरप्रिंट प्रमाणीकरण सक्षम करें</string>
<string name="settings_su_fingerprint_summary">उत्तम उपयोगकर्ता के अनुरोधों की अनुमति के लिए फिंगरप्रिंट स्कैनर का उपयोग करें</string>
<string name="auth_fingerprint">फिंगरप्रिंट को प्रमाणित करें</string>
<string name="multiuser_mode">बहु उपयोगकर्ता मोड</string> <string name="multiuser_mode">बहु उपयोगकर्ता मोड</string>
<string name="settings_owner_only">केवल डिवाइस का मालिक</string> <string name="settings_owner_only">केवल डिवाइस का मालिक</string>
...@@ -182,7 +178,6 @@ ...@@ -182,7 +178,6 @@
<string name="global_summary">सभी रूट सत्र वैश्विक माउंट नेमस्पेस का उपयोग करते हैं.</string> <string name="global_summary">सभी रूट सत्र वैश्विक माउंट नेमस्पेस का उपयोग करते हैं.</string>
<string name="requester_summary">रूट सत्रों को उनके अनुरोधकर्ताओं के नेमस्पेस विरासत में मिलेंगे.</string> <string name="requester_summary">रूट सत्रों को उनके अनुरोधकर्ताओं के नेमस्पेस विरासत में मिलेंगे.</string>
<string name="isolate_summary">प्रत्येक रूट सत्र का अपना अलग नेमस्पेस होगा.</string> <string name="isolate_summary">प्रत्येक रूट सत्र का अपना अलग नेमस्पेस होगा.</string>
<string name="disable_fingerprint">कोई फ़िंगरप्रिंट नहीं सेट किया गया या डिवाइस का समर्थन नहीं है.</string>
<string name="settings_download_path_error">फोल्डर बनाने में त्रुटि. यह स्टोरेज रूट डायरेक्टरी से एक्सेस होना चाहिए और फाइल नहीं होना चाहिए.</string> <string name="settings_download_path_error">फोल्डर बनाने में त्रुटि. यह स्टोरेज रूट डायरेक्टरी से एक्सेस होना चाहिए और फाइल नहीं होना चाहिए.</string>
<!--Superuser--> <!--Superuser-->
...@@ -210,7 +205,6 @@ ...@@ -210,7 +205,6 @@
<string name="su_revoke_msg">%1$s के अधिकारों को वापस लेने की पुष्टि करें?</string> <string name="su_revoke_msg">%1$s के अधिकारों को वापस लेने की पुष्टि करें?</string>
<string name="toast">पॉप-अप नोट</string> <string name="toast">पॉप-अप नोट</string>
<string name="none">कोई नहीं</string> <string name="none">कोई नहीं</string>
<string name="auth_fail">प्रमाणीकरण विफल हुआ</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
<string name="settings">Setelan</string> <string name="settings">Setelan</string>
<string name="install">Pasang</string> <string name="install">Pasang</string>
<string name="unsupport_magisk_title">Versi Magisk Tidak Didukung</string> <string name="unsupport_magisk_title">Versi Magisk Tidak Didukung</string>
<string name="unsupport_magisk_message">Magisk Manager versi ini tidak mendukung versi Magisk di bawah v18.0.\n\nAnda dapat memperbarui Magisk secara manual, atau menurunkan aplikasi ke versi sebelumnya.</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magisk tidak terpasang.</string> <string name="magisk_version_error">Magisk tidak terpasang.</string>
...@@ -161,9 +160,6 @@ ...@@ -161,9 +160,6 @@
<string name="request_timeout_summary">%1$d detik</string> <string name="request_timeout_summary">%1$d detik</string>
<string name="settings_su_reauth_title">Otentikasi ulang setelah pembaruan</string> <string name="settings_su_reauth_title">Otentikasi ulang setelah pembaruan</string>
<string name="settings_su_reauth_summary">Otentikasi ulang izin superuser setelah pembaruan sebuah aplikasi</string> <string name="settings_su_reauth_summary">Otentikasi ulang izin superuser setelah pembaruan sebuah aplikasi</string>
<string name="settings_su_fingerprint_title">Aktifkan Otentikasi Sidik Jari</string>
<string name="settings_su_fingerprint_summary">Gunakan pemindai sidik jari untuk mengizinkan permintaan superuser</string>
<string name="auth_fingerprint">Otentikasi Sidik Jari</string>
<string name="multiuser_mode">Mode Multipengguna</string> <string name="multiuser_mode">Mode Multipengguna</string>
<string name="settings_owner_only">Pemilik Perangkat Saja</string> <string name="settings_owner_only">Pemilik Perangkat Saja</string>
...@@ -180,7 +176,6 @@ ...@@ -180,7 +176,6 @@
<string name="global_summary">Semua sesi root menggunakan mount ruang nama global.</string> <string name="global_summary">Semua sesi root menggunakan mount ruang nama global.</string>
<string name="requester_summary">Sesi root akan mewarisi ruang nama peminta mereka.</string> <string name="requester_summary">Sesi root akan mewarisi ruang nama peminta mereka.</string>
<string name="isolate_summary">Setiap sesi root akan memiliki ruang nama tersendiri.</string> <string name="isolate_summary">Setiap sesi root akan memiliki ruang nama tersendiri.</string>
<string name="disable_fingerprint">Tidak ada sidik jari diatur atau tidak ada dukungan perangkat.</string>
<string name="settings_download_path_error">Kesalahan membuat folder. Folder harus dapat diakses dari direktori penyimpanan root dan bukan merupakan file.</string> <string name="settings_download_path_error">Kesalahan membuat folder. Folder harus dapat diakses dari direktori penyimpanan root dan bukan merupakan file.</string>
<!--Superuser--> <!--Superuser-->
...@@ -208,7 +203,6 @@ ...@@ -208,7 +203,6 @@
<string name="su_revoke_msg">Konfirmasi untuk mencabut akses %1$s?</string> <string name="su_revoke_msg">Konfirmasi untuk mencabut akses %1$s?</string>
<string name="toast">Toast</string> <string name="toast">Toast</string>
<string name="none">Tidak ada</string> <string name="none">Tidak ada</string>
<string name="auth_fail">Otentikasi Gagal</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
<string name="settings">Impostazioni</string> <string name="settings">Impostazioni</string>
<string name="install">Installa</string> <string name="install">Installa</string>
<string name="unsupport_magisk_title">Versione di Magisk non supportata</string> <string name="unsupport_magisk_title">Versione di Magisk non supportata</string>
<string name="unsupport_magisk_message">Questa versione di Magisk Manager non supporta versioni di Magisk inferiori alla v18.0.\n\nPuoi aggiornare manualmente Magisk o tornare a una versione meno recente dell\'app.</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magisk non è installato.</string> <string name="magisk_version_error">Magisk non è installato.</string>
...@@ -18,7 +17,7 @@ ...@@ -18,7 +17,7 @@
<string name="checking_safetyNet_status">Controllo stato SafetyNet…</string> <string name="checking_safetyNet_status">Controllo stato SafetyNet…</string>
<string name="safetyNet_check_success">Controllo SafetyNet Superato</string> <string name="safetyNet_check_success">Controllo SafetyNet Superato</string>
<string name="safetyNet_api_error">Errore API SafetyNet</string> <string name="safetyNet_api_error">Errore API SafetyNet</string>
<string name="safetyNet_res_invalid">La risposta non è valida.</string> <string name="safetyNet_res_invalid">La risposta non è valida</string>
<string name="magisk_up_to_date">Magisk è aggiornato</string> <string name="magisk_up_to_date">Magisk è aggiornato</string>
<string name="manager_up_to_date">Magisk Manager è aggiornato</string> <string name="manager_up_to_date">Magisk Manager è aggiornato</string>
<string name="advanced_settings_title">Impostazioni avanzate</string> <string name="advanced_settings_title">Impostazioni avanzate</string>
...@@ -28,7 +27,7 @@ ...@@ -28,7 +27,7 @@
<string name="latest_version">Ultima: %1$s</string> <string name="latest_version">Ultima: %1$s</string>
<string name="uninstall">Disinstalla</string> <string name="uninstall">Disinstalla</string>
<string name="uninstall_magisk_title">Disinstalla Magisk</string> <string name="uninstall_magisk_title">Disinstalla Magisk</string>
<string name="uninstall_magisk_msg">Tutti i moduli verranno disabilitati/rimossi. Il root verrà rimosso e i tuoi dati potrebbero essere criptati, nel caso non lo siano già.</string> <string name="uninstall_magisk_msg">Tutti i moduli verranno disabilitati/rimossi!\nIl root verrà rimosso e i tuoi dati potrebbero essere criptati, nel caso non lo siano già.</string>
<string name="update">Aggiorna</string> <string name="update">Aggiorna</string>
<string name="core_only_enabled">(Modalità solo Core abilitata)</string> <string name="core_only_enabled">(Modalità solo Core abilitata)</string>
...@@ -74,7 +73,7 @@ ...@@ -74,7 +73,7 @@
<string name="manager_update_title">È disponibile un aggiornamento di Magisk Manager!</string> <string name="manager_update_title">È disponibile un aggiornamento di Magisk Manager!</string>
<!-- Installation --> <!-- Installation -->
<string name="manager_download_install">Apri questa notifica per scaricare e installare.</string> <string name="manager_download_install">Apri questa notifica per scaricare e installare</string>
<string name="download_zip_only">Scarica solo il file zip</string> <string name="download_zip_only">Scarica solo il file zip</string>
<string name="direct_install">Installazione diretta (raccomandata)</string> <string name="direct_install">Installazione diretta (raccomandata)</string>
<string name="install_inactive_slot">Installa nello slot inattivo (dopo un OTA)</string> <string name="install_inactive_slot">Installa nello slot inattivo (dopo un OTA)</string>
...@@ -90,7 +89,7 @@ ...@@ -90,7 +89,7 @@
<string name="repo_install_msg">Vuoi installare %1$s?</string> <string name="repo_install_msg">Vuoi installare %1$s?</string>
<string name="download">Download</string> <string name="download">Download</string>
<string name="reboot">Riavvia</string> <string name="reboot">Riavvia</string>
<string name="settings_reboot_toast">Riavvia per applicare i cambiamenti.</string> <string name="settings_reboot_toast">Riavvia per applicare i cambiamenti</string>
<string name="release_notes">Note di rilascio</string> <string name="release_notes">Note di rilascio</string>
<string name="repo_cache_cleared">La cache delle repository è stata svuotata</string> <string name="repo_cache_cleared">La cache delle repository è stata svuotata</string>
...@@ -101,7 +100,7 @@ ...@@ -101,7 +100,7 @@
<string name="failure">Fallito</string> <string name="failure">Fallito</string>
<string name="hide_manager_title">Nascondendo Magisk Manager…</string> <string name="hide_manager_title">Nascondendo Magisk Manager…</string>
<string name="hide_manager_fail_toast">Non è stato possibile nascondere Magisk Manager.</string> <string name="hide_manager_fail_toast">Non è stato possibile nascondere Magisk Manager.</string>
<string name="open_link_failed_toast">Nessuna app disponibile per aprire il link.</string> <string name="open_link_failed_toast">Nessuna app disponibile per aprire il link</string>
<string name="warning">Attenzione</string> <string name="warning">Attenzione</string>
<string name="complete_uninstall">Disinstallazione completa</string> <string name="complete_uninstall">Disinstallazione completa</string>
<string name="restore_img">Ripristina Immagini</string> <string name="restore_img">Ripristina Immagini</string>
...@@ -110,7 +109,7 @@ ...@@ -110,7 +109,7 @@
<string name="restore_fail">Non esiste un\'immagine originale di boot!</string> <string name="restore_fail">Non esiste un\'immagine originale di boot!</string>
<string name="proprietary_title">Scarica codice proprietario</string> <string name="proprietary_title">Scarica codice proprietario</string>
<string name="proprietary_notice">Magisk Manager è FOSS e non contiene codice proprietario delle API Google SafetyNet.\n\nVuoi scaricare un\'estensione (contenente GoogleApiClient) per controllare lo stato di SafetyNet?</string> <string name="proprietary_notice">Magisk Manager è FOSS e non contiene codice proprietario delle API Google SafetyNet.\n\nVuoi scaricare un\'estensione (contenente GoogleApiClient) per controllare lo stato di SafetyNet?</string>
<string name="setup_fail">Configurazione fallita.</string> <string name="setup_fail">Configurazione fallita</string>
<string name="env_fix_title">Configurazione aggiuntiva richiesta</string> <string name="env_fix_title">Configurazione aggiuntiva richiesta</string>
<string name="env_fix_msg">Il tuo dispositivo necessita di una configurazione aggiuntiva per far funzionare Magisk correttamente. Verrà scaricato il file zip di Magisk, vuoi procedere ora?</string> <string name="env_fix_msg">Il tuo dispositivo necessita di una configurazione aggiuntiva per far funzionare Magisk correttamente. Verrà scaricato il file zip di Magisk, vuoi procedere ora?</string>
<string name="setup_msg">Configurazione dell\'ambiente in corso…</string> <string name="setup_msg">Configurazione dell\'ambiente in corso…</string>
...@@ -118,20 +117,20 @@ ...@@ -118,20 +117,20 @@
<!--Settings Activity --> <!--Settings Activity -->
<string name="settings_general_category">Generale</string> <string name="settings_general_category">Generale</string>
<string name="settings_dark_theme_title">Tema scuro</string> <string name="settings_dark_theme_title">Tema scuro</string>
<string name="settings_dark_theme_summary">Abilita il tema scuro.</string> <string name="settings_dark_theme_summary">Abilita il tema scuro</string>
<string name="settings_download_path_title">Percorso di download</string> <string name="settings_download_path_title">Percorso di download</string>
<string name="settings_download_path_message">I file verranno salvati in %1$s</string> <string name="settings_download_path_message">I file verranno salvati in %1$s</string>
<string name="settings_clear_cache_title">Svuota cache repository</string> <string name="settings_clear_cache_title">Svuota cache repository</string>
<string name="settings_clear_cache_summary">Svuota la cache delle repository. Questa opzione forza l\'aggiornamento online dell\'app.</string> <string name="settings_clear_cache_summary">Svuota la cache delle repository. Questa opzione forza l\'aggiornamento online dell\'app</string>
<string name="settings_hide_manager_title">Nascondi Magisk Manager</string> <string name="settings_hide_manager_title">Nascondi Magisk Manager</string>
<string name="settings_hide_manager_summary">Reinstalla Magisk Manager con un nome pacchetto casuale.</string> <string name="settings_hide_manager_summary">Reinstalla Magisk Manager con un nome pacchetto e app casuali</string>
<string name="settings_restore_manager_title">Ripristina Magisk Manager</string> <string name="settings_restore_manager_title">Ripristina Magisk Manager</string>
<string name="settings_restore_manager_summary">Ripristina Magisk Manager con il nome pacchetto originale</string> <string name="settings_restore_manager_summary">Ripristina Magisk Manager con il nome pacchetto e app originali</string>
<string name="language">Lingua</string> <string name="language">Lingua</string>
<string name="system_default">(Sistema)</string> <string name="system_default">(Sistema)</string>
<string name="settings_update">Impostazioni aggiornamento</string> <string name="settings_update">Impostazioni aggiornamento</string>
<string name="settings_check_update_title">Controlla aggiornamenti</string> <string name="settings_check_update_title">Controlla aggiornamenti</string>
<string name="settings_check_update_summary">Controlla automaticamente gli aggiornamenti in background.</string> <string name="settings_check_update_summary">Controlla automaticamente gli aggiornamenti in background</string>
<string name="settings_update_channel_title">Canale di aggiornamento</string> <string name="settings_update_channel_title">Canale di aggiornamento</string>
<string name="settings_update_stable">Stabile</string> <string name="settings_update_stable">Stabile</string>
<string name="settings_update_beta">Beta</string> <string name="settings_update_beta">Beta</string>
...@@ -139,11 +138,15 @@ ...@@ -139,11 +138,15 @@
<string name="settings_update_custom_msg">Inserisci un URL personalizzato</string> <string name="settings_update_custom_msg">Inserisci un URL personalizzato</string>
<string name="settings_core_only_title">Modalità Magisk Core</string> <string name="settings_core_only_title">Modalità Magisk Core</string>
<string name="settings_core_only_summary">Abilita solo le funzioni principali. Nessun modulo verrà caricato. MagiskSU e MagiskHide rimarranno abilitati</string> <string name="settings_core_only_summary">Abilita solo le funzioni principali. Nessun modulo verrà caricato. MagiskSU e MagiskHide rimarranno abilitati</string>
<string name="settings_magiskhide_summary">Nasconde Magisk da numerose rilevazioni.</string> <string name="settings_magiskhide_summary">Nasconde Magisk da numerose rilevazioni</string>
<string name="settings_hosts_title">Host systemless</string> <string name="settings_hosts_title">Host systemless</string>
<string name="settings_hosts_summary">Supporto a host systemless per le app che bloccano le pubblicità.</string> <string name="settings_hosts_summary">Supporto a host systemless per le app che bloccano le pubblicità</string>
<string name="settings_hosts_toast">Aggiunto modulo per host systemless</string> <string name="settings_hosts_toast">Aggiunto modulo per host systemless</string>
<string name="settings_app_name">Inserisci il nome desiderato per l\'app</string>
<string name="settings_app_name_hint">Nuovo nome</string>
<string name="settings_app_name_helper">L\'app sarà ricreata con questo nome</string>
<string name="settings_app_name_error">Formato non valido</string>
<string name="settings_su_app_adb">App e ADB</string> <string name="settings_su_app_adb">App e ADB</string>
<string name="settings_su_app">Solo app</string> <string name="settings_su_app">Solo app</string>
<string name="settings_su_adb">Solo ADB</string> <string name="settings_su_adb">Solo ADB</string>
...@@ -161,26 +164,22 @@ ...@@ -161,26 +164,22 @@
<string name="request_timeout_summary">%1$d secondi</string> <string name="request_timeout_summary">%1$d secondi</string>
<string name="settings_su_reauth_title">Riautentica dopo aggiornamento</string> <string name="settings_su_reauth_title">Riautentica dopo aggiornamento</string>
<string name="settings_su_reauth_summary">Riautentica i permessi Superuser dopo un aggiornamento dell\'app</string> <string name="settings_su_reauth_summary">Riautentica i permessi Superuser dopo un aggiornamento dell\'app</string>
<string name="settings_su_fingerprint_title">Abilita autenticazione impronta</string>
<string name="settings_su_fingerprint_summary">Utilizza il sensore di impronte per accettare le richieste Superuser</string>
<string name="auth_fingerprint">Conferma impronta</string>
<string name="multiuser_mode">Modalità multiutente</string> <string name="multiuser_mode">Modalità multiutente</string>
<string name="settings_owner_only">Solo proprietario del dispositivo</string> <string name="settings_owner_only">Solo proprietario del dispositivo</string>
<string name="settings_owner_manage">Gestito dal proprietario utente</string> <string name="settings_owner_manage">Gestito dal proprietario utente</string>
<string name="settings_user_independent">Idipendente dall\'utente</string> <string name="settings_user_independent">Idipendente dall\'utente</string>
<string name="owner_only_summary">Solo il proprietario ha i permessi di root.</string> <string name="owner_only_summary">Solo il proprietario ha i permessi di root</string>
<string name="owner_manage_summary">Solo il proprietario può gestire accesso root e ricevere richieste.</string> <string name="owner_manage_summary">Solo il proprietario può gestire accesso root e ricevere richieste</string>
<string name="user_indepenent_summary">Ogni utente ha le sue regole di root indpendenti.</string> <string name="user_indepenent_summary">Ogni utente ha le sue regole di root indpendenti</string>
<string name="mount_namespace_mode">Modalità mount namespace</string> <string name="mount_namespace_mode">Modalità mount namespace</string>
<string name="settings_ns_global">Namespace globale</string> <string name="settings_ns_global">Namespace globale</string>
<string name="settings_ns_requester">Namespace ereditato</string> <string name="settings_ns_requester">Namespace ereditato</string>
<string name="settings_ns_isolate">Namespace isolato</string> <string name="settings_ns_isolate">Namespace isolato</string>
<string name="global_summary">Tutte le sessioni di root erediteranno il namespace globale.</string> <string name="global_summary">Tutte le sessioni di root erediteranno il namespace globale</string>
<string name="requester_summary">Le sessioni di root erediteranno il namespace del loro richiedente.</string> <string name="requester_summary">Le sessioni di root erediteranno il namespace del loro richiedente</string>
<string name="isolate_summary">Ogni sessione di root avrà il suo namespace isolato.</string> <string name="isolate_summary">Ogni sessione di root avrà il suo namespace isolato</string>
<string name="disable_fingerprint">Non è presente alcuna impronta o il dispositivo non è supportato.</string>
<string name="settings_download_path_error">Errore durante la creazione della cartella. Deve essere accessibile dalla radice della memoria di archiviazione e non essere un file.</string> <string name="settings_download_path_error">Errore durante la creazione della cartella. Deve essere accessibile dalla radice della memoria di archiviazione e non essere un file.</string>
<!--Superuser--> <!--Superuser-->
...@@ -208,7 +207,6 @@ ...@@ -208,7 +207,6 @@
<string name="su_revoke_msg">Confermi la revoca dei diritti di %1$s?</string> <string name="su_revoke_msg">Confermi la revoca dei diritti di %1$s?</string>
<string name="toast">Toast</string> <string name="toast">Toast</string>
<string name="none">Nessuno</string> <string name="none">Nessuno</string>
<string name="auth_fail">Autenticatione fallita</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
<string name="settings">設定</string> <string name="settings">設定</string>
<string name="install">インストール</string> <string name="install">インストール</string>
<string name="unsupport_magisk_title">対応していないMagiskバージョン</string> <string name="unsupport_magisk_title">対応していないMagiskバージョン</string>
<string name="unsupport_magisk_message">このバージョンのMagisk ManagerはMagisk v18.0以下には対応していません。\n\n手動でMagiskを更新するか、または古いバージョンのMagisk Managerをインストールしてください。</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magiskがインストールされていません</string> <string name="magisk_version_error">Magiskがインストールされていません</string>
...@@ -149,9 +148,6 @@ ...@@ -149,9 +148,6 @@
<string name="request_timeout_summary">%1$d秒</string> <string name="request_timeout_summary">%1$d秒</string>
<string name="settings_su_reauth_title">アップグレード後の再認証</string> <string name="settings_su_reauth_title">アップグレード後の再認証</string>
<string name="settings_su_reauth_summary">アプリのアップグレード後にスーパーユーザー権限を再認証します</string> <string name="settings_su_reauth_summary">アプリのアップグレード後にスーパーユーザー権限を再認証します</string>
<string name="settings_su_fingerprint_title">指紋認証の有効化</string>
<string name="settings_su_fingerprint_summary">スーパーユーザー権限のリクエストの許可に指紋認証を使います</string>
<string name="auth_fingerprint">指紋認証</string>
<string name="multiuser_mode">マルチユーザーモード</string> <string name="multiuser_mode">マルチユーザーモード</string>
<string name="settings_owner_only">端末の管理者のみ</string> <string name="settings_owner_only">端末の管理者のみ</string>
...@@ -168,7 +164,6 @@ ...@@ -168,7 +164,6 @@
<string name="global_summary">すべてのrootセッションがグローバル名前空間を使用します</string> <string name="global_summary">すべてのrootセッションがグローバル名前空間を使用します</string>
<string name="requester_summary">rootセッションはリクエスト者の名前空間を継承します</string> <string name="requester_summary">rootセッションはリクエスト者の名前空間を継承します</string>
<string name="isolate_summary">rootセッション毎に分離された名前空間を使用します</string> <string name="isolate_summary">rootセッション毎に分離された名前空間を使用します</string>
<string name="disable_fingerprint">指紋が登録されていないか、お使いの端末でサポートされていません。</string>
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">スーパーユーザーリクエスト</string> <string name="su_request_title">スーパーユーザーリクエスト</string>
...@@ -195,7 +190,6 @@ ...@@ -195,7 +190,6 @@
<string name="su_revoke_msg">%1$s の権限を取り消しますか?</string> <string name="su_revoke_msg">%1$s の権限を取り消しますか?</string>
<string name="toast">トースト通知</string> <string name="toast">トースト通知</string>
<string name="none">なし</string> <string name="none">なし</string>
<string name="auth_fail">認証に失敗しました</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
<string name="settings">설정</string> <string name="settings">설정</string>
<string name="install">설치</string> <string name="install">설치</string>
<string name="unsupport_magisk_title">지원되지 않는 Magisk 버전</string> <string name="unsupport_magisk_title">지원되지 않는 Magisk 버전</string>
<string name="unsupport_magisk_message">이 버전의 Magisk Manager는 v18.0보다 낮은 버전의 Magisk를 지원하지 않습니다.\n\n직접 Magisk를 업데이트 하거나 이전 버전의 앱으로 다운그레이드하십시오..</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magisk가 설치되지 않음</string> <string name="magisk_version_error">Magisk가 설치되지 않음</string>
...@@ -156,9 +155,6 @@ ...@@ -156,9 +155,6 @@
<string name="request_timeout_summary">%1$d초</string> <string name="request_timeout_summary">%1$d초</string>
<string name="settings_su_reauth_title">업데이트 후 재승인</string> <string name="settings_su_reauth_title">업데이트 후 재승인</string>
<string name="settings_su_reauth_summary">앱 업데이트 후에 슈퍼유저 권한 재승인</string> <string name="settings_su_reauth_summary">앱 업데이트 후에 슈퍼유저 권한 재승인</string>
<string name="settings_su_fingerprint_title">지문 인식 사용</string>
<string name="settings_su_fingerprint_summary">지문 인식으로 슈퍼유저 권한을 허가합니다.</string>
<string name="auth_fingerprint">지문 인식</string>
<string name="multiuser_mode">다중 사용자 모드</string> <string name="multiuser_mode">다중 사용자 모드</string>
<string name="settings_owner_only">기기 소유자만</string> <string name="settings_owner_only">기기 소유자만</string>
...@@ -175,7 +171,6 @@ ...@@ -175,7 +171,6 @@
<string name="global_summary">모든 루트 세션이 전역 마운트 이름공간을 사용합니다.</string> <string name="global_summary">모든 루트 세션이 전역 마운트 이름공간을 사용합니다.</string>
<string name="requester_summary">루트 세션은 요청자의 이름공간을 상속합니다.</string> <string name="requester_summary">루트 세션은 요청자의 이름공간을 상속합니다.</string>
<string name="isolate_summary">각각의 루트 세션은 자신만의 독립된 이름공간을 사용합니다.</string> <string name="isolate_summary">각각의 루트 세션은 자신만의 독립된 이름공간을 사용합니다.</string>
<string name="disable_fingerprint">지문이 등록되지 않았거나 기기가 지문 인식을 지원하지 않습니다.</string>
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">슈퍼유저 요청</string> <string name="su_request_title">슈퍼유저 요청</string>
...@@ -202,7 +197,6 @@ ...@@ -202,7 +197,6 @@
<string name="su_revoke_msg">정말 %1$s의 권한을 취소하시겠습니까?</string> <string name="su_revoke_msg">정말 %1$s의 권한을 취소하시겠습니까?</string>
<string name="toast">토스트</string> <string name="toast">토스트</string>
<string name="none">없음</string> <string name="none">없음</string>
<string name="auth_fail">인증 실패</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
......
...@@ -137,8 +137,6 @@ ...@@ -137,8 +137,6 @@
<string name="request_timeout_summary">%1$d sekundžių</string> <string name="request_timeout_summary">%1$d sekundžių</string>
<string name="settings_su_reauth_title">Pakartotinai patvirtinti po atnaujinimo</string> <string name="settings_su_reauth_title">Pakartotinai patvirtinti po atnaujinimo</string>
<string name="settings_su_reauth_summary">Pakartotinai patvirtinti supervartotojo leidimus po programėlės atnaujinimo</string> <string name="settings_su_reauth_summary">Pakartotinai patvirtinti supervartotojo leidimus po programėlės atnaujinimo</string>
<string name="settings_su_fingerprint_title">Įgalinti patvirtinimą piršto antspaudu</string>
<string name="settings_su_fingerprint_summary">Naudoti piršto antspaudą supervartotojo leidimo prašymų atsakymui</string>
<string name="multiuser_mode">Daugialypio vartotojo režimas</string> <string name="multiuser_mode">Daugialypio vartotojo režimas</string>
<string name="settings_owner_only">Tik įrenginio savininkas</string> <string name="settings_owner_only">Tik įrenginio savininkas</string>
...@@ -155,7 +153,6 @@ ...@@ -155,7 +153,6 @@
<string name="global_summary">Visos root sesijos naudoja globalią vardų sritį</string> <string name="global_summary">Visos root sesijos naudoja globalią vardų sritį</string>
<string name="requester_summary">Root sesijos paveldi jos išprašytojo/s vardų sritį</string> <string name="requester_summary">Root sesijos paveldi jos išprašytojo/s vardų sritį</string>
<string name="isolate_summary">Kiekviena root sesija turi savo izoliuotą vardų sritį</string> <string name="isolate_summary">Kiekviena root sesija turi savo izoliuotą vardų sritį</string>
<string name="disable_fingerprint">Jūsų įrenginyje nebuvo surasta pirštų antspaudų arba jūsų įrenginys neturi pirštų antspaudų skaitytuvo</string>
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">Supervartotojo prašymas</string> <string name="su_request_title">Supervartotojo prašymas</string>
...@@ -182,7 +179,6 @@ ...@@ -182,7 +179,6 @@
<string name="su_revoke_msg">Neleisti %1$s naudotis supervartotojo teisėmis?</string> <string name="su_revoke_msg">Neleisti %1$s naudotis supervartotojo teisėmis?</string>
<string name="toast">Išmesti</string> <string name="toast">Išmesti</string>
<string name="none">Nėra</string> <string name="none">Nėra</string>
<string name="auth_fail">Patvirtinimas žlugo</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="target_uid">Target UID: %1$d</string> <string name="target_uid">Target UID: %1$d</string>
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
<string name="settings">Поставки</string> <string name="settings">Поставки</string>
<string name="install">Инсталирај</string> <string name="install">Инсталирај</string>
<string name="unsupport_magisk_title">Неподдржана верзија на Magisk</string> <string name="unsupport_magisk_title">Неподдржана верзија на Magisk</string>
<string name="unsupport_magisk_message">Оваа верзија на Magisk Manager не ја поддржува верзијата на Magisk пониска од v18.0.\n\nМожно е да рачно да го надградите Magisk или да ја вратите апликацијата на постара верзија.</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magisk не е инсталиран.</string> <string name="magisk_version_error">Magisk не е инсталиран.</string>
...@@ -155,9 +154,6 @@ ...@@ -155,9 +154,6 @@
<string name="request_timeout_summary">%1$d секунди</string> <string name="request_timeout_summary">%1$d секунди</string>
<string name="settings_su_reauth_title">Повторна автентикација по надградба</string> <string name="settings_su_reauth_title">Повторна автентикација по надградба</string>
<string name="settings_su_reauth_summary">Повторна автентикација за супер-корисник дозвола по надградбата на апликацијата</string> <string name="settings_su_reauth_summary">Повторна автентикација за супер-корисник дозвола по надградбата на апликацијата</string>
<string name="settings_su_fingerprint_title">Овозможи автентикација на отпечатоци</string>
<string name="settings_su_fingerprint_summary">Користете скенер за отпечатоци за да дозволите супер-корисник барања</string>
<string name="auth_fingerprint">Автентикација на отпечатоци</string>
<string name="multiuser_mode">Режим на повеќе корисници</string> <string name="multiuser_mode">Режим на повеќе корисници</string>
<string name="settings_owner_only">Само сопственикот на уредот</string> <string name="settings_owner_only">Само сопственикот на уредот</string>
...@@ -174,7 +170,6 @@ ...@@ -174,7 +170,6 @@
<string name="global_summary">Сите рут сесии го користат глобалниот именски простор.</string> <string name="global_summary">Сите рут сесии го користат глобалниот именски простор.</string>
<string name="requester_summary">Рут сесиите ќе го наследат именскиот простор на нивниот барател.</string> <string name="requester_summary">Рут сесиите ќе го наследат именскиот простор на нивниот барател.</string>
<string name="isolate_summary">Секоја рут сесија ќе има свој изолиран именски простор.</string> <string name="isolate_summary">Секоја рут сесија ќе има свој изолиран именски простор.</string>
<string name="disable_fingerprint">Нема регистрирано отпечатоци од прсти или уредот не ја поддржува оваа функција.</string>
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">Супер-корисник барање</string> <string name="su_request_title">Супер-корисник барање</string>
...@@ -201,7 +196,6 @@ ...@@ -201,7 +196,6 @@
<string name="su_revoke_msg">Дали потврдувате анулирање на поставките за пристап на %1$s?</string> <string name="su_revoke_msg">Дали потврдувате анулирање на поставките за пристап на %1$s?</string>
<string name="toast">Тост</string> <string name="toast">Тост</string>
<string name="none">Ниеден</string> <string name="none">Ниеден</string>
<string name="auth_fail">Неуспешна автентикација</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
......
...@@ -146,9 +146,6 @@ ...@@ -146,9 +146,6 @@
<string name="request_timeout_summary">%1$d sekunder</string> <string name="request_timeout_summary">%1$d sekunder</string>
<string name="settings_su_reauth_title">Autentiser på nytt etter oppdateringer after upgrade</string> <string name="settings_su_reauth_title">Autentiser på nytt etter oppdateringer after upgrade</string>
<string name="settings_su_reauth_summary">Autentiser superbruker-tillatelser på nytt etter at en app har blitt oppdatert</string> <string name="settings_su_reauth_summary">Autentiser superbruker-tillatelser på nytt etter at en app har blitt oppdatert</string>
<string name="settings_su_fingerprint_title">Skru på fingeravtrykksautentisering</string>
<string name="settings_su_fingerprint_summary">Bruk fingeravtrykksskanneren for å godkjenne superbruker-forespørsler</string>
<string name="auth_fingerprint">Autentiser fingeravtrykk</string>
<string name="multiuser_mode">Flerbrukermoduse</string> <string name="multiuser_mode">Flerbrukermoduse</string>
<string name="settings_owner_only">Kun enhetens eier</string> <string name="settings_owner_only">Kun enhetens eier</string>
...@@ -165,7 +162,6 @@ ...@@ -165,7 +162,6 @@
<string name="global_summary">Alle root-økter benytter det altdekkende monteringsnavnefeltet.</string> <string name="global_summary">Alle root-økter benytter det altdekkende monteringsnavnefeltet.</string>
<string name="requester_summary">Root-økter vil arve forespørrerens navnefelt.</string> <string name="requester_summary">Root-økter vil arve forespørrerens navnefelt.</string>
<string name="isolate_summary">Hver root-økt vil ha sitt eget isolerte navnefelt.</string> <string name="isolate_summary">Hver root-økt vil ha sitt eget isolerte navnefelt.</string>
<string name="disable_fingerprint">Ingen fingeravtrykk ble gitt, eller så støttes det ikke av enheten.</string>
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">Superbruker-forespørsel</string> <string name="su_request_title">Superbruker-forespørsel</string>
...@@ -192,7 +188,6 @@ ...@@ -192,7 +188,6 @@
<string name="su_revoke_msg">Vil du bekrefte for å oppheve %1$s sine rettigheter?</string> <string name="su_revoke_msg">Vil du bekrefte for å oppheve %1$s sine rettigheter?</string>
<string name="toast">Varselfelt</string> <string name="toast">Varselfelt</string>
<string name="none">Ingen</string> <string name="none">Ingen</string>
<string name="auth_fail">Autentisering mislyktes</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="target_uid">Mål-UID: %1$d</string> <string name="target_uid">Mål-UID: %1$d</string>
......
...@@ -132,8 +132,6 @@ ...@@ -132,8 +132,6 @@
<string name="request_timeout_summary">%1$d seconden</string> <string name="request_timeout_summary">%1$d seconden</string>
<string name="settings_su_reauth_title">Opnieuw verzoeken na bijwerken</string> <string name="settings_su_reauth_title">Opnieuw verzoeken na bijwerken</string>
<string name="settings_su_reauth_summary">Superuser rechten opnieuw opvragen na bijwerken applicatie</string> <string name="settings_su_reauth_summary">Superuser rechten opnieuw opvragen na bijwerken applicatie</string>
<string name="settings_su_fingerprint_title">Vingerafdruk authenticatie inschakelen</string>
<string name="settings_su_fingerprint_summary">Vingerafdruk gebruiken om superuser verzoeken toe te staan</string>
<string name="multiuser_mode">Multi-gebruiker modus</string> <string name="multiuser_mode">Multi-gebruiker modus</string>
<string name="settings_owner_only">Alleen apparaateigenaar</string> <string name="settings_owner_only">Alleen apparaateigenaar</string>
...@@ -150,7 +148,6 @@ ...@@ -150,7 +148,6 @@
<string name="global_summary">Alle rootsessies gebruiken de globale naamruimte</string> <string name="global_summary">Alle rootsessies gebruiken de globale naamruimte</string>
<string name="requester_summary">Rootsessies verkrijgen de verzoeker\'s naamruimte</string> <string name="requester_summary">Rootsessies verkrijgen de verzoeker\'s naamruimte</string>
<string name="isolate_summary">Iedere rootsessie heeft een eigen geïsoleerde naamruimte</string> <string name="isolate_summary">Iedere rootsessie heeft een eigen geïsoleerde naamruimte</string>
<string name="disable_fingerprint">Geen vingerafdrukken ingesteld, of geen apparaatondersteuning</string>
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">Superuser verzoek</string> <string name="su_request_title">Superuser verzoek</string>
...@@ -177,7 +174,6 @@ ...@@ -177,7 +174,6 @@
<string name="su_revoke_msg">De rechten van %1$s intrekken?</string> <string name="su_revoke_msg">De rechten van %1$s intrekken?</string>
<string name="toast">Toast</string> <string name="toast">Toast</string>
<string name="none">Geen</string> <string name="none">Geen</string>
<string name="auth_fail">Authenticatie mislukt</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="target_uid">Doel UID: %1$d</string> <string name="target_uid">Doel UID: %1$d</string>
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
<string name="settings">Ustawienia</string> <string name="settings">Ustawienia</string>
<string name="install">Instaluj</string> <string name="install">Instaluj</string>
<string name="unsupport_magisk_title">Nieobsługiwana Wersja Magisk</string> <string name="unsupport_magisk_title">Nieobsługiwana Wersja Magisk</string>
<string name="unsupport_magisk_message">Ta wersja Magisk Managera nie obsługuje wersji Magisk niższej niż v18.0.\n\nMożesz albo ręcznie zaktualizować Magisk lub obniżyć w aplikacji do starszej wersji.</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magisk nie jest zainstalowany.</string> <string name="magisk_version_error">Magisk nie jest zainstalowany.</string>
...@@ -24,6 +23,7 @@ ...@@ -24,6 +23,7 @@
<string name="advanced_settings_title">Zaawansowane Ustawienia</string> <string name="advanced_settings_title">Zaawansowane Ustawienia</string>
<string name="keep_force_encryption">Zachowaj force encryption (wymuszenie szyfrowania)</string> <string name="keep_force_encryption">Zachowaj force encryption (wymuszenie szyfrowania)</string>
<string name="keep_dm_verity">Zachowaj AVB 2.0/dm-verity (weryfikację procesu bootowania)</string> <string name="keep_dm_verity">Zachowaj AVB 2.0/dm-verity (weryfikację procesu bootowania)</string>
<string name="recovery_mode">Tryb Recovery</string>
<string name="current_installed">Zainstalowana: %1$s</string> <string name="current_installed">Zainstalowana: %1$s</string>
<string name="latest_version">Ostatnia: %1$s</string> <string name="latest_version">Ostatnia: %1$s</string>
<string name="uninstall">Odinstaluj</string> <string name="uninstall">Odinstaluj</string>
...@@ -145,6 +145,10 @@ ...@@ -145,6 +145,10 @@
<string name="settings_hosts_summary">Wsparcie systemless dla aplikacji Adblock</string> <string name="settings_hosts_summary">Wsparcie systemless dla aplikacji Adblock</string>
<string name="settings_hosts_toast">Dodano moduł systemless hosts</string> <string name="settings_hosts_toast">Dodano moduł systemless hosts</string>
<string name="settings_app_name">Wpisz żądaną nazwę aplikacji</string>
<string name="settings_app_name_hint">Nowa Nazwa</string>
<string name="settings_app_name_helper">Aplikacja zostanie ponownie spakowana do tej nazwy</string>
<string name="settings_app_name_error">Zły format</string>
<string name="settings_su_app_adb">Aplikacje i ADB</string> <string name="settings_su_app_adb">Aplikacje i ADB</string>
<string name="settings_su_app">Tylko Aplikacje</string> <string name="settings_su_app">Tylko Aplikacje</string>
<string name="settings_su_adb">Tylko ADB</string> <string name="settings_su_adb">Tylko ADB</string>
...@@ -162,9 +166,6 @@ ...@@ -162,9 +166,6 @@
<string name="request_timeout_summary">%1$d sekund</string> <string name="request_timeout_summary">%1$d sekund</string>
<string name="settings_su_reauth_title">Ponowienie uwierzytelnienia po aktualizacji</string> <string name="settings_su_reauth_title">Ponowienie uwierzytelnienia po aktualizacji</string>
<string name="settings_su_reauth_summary">Ponowne uwierzytelnianie uprawnienia superużytkownika po aktualizacji aplikacji</string> <string name="settings_su_reauth_summary">Ponowne uwierzytelnianie uprawnienia superużytkownika po aktualizacji aplikacji</string>
<string name="settings_su_fingerprint_title">Włącz Uwierzytelnienie Odciskiem Palca</string>
<string name="settings_su_fingerprint_summary">Użyj skanera linii papilarnych, aby zezwolić na żądania supersu</string>
<string name="auth_fingerprint">Uwierzytelnianie Odciskiem Palca</string>
<string name="multiuser_mode">Tryb Multiusera</string> <string name="multiuser_mode">Tryb Multiusera</string>
<string name="settings_owner_only">Tylko Właściciel Urządzenia</string> <string name="settings_owner_only">Tylko Właściciel Urządzenia</string>
...@@ -181,7 +182,6 @@ ...@@ -181,7 +182,6 @@
<string name="global_summary">Wszystkie sesje root za pomocą globalnej przestrzeni montowań nazw</string> <string name="global_summary">Wszystkie sesje root za pomocą globalnej przestrzeni montowań nazw</string>
<string name="requester_summary">Sesje Root będzie dziedziczyć prośby i nazwy</string> <string name="requester_summary">Sesje Root będzie dziedziczyć prośby i nazwy</string>
<string name="isolate_summary">W każdej sesji root będzie miał własną odosobnioną nazwę</string> <string name="isolate_summary">W każdej sesji root będzie miał własną odosobnioną nazwę</string>
<string name="disable_fingerprint">Nie ustawiono żadnych odcisków palców lub brak obsługi urządzenia</string>
<string name="settings_download_path_error">Błąd podczas tworzenia folderu. Musi być dostępny z głównego katalogu pamięci i nie może być plikiem.</string> <string name="settings_download_path_error">Błąd podczas tworzenia folderu. Musi być dostępny z głównego katalogu pamięci i nie może być plikiem.</string>
<!--Superuser--> <!--Superuser-->
...@@ -209,7 +209,6 @@ ...@@ -209,7 +209,6 @@
<string name="su_revoke_msg">Potwierdzasz odwołanie uprawnień %1$s?</string> <string name="su_revoke_msg">Potwierdzasz odwołanie uprawnień %1$s?</string>
<string name="toast">Powiadomienie</string> <string name="toast">Powiadomienie</string>
<string name="none">Brak</string> <string name="none">Brak</string>
<string name="auth_fail">Uwierzytelnienie Nieudane</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
......
...@@ -134,8 +134,6 @@ ...@@ -134,8 +134,6 @@
<string name="request_timeout_summary">%1$d segundos</string> <string name="request_timeout_summary">%1$d segundos</string>
<string name="settings_su_reauth_title">Reautenticar após atualizar</string> <string name="settings_su_reauth_title">Reautenticar após atualizar</string>
<string name="settings_su_reauth_summary">Reautenticar permissões de superusuário após um app atualizar</string> <string name="settings_su_reauth_summary">Reautenticar permissões de superusuário após um app atualizar</string>
<string name="settings_su_fingerprint_title">Ativar Autenticação de Impressão Digital</string>
<string name="settings_su_fingerprint_summary">Usar escaneador de impressão digital para permitir solicitações de superusuário</string>
<string name="multiuser_mode">Modo de Multiusuário</string> <string name="multiuser_mode">Modo de Multiusuário</string>
<string name="settings_owner_only">Proprietário do Dispositivo Apenas</string> <string name="settings_owner_only">Proprietário do Dispositivo Apenas</string>
...@@ -152,7 +150,6 @@ ...@@ -152,7 +150,6 @@
<string name="global_summary">Todas as sessões root usam montagem de espaço de nome global</string> <string name="global_summary">Todas as sessões root usam montagem de espaço de nome global</string>
<string name="requester_summary">As sessões root herdarão espaço de nome de seu solicitante</string> <string name="requester_summary">As sessões root herdarão espaço de nome de seu solicitante</string>
<string name="isolate_summary">Cada sessão root terá seu próprio espaço de nome isolado</string> <string name="isolate_summary">Cada sessão root terá seu próprio espaço de nome isolado</string>
<string name="disable_fingerprint">Nenhuma impressão digital foi definida ou o dispostivo não tem suporte</string>
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">Solicitação de Superusuário</string> <string name="su_request_title">Solicitação de Superusuário</string>
...@@ -179,7 +176,6 @@ ...@@ -179,7 +176,6 @@
<string name="su_revoke_msg">Revogar os direitos de %1$s?</string> <string name="su_revoke_msg">Revogar os direitos de %1$s?</string>
<string name="toast">Notificação toast</string> <string name="toast">Notificação toast</string>
<string name="none">Nenhuma</string> <string name="none">Nenhuma</string>
<string name="auth_fail">Falha de Autenticação</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
<string name="settings">Setări</string> <string name="settings">Setări</string>
<string name="install">Instalează</string> <string name="install">Instalează</string>
<string name="unsupport_magisk_title">Versiune Magisk nesuportată</string> <string name="unsupport_magisk_title">Versiune Magisk nesuportată</string>
<string name="unsupport_magisk_message">Această versiune de Magisk Manager nu suportă versiunea Magisk mai mică de v18.0.\n\nPoți fie să actualizezi manual Magisk, fie să treci la o versiune mai veche.</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magisk nu este instalat.</string> <string name="magisk_version_error">Magisk nu este instalat.</string>
...@@ -162,9 +161,6 @@ ...@@ -162,9 +161,6 @@
<string name="request_timeout_summary">%1$d secunde</string> <string name="request_timeout_summary">%1$d secunde</string>
<string name="settings_su_reauth_title">Reautentificare după actualizare</string> <string name="settings_su_reauth_title">Reautentificare după actualizare</string>
<string name="settings_su_reauth_summary">Reautentifică permisiunile pentru superutilizator după o actualizare a aplicației</string> <string name="settings_su_reauth_summary">Reautentifică permisiunile pentru superutilizator după o actualizare a aplicației</string>
<string name="settings_su_fingerprint_title">Activează autentificarea cu amprenta</string>
<string name="settings_su_fingerprint_summary">Folosește scannerul de amprente pentru a permite solicitările de superutilizator</string>
<string name="auth_fingerprint">Autentifică amprenta</string>
<string name="multiuser_mode">Mod de multiutilizator</string> <string name="multiuser_mode">Mod de multiutilizator</string>
<string name="settings_owner_only">Numai proprietarul dispozitivului</string> <string name="settings_owner_only">Numai proprietarul dispozitivului</string>
...@@ -181,7 +177,6 @@ ...@@ -181,7 +177,6 @@
<string name="global_summary">Toate sesiunile de root folosesc spațiul de nume global.</string> <string name="global_summary">Toate sesiunile de root folosesc spațiul de nume global.</string>
<string name="requester_summary">Sesiunile de root vor moșteni spațiul de nume al solicitantului.</string> <string name="requester_summary">Sesiunile de root vor moșteni spațiul de nume al solicitantului.</string>
<string name="isolate_summary">Fiecare sesiune de root va avea propriul spațiu de nume izolat.</string> <string name="isolate_summary">Fiecare sesiune de root va avea propriul spațiu de nume izolat.</string>
<string name="disable_fingerprint">Nu au fost setate amprente sau scannerul de amprentă lipsește.</string>
<string name="settings_download_path_error">Eroare la crearea dosarului. Acesta trebuie să fie accesibil din directorul rădăcină al stocării și nu trebuie să fie un fișier.</string> <string name="settings_download_path_error">Eroare la crearea dosarului. Acesta trebuie să fie accesibil din directorul rădăcină al stocării și nu trebuie să fie un fișier.</string>
<!--Superuser--> <!--Superuser-->
...@@ -209,7 +204,6 @@ ...@@ -209,7 +204,6 @@
<string name="su_revoke_msg">Confirmi revocarea drepturilor pentru %1$s?</string> <string name="su_revoke_msg">Confirmi revocarea drepturilor pentru %1$s?</string>
<string name="toast">Mesaj</string> <string name="toast">Mesaj</string>
<string name="none">Nimic</string> <string name="none">Nimic</string>
<string name="auth_fail">Autentificare eșuată</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<string name="settings">Настройки</string> <string name="settings">Настройки</string>
<string name="install">Установка</string> <string name="install">Установка</string>
<string name="unsupport_magisk_title">Неподдерживаемая версия Magisk</string> <string name="unsupport_magisk_title">Неподдерживаемая версия Magisk</string>
<string name="unsupport_magisk_message">Эта версия Magisk Manager не поддерживает версию Magisk ниже v18.0.\n\nВы можете вручную обновить Magisk или понизить версию приложения до более старой.</string> <string name="unsupport_magisk_msg">Эта версия Magisk Manager не поддерживает версию Magisk ниже %1$s.\n\nПриложение будет работать так, как будто Magisk не установлен, пожалуйста, обновите Magisk как можно быстрее.</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magisk не установлен</string> <string name="magisk_version_error">Magisk не установлен</string>
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
<string name="logs_cleared">Лог успешно очищен</string> <string name="logs_cleared">Лог успешно очищен</string>
<!--About Activity--> <!--About Activity-->
<string name="app_changelog">Список изменений</string> <string name="app_changelog">Изменения</string>
<!-- System Components, Notifications --> <!-- System Components, Notifications -->
<string name="update_channel">Обновления Magisk</string> <string name="update_channel">Обновления Magisk</string>
...@@ -77,13 +77,13 @@ ...@@ -77,13 +77,13 @@
<!-- Installation --> <!-- Installation -->
<string name="manager_download_install">Нажмите, чтобы загрузить и установить</string> <string name="manager_download_install">Нажмите, чтобы загрузить и установить</string>
<string name="download_zip_only">Только загрузка ZIP</string> <string name="download_zip_only">Загрузка установочного ZIP</string>
<string name="direct_install">Прямая установка (Рекомендуется)</string> <string name="direct_install">Прямая установка (Рекомендуется)</string>
<string name="install_inactive_slot">Установка во второй слот (OTA)</string> <string name="install_inactive_slot">Установка во второй слот (OTA)</string>
<string name="install_inactive_slot_msg">Ваше устройство будет принудительно перезагружено в неактивный (противоположный) слот!\nИспользуйте эту опцию только при интеграции после OTA.\nПродолжить?</string> <string name="install_inactive_slot_msg">Ваше устройство будет принудительно перезагружено в неактивный (противоположный) слот!\nИспользуйте эту опцию только при интеграции после OTA.\nПродолжить?</string>
<string name="select_method">Выбор способа</string> <string name="select_method">Способ установки</string>
<string name="setup_title">Дополнительная установка</string> <string name="setup_title">Дополнительная установка</string>
<string name="select_patch_file">Вручную пропатчить образ</string> <string name="select_patch_file">Пропатчить образ вручную</string>
<string name="patch_file_msg">Выберите файл образа (*.img) или архив ODIN (*.tar)</string> <string name="patch_file_msg">Выберите файл образа (*.img) или архив ODIN (*.tar)</string>
<string name="reboot_delay_toast">Перезагрузка через 5 секунд…</string> <string name="reboot_delay_toast">Перезагрузка через 5 секунд…</string>
...@@ -95,7 +95,6 @@ ...@@ -95,7 +95,6 @@
<string name="settings_reboot_toast">Для применения настроек перезагрузите устройство</string> <string name="settings_reboot_toast">Для применения настроек перезагрузите устройство</string>
<string name="release_notes">О версии</string> <string name="release_notes">О версии</string>
<string name="repo_cache_cleared">Кэш репозитория очищен</string> <string name="repo_cache_cleared">Кэш репозитория очищен</string>
<string name="dtbo_patched_title">DTBO пропатчен!</string> <string name="dtbo_patched_title">DTBO пропатчен!</string>
<string name="dtbo_patched_reboot">Magisk Manager пропатчил dtbo.img. Перезагрузите устройство.</string> <string name="dtbo_patched_reboot">Magisk Manager пропатчил dtbo.img. Перезагрузите устройство.</string>
<string name="flashing">Прошивка…</string> <string name="flashing">Прошивка…</string>
...@@ -116,15 +115,16 @@ ...@@ -116,15 +115,16 @@
<string name="env_fix_title">Требуется дополнительная установка</string> <string name="env_fix_title">Требуется дополнительная установка</string>
<string name="env_fix_msg">Вашему устройству требуется дополнительная установка Magisk для корректной работы. Будет загружен установочный ZIP Magisk, продолжить?</string> <string name="env_fix_msg">Вашему устройству требуется дополнительная установка Magisk для корректной работы. Будет загружен установочный ZIP Magisk, продолжить?</string>
<string name="setup_msg">Настройка рабочей среды…</string> <string name="setup_msg">Настройка рабочей среды…</string>
<string name="authenticate">Аутентификация</string>
<!--Settings Activity --> <!--Settings Activity -->
<string name="settings_general_category">Основные</string> <string name="settings_general_category">Основные</string>
<string name="settings_dark_theme_title">Тёмная тема</string> <string name="settings_dark_theme_title">Тёмная тема</string>
<string name="settings_dark_theme_summary">Включить тёмное оформление</string> <string name="settings_dark_theme_summary">Включить тёмное оформление</string>
<string name="settings_download_path_title">Папка загрузки</string> <string name="settings_download_path_title">Папка для загрузки</string>
<string name="settings_download_path_message">Файлы будут загружаться в %1$s</string> <string name="settings_download_path_message">Файлы будут загружаться в %1$s</string>
<string name="settings_clear_cache_title">Очистка кэша репозитория</string> <string name="settings_clear_cache_title">Очистка кэша репозитория</string>
<string name="settings_clear_cache_summary">Очистить кэш репозитория. Будет загружен заново</string> <string name="settings_clear_cache_summary">Очистить кэш репозитория. Кэш будет загружен заново</string>
<string name="settings_hide_manager_title">Скрытие Magisk Manager</string> <string name="settings_hide_manager_title">Скрытие Magisk Manager</string>
<string name="settings_hide_manager_summary">Пересобрать Magisk Manager со случайным названием и именем пакета</string> <string name="settings_hide_manager_summary">Пересобрать Magisk Manager со случайным названием и именем пакета</string>
<string name="settings_restore_manager_title">Восстановление Magisk Manager</string> <string name="settings_restore_manager_title">Восстановление Magisk Manager</string>
...@@ -142,9 +142,9 @@ ...@@ -142,9 +142,9 @@
<string name="settings_core_only_title">Magisk Core</string> <string name="settings_core_only_title">Magisk Core</string>
<string name="settings_core_only_summary">Активировать только основные возможности. Модули не будут загружены. MagiskSU и Magisk Hide останутся активными</string> <string name="settings_core_only_summary">Активировать только основные возможности. Модули не будут загружены. MagiskSU и Magisk Hide останутся активными</string>
<string name="settings_magiskhide_summary">Скрывать Magisk от различных обнаружений</string> <string name="settings_magiskhide_summary">Скрывать Magisk от различных обнаружений</string>
<string name="settings_hosts_title">Внесистемные хосты</string> <string name="settings_hosts_title">Внесистемный hosts файл</string>
<string name="settings_hosts_summary">Поддержка внесистемных хостов для приложений, блокирующих рекламу</string> <string name="settings_hosts_summary">Поддержка внесистемного hosts файла для приложений, блокирующих рекламу</string>
<string name="settings_hosts_toast">Добавлен модуль внесистемных хостов</string> <string name="settings_hosts_toast">Добавлен модуль внесистемного hosts файла</string>
<string name="settings_app_name">Укажите имя приложения</string> <string name="settings_app_name">Укажите имя приложения</string>
<string name="settings_app_name_hint">Новое имя</string> <string name="settings_app_name_hint">Новое имя</string>
...@@ -167,9 +167,9 @@ ...@@ -167,9 +167,9 @@
<string name="request_timeout_summary">%1$d секунд</string> <string name="request_timeout_summary">%1$d секунд</string>
<string name="settings_su_reauth_title">Повторная аутентификация</string> <string name="settings_su_reauth_title">Повторная аутентификация</string>
<string name="settings_su_reauth_summary">Повторный запрос прав суперпользователя после обновления приложений</string> <string name="settings_su_reauth_summary">Повторный запрос прав суперпользователя после обновления приложений</string>
<string name="settings_su_fingerprint_title">Биометрическая аутентификация</string> <string name="settings_su_biometric_title">Биометрическая аутентификация</string>
<string name="settings_su_fingerprint_summary">Использовать сканер отпечатков пальцев для запросов прав суперпользователя</string> <string name="settings_su_biometric_summary">Использовать биометрическую аутентификацию для запросов прав суперпользователя</string>
<string name="auth_fingerprint">Подтвердите отпечаток пальца</string> <string name="no_biometric">Эта функция не поддерживается устройством или не заданы настройки блокировки экрана</string>
<string name="multiuser_mode">Многопользовательский режим</string> <string name="multiuser_mode">Многопользовательский режим</string>
<string name="settings_owner_only">Только владелец</string> <string name="settings_owner_only">Только владелец</string>
...@@ -186,7 +186,6 @@ ...@@ -186,7 +186,6 @@
<string name="global_summary">Сессии суперпользователя используют общее пространство имён</string> <string name="global_summary">Сессии суперпользователя используют общее пространство имён</string>
<string name="requester_summary">Сессии суперпользователя наследуют пространство имён запрашивающего</string> <string name="requester_summary">Сессии суперпользователя наследуют пространство имён запрашивающего</string>
<string name="isolate_summary">Сессии суперпользователя используют изолированные пространства имён</string> <string name="isolate_summary">Сессии суперпользователя используют изолированные пространства имён</string>
<string name="disable_fingerprint">Не поддерживается устройством или не заданы отпечатки</string>
<string name="settings_download_path_error">Ошибка создания папки. Она должна быть доступна из корневой директории хранилища и не должна быть файлом.</string> <string name="settings_download_path_error">Ошибка создания папки. Она должна быть доступна из корневой директории хранилища и не должна быть файлом.</string>
<!--Superuser--> <!--Superuser-->
...@@ -214,7 +213,6 @@ ...@@ -214,7 +213,6 @@
<string name="su_revoke_msg">Сбросить настройки для %1$s?</string> <string name="su_revoke_msg">Сбросить настройки для %1$s?</string>
<string name="toast">Всплывающие уведомления</string> <string name="toast">Всплывающие уведомления</string>
<string name="none">Нет</string> <string name="none">Нет</string>
<string name="auth_fail">Ошибка аутентификации</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
......
...@@ -8,8 +8,6 @@ ...@@ -8,8 +8,6 @@
<string name="settings">Nastavenia</string> <string name="settings">Nastavenia</string>
<string name="install">Inštalovať</string> <string name="install">Inštalovať</string>
<string name="unsupport_magisk_title">Nepodporovaná verzia Magisku</string> <string name="unsupport_magisk_title">Nepodporovaná verzia Magisku</string>
<string name="unsupport_magisk_message">Táto verzia Magisk Managera podporuje Magisk od verzie v18.0.\n\nBuď upgradujte Magisk manuálne alebo nainštalujte staršiu verziu aplikácie.</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magisk nie je nainštalovaný</string> <string name="magisk_version_error">Magisk nie je nainštalovaný</string>
...@@ -25,26 +23,28 @@ ...@@ -25,26 +23,28 @@
<string name="advanced_settings_title">Pokročilé nastavenia</string> <string name="advanced_settings_title">Pokročilé nastavenia</string>
<string name="keep_force_encryption">Ponechať vynútené šifrovanie</string> <string name="keep_force_encryption">Ponechať vynútené šifrovanie</string>
<string name="keep_dm_verity">Ponechať AVB 2.0/dm-verity</string> <string name="keep_dm_verity">Ponechať AVB 2.0/dm-verity</string>
<string name="recovery_mode">Režim Recovery</string>
<string name="current_installed">Nainštalovaná verzia: %1$s</string> <string name="current_installed">Nainštalovaná verzia: %1$s</string>
<string name="latest_version">Najnovšia verzia: %1$s</string> <string name="latest_version">Najnovšia verzia: %1$s</string>
<string name="uninstall">Odinštalovať</string> <string name="uninstall">Odinštalovať</string>
<string name="uninstall_magisk_msg">Všetky moduly budú zakázané/odstránené. Root bude odstránený a teoreticky môže dôjsť k zašifrovaniu vašich dát, ak ich momentálne nemáte zašifrované</string>
<string name="uninstall_magisk_title">Odinštalovať Magisk</string> <string name="uninstall_magisk_title">Odinštalovať Magisk</string>
<string name="uninstall_magisk_msg">Všetky moduly budú zakázané/odstránené!\nRoot bude odstránený!\nMôže dôjsť k zašifrovaniu vašich dát, ak už nie sú zašifrované!</string>
<string name="update">Aktualizovať</string> <string name="update">Aktualizovať</string>
<string name="core_only_enabled">(Režim Core povolený)</string> <string name="core_only_enabled">(Povolený iba režim Core)</string>
<!--Module Fragment--> <!--Module Fragment-->
<string name="no_info_provided">(Nie sú k dispozícii žiadne informácie)</string> <string name="no_info_provided">(Nie sú k dispozícii žiadne informácie)</string>
<string name="no_modules_found">Neboli nájdené žiadne moduly</string> <string name="no_modules_found">Neboli nájdené žiadne moduly</string>
<string name="update_file_created">Modul bude aktualizovaný pri ďalšom reštarte</string> <string name="update_file_created">Modul bude aktualizovaný pri ďalšom reštarte!</string>
<string name="remove_file_created">Modul bude odstránený pri ďalšom reštarte</string> <string name="remove_file_created">Modul bude odstránený pri ďalšom reštarte!</string>
<string name="remove_file_deleted">Modul nebude odstránený pri ďalšom reštarte</string> <string name="remove_file_deleted">Modul nebude odstránený pri ďalšom reštarte!</string>
<string name="disable_file_created">Modul bude zakázaný pri ďalšom reštarte</string> <string name="disable_file_created">Modul bude zakázaný pri ďalšom reštarte!</string>
<string name="disable_file_removed">Modul bude povolený pri ďalšom reštarte</string> <string name="disable_file_removed">Modul bude povolený pri ďalšom reštarte!</string>
<string name="author">Vytvoril %1$s</string> <string name="author">Vytvoril %1$s</string>
<string name="reboot_recovery">Reštartovať do Recovery</string> <string name="reboot_recovery">Reštartovať do Recovery</string>
<string name="reboot_bootloader">Reštartovať do Bootloader</string> <string name="reboot_bootloader">Reštartovať do Bootloader</string>
<string name="reboot_download">Reštartovať do Download</string> <string name="reboot_download">Reštartovať do Download</string>
<string name="reboot_edl">Reštartovať do EDL</string>
<!--Repo Fragment--> <!--Repo Fragment-->
<string name="update_available">Dostupná aktualizácia</string> <string name="update_available">Dostupná aktualizácia</string>
...@@ -59,18 +59,33 @@ ...@@ -59,18 +59,33 @@
<string name="menuSaveLog">Uložiť záznam</string> <string name="menuSaveLog">Uložiť záznam</string>
<string name="menuReload">Obnoviť</string> <string name="menuReload">Obnoviť</string>
<string name="menuClearLog">Odstrániť záznamy</string> <string name="menuClearLog">Odstrániť záznamy</string>
<string name="logs_cleared">Záznamy boli odstránené</string> <string name="logs_cleared">Záznamy boli odstránené.</string>
<!--About Activity--> <!--About Activity-->
<string name="app_changelog">Zoznam zmien</string> <string name="app_changelog">Zoznam zmien</string>
<!-- System Components, Notifications --> <!-- System Components, Notifications -->
<string name="update_channel">Aktualizácie Magisk</string> <string name="update_channel">Aktualizácie Magisk</string>
<string name="progress_channel">Notifkikácie priebehu</string> <string name="progress_channel">Upozornenia o priebehu</string>
<string name="download_complete">Sťahovanie ukončené</string> <string name="download_complete">Sťahovanie ukončené</string>
<string name="download_file_error">Chyba sťahovania súboru</string> <string name="download_file_error">Chyba sťahovania súboru</string>
<string name="download_open_parent">Zobraziť v nadradenom priečinku</string>
<string name="download_open_self">Zobraziť súbor</string>
<string name="magisk_update_title">Je dostupná aktualizácia Magisk!</string> <string name="magisk_update_title">Je dostupná aktualizácia Magisk!</string>
<string name="manager_update_title">Je dostupná aktualizácia Magisk Manager!</string> <string name="manager_update_title">Je dostupná aktualizácia Magisk Manager!</string>
<!-- Installation -->
<string name="manager_download_install">Stlačte pre stiahnutie a inštaláciu</string>
<string name="download_zip_only">Stiahnuť iba zip</string>
<string name="direct_install">Priama inštalácia (Odporúča sa)</string>
<string name="install_inactive_slot">Inštalovať na aktívny slot (Po OTA)</string>
<string name="install_inactive_slot_msg">Vaše zariadenie bude po reštarte PRINÚTENÉ nabootovať do aktuálne neaktívneho slotu!\nTúto voľbu použite iba po skončení OTA.\nPokračovať?</string>
<string name="select_method">Vyberte metódu</string>
<string name="setup_title">Ďalšie nastavenie</string>
<string name="select_patch_file">Vybrať a zaplátať súbor</string>
<string name="patch_file_msg">Vyberte raw súbor (*.img) alebo tar súbor ODIN (*.tar)</string>
<string name="reboot_delay_toast">Reštart o 5 sekúnd…</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="repo_install_title">Nainštalovať %1$s</string> <string name="repo_install_title">Nainštalovať %1$s</string>
<string name="repo_install_msg">Chcete teraz nainštalovať %1$s?</string> <string name="repo_install_msg">Chcete teraz nainštalovať %1$s?</string>
...@@ -79,17 +94,16 @@ ...@@ -79,17 +94,16 @@
<string name="settings_reboot_toast">Pre aplikovanie nastavení je potrebný reštart</string> <string name="settings_reboot_toast">Pre aplikovanie nastavení je potrebný reštart</string>
<string name="release_notes">Poznámky k vydaniu</string> <string name="release_notes">Poznámky k vydaniu</string>
<string name="repo_cache_cleared">Cache repo odstránená</string> <string name="repo_cache_cleared">Cache repo odstránená</string>
<string name="manager_download_install">Stlačte pre stiahnutie a inštaláciu</string>
<string name="dtbo_patched_title">DTBO bol zaplátaný!</string> <string name="dtbo_patched_title">DTBO bol zaplátaný!</string>
<string name="dtbo_patched_reboot">Magisk Manager zaplátal dtbo.img, prosím, reštartujte</string> <string name="dtbo_patched_reboot">Magisk Manager zaplátal dtbo.img, prosím, reštartujte.</string>
<string name="flashing">Flashovanie</string> <string name="flashing">Flashovanie</string>
<string name="done">Hotovo!</string>
<string name="failure">Zlyhalo</string>
<string name="hide_manager_title">Skrytie Magisk Managera…</string>
<string name="hide_manager_fail_toast">Skrytie Magisk Manager zlyhalo.</string>
<string name="open_link_failed_toast">Nepodarilo sa nájsť vhodnú aplikáciu na otvorenie odkazu…</string> <string name="open_link_failed_toast">Nepodarilo sa nájsť vhodnú aplikáciu na otvorenie odkazu…</string>
<string name="download_zip_only">Stiahnuť iba zip</string>
<string name="direct_install">Priama inštalácia (Odporúča sa)</string>
<string name="install_inactive_slot">Inštalovať na aktívny slot (Po OTA)</string>
<string name="warning">Upozornenie</string> <string name="warning">Upozornenie</string>
<string name="install_inactive_slot_msg">Vaše zariadenie bude po reštarte PRINÚTENÉ nabootovať do aktuálne neaktívneho slotu!\nTúto voľbu použite iba po skončení OTA.\nPokračovať?</string>
<string name="select_method">Vyberte metódu</string>
<string name="complete_uninstall">Úplne odinštalovať</string> <string name="complete_uninstall">Úplne odinštalovať</string>
<string name="restore_img">Obnoviť obrazy</string> <string name="restore_img">Obnoviť obrazy</string>
<string name="restore_img_msg">Obnovovanie…</string> <string name="restore_img_msg">Obnovovanie…</string>
...@@ -100,14 +114,15 @@ ...@@ -100,14 +114,15 @@
<string name="setup_fail">Nastavenie zlyhalo</string> <string name="setup_fail">Nastavenie zlyhalo</string>
<string name="env_fix_title">Vyžaduje sa ďalšie nastavenie</string> <string name="env_fix_title">Vyžaduje sa ďalšie nastavenie</string>
<string name="env_fix_msg">Vaše zariadenie si vyžaduje ďalšie nastavenie pre správne fungovanie Magisk. Stiahne sa inštalačný zip súbor Magisk, chcete pokračovať teraz?</string> <string name="env_fix_msg">Vaše zariadenie si vyžaduje ďalšie nastavenie pre správne fungovanie Magisk. Stiahne sa inštalačný zip súbor Magisk, chcete pokračovať teraz?</string>
<string name="setup_title">Ďalšie nastavenie</string> <string name="setup_msg">Nastavenie je spustené…</string>
<string name="setup_msg">Spustené prostredie nastavenia…</string>
<!--Settings Activity --> <!--Settings Activity -->
<string name="settings_general_category">Všeobecné</string> <string name="settings_general_category">Všeobecné</string>
<string name="settings_dark_theme_title">Tmavý motív</string> <string name="settings_dark_theme_title">Tmavý motív</string>
<string name="settings_dark_theme_summary">Povoliť tmavý motív</string> <string name="settings_dark_theme_summary">Povoliť tmavý motív</string>
<string name="settings_clear_cache_title">Vyčistiť cache repo </string> <string name="settings_download_path_title">Cesta na sťahovanie</string>
<string name="settings_download_path_message">Súbory budú uložené do %1$s</string>
<string name="settings_clear_cache_title">Vyčistiť cache repo</string>
<string name="settings_clear_cache_summary">Vyčistí uložené informácie pre online repo, aplikácia si nové informácie stiahne online</string> <string name="settings_clear_cache_summary">Vyčistí uložené informácie pre online repo, aplikácia si nové informácie stiahne online</string>
<string name="settings_hide_manager_title">Skryť Magisk Manager</string> <string name="settings_hide_manager_title">Skryť Magisk Manager</string>
<string name="settings_hide_manager_summary">Prebalí Magisk Manager s náhodným názvom balíčka</string> <string name="settings_hide_manager_summary">Prebalí Magisk Manager s náhodným názvom balíčka</string>
...@@ -124,12 +139,16 @@ ...@@ -124,12 +139,16 @@
<string name="settings_update_custom">Vlastný</string> <string name="settings_update_custom">Vlastný</string>
<string name="settings_update_custom_msg">Zadajte vlastnú URL</string> <string name="settings_update_custom_msg">Zadajte vlastnú URL</string>
<string name="settings_core_only_title">Režim Magisk Core</string> <string name="settings_core_only_title">Režim Magisk Core</string>
<string name="settings_core_only_summary">Povolí iba hlavné funkcie. Stále budú povolené MagiskSU, MagiskHide a systemless hosts, ale žiadne moduly nebudú zavedené.</string> <string name="settings_core_only_summary">Povolí iba hlavné funkcie. Stále budú povolené MagiskSU, MagiskHide a systemless hosts, ale žiadne moduly nebudú zavedené</string>
<string name="settings_magiskhide_summary">Ukryje Magisk pre rôznymi detekciami</string> <string name="settings_magiskhide_summary">Ukryje Magisk pre rôznymi detekciami</string>
<string name="settings_hosts_title">Systemless hosts</string> <string name="settings_hosts_title">Systemless hosts</string>
<string name="settings_hosts_summary">Podpora pre aplikácie systemless hosts Adblock</string> <string name="settings_hosts_summary">Podpora pre aplikácie systemless hosts Adblock</string>
<string name="settings_hosts_toast">Pridaný modul systemless hosts</string> <string name="settings_hosts_toast">Pridaný modul systemless hosts</string>
<string name="settings_app_name">Zadajte požadovaný názov aplikácie</string>
<string name="settings_app_name_hint">Nový názov</string>
<string name="settings_app_name_helper">Zadajte požadovaný názov aplikácie</string>
<string name="settings_app_name_error">Neplatný formát</string>
<string name="settings_su_app_adb">Aplikácie a ADB</string> <string name="settings_su_app_adb">Aplikácie a ADB</string>
<string name="settings_su_app">Iba aplikácie</string> <string name="settings_su_app">Iba aplikácie</string>
<string name="settings_su_adb">Iba ADB</string> <string name="settings_su_adb">Iba ADB</string>
...@@ -147,9 +166,6 @@ ...@@ -147,9 +166,6 @@
<string name="request_timeout_summary">%1$d sekúnd</string> <string name="request_timeout_summary">%1$d sekúnd</string>
<string name="settings_su_reauth_title">Overenie autentifikácie po upgrade</string> <string name="settings_su_reauth_title">Overenie autentifikácie po upgrade</string>
<string name="settings_su_reauth_summary">Overí autentifikáciu oprávnení superuser po upgrade aplikácie</string> <string name="settings_su_reauth_summary">Overí autentifikáciu oprávnení superuser po upgrade aplikácie</string>
<string name="settings_su_fingerprint_title">Povoliť autentifikáciu odtlačkom prsta</string>
<string name="settings_su_fingerprint_summary">Použite snímač odtlačkov prstov pre povolenie žiadostí superuser</string>
<string name="auth_fingerprint">Autentifikovať odtlačok prsta</string>
<string name="multiuser_mode">Režim viacerých používateľov</string> <string name="multiuser_mode">Režim viacerých používateľov</string>
<string name="settings_owner_only">Iba majiteľ zariadenia</string> <string name="settings_owner_only">Iba majiteľ zariadenia</string>
...@@ -160,13 +176,13 @@ ...@@ -160,13 +176,13 @@
<string name="user_indepenent_summary">Každý používateľ má vlastné pravidlá pre root</string> <string name="user_indepenent_summary">Každý používateľ má vlastné pravidlá pre root</string>
<string name="mount_namespace_mode">Režim Mount Namespace</string> <string name="mount_namespace_mode">Režim Mount Namespace</string>
<string name="settings_ns_global">Globálne Namespace</string> <string name="settings_ns_global">Globálny Namespace</string>
<string name="settings_ns_requester">Zdedené Namespace</string> <string name="settings_ns_requester">Zdedený Namespace</string>
<string name="settings_ns_isolate">Izolované Namespace</string> <string name="settings_ns_isolate">Izolovaný Namespace</string>
<string name="global_summary">Všetky relácie root použijú globálne mount namespace</string> <string name="global_summary">Všetky relácie root použijú globálny mount namespace</string>
<string name="requester_summary">Relácie root zdedia namespace od požadovateľa</string> <string name="requester_summary">Relácie root zdedia namespace od žiadateľa</string>
<string name="isolate_summary">Každá relácia root bude mať vlastný izolovaný namespace</string> <string name="isolate_summary">Každá relácia root bude mať vlastný izolovaný namespace</string>
<string name="disable_fingerprint">Neboli odoslané žiadne odtlačky prsta alebo ich zariadenie nepodporuje</string> <string name="settings_download_path_error">Chyba pri vytváraní priečinka. Musí byť prístupný z koreňového adresára a nemôže to byť súbor.</string>
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">Žiadosť superuser</string> <string name="su_request_title">Žiadosť superuser</string>
...@@ -193,12 +209,12 @@ ...@@ -193,12 +209,12 @@
<string name="su_revoke_msg">Potvrdzujete zrušenie práv %1$s?</string> <string name="su_revoke_msg">Potvrdzujete zrušenie práv %1$s?</string>
<string name="toast">Toast</string> <string name="toast">Toast</string>
<string name="none">Nič</string> <string name="none">Nič</string>
<string name="auth_fail">Autentifikácia zlyhala</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID:%1$d</string> <string name="pid">PID:%1$d</string>
<string name="target_uid">Cieľový UID: %1$d</string> <string name="target_uid">Cieľový UID: %1$d</string>
<string name="command">Príkaz: %1$s</string> <string name="command">Príkaz: %1$s</string>
<!-- MagiskHide --> <!-- MagiskHide -->
<string name="show_system_app">Zobraziť systémové aplikácie</string> <string name="show_system_app">Zobraziť systémové aplikácie</string>
......
...@@ -146,9 +146,6 @@ ...@@ -146,9 +146,6 @@
<string name="request_timeout_summary">%1$d วินาที</string> <string name="request_timeout_summary">%1$d วินาที</string>
<string name="settings_su_reauth_title">ขอสิทธิ์ใหม่หลังจากอัพเกรด</string> <string name="settings_su_reauth_title">ขอสิทธิ์ใหม่หลังจากอัพเกรด</string>
<string name="settings_su_reauth_summary">ขอสิทธิ์ superuser ใหม่หลังจากแอปถูกอัพเกรด</string> <string name="settings_su_reauth_summary">ขอสิทธิ์ superuser ใหม่หลังจากแอปถูกอัพเกรด</string>
<string name="settings_su_fingerprint_title">ใช้การยืนยันลายนิ้วมือ</string>
<string name="settings_su_fingerprint_summary">ใช้ตัวแสกนลายนิ้วมือเพื่ออนุญาตารขอเข้าถึง superuser</string>
<string name="auth_fingerprint">ยืนยันลายนิ้วมือ</string>
<string name="multiuser_mode">โหมดผู้ใช้หลายคน</string> <string name="multiuser_mode">โหมดผู้ใช้หลายคน</string>
<string name="settings_owner_only">เจ้าของอุปกรณ์เท่านั้น</string> <string name="settings_owner_only">เจ้าของอุปกรณ์เท่านั้น</string>
...@@ -165,7 +162,6 @@ ...@@ -165,7 +162,6 @@
<string name="global_summary">All root sessions use the global mount namespace.</string> <string name="global_summary">All root sessions use the global mount namespace.</string>
<string name="requester_summary">Root sessions will inherit their requester\'s namespace.</string> <string name="requester_summary">Root sessions will inherit their requester\'s namespace.</string>
<string name="isolate_summary">Each root session will have its own isolated namespace.</string> <string name="isolate_summary">Each root session will have its own isolated namespace.</string>
<string name="disable_fingerprint">ไม่มีลายนิ้วมือหรืออุปกรณ์แสกน</string>
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">การขอเข้าถึง Superuser</string> <string name="su_request_title">การขอเข้าถึง Superuser</string>
...@@ -192,7 +188,6 @@ ...@@ -192,7 +188,6 @@
<string name="su_revoke_msg">ยืนยันการลบสิทธิ์ของ %1$s?</string> <string name="su_revoke_msg">ยืนยันการลบสิทธิ์ของ %1$s?</string>
<string name="toast">การเตือน</string> <string name="toast">การเตือน</string>
<string name="none">ไม่มี</string> <string name="none">ไม่มี</string>
<string name="auth_fail">การยืนยันล้มเหลว</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="target_uid">UID เป้าหมาย: %1$d</string> <string name="target_uid">UID เป้าหมาย: %1$d</string>
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
<string name="settings">Ayarlar</string> <string name="settings">Ayarlar</string>
<string name="install">Yükle</string> <string name="install">Yükle</string>
<string name="unsupport_magisk_title">Desteklenmeyen Magisk Sürümü</string> <string name="unsupport_magisk_title">Desteklenmeyen Magisk Sürümü</string>
<string name="unsupport_magisk_message">Magisk Manager\'ın bu sürümü v18.0\'dan daha düşük Magisk versiyonlarını desteklememektedir.\n\nMagisk\'i manuel olarak yükseltebilir veya uygulamayı daha eski bir sürüme düşürebilirsiniz.</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magisk yüklü değil</string> <string name="magisk_version_error">Magisk yüklü değil</string>
...@@ -167,9 +166,6 @@ ...@@ -167,9 +166,6 @@
<string name="request_timeout_summary">%1$d saniye</string> <string name="request_timeout_summary">%1$d saniye</string>
<string name="settings_su_reauth_title">Yükseltmeden sonra yeniden kimlik doğrula</string> <string name="settings_su_reauth_title">Yükseltmeden sonra yeniden kimlik doğrula</string>
<string name="settings_su_reauth_summary">Uygulama yükseltmeleri sonrasında yetkili kullanıcı izinlerini yeniden doğrula</string> <string name="settings_su_reauth_summary">Uygulama yükseltmeleri sonrasında yetkili kullanıcı izinlerini yeniden doğrula</string>
<string name="settings_su_fingerprint_title">Parmak İzi Kimlik Doğrulamayı Etkinleştir</string>
<string name="settings_su_fingerprint_summary">Yetkili kullanıcı isteklerine izin vermek için parmak izi tarayıcısını kullan</string>
<string name="auth_fingerprint">Parmak izini doğrula</string>
<string name="multiuser_mode">Çok Kullanıcılı Mod</string> <string name="multiuser_mode">Çok Kullanıcılı Mod</string>
<string name="settings_owner_only">Yalnızca Cihaz Sahibi</string> <string name="settings_owner_only">Yalnızca Cihaz Sahibi</string>
...@@ -186,7 +182,6 @@ ...@@ -186,7 +182,6 @@
<string name="global_summary">Tüm kök oturumları genel bağlama ad alanını kullanır</string> <string name="global_summary">Tüm kök oturumları genel bağlama ad alanını kullanır</string>
<string name="requester_summary">Kök oturumları, istekte bulunanın ad alanını devralır</string> <string name="requester_summary">Kök oturumları, istekte bulunanın ad alanını devralır</string>
<string name="isolate_summary">Her bir kök oturumunun kendi izole ad alanı olacaktır</string> <string name="isolate_summary">Her bir kök oturumunun kendi izole ad alanı olacaktır</string>
<string name="disable_fingerprint">Parmak izi ayarlanmadı veya cihaz desteği yok</string>
<string name="settings_download_path_error">Klasör oluşturma hatası. Depolama kök dizininden erişilebilir olmalı ve bir dosya olmamalıdır.</string> <string name="settings_download_path_error">Klasör oluşturma hatası. Depolama kök dizininden erişilebilir olmalı ve bir dosya olmamalıdır.</string>
<!--Superuser--> <!--Superuser-->
...@@ -214,7 +209,6 @@ ...@@ -214,7 +209,6 @@
<string name="su_revoke_msg">%1$s hakları geri alınsın mı?</string> <string name="su_revoke_msg">%1$s hakları geri alınsın mı?</string>
<string name="toast">Pencere</string> <string name="toast">Pencere</string>
<string name="none">Hiçbiri</string> <string name="none">Hiçbiri</string>
<string name="auth_fail">Kimlik Doğrulama Başarısız</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
<string name="settings">Налаштування</string> <string name="settings">Налаштування</string>
<string name="install">Встановлення</string> <string name="install">Встановлення</string>
<string name="unsupport_magisk_title">Версія Magisk не підтримується</string> <string name="unsupport_magisk_title">Версія Magisk не підтримується</string>
<string name="unsupport_magisk_message">Ця версія Magisk Manager не підтримує версію Magisk нижчу, ніж v18.0.\n\nВи можете або вручну оновити Magisk, або понизити програму до старішої версії.</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magisk не встановлено.</string> <string name="magisk_version_error">Magisk не встановлено.</string>
...@@ -167,9 +166,6 @@ ...@@ -167,9 +166,6 @@
<string name="request_timeout_summary">%1$d сек.</string> <string name="request_timeout_summary">%1$d сек.</string>
<string name="settings_su_reauth_title">Повторна автентифікація</string> <string name="settings_su_reauth_title">Повторна автентифікація</string>
<string name="settings_su_reauth_summary">Перевидача прав суперкористувача після оновлення програм</string> <string name="settings_su_reauth_summary">Перевидача прав суперкористувача після оновлення програм</string>
<string name="settings_su_fingerprint_title">Автентифікація за відбитком</string>
<string name="settings_su_fingerprint_summary">Використовувати сканер відбитків пальців, щоб надавати дозвіл суперкористувача</string>
<string name="auth_fingerprint">Автентифікація за відбитком</string>
<string name="multiuser_mode">Багатокористувацький режим</string> <string name="multiuser_mode">Багатокористувацький режим</string>
<string name="settings_owner_only">Тільки власник</string> <string name="settings_owner_only">Тільки власник</string>
...@@ -186,7 +182,6 @@ ...@@ -186,7 +182,6 @@
<string name="global_summary">Всі сеанси Суперкористувача використовують глобальний простір імен.</string> <string name="global_summary">Всі сеанси Суперкористувача використовують глобальний простір імен.</string>
<string name="requester_summary">Сеанси Суперкористувача наслідують простір імен запитувача.</string> <string name="requester_summary">Сеанси Суперкористувача наслідують простір імен запитувача.</string>
<string name="isolate_summary">Кожнен сеанс Суперкористувача має власний ізольований простір імен.</string> <string name="isolate_summary">Кожнен сеанс Суперкористувача має власний ізольований простір імен.</string>
<string name="disable_fingerprint">Немає відбитків пальця або пристрій не підтримується.</string>
<string name="settings_download_path_error">Помилка створення папки. Вона повинна бути доступна з кореневої директорії сховища і не повинна бути файлом.</string> <string name="settings_download_path_error">Помилка створення папки. Вона повинна бути доступна з кореневої директорії сховища і не повинна бути файлом.</string>
<!--Superuser--> <!--Superuser-->
...@@ -214,7 +209,6 @@ ...@@ -214,7 +209,6 @@
<string name="su_revoke_msg">Підтвердити відкликання прав для %1$s?</string> <string name="su_revoke_msg">Підтвердити відкликання прав для %1$s?</string>
<string name="toast">Спливаюче сповіщення</string> <string name="toast">Спливаюче сповіщення</string>
<string name="none">Ні</string> <string name="none">Ні</string>
<string name="auth_fail">Помилка автентифікації</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
......
...@@ -141,9 +141,6 @@ ...@@ -141,9 +141,6 @@
<string name="request_timeout_summary">%1$d giây</string> <string name="request_timeout_summary">%1$d giây</string>
<string name="settings_su_reauth_title">Xác thực lại sau khi nâng cấp</string> <string name="settings_su_reauth_title">Xác thực lại sau khi nâng cấp</string>
<string name="settings_su_reauth_summary">Xác thực lại quyền superuser sau khi nâng cấp ứng dụng</string> <string name="settings_su_reauth_summary">Xác thực lại quyền superuser sau khi nâng cấp ứng dụng</string>
<string name="settings_su_fingerprint_title">Bật xác thực vân tay</string>
<string name="settings_su_fingerprint_summary">Sử dụng quét vân tay để cho phép quyền superuser</string>
<string name="auth_fingerprint">Xác thực vân tay</string>
<string name="multiuser_mode">Chế độ đa người dùng</string> <string name="multiuser_mode">Chế độ đa người dùng</string>
<string name="settings_owner_only">Chỉ chủ sở hữu thiết bị</string> <string name="settings_owner_only">Chỉ chủ sở hữu thiết bị</string>
<string name="settings_owner_manage">Chủ sở hữu thiết bị được quản lý</string> <string name="settings_owner_manage">Chủ sở hữu thiết bị được quản lý</string>
...@@ -158,7 +155,6 @@ ...@@ -158,7 +155,6 @@
<string name="global_summary">Tất cả các phiên root sử dụng không gian tên gắn kết chung.</string> <string name="global_summary">Tất cả các phiên root sử dụng không gian tên gắn kết chung.</string>
<string name="requester_summary">Các phiên root sẽ kế thừa không gian tên của người yêu cầu.</string> <string name="requester_summary">Các phiên root sẽ kế thừa không gian tên của người yêu cầu.</string>
<string name="isolate_summary">Mỗi phiên root sẽ có không gian tên riêng biệt.</string> <string name="isolate_summary">Mỗi phiên root sẽ có không gian tên riêng biệt.</string>
<string name="disable_fingerprint">Không có dấu vân tay nào được thiết lập hoặc thiết bị không hỗ trợ.</string>
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">Yêu cầu Superuser</string> <string name="su_request_title">Yêu cầu Superuser</string>
...@@ -185,7 +181,6 @@ ...@@ -185,7 +181,6 @@
<string name="su_revoke_msg">Xác nhận thu hồi quyền của %1$s?</string> <string name="su_revoke_msg">Xác nhận thu hồi quyền của %1$s?</string>
<string name="toast">Thông báo ngắn</string> <string name="toast">Thông báo ngắn</string>
<string name="none">Không có</string> <string name="none">Không có</string>
<string name="auth_fail">Xác thực thất bại</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="target_uid">Mục UID: %1$d</string> <string name="target_uid">Mục UID: %1$d</string>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<string name="settings">设置</string> <string name="settings">设置</string>
<string name="install">安装</string> <string name="install">安装</string>
<string name="unsupport_magisk_title">不支持的 Magisk 版本</string> <string name="unsupport_magisk_title">不支持的 Magisk 版本</string>
<string name="unsupport_magisk_message">此版本的 Magisk Manager 不支持低于 v18.0 的 Magisk。\n\n请手动升级 Magisk,或将应用降级到旧版本</string> <string name="unsupport_magisk_msg">此版本的 Magisk Manager 不支持低于 %1$s 的 Magisk。\n\n进入后表现为未安装状态,但升级功能可用,请尽快升级</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">未安装 Magisk</string> <string name="magisk_version_error">未安装 Magisk</string>
...@@ -95,7 +95,6 @@ ...@@ -95,7 +95,6 @@
<string name="settings_reboot_toast">重启设备以应用设置</string> <string name="settings_reboot_toast">重启设备以应用设置</string>
<string name="release_notes">发布说明</string> <string name="release_notes">发布说明</string>
<string name="repo_cache_cleared">仓库缓存已清除</string> <string name="repo_cache_cleared">仓库缓存已清除</string>
<string name="dtbo_patched_title">已修补 DTBO!</string> <string name="dtbo_patched_title">已修补 DTBO!</string>
<string name="dtbo_patched_reboot">Magisk Manager 为 dtbo 分区进行了修补,请立即重新启动</string> <string name="dtbo_patched_reboot">Magisk Manager 为 dtbo 分区进行了修补,请立即重新启动</string>
<string name="flashing">正在刷入</string> <string name="flashing">正在刷入</string>
...@@ -116,6 +115,7 @@ ...@@ -116,6 +115,7 @@
<string name="env_fix_title">需要修复运行环境</string> <string name="env_fix_title">需要修复运行环境</string>
<string name="env_fix_msg">缺失 Magisk 正常工作所需的文件。 将下载 Magisk 安装包进行修复安装,完成后自动重启,是否继续?</string> <string name="env_fix_msg">缺失 Magisk 正常工作所需的文件。 将下载 Magisk 安装包进行修复安装,完成后自动重启,是否继续?</string>
<string name="setup_msg">正在修复运行环境…</string> <string name="setup_msg">正在修复运行环境…</string>
<string name="authenticate">验证您的身份</string>
<!--Settings Activity --> <!--Settings Activity -->
<string name="settings_general_category">常规</string> <string name="settings_general_category">常规</string>
...@@ -167,9 +167,9 @@ ...@@ -167,9 +167,9 @@
<string name="request_timeout_summary">%1$d 秒</string> <string name="request_timeout_summary">%1$d 秒</string>
<string name="settings_su_reauth_title">更新后重新认证</string> <string name="settings_su_reauth_title">更新后重新认证</string>
<string name="settings_su_reauth_summary">应用更新后重新认证超级用户权限</string> <string name="settings_su_reauth_summary">应用更新后重新认证超级用户权限</string>
<string name="settings_su_fingerprint_title">启用指纹验</string> <string name="settings_su_biometric_title">启用生物识别认</string>
<string name="settings_su_fingerprint_summary">使用指纹识别来允许超级用户请求</string> <string name="settings_su_biometric_summary">使用生物识别验证超级用户请求</string>
<string name="auth_fingerprint">验证指纹</string> <string name="no_biometric">不支持的设备或未配置生物识别功能</string>
<string name="multiuser_mode">多用户模式</string> <string name="multiuser_mode">多用户模式</string>
<string name="settings_owner_only">仅设备所有者</string> <string name="settings_owner_only">仅设备所有者</string>
...@@ -186,7 +186,6 @@ ...@@ -186,7 +186,6 @@
<string name="global_summary">所有的 ROOT 会话使用全局挂载命名空间</string> <string name="global_summary">所有的 ROOT 会话使用全局挂载命名空间</string>
<string name="requester_summary">ROOT 会话继承原程序的命名空间</string> <string name="requester_summary">ROOT 会话继承原程序的命名空间</string>
<string name="isolate_summary">每一个 ROOT 会话使用自己独立的命名空间</string> <string name="isolate_summary">每一个 ROOT 会话使用自己独立的命名空间</string>
<string name="disable_fingerprint">没有设置指纹或设备不支持</string>
<string name="settings_download_path_error">创建文件夹出错。路径需要位于内部存储空间,并且不能有同名文件。</string> <string name="settings_download_path_error">创建文件夹出错。路径需要位于内部存储空间,并且不能有同名文件。</string>
<!--Superuser--> <!--Superuser-->
...@@ -214,7 +213,6 @@ ...@@ -214,7 +213,6 @@
<string name="su_revoke_msg">确认撤销 %1$s 的权限?</string> <string name="su_revoke_msg">确认撤销 %1$s 的权限?</string>
<string name="toast">消息提示</string> <string name="toast">消息提示</string>
<string name="none"></string> <string name="none"></string>
<string name="auth_fail">验证失败</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
......
...@@ -2,129 +2,135 @@ ...@@ -2,129 +2,135 @@
<!--Welcome Activity--> <!--Welcome Activity-->
<string name="modules">模組</string> <string name="modules">模組</string>
<string name="downloads">下載</string> <string name="downloads">線上模組庫</string>
<string name="superuser">Superuser</string> <string name="superuser">超級使用者</string>
<string name="log">記錄檔</string> <string name="log">紀錄</string>
<string name="settings">設定</string> <string name="settings">設定</string>
<string name="install">安裝</string> <string name="install">安裝</string>
<string name="unsupport_magisk_title">不支援 Magisk 版本</string> <string name="unsupport_magisk_title">不支援 Magisk 版本</string>
<string name="unsupport_magisk_message">此 Magisk Manager 版本最低支援 Magisk 18.0。\n\n請手動更新 Magisk,或安裝舊版 Magisk Manager</string> <string name="unsupport_magisk_msg">此 Magisk Manager 的版本不支援 Magisk %1$s 版或更低的版本。\n\nMagisk 將顯示為未安裝的狀態。不過您仍然可以升級功能,請盡快升級</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">未安裝 Magisk</string> <string name="magisk_version_error">未安裝 Magisk</string>
<string name="checking_for_updates">正在檢查更新…</string> <string name="checking_for_updates">正在檢查更新…</string>
<string name="invalid_update_channel">更新頻道無效</string> <string name="invalid_update_channel">更新頻道無效</string>
<string name="safetyNet_check_text">啟動 SafetyNet 檢查</string> <string name="safetyNet_check_text">點選以檢查 SafetyNet 狀態</string>
<string name="checking_safetyNet_status">正在檢查 SafetyNet 狀態…</string> <string name="checking_safetyNet_status">正在檢查 SafetyNet 狀態…</string>
<string name="safetyNet_check_success">SafetyNet 檢查通過</string> <string name="safetyNet_check_success">SafetyNet 檢查成功</string>
<string name="safetyNet_api_error">SafetyNet API 錯誤</string> <string name="safetyNet_api_error">SafetyNet API 錯誤</string>
<string name="safetyNet_res_invalid">回傳值無效</string> <string name="safetyNet_res_invalid">回傳值無效</string>
<string name="magisk_up_to_date">Magisk 為最新版本</string> <string name="magisk_up_to_date">Magisk 為最新版本</string>
<string name="manager_up_to_date">Magisk Manager 為最新版本</string> <string name="manager_up_to_date">Magisk Manager 為最新版本</string>
<string name="advanced_settings_title">進階設定</string> <string name="advanced_settings_title">安裝選項</string>
<string name="keep_force_encryption">保留強制加密</string> <string name="keep_force_encryption">保持強制加密</string>
<string name="keep_dm_verity">保留 AVB 2.0/dm-verity</string> <string name="keep_dm_verity">保持 AVB 2.0/dm-verity</string>
<string name="recovery_mode">安裝至 Recovery</string>
<string name="current_installed">已安裝版本:%1$s</string> <string name="current_installed">已安裝版本:%1$s</string>
<string name="latest_version">最新版本:%1$s</string> <string name="latest_version">最新版本:%1$s</string>
<string name="uninstall">解除安裝</string> <string name="uninstall">解除安裝</string>
<string name="uninstall_magisk_title">解除安裝 Magisk</string> <string name="uninstall_magisk_title">解除安裝 Magisk</string>
<string name="uninstall_magisk_msg">將停用或移除所有模組。同時移除 Root 權限且可能加密尚未加密的資料。</string> <string name="uninstall_magisk_msg">所有模組將會被停用並移除!\nRoot 將會被移除且若有還沒被加密過的資料將可能被加密!</string>
<string name="update">更新</string> <string name="update">更新</string>
<string name="core_only_enabled">(已啟用核心模式)</string> <string name="core_only_enabled">(已啟用核心模式)</string>
<!--Module Fragment--> <!--Module Fragment-->
<string name="no_info_provided">(未提供資訊)</string> <string name="no_info_provided">(未提供資訊)</string>
<string name="no_modules_found">未找到模組</string> <string name="no_modules_found">沒有發現模組</string>
<string name="update_file_created">模組將在下次重新啟動後更新</string> <string name="update_file_created">模組將在下次重新啟動後更新</string>
<string name="remove_file_created">模組將在下次重新啟動後移除</string> <string name="remove_file_created">模組將在下次重新啟動後移除</string>
<string name="remove_file_deleted">模組不會在下次重新啟動後移除</string> <string name="remove_file_deleted">模組將不在下次重新啟動後移除!</string>
<string name="disable_file_created">模組將在下次重新啟動後禁用</string> <string name="disable_file_created">模組將在下次重新啟動後停用!</string>
<string name="disable_file_removed">模組將在下次重新啟動後啟用</string> <string name="disable_file_removed">模組將在下次重新啟動後啟用</string>
<string name="author">作者:%1$s</string> <string name="author">作者:%1$s</string>
<string name="reboot_recovery">重新啟動至 Recovery</string> <string name="reboot_recovery">重新啟動至 Recovery</string>
<string name="reboot_bootloader">重新啟動至 Bootloader</string> <string name="reboot_bootloader">重新啟動至 Bootloader</string>
<string name="reboot_download">重新啟動至 Download</string> <string name="reboot_download">重新啟動至 Download</string>
<string name="reboot_edl">重新啟動至 EDL</string>
<!--Repo Fragment--> <!--Repo Fragment-->
<string name="update_available">可更新</string> <string name="update_available">可更新</string>
<string name="installed">已安裝</string> <string name="installed">已安裝</string>
<string name="not_installed">未安裝</string> <string name="not_installed">未安裝</string>
<string name="updated_on">上次更新:%1$s</string> <string name="updated_on">上次更新:%1$s</string>
<string name="sorting_order">列順序</string> <string name="sorting_order">序方式</string>
<string name="sort_by_name">依名稱排序</string> <string name="sort_by_name">依名稱排序</string>
<string name="sort_by_update">依更新日期排序</string> <string name="sort_by_update">依更新日期排序</string>
<!--Log Fragment--> <!--Log Fragment-->
<string name="menuSaveLog">儲存記錄</string> <string name="menuSaveLog">儲存記錄</string>
<string name="menuReload">重新載入</string> <string name="menuReload">重新整理</string>
<string name="menuClearLog">清除記錄</string> <string name="menuClearLog">清除記錄</string>
<string name="logs_cleared">記錄檔清除完成</string> <string name="logs_cleared">已成功清除記錄</string>
<!--About Activity--> <!--About Activity-->
<string name="app_changelog">更新日誌</string> <string name="app_changelog">更新日誌</string>
<!-- System Components, Notifications --> <!-- System Components, Notifications -->
<string name="update_channel">Magisk 更新</string> <string name="update_channel">Magisk 更新提示</string>
<string name="progress_channel">進度通知</string> <string name="progress_channel">進度通知</string>
<string name="download_complete">下載完成</string> <string name="download_complete">下載完成</string>
<string name="download_file_error">下載錯誤</string> <string name="download_file_error">下載錯誤</string>
<string name="magisk_update_title">Magisk 已推出新版本!</string> <string name="download_open_parent">在資料夾內顯示</string>
<string name="manager_update_title">Magisk Manager 已推出新版本!</string> <string name="download_open_self">顯示檔案</string>
<string name="magisk_update_title">Magisk 已發布新版本!</string>
<string name="manager_update_title">Magisk Manager 已發布新版本!</string>
<!-- Installation --> <!-- Installation -->
<string name="manager_download_install">輕觸以下載及安裝</string> <string name="manager_download_install">點選以下載並安裝</string>
<string name="download_zip_only">僅下載 ZIP</string> <string name="download_zip_only">僅下載 ZIP 安裝包</string>
<string name="direct_install">直接安裝(建議)</string> <string name="direct_install">直接安裝(建議)</string>
<string name="install_inactive_slot">安裝至未使用的分割區(OTA 後)</string> <string name="install_inactive_slot">安裝到非使用中的槽位(在 OTA 更新後)</string>
<string name="install_inactive_slot_msg">下次重新啟動後,裝置將切換至未使用的分割區!\n請僅在 OTA 後使用此選項。\n是否繼續?</string> <string name="install_inactive_slot_msg">您的裝置將在下次重新啟動後強制切換到非使用中的槽位!\n這個選項僅在 OTA 更新完畢後使用。\n是否繼續?</string>
<string name="select_method">選擇模式</string> <string name="select_method">選擇方法</string>
<string name="setup_title">增量更新</string> <string name="setup_title">修復安裝</string>
<string name="select_patch_file">選擇並修檔案</string> <string name="select_patch_file">選擇並修檔案</string>
<string name="patch_file_msg">請選取未修改過的映像檔(*.img)或 ODIN TAR 檔 (*.tar)</string> <string name="patch_file_msg">請選取未修改過的映像檔(*.img)或 Odin 的 TAR 檔案 (*.tar)</string>
<string name="reboot_delay_toast">將在 5 秒後重新啟動…</string> <string name="reboot_delay_toast">將在 5 秒後重新啟動…</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="repo_install_title">安裝 %1$s</string> <string name="repo_install_title">安裝 %1$s</string>
<string name="repo_install_msg">即將安裝 %1$s</string> <string name="repo_install_msg">您現在想要安裝 %1$s 嗎?</string>
<string name="download">下載</string> <string name="download">下載</string>
<string name="reboot">重新啟動</string> <string name="reboot">重新啟動</string>
<string name="settings_reboot_toast">重新啟動即可套用變更</string> <string name="settings_reboot_toast">重新啟動以套用設定</string>
<string name="release_notes">發布說明</string> <string name="release_notes">發布說明</string>
<string name="repo_cache_cleared">已清除資源庫快取</string> <string name="repo_cache_cleared">已清除線上模組庫的快取</string>
<string name="dtbo_patched_title">已修 DTBO!</string> <string name="dtbo_patched_title">已修 DTBO!</string>
<string name="dtbo_patched_reboot">Magisk Manager 已修改 DTBO,請重新啟動</string> <string name="dtbo_patched_reboot">Magisk Manager 已修補了 DTBO.img,請立即重新啟動。</string>
<string name="flashing">正在刷入</string> <string name="flashing">正在刷入……</string>
<string name="done">完成!</string> <string name="done">完成!</string>
<string name="failure">失敗</string> <string name="failure">失敗</string>
<string name="hide_manager_title">正在隱藏 Magisk Manager…</string> <string name="hide_manager_title">正在隱藏 Magisk Manager…</string>
<string name="hide_manager_fail_toast">隱藏 Magisk Manager 失敗</string> <string name="hide_manager_fail_toast">隱藏 Magisk Manager 失敗</string>
<string name="open_link_failed_toast">無可開啟此連結的應用程式</string> <string name="open_link_failed_toast">沒有可以開啟此連結的應用程式</string>
<string name="warning">警告</string> <string name="warning">警告</string>
<string name="complete_uninstall">解除安裝</string> <string name="complete_uninstall">解除安裝</string>
<string name="restore_img">還原映像檔</string> <string name="restore_img">還原原始映像檔</string>
<string name="restore_img_msg">正在還原…</string> <string name="restore_img_msg">正在還原…</string>
<string name="restore_done">還原完成!</string> <string name="restore_done">還原完成!</string>
<string name="restore_fail">無原始備份檔案</string> <string name="restore_fail">不存在原始備份的映像檔</string>
<string name="proprietary_title">下載非開源程式</string> <string name="proprietary_title">下載專有程式碼</string>
<string name="proprietary_notice">Magisk Manager 完全開源,因此不包含 SafetyNet 檢查所需的 Google 專有程式。\n\n需要下載額外元件(包含 GoogleApiClient)以進行檢查,是否繼續</string> <string name="proprietary_notice">Magisk Manager 是完全免費且開源的軟體,因此不包含 Google 專有的 SafetyNet API 程式碼。\n\n您同意讓 Magisk Manager 額外的下載一個元件(包含 GoogleApiClient)以進行 SafetyNet 的檢查嗎</string>
<string name="setup_fail">設定失敗。</string> <string name="setup_fail">安裝失敗。</string>
<string name="env_fix_title">需要增量更新</string> <string name="env_fix_title">運作環境需被修復</string>
<string name="env_fix_msg">為確保 Magisk 正常運作,此裝置需要安裝增量更新。將開始下載 Magisk 安裝套件,是否繼續?</string> <string name="env_fix_msg">缺少讓 Magisk 正常運作所需的檔案。您同意讓 Magisk Manager 額外下載安裝包進行修復安裝,修復完成後自動重新啟動。是否繼續?</string>
<string name="setup_msg">正在設定安裝環境</string> <string name="setup_msg">正在修復運作環境…</string>
<!--Settings Activity --> <!--Settings Activity -->
<string name="settings_general_category">一般</string> <string name="settings_general_category">一般</string>
<string name="settings_dark_theme_title">深色主題</string> <string name="settings_dark_theme_title">深色主題</string>
<string name="settings_dark_theme_summary">啟用深色主題</string> <string name="settings_dark_theme_summary">啟用深色主題</string>
<string name="settings_clear_cache_title">清除資源庫快取</string> <string name="settings_download_path_title">下載路徑</string>
<string name="settings_clear_cache_summary">清除暫存資源庫快取,並強制重新載入線上資源庫。</string> <string name="settings_download_path_message">檔案將被儲存在:%1$s</string>
<string name="settings_clear_cache_title">清除線上模組庫快取</string>
<string name="settings_clear_cache_summary">將強制重新載入線上模組庫</string>
<string name="settings_hide_manager_title">隱藏 Magisk Manager</string> <string name="settings_hide_manager_title">隱藏 Magisk Manager</string>
<string name="settings_hide_manager_summary">亂碼套件名重新安裝 Magisk Manager</string> <string name="settings_hide_manager_summary">隨機的包裝名稱重新安裝 Magisk Manager</string>
<string name="settings_restore_manager_title">還原 Magisk Manager</string> <string name="settings_restore_manager_title">還原 Magisk Manager</string>
<string name="settings_restore_manager_summary">以原始套件名重新安裝 Magisk Manager</string> <string name="settings_restore_manager_summary">以原始包裝名稱重新安裝 Magisk Manager</string>
<string name="language">語言</string> <string name="language">語言</string>
<string name="system_default">(系統預設)</string> <string name="system_default">系統預設值</string>
<string name="settings_update">更新設定</string> <string name="settings_update">更新設定</string>
<string name="settings_check_update_title">檢查更新</string> <string name="settings_check_update_title">檢查更新</string>
<string name="settings_check_update_summary">定期於背景檢查更新</string> <string name="settings_check_update_summary">定期於背景檢查更新</string>
...@@ -132,14 +138,18 @@ ...@@ -132,14 +138,18 @@
<string name="settings_update_stable">穩定版</string> <string name="settings_update_stable">穩定版</string>
<string name="settings_update_beta">測試版</string> <string name="settings_update_beta">測試版</string>
<string name="settings_update_custom">自訂</string> <string name="settings_update_custom">自訂</string>
<string name="settings_update_custom_msg">請輸入自訂網址</string> <string name="settings_update_custom_msg">請輸入自訂網址</string>
<string name="settings_core_only_title">Magisk 核心模式</string> <string name="settings_core_only_title">Magisk 核心模式</string>
<string name="settings_core_only_summary">僅啟用 Magisk 核心功能。MagiskSU 及 MagiskHide 將保持開啟,模組則不會被載入。</string> <string name="settings_core_only_summary">保持 MagiskSU 及 MagiskHide 開啟並停止載入模組。</string>
<string name="settings_magiskhide_summary">隱藏 Magisk 以避免偵測</string> <string name="settings_magiskhide_summary">隱藏 Magisk 以避免被偵測</string>
<string name="settings_hosts_title">Systemless hosts</string> <string name="settings_hosts_title">主機(Hosts) 模組化</string>
<string name="settings_hosts_summary">為廣告阻擋程式提供 Systemless hosts</string> <string name="settings_hosts_summary">為廣告阻擋程式提供主機模組</string>
<string name="settings_hosts_toast">已安裝 Systemless hosts 模組</string> <string name="settings_hosts_toast">已安裝主機模組</string>
<string name="settings_app_name">請輸入想要的應用程式名稱</string>
<string name="settings_app_name_hint">新的名稱</string>
<string name="settings_app_name_helper">隨機包裝的應用程式將會使用這個新的名稱</string>
<string name="settings_app_name_error">無效的輸入</string>
<string name="settings_su_app_adb">應用程式及 ADB</string> <string name="settings_su_app_adb">應用程式及 ADB</string>
<string name="settings_su_app">僅限應用程式</string> <string name="settings_su_app">僅限應用程式</string>
<string name="settings_su_adb">僅限 ADB</string> <string name="settings_su_adb">僅限 ADB</string>
...@@ -150,60 +160,56 @@ ...@@ -150,60 +160,56 @@
<string name="settings_su_request_30">30 秒</string> <string name="settings_su_request_30">30 秒</string>
<string name="settings_su_request_45">45 秒</string> <string name="settings_su_request_45">45 秒</string>
<string name="settings_su_request_60">60 秒</string> <string name="settings_su_request_60">60 秒</string>
<string name="superuser_access">Superuser 存取權限</string> <string name="superuser_access">超級使用者存取權限</string>
<string name="auto_response">自動回應</string> <string name="auto_response">自動回應</string>
<string name="request_timeout">請求逾時</string> <string name="request_timeout">請求逾時</string>
<string name="superuser_notification">Superuser 通知</string> <string name="superuser_notification">超級使用者通知</string>
<string name="request_timeout_summary">%1$d 秒</string> <string name="request_timeout_summary">%1$d 秒</string>
<string name="settings_su_reauth_title">更新後重新驗證</string> <string name="settings_su_reauth_title">更新後重新驗證</string>
<string name="settings_su_reauth_summary">應用程式更新後,重新驗證 Superuser 請求</string> <string name="settings_su_reauth_summary">應用程式更新後,重新驗證超級使用者的請求</string>
<string name="settings_su_fingerprint_title">指紋驗證</string>
<string name="settings_su_fingerprint_summary">使用指紋辨識器允許 Superuser 請求</string>
<string name="auth_fingerprint">指紋驗證</string>
<string name="multiuser_mode">多使用者模式</string> <string name="multiuser_mode">使用者模式</string>
<string name="settings_owner_only">僅限裝置擁有者</string> <string name="settings_owner_only">僅限裝置擁有者</string>
<string name="settings_owner_manage">由裝置擁有者管理</string> <string name="settings_owner_manage">由裝置擁有者管理</string>
<string name="settings_user_independent">使用者獨立管理</string> <string name="settings_user_independent">使用者獨立管理</string>
<string name="owner_only_summary">僅限裝置擁有者可使用 Root 權限</string> <string name="owner_only_summary">僅限裝置擁有者可使用 Root 權限</string>
<string name="owner_manage_summary">僅限裝置擁有者可管理及接收 Root 請求</string> <string name="owner_manage_summary">僅限裝置擁有者可管理及接收 Root 請求提示</string>
<string name="user_indepenent_summary">各使用者皆擁有獨立權限設定</string> <string name="user_indepenent_summary">每個使用者皆擁有獨立權限規則</string>
<string name="mount_namespace_mode">掛載 Namespace 模式</string> <string name="mount_namespace_mode">掛載命名空間模式</string>
<string name="settings_ns_global">全域 Namespace</string> <string name="settings_ns_global">全域命名空間</string>
<string name="settings_ns_requester">繼承 Namespace</string> <string name="settings_ns_requester">繼承命名空間</string>
<string name="settings_ns_isolate">獨立 Namespace</string> <string name="settings_ns_isolate">獨立命名空間</string>
<string name="global_summary">所有 Root 工作階段皆使用全域 Namespace</string> <string name="global_summary">所有 Root 工作階段皆使用全域命名空間</string>
<string name="requester_summary">所有 Root 工作階段皆繼承原程式 Namespace</string> <string name="requester_summary">所有 Root 工作階段皆繼承原程式的命名空間</string>
<string name="isolate_summary">所有 Root 工作階段個別擁有獨立 Namespace</string> <string name="isolate_summary">所有 Root 工作階段都擁有獨立的命名空間</string>
<string name="disable_fingerprint">未設定指紋或無指紋辨識器</string> <string name="settings_download_path_error">建立資料夾發生錯誤。檔案存放的路徑需要位於內部儲存空間且不能有同樣名稱的檔案。</string>
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">Superuser 請求</string> <string name="su_request_title">超級使用者請求</string>
<string name="deny">拒絕</string> <string name="deny">拒絕</string>
<string name="prompt">提示</string> <string name="prompt">提示</string>
<string name="grant">允許</string> <string name="grant">允許</string>
<string name="su_warning">授予其此裝置之最高權限。\n若為不明來源,請拒絕!</string> <string name="su_warning">將允許本裝置使用 Root 權限。\n如果不確定,請拒絕!</string>
<string name="forever">一律</string> <string name="forever">永久</string>
<string name="once">僅限本</string> <string name="once"></string>
<string name="tenmin">10 分鐘</string> <string name="tenmin">10 分鐘</string>
<string name="twentymin">20 分鐘</string> <string name="twentymin">20 分鐘</string>
<string name="thirtymin">30 分鐘</string> <string name="thirtymin">30 分鐘</string>
<string name="sixtymin">60 分鐘</string> <string name="sixtymin">60 分鐘</string>
<string name="su_allow_toast">授予 %1$s Superuser 權限</string> <string name="su_allow_toast">允許 %1$s 使用超級使用者的權限</string>
<string name="su_deny_toast">已拒絕授予 %1$s Superuser 權限</string> <string name="su_deny_toast">已拒絕 %1$s 使用超級使用者的權限</string>
<string name="no_apps_found">找不到應用程式</string> <string name="no_apps_found">找不到應用程式</string>
<string name="su_snack_grant">授予 %1$s Superuser 權限</string> <string name="su_snack_grant">允許 %1$s 使用超級使用者的權限</string>
<string name="su_snack_deny">已拒絕授予 %1$s Superuser 權限</string> <string name="su_snack_deny">已拒絕 %1$s 使用超級使用者的權限</string>
<string name="su_snack_notif_on">已允許 %1$s 發送通知</string> <string name="su_snack_notif_on">已允許 %1$s 發送通知</string>
<string name="su_snack_notif_off">已拒絕 %1$s 發送通知</string> <string name="su_snack_notif_off">已拒絕 %1$s 發送通知</string>
<string name="su_snack_log_on">已啟用 %1$s 寫入記錄</string> <string name="su_snack_log_on">已啟用 %1$s 寫入記錄</string>
<string name="su_snack_log_off">已停用 %1$s 寫入記錄</string> <string name="su_snack_log_off">已停用 %1$s 寫入記錄</string>
<string name="su_revoke_title">撤銷</string> <string name="su_revoke_title">撤銷</string>
<string name="su_revoke_msg">確定撤銷 %1$s 的權限?</string> <string name="su_revoke_msg">確定撤銷 %1$s 的權限?</string>
<string name="toast">快顯通知</string> <string name="toast">快顯通知</string>
<string name="none"></string> <string name="none"></string>
<string name="auth_fail">驗證失敗</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID:%1$d</string> <string name="pid">PID:%1$d</string>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<string name="settings">Settings</string> <string name="settings">Settings</string>
<string name="install">Install</string> <string name="install">Install</string>
<string name="unsupport_magisk_title">Unsupported Magisk Version</string> <string name="unsupport_magisk_title">Unsupported Magisk Version</string>
<string name="unsupport_magisk_message">This version of Magisk Manager does not support Magisk version lower than v18.0.\n\nYou can either manually upgrade Magisk, or downgrade the app to an older version.</string> <string name="unsupport_magisk_msg">This version of Magisk Manager does not support Magisk version lower than %1$s.\n\nThe app will behave as if no Magisk is installed, please upgrade Magisk as soon as possible.</string>
<!--Status Fragment--> <!--Status Fragment-->
<string name="magisk_version_error">Magisk is not installed</string> <string name="magisk_version_error">Magisk is not installed</string>
...@@ -95,7 +95,6 @@ ...@@ -95,7 +95,6 @@
<string name="settings_reboot_toast">Reboot to apply settings</string> <string name="settings_reboot_toast">Reboot to apply settings</string>
<string name="release_notes">Release notes</string> <string name="release_notes">Release notes</string>
<string name="repo_cache_cleared">Repo cache cleared</string> <string name="repo_cache_cleared">Repo cache cleared</string>
<string name="dtbo_patched_title">DTBO was patched!</string> <string name="dtbo_patched_title">DTBO was patched!</string>
<string name="dtbo_patched_reboot">Magisk Manager has patched dtbo.img. Please reboot.</string> <string name="dtbo_patched_reboot">Magisk Manager has patched dtbo.img. Please reboot.</string>
<string name="flashing">Flashing…</string> <string name="flashing">Flashing…</string>
...@@ -116,6 +115,7 @@ ...@@ -116,6 +115,7 @@
<string name="env_fix_title">Requires Additional Setup</string> <string name="env_fix_title">Requires Additional Setup</string>
<string name="env_fix_msg">Your device needs additional setup for Magisk to work properly. It will download the Magisk setup zip, do you want to proceed now?</string> <string name="env_fix_msg">Your device needs additional setup for Magisk to work properly. It will download the Magisk setup zip, do you want to proceed now?</string>
<string name="setup_msg">Running environment setup…</string> <string name="setup_msg">Running environment setup…</string>
<string name="authenticate">Authenticate</string>
<!--Settings Activity --> <!--Settings Activity -->
<string name="settings_general_category">General</string> <string name="settings_general_category">General</string>
...@@ -167,9 +167,9 @@ ...@@ -167,9 +167,9 @@
<string name="request_timeout_summary">%1$d seconds</string> <string name="request_timeout_summary">%1$d seconds</string>
<string name="settings_su_reauth_title">Reauthenticate after upgrade</string> <string name="settings_su_reauth_title">Reauthenticate after upgrade</string>
<string name="settings_su_reauth_summary">Reauthenticate superuser permissions after an application upgrades</string> <string name="settings_su_reauth_summary">Reauthenticate superuser permissions after an application upgrades</string>
<string name="settings_su_fingerprint_title">Enable Fingerprint Authentication</string> <string name="settings_su_biometric_title">Enable Biometric Authentication</string>
<string name="settings_su_fingerprint_summary">Use fingerprint scanner to allow superuser requests</string> <string name="settings_su_biometric_summary">Use biometric authentication to allow superuser requests</string>
<string name="auth_fingerprint">Authenticate Fingerprint</string> <string name="no_biometric">Unsupported device or no biometric settings are enabled</string>
<string name="multiuser_mode">Multiuser Mode</string> <string name="multiuser_mode">Multiuser Mode</string>
<string name="settings_owner_only">Device Owner Only</string> <string name="settings_owner_only">Device Owner Only</string>
...@@ -186,7 +186,6 @@ ...@@ -186,7 +186,6 @@
<string name="global_summary">All root sessions use the global mount namespace</string> <string name="global_summary">All root sessions use the global mount namespace</string>
<string name="requester_summary">Root sessions will inherit their requester\'s namespace</string> <string name="requester_summary">Root sessions will inherit their requester\'s namespace</string>
<string name="isolate_summary">Each root session will have its own isolated namespace</string> <string name="isolate_summary">Each root session will have its own isolated namespace</string>
<string name="disable_fingerprint">No fingerprints were set or no device support</string>
<string name="settings_download_path_error">Error creating folder. It must be accessible from storage root directory and must not be a file.</string> <string name="settings_download_path_error">Error creating folder. It must be accessible from storage root directory and must not be a file.</string>
<!--Superuser--> <!--Superuser-->
...@@ -214,7 +213,6 @@ ...@@ -214,7 +213,6 @@
<string name="su_revoke_msg">Confirm to revoke %1$s rights?</string> <string name="su_revoke_msg">Confirm to revoke %1$s rights?</string>
<string name="toast">Toast</string> <string name="toast">Toast</string>
<string name="none">None</string> <string name="none">None</string>
<string name="auth_fail">Authentication Failed</string>
<!--Superuser logs--> <!--Superuser logs-->
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
......
...@@ -79,6 +79,12 @@ ...@@ -79,6 +79,12 @@
android:key="superuser" android:key="superuser"
android:title="@string/superuser"> android:title="@string/superuser">
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="su_biometric"
android:title="@string/settings_su_biometric_title"
android:summary="@string/settings_su_biometric_summary" />
<ListPreference <ListPreference
android:entries="@array/su_access" android:entries="@array/su_access"
android:entryValues="@array/value_array" android:entryValues="@array/value_array"
...@@ -118,12 +124,6 @@ ...@@ -118,12 +124,6 @@
android:key="su_notification" android:key="su_notification"
android:title="@string/superuser_notification" /> android:title="@string/superuser_notification" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="su_fingerprint"
android:summary="@string/settings_su_fingerprint_summary"
android:title="@string/settings_su_fingerprint_title" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:defaultValue="false" android:defaultValue="false"
android:key="su_reauth" android:key="su_reauth"
......
...@@ -16,7 +16,7 @@ buildscript { ...@@ -16,7 +16,7 @@ buildscript {
maven { url 'https://kotlin.bintray.com/kotlinx' } maven { url 'https://kotlin.bintray.com/kotlinx' }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.5.1' classpath 'com.android.tools.build:gradle:3.5.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${vKotlin}" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${vKotlin}"
......
...@@ -449,6 +449,33 @@ static void prepare_modules() { ...@@ -449,6 +449,33 @@ static void prepare_modules() {
chmod(SECURE_DIR, 0700); chmod(SECURE_DIR, 0700);
} }
static void reboot() {
if (RECOVERY_MODE)
exec_command_sync("/system/bin/reboot", "recovery");
else
exec_command_sync("/system/bin/reboot");
}
void remove_modules() {
LOGI("* Remove all modules and reboot");
chdir(MODULEROOT);
rm_rf("lost+found");
DIR *dir = xopendir(".");
struct dirent *entry;
while ((entry = xreaddir(dir))) {
if (entry->d_type == DT_DIR) {
if (entry->d_name == "."sv || entry->d_name == ".."sv || entry->d_name == ".core"sv)
continue;
chdir(entry->d_name);
close(creat("remove", 0644));
chdir("..");
}
}
closedir(dir);
chdir("/");
reboot();
}
static void collect_modules() { static void collect_modules() {
chdir(MODULEROOT); chdir(MODULEROOT);
rm_rf("lost+found"); rm_rf("lost+found");
...@@ -757,6 +784,8 @@ void boot_complete(int client) { ...@@ -757,6 +784,8 @@ void boot_complete(int client) {
if (!pfs_done) if (!pfs_done)
return; return;
auto_start_magiskhide();
if (access(MANAGERAPK, F_OK) == 0) { if (access(MANAGERAPK, F_OK) == 0) {
// Install Magisk Manager if exists // Install Magisk Manager if exists
rename(MANAGERAPK, "/data/magisk.apk"); rename(MANAGERAPK, "/data/magisk.apk");
...@@ -771,7 +800,4 @@ void boot_complete(int client) { ...@@ -771,7 +800,4 @@ void boot_complete(int client) {
install_apk("/data/magisk.apk"); install_apk("/data/magisk.apk");
} }
} }
// Test whether broadcast can be used or not
broadcast_test();
} }
...@@ -31,13 +31,6 @@ static void verify_client(int client, pid_t pid) { ...@@ -31,13 +31,6 @@ static void verify_client(int client, pid_t pid) {
} }
} }
static void remove_modules() {
LOGI("* Remove all modules and reboot");
rm_rf(MODULEROOT);
rm_rf(MODULEUPGRADE);
reboot();
}
static void *request_handler(void *args) { static void *request_handler(void *args) {
int client = reinterpret_cast<intptr_t>(args); int client = reinterpret_cast<intptr_t>(args);
...@@ -53,8 +46,6 @@ static void *request_handler(void *args) { ...@@ -53,8 +46,6 @@ static void *request_handler(void *args) {
case LATE_START: case LATE_START:
case BOOT_COMPLETE: case BOOT_COMPLETE:
case SQLITE_CMD: case SQLITE_CMD:
case BROADCAST_ACK:
case BROADCAST_TEST:
if (credential.uid != 0) { if (credential.uid != 0) {
write_int(client, ROOT_REQUIRED); write_int(client, ROOT_REQUIRED);
close(client); close(client);
...@@ -91,12 +82,6 @@ static void *request_handler(void *args) { ...@@ -91,12 +82,6 @@ static void *request_handler(void *args) {
case SQLITE_CMD: case SQLITE_CMD:
exec_sql(client); exec_sql(client);
break; break;
case BROADCAST_ACK:
broadcast_ack(client);
break;
case BROADCAST_TEST:
broadcast_test(client);
break;
case REMOVE_MODULES: case REMOVE_MODULES:
if (credential.uid == UID_SHELL || credential.uid == UID_ROOT) { if (credential.uid == UID_SHELL || credential.uid == UID_ROOT) {
remove_modules(); remove_modules();
...@@ -179,27 +164,6 @@ static void main_daemon() { ...@@ -179,27 +164,6 @@ static void main_daemon() {
} }
} }
void reboot() {
if (RECOVERY_MODE)
exec_command_sync("/system/bin/reboot", "recovery");
else
exec_command_sync("/system/bin/reboot");
}
int switch_mnt_ns(int pid) {
char mnt[32];
snprintf(mnt, sizeof(mnt), "/proc/%d/ns/mnt", pid);
if (access(mnt, R_OK) == -1) return 1; // Maybe process died..
int fd, ret;
fd = xopen(mnt, O_RDONLY);
if (fd < 0) return 1;
// Switch to its namespace
ret = xsetns(fd, 0);
close(fd);
return ret;
}
int connect_daemon(bool create) { int connect_daemon(bool create) {
struct sockaddr_un sun; struct sockaddr_un sun;
socklen_t len = setup_sockaddr(&sun, MAIN_SOCKET); socklen_t len = setup_sockaddr(&sun, MAIN_SOCKET);
...@@ -210,9 +174,17 @@ int connect_daemon(bool create) { ...@@ -210,9 +174,17 @@ int connect_daemon(bool create) {
exit(1); exit(1);
} }
int ppid = getpid();
LOGD("client: launching new main daemon process\n"); LOGD("client: launching new main daemon process\n");
if (fork_dont_care() == 0) { if (fork_dont_care() == 0) {
close(fd); close(fd);
// Make sure ppid is not in acct
char src[64], dest[64];
sprintf(src, "/acct/uid_0/pid_%d", ppid);
sprintf(dest, "/acct/uid_0/pid_%d", getpid());
rename(src, dest);
main_daemon(); main_daemon();
} }
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
#include <daemon.h> #include <daemon.h>
#include <utils.h> #include <utils.h>
#define DB_VERSION 9 #define DB_VERSION 10
using namespace std; using namespace std;
...@@ -72,13 +72,6 @@ static char *open_and_init_db(sqlite3 *&db) { ...@@ -72,13 +72,6 @@ static char *open_and_init_db(sqlite3 *&db) {
"logging INT, notification INT, PRIMARY KEY(uid))", "logging INT, notification INT, PRIMARY KEY(uid))",
nullptr, nullptr, &err); nullptr, nullptr, &err);
err_ret(err); err_ret(err);
// Logs
sqlite3_exec(db,
"CREATE TABLE IF NOT EXISTS logs "
"(from_uid INT, package_name TEXT, app_name TEXT, from_pid INT, "
"to_uid INT, action INT, time INT, command TEXT)",
nullptr, nullptr, &err);
err_ret(err);
// Settings // Settings
sqlite3_exec(db, sqlite3_exec(db,
"CREATE TABLE IF NOT EXISTS settings " "CREATE TABLE IF NOT EXISTS settings "
...@@ -144,6 +137,12 @@ static char *open_and_init_db(sqlite3 *&db) { ...@@ -144,6 +137,12 @@ static char *open_and_init_db(sqlite3 *&db) {
ver = 9; ver = 9;
upgrade = true; upgrade = true;
} }
if (ver < 10) {
sqlite3_exec(db, "DROP TABLE logs", nullptr, nullptr, &err);
err_ret(err);
ver = 10;
upgrade = true;
}
if (upgrade) { if (upgrade) {
// Set version // Set version
......
...@@ -35,8 +35,6 @@ Advanced Options (Internal APIs): ...@@ -35,8 +35,6 @@ Advanced Options (Internal APIs):
--clone-attr SRC DEST clone permission, owner, and selinux context --clone-attr SRC DEST clone permission, owner, and selinux context
--clone SRC DEST clone SRC to DEST --clone SRC DEST clone SRC to DEST
--sqlite SQL exec SQL commands to Magisk database --sqlite SQL exec SQL commands to Magisk database
--connect-mode [MODE] get/set connect mode for su request and notify
--broadcast-test manually trigger broadcast tests
Supported init triggers: Supported init triggers:
post-fs-data, service, boot-complete post-fs-data, service, boot-complete
...@@ -113,23 +111,10 @@ int magisk_main(int argc, char *argv[]) { ...@@ -113,23 +111,10 @@ int magisk_main(int argc, char *argv[]) {
printf("%s\n", res); printf("%s\n", res);
free(res); free(res);
} }
} else if (argv[1] == "--connect-mode"sv) {
int fd = connect_daemon();
write_int(fd, BROADCAST_ACK);
if (argc >= 3) {
write_int(fd, parse_int(argv[2]));
} else {
write_int(fd, -1);
}
return read_int(fd);
} else if (argv[1] == "--remove-modules"sv) { } else if (argv[1] == "--remove-modules"sv) {
int fd = connect_daemon(); int fd = connect_daemon();
write_int(fd, REMOVE_MODULES); write_int(fd, REMOVE_MODULES);
return read_int(fd); return read_int(fd);
} else if (argv[1] == "--broadcast-test"sv) {
int fd = connect_daemon();
write_int(fd, BROADCAST_TEST);
return read_int(fd);
} }
#if 0 #if 0
/* Entry point for testing stuffs */ /* Entry point for testing stuffs */
......
Subproject commit b7cba59ffb2720b09a0bd81bf40f2a85db64153e Subproject commit 23132f3d45089f9c0e495ec5365993f4b2ef348c
...@@ -17,9 +17,7 @@ enum { ...@@ -17,9 +17,7 @@ enum {
BOOT_COMPLETE, BOOT_COMPLETE,
MAGISKHIDE, MAGISKHIDE,
SQLITE_CMD, SQLITE_CMD,
BROADCAST_ACK,
REMOVE_MODULES, REMOVE_MODULES,
BROADCAST_TEST,
}; };
// Return codes for daemon // Return codes for daemon
...@@ -30,13 +28,11 @@ enum { ...@@ -30,13 +28,11 @@ enum {
DAEMON_LAST DAEMON_LAST
}; };
// daemon.c // daemon.cpp
int connect_daemon(bool create = false); int connect_daemon(bool create = false);
int switch_mnt_ns(int pid);
void reboot();
// socket.c // socket.cpp
socklen_t setup_sockaddr(struct sockaddr_un *sun, const char *name); socklen_t setup_sockaddr(struct sockaddr_un *sun, const char *name);
int create_rand_socket(struct sockaddr_un *sun); int create_rand_socket(struct sockaddr_un *sun);
...@@ -63,6 +59,7 @@ void unlock_blocks(); ...@@ -63,6 +59,7 @@ void unlock_blocks();
void post_fs_data(int client); void post_fs_data(int client);
void late_start(int client); void late_start(int client);
void boot_complete(int client); void boot_complete(int client);
void remove_modules();
/************* /*************
* Scripting * * Scripting *
...@@ -85,8 +82,6 @@ void magiskhide_handler(int client); ...@@ -85,8 +82,6 @@ void magiskhide_handler(int client);
*************/ *************/
void su_daemon_handler(int client, struct ucred *credential); void su_daemon_handler(int client, struct ucred *credential);
void broadcast_test(int client = -1);
void broadcast_ack(int client);
/********************* /*********************
* Daemon Global Vars * Daemon Global Vars
......
...@@ -84,6 +84,14 @@ static bool is_lnk(const char *name) { ...@@ -84,6 +84,14 @@ static bool is_lnk(const char *name) {
return S_ISLNK(st.st_mode); return S_ISLNK(st.st_mode);
} }
void BaseInit::cleanup() {
// Unmount in reverse order
for (auto &p : reversed(mount_list)) {
LOGD("Unmount [%s]\n", p.data());
umount(p.data());
}
}
bool MagiskInit::read_dt_fstab(const char *name, char *partname, char *fstype) { bool MagiskInit::read_dt_fstab(const char *name, char *partname, char *fstype) {
char path[128]; char path[128];
int fd; int fd;
...@@ -118,7 +126,7 @@ if (!is_lnk("/" #name) && read_dt_fstab(#name, partname, fstype)) { \ ...@@ -118,7 +126,7 @@ if (!is_lnk("/" #name) && read_dt_fstab(#name, partname, fstype)) { \
setup_block(partname, block_dev); \ setup_block(partname, block_dev); \
xmkdir("/" #name, 0755); \ xmkdir("/" #name, 0755); \
xmount(block_dev, "/" #name, fstype, MS_RDONLY, nullptr); \ xmount(block_dev, "/" #name, fstype, MS_RDONLY, nullptr); \
mnt_##name = true; \ mount_list.emplace_back("/" #name); \
} }
void RootFSInit::early_mount() { void RootFSInit::early_mount() {
...@@ -237,16 +245,3 @@ void SecondStageInit::early_mount() { ...@@ -237,16 +245,3 @@ void SecondStageInit::early_mount() {
switch_root("/system_root"); switch_root("/system_root");
} }
#define umount_root(name) \
if (mnt_##name) \
umount("/" #name);
void MagiskInit::cleanup() {
umount(SELINUX_MNT);
BaseInit::cleanup();
umount_root(system);
umount_root(vendor);
umount_root(product);
umount_root(odm);
}
...@@ -31,31 +31,44 @@ constexpr int (*init_applet_main[])(int, char *[]) = ...@@ -31,31 +31,44 @@ constexpr int (*init_applet_main[])(int, char *[]) =
#ifdef MAGISK_DEBUG #ifdef MAGISK_DEBUG
static FILE *kmsg; static FILE *kmsg;
static char kmsg_buf[4096];
static int vprintk(const char *fmt, va_list ap) { static int vprintk(const char *fmt, va_list ap) {
fprintf(kmsg, "magiskinit: "); vsnprintf(kmsg_buf + 12, sizeof(kmsg_buf) - 12, fmt, ap);
return vfprintf(kmsg, fmt, ap); return fprintf(kmsg, "%s", kmsg_buf);
} }
static void setup_klog() { void setup_klog() {
mknod("/kmsg", S_IFCHR | 0666, makedev(1, 11)); // Shut down first 3 fds
int fd = xopen("/kmsg", O_WRONLY | O_CLOEXEC); int null;
kmsg = fdopen(fd, "w"); if (access("/dev/null", W_OK) == 0) {
setbuf(kmsg, nullptr); null = xopen("/dev/null", O_RDWR | O_CLOEXEC);
unlink("/kmsg"); } else {
log_cb.d = log_cb.i = log_cb.w = log_cb.e = vprintk;
log_cb.ex = nop_ex;
// Prevent file descriptor confusion
mknod("/null", S_IFCHR | 0666, makedev(1, 3)); mknod("/null", S_IFCHR | 0666, makedev(1, 3));
int null = xopen("/null", O_RDWR | O_CLOEXEC); null = xopen("/null", O_RDWR | O_CLOEXEC);
unlink("/null"); unlink("/null");
}
xdup3(null, STDIN_FILENO, O_CLOEXEC); xdup3(null, STDIN_FILENO, O_CLOEXEC);
xdup3(null, STDOUT_FILENO, O_CLOEXEC); xdup3(null, STDOUT_FILENO, O_CLOEXEC);
xdup3(null, STDERR_FILENO, O_CLOEXEC); xdup3(null, STDERR_FILENO, O_CLOEXEC);
if (null > STDERR_FILENO) if (null > STDERR_FILENO)
close(null); close(null);
int fd;
if (access("/proc/kmsg", W_OK) == 0) {
fd = xopen("/proc/kmsg", O_WRONLY | O_CLOEXEC);
} else {
mknod("/kmsg", S_IFCHR | 0666, makedev(1, 11));
fd = xopen("/kmsg", O_WRONLY | O_CLOEXEC);
unlink("/kmsg");
}
kmsg = fdopen(fd, "w");
setbuf(kmsg, nullptr);
log_cb.d = log_cb.i = log_cb.w = log_cb.e = vprintk;
log_cb.ex = nop_ex;
strcpy(kmsg_buf, "magiskinit: ");
} }
#else #else
#define setup_klog(...) void setup_klog() {}
#endif #endif
static bool unxz(int fd, const uint8_t *buf, size_t size) { static bool unxz(int fd, const uint8_t *buf, size_t size) {
...@@ -131,13 +144,11 @@ public: ...@@ -131,13 +144,11 @@ public:
} }
}; };
class TestInit : public SARInit { class TestInit : public BaseInit {
public: public:
TestInit(char *argv[], cmdline *cmd) : SARInit(argv, cmd) {}; TestInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {};
void start() override { void start() override {
early_mount(); // Write init tests here
patch_rootdir();
cleanup();
} }
}; };
......
#include <sys/mount.h> #include <sys/mount.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <vector>
struct cmdline { struct cmdline {
bool system_as_root; bool system_as_root;
...@@ -34,19 +35,17 @@ class BaseInit { ...@@ -34,19 +35,17 @@ class BaseInit {
protected: protected:
cmdline *cmd; cmdline *cmd;
char **argv; char **argv;
std::vector<std::string> mount_list;
void exec_init(const char *init = "/init") { void exec_init(const char *init = "/init") {
cleanup(); cleanup();
execv(init, argv); execv(init, argv);
exit(1); exit(1);
} }
virtual void cleanup() { virtual void cleanup();
umount("/sys");
umount("/proc");
umount("/dev");
}
public: public:
BaseInit(char *argv[], cmdline *cmd) : cmd(cmd), argv(argv) {} BaseInit(char *argv[], cmdline *cmd) :
cmd(cmd), argv(argv), mount_list{"/sys", "/proc", "/dev"} {}
virtual ~BaseInit() = default; virtual ~BaseInit() = default;
virtual void start() = 0; virtual void start() = 0;
}; };
...@@ -54,15 +53,10 @@ public: ...@@ -54,15 +53,10 @@ public:
class MagiskInit : public BaseInit { class MagiskInit : public BaseInit {
protected: protected:
raw_data self; raw_data self;
bool mnt_system = false;
bool mnt_vendor = false;
bool mnt_product = false;
bool mnt_odm = false;
virtual void early_mount() = 0; virtual void early_mount() = 0;
bool read_dt_fstab(const char *name, char *partname, char *fstype); bool read_dt_fstab(const char *name, char *partname, char *fstype);
bool patch_sepolicy(const char *file = "/sepolicy"); bool patch_sepolicy(const char *file = "/sepolicy");
void cleanup() override;
public: public:
MagiskInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {}; MagiskInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {};
}; };
...@@ -168,3 +162,4 @@ public: ...@@ -168,3 +162,4 @@ public:
void load_kernel_info(cmdline *cmd); void load_kernel_info(cmdline *cmd);
int dump_magisk(const char *path, mode_t mode); int dump_magisk(const char *path, mode_t mode);
int magisk_proxy_main(int argc, char *argv[]); int magisk_proxy_main(int argc, char *argv[]);
void setup_klog();
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
#include <utils.h> #include <utils.h>
#include "init.h" #include "init.h"
#include "flags.h"
#include "magiskrc.h" #include "magiskrc.h"
#ifdef USE_64BIT #ifdef USE_64BIT
...@@ -168,6 +167,7 @@ bool MagiskInit::patch_sepolicy(const char *file) { ...@@ -168,6 +167,7 @@ bool MagiskInit::patch_sepolicy(const char *file) {
// Mount selinuxfs to communicate with kernel // Mount selinuxfs to communicate with kernel
xmount("selinuxfs", SELINUX_MNT, "selinuxfs", 0, nullptr); xmount("selinuxfs", SELINUX_MNT, "selinuxfs", 0, nullptr);
mount_list.emplace_back(SELINUX_MNT);
if (patch_init) if (patch_init)
load_split_cil(); load_split_cil();
...@@ -231,7 +231,7 @@ static void sbin_overlay(const raw_data &self, const raw_data &config) { ...@@ -231,7 +231,7 @@ static void sbin_overlay(const raw_data &self, const raw_data &config) {
#define PATCHPOLICY "/sbin/.se" #define PATCHPOLICY "/sbin/.se"
#define LIBSELINUX "/system/" LIBNAME "/libselinux.so" #define LIBSELINUX "/system/" LIBNAME "/libselinux.so"
static string mount_list; static string magic_mount_list;
static void magic_mount(int dirfd, const string &path) { static void magic_mount(int dirfd, const string &path) {
DIR *dir = xfdopendir(dirfd); DIR *dir = xfdopendir(dirfd);
...@@ -249,8 +249,8 @@ static void magic_mount(int dirfd, const string &path) { ...@@ -249,8 +249,8 @@ static void magic_mount(int dirfd, const string &path) {
string src = ROOTOVL + dest; string src = ROOTOVL + dest;
LOGD("Mount [%s] -> [%s]\n", src.data(), dest.data()); LOGD("Mount [%s] -> [%s]\n", src.data(), dest.data());
xmount(src.data(), dest.data(), nullptr, MS_BIND, nullptr); xmount(src.data(), dest.data(), nullptr, MS_BIND, nullptr);
mount_list += dest; magic_mount_list += dest;
mount_list += '\n'; magic_mount_list += '\n';
} }
} }
} }
...@@ -380,7 +380,7 @@ void SARBase::patch_rootdir() { ...@@ -380,7 +380,7 @@ void SARBase::patch_rootdir() {
magic_mount(src, ""); magic_mount(src, "");
close(src); close(src);
dest = xopen(ROOTMNT, O_WRONLY | O_CREAT | O_CLOEXEC); dest = xopen(ROOTMNT, O_WRONLY | O_CREAT | O_CLOEXEC);
write(dest, mount_list.data(), mount_list.length()); write(dest, magic_mount_list.data(), magic_mount_list.length());
close(dest); close(dest);
} }
...@@ -466,23 +466,6 @@ void AFirstStageInit::prepare() { ...@@ -466,23 +466,6 @@ void AFirstStageInit::prepare() {
rename("/.backup/init", "/init"); rename("/.backup/init", "/init");
} }
#ifdef MAGISK_DEBUG
static FILE *kmsg;
static int vprintk(const char *fmt, va_list ap) {
fprintf(kmsg, "magiskinit: ");
return vfprintf(kmsg, fmt, ap);
}
static void setup_klog() {
int fd = xopen("/proc/kmsg", O_WRONLY | O_CLOEXEC);
kmsg = fdopen(fd, "w");
setbuf(kmsg, nullptr);
log_cb.d = log_cb.i = log_cb.w = log_cb.e = vprintk;
log_cb.ex = nop_ex;
}
#else
#define setup_klog(...)
#endif
int magisk_proxy_main(int argc, char *argv[]) { int magisk_proxy_main(int argc, char *argv[]) {
setup_klog(); setup_klog();
......
...@@ -21,19 +21,18 @@ using namespace std; ...@@ -21,19 +21,18 @@ using namespace std;
uint32_t dyn_img_hdr::j32 = 0; uint32_t dyn_img_hdr::j32 = 0;
uint64_t dyn_img_hdr::j64 = 0; uint64_t dyn_img_hdr::j64 = 0;
static int64_t one_step(unique_ptr<Compression> &&ptr, int fd, const void *in, size_t size) { static void decompress(format_t type, int fd, const void *in, size_t size) {
ptr->setOut(make_unique<FDOutStream>(fd)); unique_ptr<stream> ptr(get_decoder(type, open_stream<fd_stream>(fd)));
if (!ptr->write(in, size)) ptr->write(in, size);
return -1;
return ptr->finalize();
}
static int64_t decompress(format_t type, int fd, const void *in, size_t size) {
return one_step(unique_ptr<Compression>(get_decoder(type)), fd, in, size);
} }
static int64_t compress(format_t type, int fd, const void *in, size_t size) { static int64_t compress(format_t type, int fd, const void *in, size_t size) {
return one_step(unique_ptr<Compression>(get_encoder(type)), fd, in, size); auto prev = lseek(fd, 0, SEEK_CUR);
unique_ptr<stream> ptr(get_encoder(type, open_stream<fd_stream>(fd)));
ptr->write(in, size);
ptr->close();
auto now = lseek(fd, 0, SEEK_CUR);
return now - prev;
} }
static void dump(void *buf, size_t size, const char *filename) { static void dump(void *buf, size_t size, const char *filename) {
...@@ -221,7 +220,7 @@ void boot_img::parse_image(uint8_t *addr) { ...@@ -221,7 +220,7 @@ void boot_img::parse_image(uint8_t *addr) {
hdr = new dyn_img_v0(addr); hdr = new dyn_img_v0(addr);
} }
for (int i = SHA_DIGEST_SIZE; i < SHA256_DIGEST_SIZE; ++i) { for (int i = SHA_DIGEST_SIZE + 4; i < SHA256_DIGEST_SIZE; ++i) {
if (hdr->id()[i]) { if (hdr->id()[i]) {
flags |= SHA256_FLAG; flags |= SHA256_FLAG;
break; break;
...@@ -312,14 +311,14 @@ void boot_img::find_kernel_dtb() { ...@@ -312,14 +311,14 @@ void boot_img::find_kernel_dtb() {
} }
} }
int unpack(const char *image, bool hdr) { int unpack(const char *image, bool nodecomp, bool hdr) {
boot_img boot(image); boot_img boot(image);
if (hdr) if (hdr)
boot.hdr->dump_hdr_file(); boot.hdr->dump_hdr_file();
// Dump kernel // Dump kernel
if (COMPRESSED(boot.k_fmt)) { if (!nodecomp && COMPRESSED(boot.k_fmt)) {
int fd = creat(KERNEL_FILE, 0644); int fd = creat(KERNEL_FILE, 0644);
decompress(boot.k_fmt, fd, boot.kernel, boot.hdr->kernel_size()); decompress(boot.k_fmt, fd, boot.kernel, boot.hdr->kernel_size());
close(fd); close(fd);
...@@ -331,7 +330,7 @@ int unpack(const char *image, bool hdr) { ...@@ -331,7 +330,7 @@ int unpack(const char *image, bool hdr) {
dump(boot.kernel_dtb, boot.kernel_dt_size, KER_DTB_FILE); dump(boot.kernel_dtb, boot.kernel_dt_size, KER_DTB_FILE);
// Dump ramdisk // Dump ramdisk
if (COMPRESSED(boot.r_fmt)) { if (!nodecomp && COMPRESSED(boot.r_fmt)) {
int fd = creat(RAMDISK_FILE, 0644); int fd = creat(RAMDISK_FILE, 0644);
decompress(boot.r_fmt, fd, boot.ramdisk, boot.hdr->ramdisk_size()); decompress(boot.r_fmt, fd, boot.ramdisk, boot.hdr->ramdisk_size());
close(fd); close(fd);
...@@ -436,7 +435,7 @@ void repack(const char* src_img, const char* out_img, bool nocomp) { ...@@ -436,7 +435,7 @@ void repack(const char* src_img, const char* out_img, bool nocomp) {
size_t raw_size; size_t raw_size;
void *raw_buf; void *raw_buf;
mmap_ro(RAMDISK_FILE, raw_buf, raw_size); mmap_ro(RAMDISK_FILE, raw_buf, raw_size);
if (!COMPRESSED_ANY(check_fmt(raw_buf, raw_size)) && COMPRESSED(boot.r_fmt) && !nocomp) { if (!nocomp && !COMPRESSED_ANY(check_fmt(raw_buf, raw_size)) && COMPRESSED(boot.r_fmt)) {
boot.hdr->ramdisk_size() = compress(boot.r_fmt, fd, raw_buf, raw_size); boot.hdr->ramdisk_size() = compress(boot.r_fmt, fd, raw_buf, raw_size);
} else { } else {
boot.hdr->ramdisk_size() = xwrite(fd, raw_buf, raw_size); boot.hdr->ramdisk_size() = xwrite(fd, raw_buf, raw_size);
......
...@@ -6,6 +6,13 @@ ...@@ -6,6 +6,13 @@
#include <memory> #include <memory>
#include <functional> #include <functional>
#include <zlib.h>
#include <bzlib.h>
#include <lzma.h>
#include <lz4.h>
#include <lz4frame.h>
#include <lz4hc.h>
#include <logging.h> #include <logging.h>
#include <utils.h> #include <utils.h>
...@@ -14,260 +21,201 @@ ...@@ -14,260 +21,201 @@
using namespace std; using namespace std;
static bool read_file(FILE *fp, const function<void (void *, size_t)> &fn) { #define bwrite filter_stream::write
char buf[4096]; #define bclose filter_stream::close
size_t len;
while ((len = fread(buf, 1, sizeof(buf), fp)))
fn(buf, len);
return true;
}
void decompress(char *infile, const char *outfile) {
bool in_std = strcmp(infile, "-") == 0;
bool rm_in = false;
FILE *in_file = in_std ? stdin : xfopen(infile, "re");
int out_fd = -1;
unique_ptr<Compression> cmp;
read_file(in_file, [&](void *buf, size_t len) -> void {
if (out_fd < 0) {
format_t type = check_fmt(buf, len);
fprintf(stderr, "Detected format: [%s]\n", fmt2name[type]);
if (!COMPRESSED(type))
LOGE("Input file is not a supported compressed type!\n");
cmp.reset(get_decoder(type));
/* If user does not provide outfile, infile has to be either constexpr size_t CHUNK = 0x40000;
* <path>.[ext], or '-'. Outfile will be either <path> or '-'. constexpr size_t LZ4_UNCOMPRESSED = 0x800000;
* If the input does not have proper format, abort */ constexpr size_t LZ4_COMPRESSED = LZ4_COMPRESSBOUND(LZ4_UNCOMPRESSED);
char *ext = nullptr; class cpr_stream : public filter_stream {
if (outfile == nullptr) { public:
outfile = infile; explicit cpr_stream(FILE *fp) : filter_stream(fp) {}
if (!in_std) {
ext = strrchr(infile, '.');
if (ext == nullptr || strcmp(ext, fmt2ext[type]) != 0)
LOGE("Input file is not a supported type!\n");
// Strip out extension and remove input
*ext = '\0';
rm_in = true;
fprintf(stderr, "Decompressing to [%s]\n", outfile);
}
}
out_fd = strcmp(outfile, "-") == 0 ? int read(void *buf, size_t len) final {
STDOUT_FILENO : xopen(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644); return stream::read(buf, len);
cmp->setOut(make_unique<FDOutStream>(out_fd));
if (ext) *ext = '.';
} }
if (!cmp->write(buf, len))
LOGE("Decompression error!\n");
});
cmp->finalize();
fclose(in_file);
close(out_fd);
if (rm_in) int close() final {
unlink(infile); finish();
} return bclose();
void compress(const char *method, const char *infile, const char *outfile) {
auto it = name2fmt.find(method);
if (it == name2fmt.end())
LOGE("Unsupported compression method: [%s]\n", method);
unique_ptr<Compression> cmp(get_encoder(it->second));
bool in_std = strcmp(infile, "-") == 0;
bool rm_in = false;
FILE *in_file = in_std ? stdin : xfopen(infile, "re");
int out_fd;
if (outfile == nullptr) {
if (in_std) {
out_fd = STDOUT_FILENO;
} else {
/* If user does not provide outfile and infile is not
* STDIN, output to <infile>.[ext] */
char *tmp = new char[strlen(infile) + 5];
sprintf(tmp, "%s%s", infile, fmt2ext[it->second]);
out_fd = xopen(tmp, O_WRONLY | O_CREAT | O_TRUNC, 0644);
fprintf(stderr, "Compressing to [%s]\n", tmp);
delete[] tmp;
rm_in = true;
} }
} else {
out_fd = strcmp(outfile, "-") == 0 ?
STDOUT_FILENO : xopen(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
}
cmp->setOut(make_unique<FDOutStream>(out_fd));
read_file(in_file, [&](void *buf, size_t len) -> void { protected:
if (!cmp->write(buf, len)) // If finish is overridden, destroy should be called in the destructor
LOGE("Compression error!\n"); virtual void finish() {}
}); void destroy() { if (fp) finish(); }
};
cmp->finalize(); class gz_strm : public cpr_stream {
fclose(in_file); public:
close(out_fd); ~gz_strm() override { destroy(); }
if (rm_in) int write(const void *buf, size_t len) override {
unlink(infile); return len ? write(buf, len, Z_NO_FLUSH) : 0;
}
Compression *get_encoder(format_t type) {
switch (type) {
case XZ:
return new XZEncoder();
case LZMA:
return new LZMAEncoder();
case BZIP2:
return new BZEncoder();
case LZ4:
return new LZ4FEncoder();
case LZ4_LEGACY:
return new LZ4Encoder();
case GZIP:
default:
return new GZEncoder();
} }
}
Compression *get_decoder(format_t type) { protected:
switch (type) { enum mode_t {
case XZ: DECODE,
case LZMA: ENCODE
return new LZMADecoder(); } mode;
case BZIP2:
return new BZDecoder();
case LZ4:
return new LZ4FDecoder();
case LZ4_LEGACY:
return new LZ4Decoder();
case GZIP:
default:
return new GZDecoder();
}
}
GZStream::GZStream(int mode) : mode(mode), strm({}) { gz_strm(mode_t mode, FILE *fp) : cpr_stream(fp), mode(mode) {
switch(mode) { switch(mode) {
case 0: case DECODE:
inflateInit2(&strm, 15 | 16); inflateInit2(&strm, 15 | 16);
break; break;
case 1: case ENCODE:
deflateInit2(&strm, 9, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY); deflateInit2(&strm, 9, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY);
break; break;
} }
} }
bool GZStream::write(const void *in, size_t size) {
return size ? write(in, size, Z_NO_FLUSH) : true;
}
uint64_t GZStream::finalize() { void finish() override {
write(nullptr, 0, Z_FINISH); write(nullptr, 0, Z_FINISH);
uint64_t total = strm.total_out;
switch(mode) { switch(mode) {
case 0: case DECODE:
inflateEnd(&strm); inflateEnd(&strm);
break; break;
case 1: case ENCODE:
deflateEnd(&strm); deflateEnd(&strm);
break; break;
} }
return total; }
}
bool GZStream::write(const void *in, size_t size, int flush) { private:
int ret; z_stream strm;
strm.next_in = (Bytef *) in; uint8_t outbuf[CHUNK];
strm.avail_in = size;
int write(const void *buf, size_t len, int flush) {
strm.next_in = (Bytef *) buf;
strm.avail_in = len;
do { do {
int code;
strm.next_out = outbuf; strm.next_out = outbuf;
strm.avail_out = sizeof(outbuf); strm.avail_out = sizeof(outbuf);
switch(mode) { switch(mode) {
case 0: case DECODE:
ret = inflate(&strm, flush); code = inflate(&strm, flush);
break; break;
case 1: case ENCODE:
ret = deflate(&strm, flush); code = deflate(&strm, flush);
break; break;
} }
if (ret == Z_STREAM_ERROR) { if (code == Z_STREAM_ERROR) {
LOGW("Gzip %s failed (%d)\n", mode ? "encode" : "decode", ret); LOGW("gzip %s failed (%d)\n", mode ? "encode" : "decode", code);
return false; return -1;
} }
FilterOutStream::write(outbuf, sizeof(outbuf) - strm.avail_out); bwrite(outbuf, sizeof(outbuf) - strm.avail_out);
} while (strm.avail_out == 0); } while (strm.avail_out == 0);
return true; return len;
} }
};
class gz_decoder : public gz_strm {
public:
explicit gz_decoder(FILE *fp) : gz_strm(DECODE, fp) {};
};
BZStream::BZStream(int mode) : mode(mode), strm({}) { class gz_encoder : public gz_strm {
public:
explicit gz_encoder(FILE *fp) : gz_strm(ENCODE, fp) {};
};
class bz_strm : public cpr_stream {
public:
~bz_strm() override { destroy(); }
int write(const void *buf, size_t len) override {
return len ? write(buf, len, BZ_RUN) : 0;
}
protected:
enum mode_t {
DECODE,
ENCODE
} mode;
bz_strm(mode_t mode, FILE *fp) : cpr_stream(fp), mode(mode) {
switch(mode) { switch(mode) {
case 0: case DECODE:
BZ2_bzDecompressInit(&strm, 0, 0); BZ2_bzDecompressInit(&strm, 0, 0);
break; break;
case 1: case ENCODE:
BZ2_bzCompressInit(&strm, 9, 0, 0); BZ2_bzCompressInit(&strm, 9, 0, 0);
break; break;
} }
} }
bool BZStream::write(const void *in, size_t size) {
return size ? write(in, size, BZ_RUN) : true;
}
uint64_t BZStream::finalize() { void finish() override {
if (mode)
write(nullptr, 0, BZ_FINISH);
uint64_t total = ((uint64_t) strm.total_out_hi32 << 32) + strm.total_out_lo32;
switch(mode) { switch(mode) {
case 0: case DECODE:
BZ2_bzDecompressEnd(&strm); BZ2_bzDecompressEnd(&strm);
break; break;
case 1: case ENCODE:
write(nullptr, 0, BZ_FINISH);
BZ2_bzCompressEnd(&strm); BZ2_bzCompressEnd(&strm);
break; break;
} }
return total; }
}
bool BZStream::write(const void *in, size_t size, int flush) { private:
int ret; bz_stream strm;
strm.next_in = (char *) in; char outbuf[CHUNK];
strm.avail_in = size;
int write(const void *buf, size_t len, int flush) {
strm.next_in = (char *) buf;
strm.avail_in = len;
do { do {
int code;
strm.avail_out = sizeof(outbuf); strm.avail_out = sizeof(outbuf);
strm.next_out = outbuf; strm.next_out = outbuf;
switch(mode) { switch(mode) {
case 0: case DECODE:
ret = BZ2_bzDecompress(&strm); code = BZ2_bzDecompress(&strm);
break; break;
case 1: case ENCODE:
ret = BZ2_bzCompress(&strm, flush); code = BZ2_bzCompress(&strm, flush);
break; break;
} }
if (ret < 0) { if (code < 0) {
LOGW("Bzip2 %s failed (%d)\n", mode ? "encode" : "decode", ret); LOGW("bzip2 %s failed (%d)\n", mode ? "encode" : "decode", code);
return false; return -1;
} }
FilterOutStream::write(outbuf, sizeof(outbuf) - strm.avail_out); bwrite(outbuf, sizeof(outbuf) - strm.avail_out);
} while (strm.avail_out == 0); } while (strm.avail_out == 0);
return true; return len;
} }
};
class bz_decoder : public bz_strm {
public:
explicit bz_decoder(FILE *fp) : bz_strm(DECODE, fp) {};
};
class bz_encoder : public bz_strm {
public:
explicit bz_encoder(FILE *fp) : bz_strm(ENCODE, fp) {};
};
class lzma_strm : public cpr_stream {
public:
~lzma_strm() override { destroy(); }
LZMAStream::LZMAStream(int mode) : mode(mode), strm(LZMA_STREAM_INIT) { int write(const void *buf, size_t len) override {
return len ? write(buf, len, LZMA_RUN) : 0;
}
protected:
enum mode_t {
DECODE,
ENCODE_XZ,
ENCODE_LZMA
} mode;
lzma_strm(mode_t mode, FILE *fp) : cpr_stream(fp), mode(mode), strm(LZMA_STREAM_INIT) {
lzma_options_lzma opt; lzma_options_lzma opt;
int ret;
// Initialize preset // Initialize preset
lzma_lzma_preset(&opt, 9); lzma_lzma_preset(&opt, 9);
...@@ -276,83 +224,100 @@ LZMAStream::LZMAStream(int mode) : mode(mode), strm(LZMA_STREAM_INIT) { ...@@ -276,83 +224,100 @@ LZMAStream::LZMAStream(int mode) : mode(mode), strm(LZMA_STREAM_INIT) {
{ .id = LZMA_VLI_UNKNOWN, .options = nullptr }, { .id = LZMA_VLI_UNKNOWN, .options = nullptr },
}; };
lzma_ret ret;
switch(mode) { switch(mode) {
case 0: case DECODE:
ret = lzma_auto_decoder(&strm, UINT64_MAX, 0); ret = lzma_auto_decoder(&strm, UINT64_MAX, 0);
break; break;
case 1: case ENCODE_XZ:
ret = lzma_stream_encoder(&strm, filters, LZMA_CHECK_CRC32); ret = lzma_stream_encoder(&strm, filters, LZMA_CHECK_CRC32);
break; break;
case 2: case ENCODE_LZMA:
ret = lzma_alone_encoder(&strm, &opt); ret = lzma_alone_encoder(&strm, &opt);
break; break;
} }
} }
bool LZMAStream::write(const void *in, size_t size) {
return size ? write(in, size, LZMA_RUN) : true;
}
uint64_t LZMAStream::finalize() { void finish() override {
write(nullptr, 0, LZMA_FINISH); write(nullptr, 0, LZMA_FINISH);
uint64_t total = strm.total_out;
lzma_end(&strm); lzma_end(&strm);
return total; }
}
bool LZMAStream::write(const void *in, size_t size, lzma_action flush) { private:
int ret; lzma_stream strm;
strm.next_in = (uint8_t *) in; uint8_t outbuf[CHUNK];
strm.avail_in = size;
int write(const void *buf, size_t len, lzma_action flush) {
strm.next_in = (uint8_t *) buf;
strm.avail_in = len;
do { do {
strm.avail_out = sizeof(outbuf); strm.avail_out = sizeof(outbuf);
strm.next_out = outbuf; strm.next_out = outbuf;
ret = lzma_code(&strm, flush); int code = lzma_code(&strm, flush);
if (ret != LZMA_OK && ret != LZMA_STREAM_END) { if (code != LZMA_OK && code != LZMA_STREAM_END) {
LOGW("LZMA %s failed (%d)\n", mode ? "encode" : "decode", ret); LOGW("LZMA %s failed (%d)\n", mode ? "encode" : "decode", code);
return false; return -1;
} }
FilterOutStream::write(outbuf, sizeof(outbuf) - strm.avail_out); bwrite(outbuf, sizeof(outbuf) - strm.avail_out);
} while (strm.avail_out == 0); } while (strm.avail_out == 0);
return true; return len;
} }
};
LZ4FDecoder::LZ4FDecoder() : outbuf(nullptr), total(0) {
class lzma_decoder : public lzma_strm {
public:
lzma_decoder(FILE *fp) : lzma_strm(DECODE, fp) {}
};
class xz_encoder : public lzma_strm {
public:
xz_encoder(FILE *fp) : lzma_strm(ENCODE_XZ, fp) {}
};
class lzma_encoder : public lzma_strm {
public:
lzma_encoder(FILE *fp) : lzma_strm(ENCODE_LZMA, fp) {}
};
class LZ4F_decoder : public cpr_stream {
public:
explicit LZ4F_decoder(FILE *fp) : cpr_stream(fp), outbuf(nullptr) {
LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION); LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
} }
LZ4FDecoder::~LZ4FDecoder() { ~LZ4F_decoder() override {
LZ4F_freeDecompressionContext(ctx); LZ4F_freeDecompressionContext(ctx);
delete[] outbuf; delete[] outbuf;
} }
bool LZ4FDecoder::write(const void *in, size_t size) { int write(const void *buf, size_t len) override {
auto inbuf = (const uint8_t *) in; auto ret = len;
auto inbuf = reinterpret_cast<const uint8_t *>(buf);
if (!outbuf) if (!outbuf)
read_header(inbuf, size); read_header(inbuf, len);
size_t read, write; size_t read, write;
LZ4F_errorCode_t ret; LZ4F_errorCode_t code;
do { do {
read = size; read = len;
write = outCapacity; write = outCapacity;
ret = LZ4F_decompress(ctx, outbuf, &write, inbuf, &read, nullptr); code = LZ4F_decompress(ctx, outbuf, &write, inbuf, &read, nullptr);
if (LZ4F_isError(ret)) { if (LZ4F_isError(code)) {
LOGW("LZ4 decode error: %s\n", LZ4F_getErrorName(ret)); LOGW("LZ4F decode error: %s\n", LZ4F_getErrorName(code));
return false; return -1;
} }
size -= read; len -= read;
inbuf += read; inbuf += read;
total += write; bwrite(outbuf, write);
FilterOutStream::write(outbuf, write); } while (len != 0 || write != 0);
} while (size != 0 || write != 0); return ret;
return true; }
}
uint64_t LZ4FDecoder::finalize() { private:
return total; LZ4F_decompressionContext_t ctx;
} uint8_t *outbuf;
size_t outCapacity;
void LZ4FDecoder::read_header(const uint8_t *&in, size_t &size) { void read_header(const uint8_t *&in, size_t &size) {
size_t read = size; size_t read = size;
LZ4F_frameInfo_t info; LZ4F_frameInfo_t info;
LZ4F_getFrameInfo(ctx, &info, in, &read); LZ4F_getFrameInfo(ctx, &info, in, &read);
...@@ -366,47 +331,57 @@ void LZ4FDecoder::read_header(const uint8_t *&in, size_t &size) { ...@@ -366,47 +331,57 @@ void LZ4FDecoder::read_header(const uint8_t *&in, size_t &size) {
outbuf = new uint8_t[outCapacity]; outbuf = new uint8_t[outCapacity];
in += read; in += read;
size -= read; size -= read;
} }
};
LZ4FEncoder::LZ4FEncoder() : outbuf(nullptr), outCapacity(0), total(0) { class LZ4F_encoder : public cpr_stream {
public:
explicit LZ4F_encoder(FILE *fp) : cpr_stream(fp), outbuf(nullptr), outCapacity(0) {
LZ4F_createCompressionContext(&ctx, LZ4F_VERSION); LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
} }
LZ4FEncoder::~LZ4FEncoder() { ~LZ4F_encoder() override {
destroy();
LZ4F_freeCompressionContext(ctx); LZ4F_freeCompressionContext(ctx);
delete[] outbuf; delete[] outbuf;
} }
bool LZ4FEncoder::write(const void *in, size_t size) { int write(const void *buf, size_t len) override {
auto ret = len;
if (!outbuf) if (!outbuf)
write_header(); write_header();
if (size == 0) if (len == 0)
return true; return 0;
auto inbuf = (const uint8_t *) in; auto inbuf = reinterpret_cast<const uint8_t *>(buf);
size_t read, write; size_t read, write;
do { do {
read = size > BLOCK_SZ ? BLOCK_SZ : size; read = len > BLOCK_SZ ? BLOCK_SZ : len;
write = LZ4F_compressUpdate(ctx, outbuf, outCapacity, inbuf, read, nullptr); write = LZ4F_compressUpdate(ctx, outbuf, outCapacity, inbuf, read, nullptr);
if (LZ4F_isError(write)) { if (LZ4F_isError(write)) {
LOGW("LZ4 encode error: %s\n", LZ4F_getErrorName(write)); LOGW("LZ4F encode error: %s\n", LZ4F_getErrorName(write));
return false; return -1;
} }
size -= read; len -= read;
inbuf += read; inbuf += read;
total += write; bwrite(outbuf, write);
FilterOutStream::write(outbuf, write); } while (len != 0);
} while (size != 0); return ret;
return true; }
}
uint64_t LZ4FEncoder::finalize() { protected:
size_t write = LZ4F_compressEnd(ctx, outbuf, outCapacity, nullptr); void finish() override {
total += write; size_t len = LZ4F_compressEnd(ctx, outbuf, outCapacity, nullptr);
FilterOutStream::write(outbuf, write); bwrite(outbuf, len);
return total; }
}
private:
LZ4F_compressionContext_t ctx;
uint8_t *outbuf;
size_t outCapacity;
static constexpr size_t BLOCK_SZ = 1 << 22;
void LZ4FEncoder::write_header() { void write_header() {
LZ4F_preferences_t prefs { LZ4F_preferences_t prefs {
.autoFlush = 1, .autoFlush = 1,
.compressionLevel = 9, .compressionLevel = 9,
...@@ -420,20 +395,24 @@ void LZ4FEncoder::write_header() { ...@@ -420,20 +395,24 @@ void LZ4FEncoder::write_header() {
outCapacity = LZ4F_compressBound(BLOCK_SZ, &prefs); outCapacity = LZ4F_compressBound(BLOCK_SZ, &prefs);
outbuf = new uint8_t[outCapacity]; outbuf = new uint8_t[outCapacity];
size_t write = LZ4F_compressBegin(ctx, outbuf, outCapacity, &prefs); size_t write = LZ4F_compressBegin(ctx, outbuf, outCapacity, &prefs);
total += write; bwrite(outbuf, write);
FilterOutStream::write(outbuf, write); }
} };
LZ4Decoder::LZ4Decoder() : outbuf(new char[LZ4_UNCOMPRESSED]), buf(new char[LZ4_COMPRESSED]), class LZ4_decoder : public cpr_stream {
init(false), block_sz(0), buf_off(0), total(0) {} public:
explicit LZ4_decoder(FILE *fp)
: cpr_stream(fp), out_buf(new char[LZ4_UNCOMPRESSED]), buffer(new char[LZ4_COMPRESSED]),
init(false), block_sz(0), buf_off(0) {}
LZ4Decoder::~LZ4Decoder() { ~LZ4_decoder() override {
delete[] outbuf; delete[] out_buf;
delete[] buf; delete[] buffer;
} }
bool LZ4Decoder::write(const void *in, size_t size) { int write(const void *in, size_t size) override {
const char *inbuf = (const char *) in; auto ret = size;
auto inbuf = static_cast<const char *>(in);
if (!init) { if (!init) {
// Skip magic // Skip magic
inbuf += 4; inbuf += 4;
...@@ -449,50 +428,57 @@ bool LZ4Decoder::write(const void *in, size_t size) { ...@@ -449,50 +428,57 @@ bool LZ4Decoder::write(const void *in, size_t size) {
size -= sizeof(unsigned); size -= sizeof(unsigned);
} else if (buf_off + size >= block_sz) { } else if (buf_off + size >= block_sz) {
consumed = block_sz - buf_off; consumed = block_sz - buf_off;
memcpy(buf + buf_off, inbuf, consumed); memcpy(buffer + buf_off, inbuf, consumed);
inbuf += consumed; inbuf += consumed;
size -= consumed; size -= consumed;
write = LZ4_decompress_safe(buf, outbuf, block_sz, LZ4_UNCOMPRESSED); write = LZ4_decompress_safe(buffer, out_buf, block_sz, LZ4_UNCOMPRESSED);
if (write < 0) { if (write < 0) {
LOGW("LZ4HC decompression failure (%d)\n", write); LOGW("LZ4HC decompression failure (%d)\n", write);
return false; return -1;
} }
FilterOutStream::write(outbuf, write); bwrite(out_buf, write);
total += write;
// Reset // Reset
buf_off = 0; buf_off = 0;
block_sz = 0; block_sz = 0;
} else { } else {
// Copy to internal buffer // Copy to internal buffer
memcpy(buf + buf_off, inbuf, size); memcpy(buffer + buf_off, inbuf, size);
buf_off += size; buf_off += size;
size = 0; size = 0;
} }
} while (size != 0); } while (size != 0);
return true; return ret;
} }
uint64_t LZ4Decoder::finalize() {
return total;
}
LZ4Encoder::LZ4Encoder() : outbuf(new char[LZ4_COMPRESSED]), buf(new char[LZ4_UNCOMPRESSED]),
init(false), buf_off(0), out_total(0), in_total(0) {}
LZ4Encoder::~LZ4Encoder() { private:
char *out_buf;
char *buffer;
bool init;
unsigned block_sz;
int buf_off;
};
class LZ4_encoder : public cpr_stream {
public:
explicit LZ4_encoder(FILE *fp)
: cpr_stream(fp), outbuf(new char[LZ4_COMPRESSED]), buf(new char[LZ4_UNCOMPRESSED]),
init(false), buf_off(0), in_total(0) {}
~LZ4_encoder() override {
destroy();
delete[] outbuf; delete[] outbuf;
delete[] buf; delete[] buf;
} }
bool LZ4Encoder::write(const void *in, size_t size) { int write(const void *in, size_t size) override {
if (!init) { if (!init) {
FilterOutStream::write("\x02\x21\x4c\x18", 4); bwrite("\x02\x21\x4c\x18", 4);
init = true; init = true;
} }
if (size == 0) if (size == 0)
return true; return 0;
in_total += size; in_total += size;
const char *inbuf = (const char *) in; const char *inbuf = (const char *) in;
size_t consumed; size_t consumed;
...@@ -509,9 +495,8 @@ bool LZ4Encoder::write(const void *in, size_t size) { ...@@ -509,9 +495,8 @@ bool LZ4Encoder::write(const void *in, size_t size) {
LOGW("LZ4HC compression failure\n"); LOGW("LZ4HC compression failure\n");
return false; return false;
} }
FilterOutStream::write(&write, sizeof(write)); bwrite(&write, sizeof(write));
FilterOutStream::write(outbuf, write); bwrite(outbuf, write);
out_total += write + sizeof(write);
// Reset buffer // Reset buffer
buf_off = 0; buf_off = 0;
...@@ -523,15 +508,152 @@ bool LZ4Encoder::write(const void *in, size_t size) { ...@@ -523,15 +508,152 @@ bool LZ4Encoder::write(const void *in, size_t size) {
} }
} while (size != 0); } while (size != 0);
return true; return true;
} }
uint64_t LZ4Encoder::finalize() { protected:
void finish() override {
if (buf_off) { if (buf_off) {
int write = LZ4_compress_HC(buf, outbuf, buf_off, LZ4_COMPRESSED, 9); int write = LZ4_compress_HC(buf, outbuf, buf_off, LZ4_COMPRESSED, 9);
FilterOutStream::write(&write, sizeof(write)); bwrite(&write, sizeof(write));
FilterOutStream::write(outbuf, write); bwrite(outbuf, write);
out_total += write + sizeof(write); }
bwrite(&in_total, sizeof(in_total));
}
private:
char *outbuf;
char *buf;
bool init;
int buf_off;
unsigned in_total;
};
filter_stream *get_encoder(format_t type, FILE *fp) {
switch (type) {
case XZ:
return new xz_encoder(fp);
case LZMA:
return new lzma_encoder(fp);
case BZIP2:
return new bz_encoder(fp);
case LZ4:
return new LZ4F_encoder(fp);
case LZ4_LEGACY:
return new LZ4_encoder(fp);
case GZIP:
default:
return new gz_encoder(fp);
}
}
filter_stream *get_decoder(format_t type, FILE *fp) {
switch (type) {
case XZ:
case LZMA:
return new lzma_decoder(fp);
case BZIP2:
return new bz_decoder(fp);
case LZ4:
return new LZ4F_decoder(fp);
case LZ4_LEGACY:
return new LZ4_decoder(fp);
case GZIP:
default:
return new gz_decoder(fp);
}
}
void decompress(char *infile, const char *outfile) {
bool in_std = infile == "-"sv;
bool rm_in = false;
FILE *in_fp = in_std ? stdin : xfopen(infile, "re");
unique_ptr<stream> strm;
char buf[4096];
size_t len;
while ((len = fread(buf, 1, sizeof(buf), in_fp))) {
if (!strm) {
format_t type = check_fmt(buf, len);
if (!COMPRESSED(type))
LOGE("Input file is not a supported compressed type!\n");
fprintf(stderr, "Detected format: [%s]\n", fmt2name[type]);
/* If user does not provide outfile, infile has to be either
* <path>.[ext], or '-'. Outfile will be either <path> or '-'.
* If the input does not have proper format, abort */
char *ext = nullptr;
if (outfile == nullptr) {
outfile = infile;
if (!in_std) {
ext = strrchr(infile, '.');
if (ext == nullptr || strcmp(ext, fmt2ext[type]) != 0)
LOGE("Input file is not a supported type!\n");
// Strip out extension and remove input
*ext = '\0';
rm_in = true;
fprintf(stderr, "Decompressing to [%s]\n", outfile);
}
}
FILE *out_fp = outfile == "-"sv ? stdout : xfopen(outfile, "we");
strm.reset(get_decoder(type, out_fp));
if (ext) *ext = '.';
} }
FilterOutStream::write(&in_total, sizeof(in_total)); if (strm->write(buf, len) < 0)
return out_total + sizeof(in_total); LOGE("Decompression error!\n");
}
strm->close();
fclose(in_fp);
if (rm_in)
unlink(infile);
}
void compress(const char *method, const char *infile, const char *outfile) {
auto it = name2fmt.find(method);
if (it == name2fmt.end())
LOGE("Unknown compression method: [%s]\n", method);
bool in_std = infile == "-"sv;
bool rm_in = false;
FILE *in_fp = in_std ? stdin : xfopen(infile, "re");
FILE *out_fp;
if (outfile == nullptr) {
if (in_std) {
out_fp = stdout;
} else {
/* If user does not provide outfile and infile is not
* STDIN, output to <infile>.[ext] */
string tmp(infile);
tmp += fmt2ext[it->second];
out_fp = xfopen(tmp.data(), "we");
fprintf(stderr, "Compressing to [%s]\n", tmp.data());
rm_in = true;
}
} else {
out_fp = outfile == "-"sv ? stdout : xfopen(outfile, "we");
}
unique_ptr<stream> strm(get_encoder(it->second, out_fp));
char buf[4096];
size_t len;
while ((len = fread(buf, 1, sizeof(buf), in_fp))) {
if (strm->write(buf, len) < 0)
LOGE("Compression error!\n");
};
strm->close();
fclose(in_fp);
if (rm_in)
unlink(infile);
} }
#pragma once #pragma once
#include <zlib.h>
#include <bzlib.h>
#include <lzma.h>
#include <lz4.h>
#include <lz4frame.h>
#include <lz4hc.h>
#include <stream.h> #include <stream.h>
#include "format.h" #include "format.h"
#define CHUNK 0x40000 filter_stream *get_encoder(format_t type, FILE *fp = nullptr);
filter_stream *get_decoder(format_t type, FILE *fp = nullptr);
class Compression : public FilterOutStream {
public:
virtual uint64_t finalize() = 0;
};
class GZStream : public Compression {
public:
bool write(const void *in, size_t size) override;
uint64_t finalize() override;
protected:
explicit GZStream(int mode);
private:
int mode;
z_stream strm;
uint8_t outbuf[CHUNK];
bool write(const void *in, size_t size, int flush);
};
class GZDecoder : public GZStream {
public:
GZDecoder() : GZStream(0) {};
};
class GZEncoder : public GZStream {
public:
GZEncoder() : GZStream(1) {};
};
class BZStream : public Compression {
public:
bool write(const void *in, size_t size) override;
uint64_t finalize() override;
protected:
explicit BZStream(int mode);
private:
int mode;
bz_stream strm;
char outbuf[CHUNK];
bool write(const void *in, size_t size, int flush);
};
class BZDecoder : public BZStream {
public:
BZDecoder() : BZStream(0) {};
};
class BZEncoder : public BZStream {
public:
BZEncoder() : BZStream(1) {};
};
class LZMAStream : public Compression {
public:
bool write(const void *in, size_t size) override;
uint64_t finalize() override;
protected:
explicit LZMAStream(int mode);
private:
int mode;
lzma_stream strm;
uint8_t outbuf[CHUNK];
bool write(const void *in, size_t size, lzma_action flush);
};
class LZMADecoder : public LZMAStream {
public:
LZMADecoder() : LZMAStream(0) {}
};
class XZEncoder : public LZMAStream {
public:
XZEncoder() : LZMAStream(1) {}
};
class LZMAEncoder : public LZMAStream {
public:
LZMAEncoder() : LZMAStream(2) {}
};
class LZ4FDecoder : public Compression {
public:
LZ4FDecoder();
~LZ4FDecoder() override;
bool write(const void *in, size_t size) override;
uint64_t finalize() override;
private:
LZ4F_decompressionContext_t ctx;
uint8_t *outbuf;
size_t outCapacity;
uint64_t total;
void read_header(const uint8_t *&in, size_t &size);
};
class LZ4FEncoder : public Compression {
public:
LZ4FEncoder();
~LZ4FEncoder() override;
bool write(const void *in, size_t size) override;
uint64_t finalize() override;
private:
static constexpr size_t BLOCK_SZ = 1 << 22;
LZ4F_compressionContext_t ctx;
uint8_t *outbuf;
size_t outCapacity;
uint64_t total;
void write_header();
};
#define LZ4_UNCOMPRESSED 0x800000
#define LZ4_COMPRESSED LZ4_COMPRESSBOUND(LZ4_UNCOMPRESSED)
class LZ4Decoder : public Compression {
public:
LZ4Decoder();
~LZ4Decoder() override;
bool write(const void *in, size_t size) override;
uint64_t finalize() override;
private:
char *outbuf;
char *buf;
bool init;
unsigned block_sz;
int buf_off;
uint64_t total;
};
class LZ4Encoder : public Compression {
public:
LZ4Encoder();
~LZ4Encoder() override;
bool write(const void *in, size_t size) override;
uint64_t finalize() override;
private:
char *outbuf;
char *buf;
bool init;
int buf_off;
uint64_t out_total;
unsigned in_total;
};
Compression *get_encoder(format_t type);
Compression *get_decoder(format_t type);
void compress(const char *method, const char *infile, const char *outfile); void compress(const char *method, const char *infile, const char *outfile);
void decompress(char *infile, const char *outfile); void decompress(char *infile, const char *outfile);
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
#define DTB_FILE "dtb" #define DTB_FILE "dtb"
#define NEW_BOOT "new-boot.img" #define NEW_BOOT "new-boot.img"
int unpack(const char *image, bool hdr = false); int unpack(const char *image, bool nodecomp = false, bool hdr = false);
void repack(const char* src_img, const char* out_img, bool nocomp = false); void repack(const char* src_img, const char* out_img, bool nocomp = false);
int hexpatch(const char *image, const char *from, const char *to); int hexpatch(const char *image, const char *from, const char *to);
int cpio_commands(int argc, char *argv[]); int cpio_commands(int argc, char *argv[]);
......
...@@ -21,9 +21,11 @@ FULL_VER(MagiskBoot) R"EOF( - Boot Image Modification Tool ...@@ -21,9 +21,11 @@ FULL_VER(MagiskBoot) R"EOF( - Boot Image Modification Tool
Usage: %s <action> [args...] Usage: %s <action> [args...]
Supported actions: Supported actions:
unpack [-h] <bootimg> unpack [-n] [-h] <bootimg>
Unpack <bootimg> to, if available, kernel, kernel_dtb, ramdisk.cpio, Unpack <bootimg> to, if available, kernel, kernel_dtb, ramdisk.cpio,
second, dtb, extra, and recovery_dtbo into current directory. second, dtb, extra, and recovery_dtbo into current directory.
If '-n' is provided, it will not attempt to decompress kernel or
ramdisk.cpio from their original formats.
If '-h' is provided, it will dump header info to 'header', If '-h' is provided, it will dump header info to 'header',
which will be parsed when repacking. which will be parsed when repacking.
Return values: Return values:
...@@ -146,13 +148,25 @@ int main(int argc, char *argv[]) { ...@@ -146,13 +148,25 @@ int main(int argc, char *argv[]) {
printf("\n"); printf("\n");
munmap(buf, size); munmap(buf, size);
} else if (argc > 2 && action == "unpack") { } else if (argc > 2 && action == "unpack") {
if (argv[2] == "-h"sv) { int idx = 2;
if (argc == 3) bool nodecomp = false;
bool hdr = false;
for (;;) {
if (idx >= argc)
usage(argv[0]); usage(argv[0]);
return unpack(argv[3], true); if (argv[idx][0] != '-')
} else { break;
return unpack(argv[2]); for (char *flag = &argv[idx][1]; *flag; ++flag) {
if (*flag == 'n')
nodecomp = true;
else if (*flag == 'h')
hdr = true;
else
usage(argv[0]);
}
++idx;
} }
return unpack(argv[idx], nodecomp, hdr);
} else if (argc > 2 && action == "repack") { } else if (argc > 2 && action == "repack") {
if (argv[2] == "-n"sv) { if (argv[2] == "-n"sv) {
if (argc == 3) if (argc == 3)
......
...@@ -241,14 +241,17 @@ void magisk_cpio::compress() { ...@@ -241,14 +241,17 @@ void magisk_cpio::compress() {
return; return;
fprintf(stderr, "Compressing cpio -> [%s]\n", RAMDISK_XZ); fprintf(stderr, "Compressing cpio -> [%s]\n", RAMDISK_XZ);
auto init = entries.extract("init"); auto init = entries.extract("init");
XZEncoder encoder;
encoder.setOut(make_unique<BufOutStream>()); uint8_t *data;
output(encoder); size_t len;
encoder.finalize(); FILE *fp = open_stream(get_encoder(XZ, open_stream<byte_stream>(data, len)));
dump(fp);
entries.clear(); entries.clear();
entries.insert(std::move(init)); entries.insert(std::move(init));
auto xz = new cpio_entry(RAMDISK_XZ, S_IFREG); auto xz = new cpio_entry(RAMDISK_XZ, S_IFREG);
static_cast<BufOutStream *>(encoder.getOut())->release(xz->data, xz->filesize); xz->data = data;
xz->filesize = len;
insert(xz); insert(xz);
} }
...@@ -257,15 +260,16 @@ void magisk_cpio::decompress() { ...@@ -257,15 +260,16 @@ void magisk_cpio::decompress() {
if (it == entries.end()) if (it == entries.end())
return; return;
fprintf(stderr, "Decompressing cpio [%s]\n", RAMDISK_XZ); fprintf(stderr, "Decompressing cpio [%s]\n", RAMDISK_XZ);
LZMADecoder decoder;
decoder.setOut(make_unique<BufOutStream>()); char *data;
decoder.write(it->second->data, it->second->filesize); size_t len;
decoder.finalize(); auto strm = get_decoder(XZ, open_stream<byte_stream>(data, len));
strm->write(it->second->data, it->second->filesize);
delete strm;
entries.erase(it); entries.erase(it);
char *buf; load_cpio(data, len);
size_t sz; free(data);
static_cast<BufOutStream *>(decoder.getOut())->getbuf(buf, sz);
load_cpio(buf, sz);
} }
int cpio_commands(int argc, char *argv[]) { int cpio_commands(int argc, char *argv[]) {
......
...@@ -217,7 +217,7 @@ static void set_hide_config() { ...@@ -217,7 +217,7 @@ static void set_hide_config() {
db_err(err); db_err(err);
} }
static inline void launch_err(int client, int code = DAEMON_ERROR) { [[noreturn]] static void launch_err(int client, int code = DAEMON_ERROR) {
if (code != HIDE_IS_ENABLED) if (code != HIDE_IS_ENABLED)
hide_enabled = false; hide_enabled = false;
if (client >= 0) { if (client >= 0) {
...@@ -280,10 +280,14 @@ int stop_magiskhide() { ...@@ -280,10 +280,14 @@ int stop_magiskhide() {
} }
void auto_start_magiskhide() { void auto_start_magiskhide() {
if (hide_enabled) {
pthread_kill(proc_monitor_thread, SIGZYGOTE);
} else if (SDK_INT >= 19) {
db_settings dbs; db_settings dbs;
get_db_settings(dbs, HIDE_CONFIG); get_db_settings(dbs, HIDE_CONFIG);
if (dbs[HIDE_CONFIG]) { if (dbs[HIDE_CONFIG]) {
new_daemon_thread([]() -> void { launch_magiskhide(-1); }); new_daemon_thread([]{ launch_magiskhide(-1); });
}
} }
} }
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <daemon.h> #include <daemon.h>
#define SIGTERMTHRD SIGUSR1 #define SIGTERMTHRD SIGUSR1
#define SIGZYGOTE SIGUSR2
#define SAFETYNET_COMPONENT "com.google.android.gms/.droidguard.DroidGuardService" #define SAFETYNET_COMPONENT "com.google.android.gms/.droidguard.DroidGuardService"
#define SAFETYNET_PROCESS "com.google.android.gms.unstable" #define SAFETYNET_PROCESS "com.google.android.gms.unstable"
......
...@@ -155,6 +155,10 @@ static void inotify_event(int) { ...@@ -155,6 +155,10 @@ static void inotify_event(int) {
check_zygote(); check_zygote();
} }
static void check_zygote(int) {
check_zygote();
}
// Workaround for the lack of pthread_cancel // Workaround for the lack of pthread_cancel
static void term_thread(int) { static void term_thread(int) {
LOGD("proc_monitor: cleaning up\n"); LOGD("proc_monitor: cleaning up\n");
...@@ -312,6 +316,8 @@ void proc_monitor() { ...@@ -312,6 +316,8 @@ void proc_monitor() {
sigaction(SIGTERMTHRD, &act, nullptr); sigaction(SIGTERMTHRD, &act, nullptr);
act.sa_handler = inotify_event; act.sa_handler = inotify_event;
sigaction(SIGIO, &act, nullptr); sigaction(SIGIO, &act, nullptr);
act.sa_handler = check_zygote;
sigaction(SIGZYGOTE, &act, nullptr);
setup_inotify(); setup_inotify();
......
#include "magiskpolicy.h" #include "magiskpolicy.h"
#include "sepolicy.h" #include "sepolicy.h"
//#define vprint(fmt, ...) printf(fmt, __VA_ARGS__)
#define vprint(...)
int sepol_allow(const char *s, const char *t, const char *c, const char *p) { int sepol_allow(const char *s, const char *t, const char *c, const char *p) {
// printf("allow %s %s %s %s\n", s, t, c, p); vprint("allow %s %s %s %s\n", s, t, c, p);
return add_rule(s, t, c, p, AVTAB_ALLOWED, 0); return add_rule(s, t, c, p, AVTAB_ALLOWED, 0);
} }
int sepol_deny(const char *s, const char *t, const char *c, const char *p) { int sepol_deny(const char *s, const char *t, const char *c, const char *p) {
// printf("deny %s %s %s %s\n", s, t, c, p); vprint("deny %s %s %s %s\n", s, t, c, p);
return add_rule(s, t, c, p, AVTAB_ALLOWED, 1); return add_rule(s, t, c, p, AVTAB_ALLOWED, 1);
} }
int sepol_auditallow(const char *s, const char *t, const char *c, const char *p) { int sepol_auditallow(const char *s, const char *t, const char *c, const char *p) {
// printf("auditallow %s %s %s %s\n", s, t, c, p); vprint("auditallow %s %s %s %s\n", s, t, c, p);
return add_rule(s, t, c, p, AVTAB_AUDITALLOW, 0); return add_rule(s, t, c, p, AVTAB_AUDITALLOW, 0);
} }
int sepol_dontaudit(const char *s, const char *t, const char *c, const char *p) { int sepol_dontaudit(const char *s, const char *t, const char *c, const char *p) {
// printf("dontaudit %s %s %s %s\n", s, t, c, p); vprint("dontaudit %s %s %s %s\n", s, t, c, p);
return add_rule(s, t, c, p, AVTAB_AUDITDENY, 0); return add_rule(s, t, c, p, AVTAB_AUDITDENY, 1);
} }
int sepol_allowxperm(const char *s, const char *t, const char *c, const char *range) { int sepol_allowxperm(const char *s, const char *t, const char *c, const char *range) {
// printf("allowxperm %s %s %s %s\n", s, t, c, range); vprint("allowxperm %s %s %s %s\n", s, t, c, range);
return add_xperm_rule(s, t, c, range, AVTAB_XPERMS_ALLOWED, 0); return add_xperm_rule(s, t, c, range, AVTAB_XPERMS_ALLOWED, 0);
} }
int sepol_auditallowxperm(const char *s, const char *t, const char *c, const char *range) { int sepol_auditallowxperm(const char *s, const char *t, const char *c, const char *range) {
// printf("auditallowxperm %s %s %s %s\n", s, t, c, range); vprint("auditallowxperm %s %s %s %s\n", s, t, c, range);
return add_xperm_rule(s, t, c, range, AVTAB_XPERMS_AUDITALLOW, 0); return add_xperm_rule(s, t, c, range, AVTAB_XPERMS_AUDITALLOW, 0);
} }
int sepol_dontauditxperm(const char *s, const char *t, const char *c, const char *range) { int sepol_dontauditxperm(const char *s, const char *t, const char *c, const char *range) {
// printf("dontauditxperm %s %s %s %s\n", s, t, c, range); vprint("dontauditxperm %s %s %s %s\n", s, t, c, range);
return add_xperm_rule(s, t, c, range, AVTAB_XPERMS_DONTAUDIT, 0); return add_xperm_rule(s, t, c, range, AVTAB_XPERMS_DONTAUDIT, 0);
} }
int sepol_typetrans(const char *s, const char *t, const char *c, const char *d) { int sepol_typetrans(const char *s, const char *t, const char *c, const char *d) {
// printf("type_transition %s %s %s %s\n", s, t, c, d); vprint("type_transition %s %s %s %s\n", s, t, c, d);
return add_type_rule(s, t, c, d, AVTAB_TRANSITION); return add_type_rule(s, t, c, d, AVTAB_TRANSITION);
} }
int sepol_typechange(const char *s, const char *t, const char *c, const char *d) { int sepol_typechange(const char *s, const char *t, const char *c, const char *d) {
// printf("type_change %s %s %s %s\n", s, t, c, d); vprint("type_change %s %s %s %s\n", s, t, c, d);
return add_type_rule(s, t, c, d, AVTAB_CHANGE); return add_type_rule(s, t, c, d, AVTAB_CHANGE);
} }
int sepol_typemember(const char *s, const char *t, const char *c, const char *d) { int sepol_typemember(const char *s, const char *t, const char *c, const char *d) {
// printf("type_member %s %s %s %s\n", s, t, c, d); vprint("type_member %s %s %s %s\n", s, t, c, d);
return add_type_rule(s, t, c, d, AVTAB_MEMBER); return add_type_rule(s, t, c, d, AVTAB_MEMBER);
} }
int sepol_nametrans(const char *s, const char *t, const char *c, const char *d, const char *o) {
vprint("name_trans %s %s %s %s %s\n", s, t, c, d, o);
return add_filename_trans(s, t, c, d, o);
}
int sepol_permissive(const char *s) { int sepol_permissive(const char *s) {
// printf("permissive %s\n", s); vprint("permissive %s\n", s);
return set_domain_state(s, 1); return set_domain_state(s, 1);
} }
int sepol_enforce(const char *s) { int sepol_enforce(const char *s) {
// printf("enforce %s\n", s); vprint("enforce %s\n", s);
return set_domain_state(s, 0); return set_domain_state(s, 0);
} }
int sepol_create(const char *s) { int sepol_create(const char *s) {
// printf("create %s\n", s); vprint("create %s\n", s);
return create_domain(s); return create_domain(s);
} }
int sepol_attradd(const char *s, const char *a) { int sepol_attradd(const char *s, const char *a) {
// printf("attradd %s %s\n", s, a); vprint("attradd %s %s\n", s, a);
return add_typeattribute(s, a); return add_typeattribute(s, a);
} }
int sepol_exists(const char *source) { int sepol_exists(const char *source) {
return hashtab_search(policydb->p_types.table, source) != nullptr; return hashtab_search(magisk_policydb->p_types.table, source) != nullptr;
} }
...@@ -487,7 +487,7 @@ int magiskpolicy_main(int argc, char *argv[]) { ...@@ -487,7 +487,7 @@ int magiskpolicy_main(int argc, char *argv[]) {
} }
// Use current policy if nothing is loaded // Use current policy if nothing is loaded
if (policydb == nullptr && load_policydb(SELINUX_POLICY)) { if (magisk_policydb == nullptr && load_policydb(SELINUX_POLICY)) {
fprintf(stderr, "Cannot load policy from " SELINUX_POLICY "\n"); fprintf(stderr, "Cannot load policy from " SELINUX_POLICY "\n");
return 1; return 1;
} }
......
...@@ -8,8 +8,6 @@ ...@@ -8,8 +8,6 @@
#define ALL NULL #define ALL NULL
__BEGIN_DECLS
// policydb functions // policydb functions
int load_policydb(const char *file); int load_policydb(const char *file);
int load_split_cil(); int load_split_cil();
...@@ -37,5 +35,3 @@ int sepol_exists(const char *source); ...@@ -37,5 +35,3 @@ int sepol_exists(const char *source);
// Built in rules // Built in rules
void sepol_magisk_rules(); void sepol_magisk_rules();
__END_DECLS
...@@ -7,14 +7,13 @@ ...@@ -7,14 +7,13 @@
#include <utils.h> #include <utils.h>
#include <logging.h> #include <logging.h>
#include <stream.h>
#include "magiskpolicy.h" #include "magiskpolicy.h"
#include "sepolicy.h" #include "sepolicy.h"
policydb_t *policydb = nullptr;
int load_policydb(const char *file) { int load_policydb(const char *file) {
if (policydb) if (magisk_policydb)
destroy_policydb(); destroy_policydb();
struct policy_file pf; struct policy_file pf;
...@@ -22,12 +21,13 @@ int load_policydb(const char *file) { ...@@ -22,12 +21,13 @@ int load_policydb(const char *file) {
pf.fp = xfopen(file, "re"); pf.fp = xfopen(file, "re");
pf.type = PF_USE_STDIO; pf.type = PF_USE_STDIO;
policydb = new policydb_t(); magisk_policydb = static_cast<policydb_t *>(xmalloc(sizeof(policydb_t)));
if (policydb_init(policydb) || policydb_read(policydb, &pf, 0)) if (policydb_init(magisk_policydb) || policydb_read(magisk_policydb, &pf, 0)) {
LOGE("Fail to load policy from %s\n", file);
return 1; return 1;
}
fclose(pf.fp); fclose(pf.fp);
return 0; return 0;
} }
...@@ -169,34 +169,39 @@ int compile_split_cil() { ...@@ -169,34 +169,39 @@ int compile_split_cil() {
return 1; return 1;
cil_db_destroy(&db); cil_db_destroy(&db);
policydb = &pdb->p; magisk_policydb = &pdb->p;
return 0; return 0;
} }
int dump_policydb(const char *file) { int dump_policydb(const char *file) {
int fd, ret; struct policy_file pf;
void *data = nullptr; policy_file_init(&pf);
uint8_t *data;
size_t len; size_t len;
policydb_to_image(nullptr, policydb, &data, &len);
if (data == nullptr) { pf.type = PF_USE_STDIO;
LOGE("Fail to dump policy image!\n"); pf.fp = open_stream<byte_stream>(data, len);
if (policydb_write(magisk_policydb, &pf)) {
LOGE("Fail to create policy image\n");
return 1; return 1;
} }
fclose(pf.fp);
fd = xopen(file, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); int fd = xopen(file, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
if (fd < 0) if (fd < 0)
return 1; return 1;
ret = xwrite(fd, data, len); xwrite(fd, data, len);
close(fd); close(fd);
if (ret < 0) free(data);
return 1;
return 0; return 0;
} }
void destroy_policydb() { void destroy_policydb() {
if (policydb) { if (magisk_policydb) {
policydb_destroy(policydb); policydb_destroy(magisk_policydb);
delete policydb; free(magisk_policydb);
policydb = nullptr; magisk_policydb = nullptr;
} }
} }
...@@ -21,7 +21,7 @@ static void allowSuClient(const char *target) { ...@@ -21,7 +21,7 @@ static void allowSuClient(const char *target) {
sepol_allow(target, "untrusted_app_devpts", "chr_file", "ioctl"); sepol_allow(target, "untrusted_app_devpts", "chr_file", "ioctl");
sepol_allow(target, "untrusted_app_25_devpts", "chr_file", "ioctl"); sepol_allow(target, "untrusted_app_25_devpts", "chr_file", "ioctl");
sepol_allow(target, "untrusted_app_all_devpts", "chr_file", "ioctl"); sepol_allow(target, "untrusted_app_all_devpts", "chr_file", "ioctl");
if (policydb->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) { if (magisk_policydb->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) {
sepol_allowxperm(target, "devpts", "chr_file", "0x5400-0x54FF"); sepol_allowxperm(target, "devpts", "chr_file", "0x5400-0x54FF");
sepol_allowxperm(target, "untrusted_app_devpts", "chr_file", "0x5400-0x54FF"); sepol_allowxperm(target, "untrusted_app_devpts", "chr_file", "0x5400-0x54FF");
sepol_allowxperm(target, "untrusted_app_25_devpts", "chr_file", "0x5400-0x54FF"); sepol_allowxperm(target, "untrusted_app_25_devpts", "chr_file", "0x5400-0x54FF");
...@@ -166,7 +166,7 @@ void sepol_magisk_rules() { ...@@ -166,7 +166,7 @@ void sepol_magisk_rules() {
sepol_allow(SEPOL_PROC_DOMAIN, ALL, "fifo_file", ALL); sepol_allow(SEPOL_PROC_DOMAIN, ALL, "fifo_file", ALL);
// Allow us to do any ioctl on all block devices // Allow us to do any ioctl on all block devices
if (policydb->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) if (magisk_policydb->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL)
sepol_allowxperm(SEPOL_PROC_DOMAIN, ALL, "blk_file", "0x0000-0xFFFF"); sepol_allowxperm(SEPOL_PROC_DOMAIN, ALL, "blk_file", "0x0000-0xFFFF");
// Allow all binder transactions // Allow all binder transactions
...@@ -198,11 +198,7 @@ void sepol_magisk_rules() { ...@@ -198,11 +198,7 @@ void sepol_magisk_rules() {
#ifdef MAGISK_DEBUG #ifdef MAGISK_DEBUG
// Remove all dontaudit in debug mode // Remove all dontaudit in debug mode
avtab_ptr_t av; strip_dontaudit();
avtab_for_each(&policydb->te_avtab, av, {
if (av->key.specified == AVTAB_AUDITDENY || av->key.specified == AVTAB_XPERMS_DONTAUDIT)
avtab_remove_node(&policydb->te_avtab, av);
})
#endif #endif
log_cb.w = bak; log_cb.w = bak;
......
#include <stdlib.h> #include <stdlib.h>
#include <sepol/policydb/expand.h> #include <sepol/policydb/expand.h>
#include <utils.h>
#include <logging.h> #include <logging.h>
#include "magiskpolicy.h"
#include "sepolicy.h" #include "sepolicy.h"
extern int policydb_index_decls(sepol_handle_t * handle, policydb_t * p); policydb_t *magisk_policydb = NULL;
#define mpdb magisk_policydb
static int get_attr(const char *type, int value) {
type_datum_t *attr = hashtab_search(policydb->p_types.table, type);
if (!attr)
return 1;
if (attr->flavor != TYPE_ATTRIB)
return 1;
return ebitmap_get_bit(&policydb->attr_type_map[attr->s.value - 1], value - 1) != 0;
}
static int get_attr_id(const char *type) {
type_datum_t *attr = hashtab_search(policydb->p_types.table, type);
if (!attr)
return 1;
if (attr->flavor != TYPE_ATTRIB) extern void *xmalloc(size_t size);
return 1; extern void *xcalloc(size_t nmemb, size_t size);
extern void *xrealloc(void *ptr, size_t size);
extern int policydb_index_decls(sepol_handle_t * handle, policydb_t * p);
return attr->s.value; // Generic hash table traversal
} #define hash_for_each(node_ptr, n_slots, table, block) \
for (int __i = 0; __i < (table)->n_slots; ++__i) { \
__typeof__(*(table)->node_ptr) node; \
__typeof__(node) __next; \
for (node = (table)->node_ptr[__i]; node; node = __next) { \
__next = node->next; \
block \
} \
} \
// hashtab traversal
#define hashtab_for_each(hashtab, block) \
hash_for_each(htable, size, hashtab, block)
// avtab traversal
#define avtab_for_each(avtab, block) \
hash_for_each(htable, nslot, avtab, block)
static int set_attr(const char *type, int value) { static int set_attr(const char *type, int value) {
type_datum_t *attr = hashtab_search(policydb->p_types.table, type); type_datum_t *attr = hashtab_search(mpdb->p_types.table, type);
if (!attr) if (!attr || attr->flavor != TYPE_ATTRIB)
return 1; return -1;
if (attr->flavor != TYPE_ATTRIB) if (ebitmap_set_bit(&mpdb->type_attr_map[value - 1], attr->s.value - 1, 1))
return 1; return -1;
if (ebitmap_set_bit(&mpdb->attr_type_map[attr->s.value - 1], value - 1, 1))
return -1;
if(ebitmap_set_bit(&policydb->type_attr_map[value-1], attr->s.value-1, 1)) return attr->s.value;
return 1;
if(ebitmap_set_bit(&policydb->attr_type_map[attr->s.value-1], value-1, 1))
return 1;
return 0;
} }
static void check_avtab_node(avtab_ptr_t node) { static void check_avtab_node(avtab_ptr_t node) {
...@@ -56,7 +54,7 @@ static void check_avtab_node(avtab_ptr_t node) { ...@@ -56,7 +54,7 @@ static void check_avtab_node(avtab_ptr_t node) {
else else
redundant = node->datum.data == 0U; redundant = node->datum.data == 0U;
if (redundant) if (redundant)
avtab_remove_node(&policydb->te_avtab, node); avtab_remove_node(&mpdb->te_avtab, node);
} }
static avtab_ptr_t get_avtab_node(avtab_key_t *key, avtab_extended_perms_t *xperms) { static avtab_ptr_t get_avtab_node(avtab_key_t *key, avtab_extended_perms_t *xperms) {
...@@ -66,7 +64,7 @@ static avtab_ptr_t get_avtab_node(avtab_key_t *key, avtab_extended_perms_t *xper ...@@ -66,7 +64,7 @@ static avtab_ptr_t get_avtab_node(avtab_key_t *key, avtab_extended_perms_t *xper
/* AVTAB_XPERMS entries are not necessarily unique */ /* AVTAB_XPERMS entries are not necessarily unique */
if (key->specified & AVTAB_XPERMS) { if (key->specified & AVTAB_XPERMS) {
node = avtab_search_node(&policydb->te_avtab, key); node = avtab_search_node(&mpdb->te_avtab, key);
while (node) { while (node) {
if ((node->datum.xperms->specified == xperms->specified) && if ((node->datum.xperms->specified == xperms->specified) &&
(node->datum.xperms->driver == xperms->driver)) { (node->datum.xperms->driver == xperms->driver)) {
...@@ -78,7 +76,7 @@ static avtab_ptr_t get_avtab_node(avtab_key_t *key, avtab_extended_perms_t *xper ...@@ -78,7 +76,7 @@ static avtab_ptr_t get_avtab_node(avtab_key_t *key, avtab_extended_perms_t *xper
if (!match) if (!match)
node = NULL; node = NULL;
} else { } else {
node = avtab_search_node(&policydb->te_avtab, key); node = avtab_search_node(&mpdb->te_avtab, key);
} }
if (!node) { if (!node) {
...@@ -89,26 +87,27 @@ static avtab_ptr_t get_avtab_node(avtab_key_t *key, avtab_extended_perms_t *xper ...@@ -89,26 +87,27 @@ static avtab_ptr_t get_avtab_node(avtab_key_t *key, avtab_extended_perms_t *xper
*/ */
avdatum.data = key->specified == AVTAB_AUDITDENY ? ~0U : 0U; avdatum.data = key->specified == AVTAB_AUDITDENY ? ~0U : 0U;
/* this is used to get the node - insertion is actually unique */ /* this is used to get the node - insertion is actually unique */
node = avtab_insert_nonunique(&policydb->te_avtab, key, &avdatum); node = avtab_insert_nonunique(&mpdb->te_avtab, key, &avdatum);
} }
return node; return node;
} }
static int add_avrule(avtab_key_t *key, int p, int not) { static int add_avrule(avtab_key_t *key, int val, int not) {
avtab_ptr_t node = get_avtab_node(key, NULL); avtab_ptr_t node = get_avtab_node(key, NULL);
// Support DONTAUDIT (AUDITDENY is inverted)
if (AVTAB_AUDITDENY == node->key.specified == !not) { if (not) {
if (p < 0) if (val < 0)
node->datum.data = 0U; node->datum.data = 0U;
else else
node->datum.data &= ~(1U << (p - 1)); node->datum.data &= ~(1U << (val - 1));
} else { } else {
if (p < 0) if (val < 0)
node->datum.data = ~0U; node->datum.data = ~0U;
else else
node->datum.data |= 1U << (p - 1); node->datum.data |= 1U << (val - 1);
} }
check_avtab_node(node); check_avtab_node(node);
return 0; return 0;
} }
...@@ -116,22 +115,21 @@ static int add_avrule(avtab_key_t *key, int p, int not) { ...@@ -116,22 +115,21 @@ static int add_avrule(avtab_key_t *key, int p, int not) {
static int add_rule_auto(type_datum_t *src, type_datum_t *tgt, class_datum_t *cls, static int add_rule_auto(type_datum_t *src, type_datum_t *tgt, class_datum_t *cls,
perm_datum_t *perm, int effect, int not) { perm_datum_t *perm, int effect, int not) {
avtab_key_t key; avtab_key_t key;
hashtab_ptr_t cur;
int ret = 0; int ret = 0;
if (src == NULL) { if (src == NULL) {
hashtab_for_each(policydb->p_types.table, cur, { hashtab_for_each(mpdb->p_types.table, {
src = cur->datum; src = node->datum;
ret |= add_rule_auto(src, tgt, cls, perm, effect, not); ret |= add_rule_auto(src, tgt, cls, perm, effect, not);
}) })
} else if (tgt == NULL) { } else if (tgt == NULL) {
hashtab_for_each(policydb->p_types.table, cur, { hashtab_for_each(mpdb->p_types.table, {
tgt = cur->datum; tgt = node->datum;
ret |= add_rule_auto(src, tgt, cls, perm, effect, not); ret |= add_rule_auto(src, tgt, cls, perm, effect, not);
}) })
} else if (cls == NULL) { } else if (cls == NULL) {
hashtab_for_each(policydb->p_classes.table, cur, { hashtab_for_each(mpdb->p_classes.table, {
cls = cur->datum; cls = node->datum;
ret |= add_rule_auto(src, tgt, cls, perm, effect, not); ret |= add_rule_auto(src, tgt, cls, perm, effect, not);
}) })
} else { } else {
...@@ -188,22 +186,21 @@ static int add_avxrule(avtab_key_t *key, uint16_t low, uint16_t high, int not) { ...@@ -188,22 +186,21 @@ static int add_avxrule(avtab_key_t *key, uint16_t low, uint16_t high, int not) {
static int add_xperm_rule_auto(type_datum_t *src, type_datum_t *tgt, class_datum_t *cls, static int add_xperm_rule_auto(type_datum_t *src, type_datum_t *tgt, class_datum_t *cls,
uint16_t low, uint16_t high, int effect, int not) { uint16_t low, uint16_t high, int effect, int not) {
avtab_key_t key; avtab_key_t key;
hashtab_ptr_t cur;
int ret = 0; int ret = 0;
if (src == NULL) { if (src == NULL) {
hashtab_for_each(policydb->p_types.table, cur, { hashtab_for_each(mpdb->p_types.table, {
src = cur->datum; src = node->datum;
ret |= add_xperm_rule_auto(src, tgt, cls, low, high, effect, not); ret |= add_xperm_rule_auto(src, tgt, cls, low, high, effect, not);
}) })
} else if (tgt == NULL) { } else if (tgt == NULL) {
hashtab_for_each(policydb->p_types.table, cur, { hashtab_for_each(mpdb->p_types.table, {
tgt = cur->datum; tgt = node->datum;
ret |= add_xperm_rule_auto(src, tgt, cls, low, high, effect, not); ret |= add_xperm_rule_auto(src, tgt, cls, low, high, effect, not);
}) })
} else if (cls == NULL) { } else if (cls == NULL) {
hashtab_for_each(policydb->p_classes.table, cur, { hashtab_for_each(mpdb->p_classes.table, {
cls = cur->datum; cls = node->datum;
ret |= add_xperm_rule_auto(src, tgt, cls, low, high, effect, not); ret |= add_xperm_rule_auto(src, tgt, cls, low, high, effect, not);
}) })
} else { } else {
...@@ -217,50 +214,50 @@ static int add_xperm_rule_auto(type_datum_t *src, type_datum_t *tgt, class_datum ...@@ -217,50 +214,50 @@ static int add_xperm_rule_auto(type_datum_t *src, type_datum_t *tgt, class_datum
} }
int create_domain(const char *d) { int create_domain(const char *d) {
symtab_datum_t *src = hashtab_search(policydb->p_types.table, d); symtab_datum_t *src = hashtab_search(mpdb->p_types.table, d);
if(src) { if (src) {
LOGW("Domain %s already exists\n", d); LOGW("Domain %s already exists\n", d);
return 0; return 0;
} }
type_datum_t *typedatum = (type_datum_t *) malloc(sizeof(type_datum_t)); type_datum_t *typedatum = xmalloc(sizeof(type_datum_t));
type_datum_init(typedatum); type_datum_init(typedatum);
typedatum->primary = 1; typedatum->primary = 1;
typedatum->flavor = TYPE_TYPE; typedatum->flavor = TYPE_TYPE;
uint32_t value = 0; uint32_t value = 0;
symtab_insert(policydb, SYM_TYPES, strdup(d), typedatum, SCOPE_DECL, 1, &value); symtab_insert(mpdb, SYM_TYPES, strdup(d), typedatum, SCOPE_DECL, 1, &value);
typedatum->s.value = value; typedatum->s.value = value;
if (ebitmap_set_bit(&policydb->global->branch_list->declared.scope[SYM_TYPES], value - 1, 1)) { if (ebitmap_set_bit(&mpdb->global->branch_list->declared.scope[SYM_TYPES], value - 1, 1)) {
return 1; return 1;
} }
policydb->type_attr_map = realloc(policydb->type_attr_map, sizeof(ebitmap_t) * policydb->p_types.nprim); mpdb->type_attr_map = xrealloc(mpdb->type_attr_map, sizeof(ebitmap_t) * mpdb->p_types.nprim);
policydb->attr_type_map = realloc(policydb->attr_type_map, sizeof(ebitmap_t) * policydb->p_types.nprim); mpdb->attr_type_map = xrealloc(mpdb->attr_type_map, sizeof(ebitmap_t) * mpdb->p_types.nprim);
ebitmap_init(&policydb->type_attr_map[value-1]); ebitmap_init(&mpdb->type_attr_map[value-1]);
ebitmap_init(&policydb->attr_type_map[value-1]); ebitmap_init(&mpdb->attr_type_map[value-1]);
ebitmap_set_bit(&policydb->type_attr_map[value-1], value-1, 1); ebitmap_set_bit(&mpdb->type_attr_map[value-1], value-1, 1);
src = hashtab_search(policydb->p_types.table, d); src = hashtab_search(mpdb->p_types.table, d);
if(!src) if(!src)
return 1; return 1;
if(policydb_index_decls(NULL, policydb)) if(policydb_index_decls(NULL, mpdb))
return 1; return 1;
if(policydb_index_classes(policydb)) if(policydb_index_classes(mpdb))
return 1; return 1;
if(policydb_index_others(NULL, policydb, 0)) if(policydb_index_others(NULL, mpdb, 0))
return 1; return 1;
//Add the domain to all roles //Add the domain to all roles
for(unsigned i=0; i<policydb->p_roles.nprim; ++i) { for(unsigned i = 0; i < mpdb->p_roles.nprim; ++i) {
//Not sure all those three calls are needed //Not sure all those three calls are needed
ebitmap_set_bit(&policydb->role_val_to_struct[i]->types.negset, value-1, 0); ebitmap_set_bit(&mpdb->role_val_to_struct[i]->types.negset, value - 1, 0);
ebitmap_set_bit(&policydb->role_val_to_struct[i]->types.types, value-1, 1); ebitmap_set_bit(&mpdb->role_val_to_struct[i]->types.types, value - 1, 1);
type_set_expand(&policydb->role_val_to_struct[i]->types, &policydb->role_val_to_struct[i]->cache, policydb, 0); type_set_expand(&mpdb->role_val_to_struct[i]->types, &mpdb->role_val_to_struct[i]->cache, mpdb, 0);
} }
return set_attr("domain", value); return set_attr("domain", value);
...@@ -268,22 +265,21 @@ int create_domain(const char *d) { ...@@ -268,22 +265,21 @@ int create_domain(const char *d) {
int set_domain_state(const char *s, int state) { int set_domain_state(const char *s, int state) {
type_datum_t *type; type_datum_t *type;
hashtab_ptr_t cur;
if (s == NULL) { if (s == NULL) {
hashtab_for_each(policydb->p_types.table, cur, { hashtab_for_each(mpdb->p_types.table, {
type = cur->datum; type = node->datum;
if (ebitmap_set_bit(&policydb->permissive_map, type->s.value, state)) { if (ebitmap_set_bit(&mpdb->permissive_map, type->s.value, state)) {
LOGW("Could not set bit in permissive map\n"); LOGW("Could not set bit in permissive map\n");
return 1; return 1;
} }
}) })
} else { } else {
type = hashtab_search(policydb->p_types.table, s); type = hashtab_search(mpdb->p_types.table, s);
if (type == NULL) { if (type == NULL) {
LOGW("type %s does not exist\n", s); LOGW("type %s does not exist\n", s);
return 1; return 1;
} }
if (ebitmap_set_bit(&policydb->permissive_map, type->s.value, state)) { if (ebitmap_set_bit(&mpdb->permissive_map, type->s.value, state)) {
LOGW("Could not set bit in permissive map\n"); LOGW("Could not set bit in permissive map\n");
return 1; return 1;
} }
...@@ -292,26 +288,26 @@ int set_domain_state(const char *s, int state) { ...@@ -292,26 +288,26 @@ int set_domain_state(const char *s, int state) {
return 0; return 0;
} }
int sepol_nametrans(const char *s, const char *t, const char *c, const char *d, const char *o) { int add_filename_trans(const char *s, const char *t, const char *c, const char *d, const char *o) {
type_datum_t *src, *tgt, *def; type_datum_t *src, *tgt, *def;
class_datum_t *cls; class_datum_t *cls;
src = hashtab_search(policydb->p_types.table, s); src = hashtab_search(mpdb->p_types.table, s);
if (src == NULL) { if (src == NULL) {
LOGW("source type %s does not exist\n", s); LOGW("source type %s does not exist\n", s);
return 1; return 1;
} }
tgt = hashtab_search(policydb->p_types.table, t); tgt = hashtab_search(mpdb->p_types.table, t);
if (tgt == NULL) { if (tgt == NULL) {
LOGW("target type %s does not exist\n", t); LOGW("target type %s does not exist\n", t);
return 1; return 1;
} }
cls = hashtab_search(policydb->p_classes.table, c); cls = hashtab_search(mpdb->p_classes.table, c);
if (cls == NULL) { if (cls == NULL) {
LOGW("class %s does not exist\n", c); LOGW("class %s does not exist\n", c);
return 1; return 1;
} }
def = hashtab_search(policydb->p_types.table, d); def = hashtab_search(mpdb->p_types.table, d);
if (def == NULL) { if (def == NULL) {
LOGW("default type %s does not exist\n", d); LOGW("default type %s does not exist\n", d);
return 1; return 1;
...@@ -324,11 +320,11 @@ int sepol_nametrans(const char *s, const char *t, const char *c, const char *d, ...@@ -324,11 +320,11 @@ int sepol_nametrans(const char *s, const char *t, const char *c, const char *d,
trans_key.name = (char *) o; trans_key.name = (char *) o;
filename_trans_datum_t *trans_datum; filename_trans_datum_t *trans_datum;
trans_datum = hashtab_search(policydb->p_types.table, (hashtab_key_t) &trans_key); trans_datum = hashtab_search(mpdb->filename_trans, (hashtab_key_t) &trans_key);
if (trans_datum == NULL) { if (trans_datum == NULL) {
trans_datum = xcalloc(sizeof(*trans_datum), 1); trans_datum = xcalloc(sizeof(*trans_datum), 1);
hashtab_insert(policydb->filename_trans, (hashtab_key_t) &trans_key, trans_datum); hashtab_insert(mpdb->filename_trans, (hashtab_key_t) &trans_key, trans_datum);
} }
// Overwrite existing // Overwrite existing
...@@ -336,32 +332,29 @@ int sepol_nametrans(const char *s, const char *t, const char *c, const char *d, ...@@ -336,32 +332,29 @@ int sepol_nametrans(const char *s, const char *t, const char *c, const char *d,
return 0; return 0;
} }
int add_typeattribute(const char *domainS, const char *attr) { int add_typeattribute(const char *type, const char *attr) {
type_datum_t *domain; type_datum_t *domain = hashtab_search(mpdb->p_types.table, type);
domain = hashtab_search(policydb->p_types.table, domainS);
if (domain == NULL) { if (domain == NULL) {
LOGW("source type %s does not exist\n", domainS); LOGW("type %s does not exist\n", type);
return 1; return 1;
} }
set_attr(attr, domain->s.value); int attr_id = set_attr(attr, domain->s.value);
if (attr_id < 0)
return 1;
int typeId = get_attr_id(attr); hashtab_for_each(mpdb->p_classes.table, {
//Now let's update all constraints! class_datum_t *cls = node->datum;
//(kernel doesn't support (yet?) type_names rules) for (constraint_node_t *n = cls->constraints; n ; n = n->next) {
for(int i=0; i<policydb->p_classes.nprim; ++i) { for (constraint_expr_t *e = n->expr; e; e = e->next) {
class_datum_t *cl = policydb->class_val_to_struct[i]; if (e->expr_type == CEXPR_NAMES &&
for(constraint_node_t *n = cl->constraints; n ; n=n->next) { ebitmap_get_bit(&e->type_names->types, attr_id - 1)) {
for(constraint_expr_t *e = n->expr; e; e=e->next) { ebitmap_set_bit(&e->names, domain->s.value - 1, 1);
if(e->expr_type == CEXPR_NAMES) {
if(ebitmap_get_bit(&e->type_names->types, typeId-1)) {
ebitmap_set_bit(&e->names, domain->s.value-1, 1);
}
}
} }
} }
} }
})
return 0; return 0;
} }
...@@ -371,7 +364,7 @@ int add_rule(const char *s, const char *t, const char *c, const char *p, int eff ...@@ -371,7 +364,7 @@ int add_rule(const char *s, const char *t, const char *c, const char *p, int eff
perm_datum_t *perm = NULL; perm_datum_t *perm = NULL;
if (s) { if (s) {
src = hashtab_search(policydb->p_types.table, s); src = hashtab_search(mpdb->p_types.table, s);
if (src == NULL) { if (src == NULL) {
LOGW("source type %s does not exist\n", s); LOGW("source type %s does not exist\n", s);
return 1; return 1;
...@@ -379,7 +372,7 @@ int add_rule(const char *s, const char *t, const char *c, const char *p, int eff ...@@ -379,7 +372,7 @@ int add_rule(const char *s, const char *t, const char *c, const char *p, int eff
} }
if (t) { if (t) {
tgt = hashtab_search(policydb->p_types.table, t); tgt = hashtab_search(mpdb->p_types.table, t);
if (tgt == NULL) { if (tgt == NULL) {
LOGW("target type %s does not exist\n", t); LOGW("target type %s does not exist\n", t);
return 1; return 1;
...@@ -387,7 +380,7 @@ int add_rule(const char *s, const char *t, const char *c, const char *p, int eff ...@@ -387,7 +380,7 @@ int add_rule(const char *s, const char *t, const char *c, const char *p, int eff
} }
if (c) { if (c) {
cls = hashtab_search(policydb->p_classes.table, c); cls = hashtab_search(mpdb->p_classes.table, c);
if (cls == NULL) { if (cls == NULL) {
LOGW("class %s does not exist\n", c); LOGW("class %s does not exist\n", c);
return 1; return 1;
...@@ -418,7 +411,7 @@ int add_xperm_rule(const char *s, const char *t, const char *c, const char *rang ...@@ -418,7 +411,7 @@ int add_xperm_rule(const char *s, const char *t, const char *c, const char *rang
class_datum_t *cls = NULL; class_datum_t *cls = NULL;
if (s) { if (s) {
src = hashtab_search(policydb->p_types.table, s); src = hashtab_search(mpdb->p_types.table, s);
if (src == NULL) { if (src == NULL) {
LOGW("source type %s does not exist\n", s); LOGW("source type %s does not exist\n", s);
return 1; return 1;
...@@ -426,7 +419,7 @@ int add_xperm_rule(const char *s, const char *t, const char *c, const char *rang ...@@ -426,7 +419,7 @@ int add_xperm_rule(const char *s, const char *t, const char *c, const char *rang
} }
if (t) { if (t) {
tgt = hashtab_search(policydb->p_types.table, t); tgt = hashtab_search(mpdb->p_types.table, t);
if (tgt == NULL) { if (tgt == NULL) {
LOGW("target type %s does not exist\n", t); LOGW("target type %s does not exist\n", t);
return 1; return 1;
...@@ -434,7 +427,7 @@ int add_xperm_rule(const char *s, const char *t, const char *c, const char *rang ...@@ -434,7 +427,7 @@ int add_xperm_rule(const char *s, const char *t, const char *c, const char *rang
} }
if (c) { if (c) {
cls = hashtab_search(policydb->p_classes.table, c); cls = hashtab_search(mpdb->p_classes.table, c);
if (cls == NULL) { if (cls == NULL) {
LOGW("class %s does not exist\n", c); LOGW("class %s does not exist\n", c);
return 1; return 1;
...@@ -462,22 +455,22 @@ int add_type_rule(const char *s, const char *t, const char *c, const char *d, in ...@@ -462,22 +455,22 @@ int add_type_rule(const char *s, const char *t, const char *c, const char *d, in
type_datum_t *src, *tgt, *def; type_datum_t *src, *tgt, *def;
class_datum_t *cls; class_datum_t *cls;
src = hashtab_search(policydb->p_types.table, s); src = hashtab_search(mpdb->p_types.table, s);
if (src == NULL) { if (src == NULL) {
LOGW("source type %s does not exist\n", s); LOGW("source type %s does not exist\n", s);
return 1; return 1;
} }
tgt = hashtab_search(policydb->p_types.table, t); tgt = hashtab_search(mpdb->p_types.table, t);
if (tgt == NULL) { if (tgt == NULL) {
LOGW("target type %s does not exist\n", t); LOGW("target type %s does not exist\n", t);
return 1; return 1;
} }
cls = hashtab_search(policydb->p_classes.table, c); cls = hashtab_search(mpdb->p_classes.table, c);
if (cls == NULL) { if (cls == NULL) {
LOGW("class %s does not exist\n", c); LOGW("class %s does not exist\n", c);
return 1; return 1;
} }
def = hashtab_search(policydb->p_types.table, d); def = hashtab_search(mpdb->p_types.table, d);
if (def == NULL) { if (def == NULL) {
LOGW("default type %s does not exist\n", d); LOGW("default type %s does not exist\n", d);
return 1; return 1;
...@@ -494,3 +487,10 @@ int add_type_rule(const char *s, const char *t, const char *c, const char *d, in ...@@ -494,3 +487,10 @@ int add_type_rule(const char *s, const char *t, const char *c, const char *d, in
return 0; return 0;
} }
void strip_dontaudit() {
avtab_for_each(&mpdb->te_avtab, {
if (node->key.specified == AVTAB_AUDITDENY || node->key.specified == AVTAB_XPERMS_DONTAUDIT)
avtab_remove_node(&magisk_policydb->te_avtab, node);
})
}
/* sepolicy.h - Header for magiskpolicy non-public APIs
*/
#pragma once #pragma once
#include <sepol/policydb/policydb.h> #include <sepol/policydb/policydb.h>
...@@ -8,29 +5,15 @@ ...@@ -8,29 +5,15 @@
__BEGIN_DECLS __BEGIN_DECLS
// Global policydb // Global policydb
extern policydb_t *policydb; extern policydb_t *magisk_policydb;
// General hash table traversal
#define hash_for_each(table, slots, tab, cur, block) \
for (int __i = 0; __i < (tab)->slots; ++__i) { \
__typeof__(cur) __next; \
for (cur = (tab)->table[__i]; cur; cur = __next) { \
__next = cur->next; \
block \
} \
} \
// hashtab traversal
#define hashtab_for_each(hashtab, cur, block) hash_for_each(htable, size, hashtab, cur, block)
// avtab traversal
#define avtab_for_each(avtab, cur, block) hash_for_each(htable, nslot, avtab, cur, block)
int create_domain(const char *d); int create_domain(const char *d);
int set_domain_state(const char *s, int state); int set_domain_state(const char *s, int state);
int add_typeattribute(const char *domainS, const char *attr); int add_typeattribute(const char *type, const char *attr);
int add_rule(const char *s, const char *t, const char *c, const char *p, int effect, int n); int add_rule(const char *s, const char *t, const char *c, const char *p, int effect, int n);
int add_xperm_rule(const char *s, const char *t, const char *c, const char *range, int effect, int n); int add_xperm_rule(const char *s, const char *t, const char *c, const char *range, int effect, int n);
int add_type_rule(const char *s, const char *t, const char *c, const char *d, int effect); int add_type_rule(const char *s, const char *t, const char *c, const char *d, int effect);
int add_filename_trans(const char *s, const char *t, const char *c, const char *d, const char *o);
void strip_dontaudit();
__END_DECLS __END_DECLS
...@@ -13,194 +13,183 @@ ...@@ -13,194 +13,183 @@
using namespace std; using namespace std;
enum connect_mode { enum {
UNINITIALIZED = 0, NAMED_ACTIVITY,
MODE_ACTIVITY, PKG_ACTIVITY,
MODE_BROADCAST_COMPONENT, CONTENT_PROVIDER
MODE_BROADCAST_PACKAGE
}; };
static connect_mode current_mode = UNINITIALIZED; #define CALL_PROVIDER \
"/system/bin/app_process", "/system/bin", "com.android.commands.content.Content", \
"call", "--uri", target, "--user", user, "--method", action
#define START_ACTIVITY \ #define START_ACTIVITY \
"/system/bin/app_process", "/system/bin", "com.android.commands.am.Am", \ "/system/bin/app_process", "/system/bin", "com.android.commands.am.Am", \
"start", "-n", nullptr, "--user", nullptr, "-f", "0x18000020", "-a" "start", "-p", target, "--user", user, "-a", "android.intent.action.VIEW", \
"-f", "0x18000020", "--es", "action", action
// 0x18000020 = FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_MULTIPLE_TASK|FLAG_INCLUDE_STOPPED_PACKAGES // 0x18000020 = FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_MULTIPLE_TASK|FLAG_INCLUDE_STOPPED_PACKAGES
#define START_BROADCAST \ #define get_user(info) \
"/system/bin/app_process", "/system/bin", "com.android.commands.am.Am", \ (info->cfg[SU_MULTIUSER_MODE] == MULTIUSER_MODE_USER \
"broadcast", "-n", nullptr, "--user", nullptr, "-f", "0x00000020", \ ? info->uid / 100000 : 0)
"-a", "android.intent.action.REBOOT", "--es", "action"
#define get_cmd(to) \
#define START_BROADCAST_PKG \ (to.command[0] ? to.command : to.shell[0] ? to.shell : DEFAULT_SHELL)
"/system/bin/app_process", "/system/bin", "com.android.commands.am.Am", \
"broadcast", "-p", nullptr, "--user", nullptr, "-f", "0x00000020", \ class Extra {
"-a", "android.intent.action.REBOOT", "--es", "action" const char *key;
enum {
// 0x00000020 = FLAG_INCLUDE_STOPPED_PACKAGES INT,
BOOL,
#define am_app_info(info, ...) \ STRING
if (current_mode == MODE_BROADCAST_PACKAGE) { \ } type;
const char *cmd[] = { START_BROADCAST_PKG, __VA_ARGS__, nullptr }; \ union {
exec_am_cmd(cmd, info); \ int int_val;
} else if (current_mode == MODE_BROADCAST_COMPONENT) { \ bool bool_val;
const char *cmd[] = { START_BROADCAST, __VA_ARGS__, nullptr }; \ const char * str_val;
exec_am_cmd(cmd, info); \ };
} else { \ char buf[32];
const char *cmd[] = { START_ACTIVITY, __VA_ARGS__, nullptr }; \ public:
exec_am_cmd(cmd, info); \ Extra(const char *k, int v): key(k), type(INT), int_val(v) {}
} Extra(const char *k, bool v): key(k), type(BOOL), bool_val(v) {}
Extra(const char *k, const char *v): key(k), type(STRING), str_val(v) {}
#define am_app(...) am_app_info(ctx.info.get(), __VA_ARGS__)
void add_intent(vector<const char *> &vec) {
static const char *get_command(const su_request *to) { const char *val;
if (to->command[0]) switch (type) {
return to->command; case INT:
if (to->shell[0]) vec.push_back("--ei");
return to->shell; sprintf(buf, "%d", int_val);
return DEFAULT_SHELL; val = buf;
} break;
case BOOL:
vec.push_back("--ez");
val = bool_val ? "true" : "false";
break;
case STRING:
vec.push_back("--es");
val = str_val;
break;
}
vec.push_back(key);
vec.push_back(val);
}
static void get_user(char *user, const su_info *info) { void add_bind(vector<const char *> &vec) {
sprintf(user, "%d", switch (type) {
info->cfg[SU_MULTIUSER_MODE] == MULTIUSER_MODE_USER case INT:
? info->uid / 100000 sprintf(buf, "%s:i:%d", key, int_val);
: 0); break;
} case BOOL:
sprintf(buf, "%s:b:%s", key, bool_val ? "true" : "false");
break;
case STRING:
sprintf(buf, "%s:s:%s", key, str_val);
break;
}
vec.push_back("--extra");
vec.push_back(buf);
}
};
static void get_uid(char *uid, const su_info *info) { static bool check_error(int fd) {
sprintf(uid, "%d", char buf[1024];
info->cfg[SU_MULTIUSER_MODE] == MULTIUSER_MODE_OWNER_MANAGED unique_ptr<FILE, decltype(&fclose)> out(xfdopen(fd, "r"), fclose);
? info->uid % 100000 while (fgets(buf, sizeof(buf), out.get())) {
: info->uid); if (strncmp(buf, "Error", 5) == 0)
return false;
}
return true;
} }
static void exec_am_cmd(const char **args, const su_info *info) { static void exec_cmd(const char *action, vector<Extra> &data,
const shared_ptr<su_info> &info, int mode = CONTENT_PROVIDER) {
char target[128]; char target[128];
if (args[3][0] == 'b') { char user[4];
// Broadcast sprintf(user, "%d", get_user(info));
if (args[4][1] == 'p') {
// Broadcast to package (receiver can be obfuscated) // First try content provider call method
strcpy(target, info->str[SU_MANAGER].data()); if (mode >= CONTENT_PROVIDER) {
} else { sprintf(target, "content://%s.provider", info->str[SU_MANAGER].data());
// a.h is the broadcast receiver vector<const char *> args{ CALL_PROVIDER };
sprintf(target, "%s/a.h", info->str[SU_MANAGER].data()); for (auto &e : data) {
e.add_bind(args);
} }
} else { args.push_back(nullptr);
// a.m is the activity exec_t exec {
sprintf(target, "%s/a.m", info->str[SU_MANAGER].data()); .err = true,
.fd = -1,
.pre_exec = [] { setenv("CLASSPATH", "/system/framework/content.jar", 1); },
.argv = args.data()
};
exec_command_sync(exec);
if (check_error(exec.fd))
return;
} }
char user[8];
get_user(user, info);
// Fill in non static arguments
args[5] = target;
args[7] = user;
vector<const char *> args{ START_ACTIVITY };
for (auto &e : data) {
e.add_intent(args);
}
args.push_back(nullptr);
exec_t exec { exec_t exec {
.pre_exec = []() -> void { .err = true,
int null = xopen("/dev/null", O_WRONLY | O_CLOEXEC); .fd = -1,
dup2(null, STDOUT_FILENO); .pre_exec = [] { setenv("CLASSPATH", "/system/framework/am.jar", 1); },
dup2(null, STDERR_FILENO); .argv = args.data()
setenv("CLASSPATH", "/system/framework/am.jar", 1);
},
.fork = fork_dont_care,
.argv = args
}; };
if (mode >= PKG_ACTIVITY) {
// Then try start activity without component name
strcpy(target, info->str[SU_MANAGER].data());
exec_command_sync(exec);
if (check_error(exec.fd))
return;
}
// Finally, fallback to start activity with component name
args[4] = "-n";
sprintf(target, "%s/a.m", info->str[SU_MANAGER].data());
exec.fd = -2;
exec.fork = fork_dont_care;
exec_command(exec); exec_command(exec);
} }
#define LOG_BODY \
"log", \
"--ei", "from.uid", fromUid, \
"--ei", "to.uid", toUid, \
"--ei", "pid", pid, \
"--ei", "policy", policy, \
"--es", "command", get_command(&ctx.req), \
"--ez", "notify", ctx.info->access.notify ? "true" : "false"
void app_log(const su_context &ctx) { void app_log(const su_context &ctx) {
char fromUid[8]; if (fork_dont_care() == 0) {
get_uid(fromUid, ctx.info.get()); vector<Extra> extras;
extras.reserve(6);
char toUid[8]; extras.emplace_back("from.uid", ctx.info->uid);
sprintf(toUid, "%d", ctx.req.uid); extras.emplace_back("to.uid", ctx.req.uid);
extras.emplace_back("pid", ctx.pid);
char pid[8]; extras.emplace_back("policy", ctx.info->access.policy);
sprintf(pid, "%d", ctx.pid); extras.emplace_back("command", get_cmd(ctx.req));
extras.emplace_back("notify", (bool) ctx.info->access.notify);
char policy[2];
sprintf(policy, "%d", ctx.info->access.policy); exec_cmd("log", extras, ctx.info);
exit(0);
am_app(LOG_BODY) }
} }
#define NOTIFY_BODY \
"notify", \
"--ei", "from.uid", fromUid, \
"--ei", "policy", policy
void app_notify(const su_context &ctx) { void app_notify(const su_context &ctx) {
char fromUid[8]; if (fork_dont_care() == 0) {
get_uid(fromUid, ctx.info.get()); vector<Extra> extras;
extras.reserve(2);
char policy[2]; extras.emplace_back("from.uid", ctx.info->uid);
sprintf(policy, "%d", ctx.info->access.policy); extras.emplace_back("policy", ctx.info->access.policy);
am_app(NOTIFY_BODY) exec_cmd("notify", extras, ctx.info);
exit(0);
}
} }
#define SOCKET_BODY \
"request", \
"--es", "socket", socket
void app_socket(const char *socket, const shared_ptr<su_info> &info) { void app_socket(const char *socket, const shared_ptr<su_info> &info) {
am_app_info(info.get(), SOCKET_BODY) vector<Extra> extras;
} extras.reserve(1);
extras.emplace_back("socket", socket);
#define TEST_BODY \
"test", "--ei", "mode", mode, nullptr
void broadcast_test(int client) {
if (client >= 0) {
// Make it not uninitialized
current_mode = MODE_ACTIVITY;
write_int(client, 0);
close(client);
}
su_info info;
get_db_settings(info.cfg);
get_db_strings(info.str);
validate_manager(info.str[SU_MANAGER], 0, &info.mgr_st);
char mode[2]; exec_cmd("request", extras, info, PKG_ACTIVITY);
{
sprintf(mode, "%d", MODE_BROADCAST_PACKAGE);
const char *cmd[] = { START_BROADCAST_PKG, TEST_BODY };
exec_am_cmd(cmd, &info);
}
{
sprintf(mode, "%d", MODE_BROADCAST_COMPONENT);
const char *cmd[] = { START_BROADCAST, TEST_BODY };
exec_am_cmd(cmd, &info);
}
}
void broadcast_ack(int client) {
int mode = read_int(client);
if (mode < 0) {
// Return connection mode to client
write_int(client, current_mode);
} else {
if (mode > current_mode) {
LOGD("* Use connect mode [%d] for su request and notify\n", mode);
current_mode = static_cast<connect_mode>(mode);
}
write_int(client, 0);
}
close(client);
} }
void socket_send_request(int fd, const shared_ptr<su_info> &info) { void socket_send_request(int fd, const shared_ptr<su_info> &info) {
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
class su_info { class su_info {
public: public:
/* Unique key */ /* Unique key */
const unsigned uid; const int uid;
/* These should be guarded with internal lock */ /* These should be guarded with internal lock */
db_settings cfg; db_settings cfg;
...@@ -39,7 +39,7 @@ private: ...@@ -39,7 +39,7 @@ private:
}; };
struct su_req_base { struct su_req_base {
unsigned uid = UID_ROOT; int uid = UID_ROOT;
bool login = false; bool login = false;
bool keepenv = false; bool keepenv = false;
bool mount_master = false; bool mount_master = false;
...@@ -63,7 +63,7 @@ private: ...@@ -63,7 +63,7 @@ private:
struct su_context { struct su_context {
std::shared_ptr<su_info> info; std::shared_ptr<su_info> info;
su_request req; su_request req;
pid_t pid; int pid;
}; };
void app_log(const su_context &ctx); void app_log(const su_context &ctx);
......
...@@ -11,4 +11,4 @@ ...@@ -11,4 +11,4 @@
#endif #endif
#define getline __getline #define getline __getline
#define fsetxattr(...) syscall(__NR_fsetxattr, __VA_ARGS__) #define fsetxattr(...) syscall(__NR_fsetxattr, __VA_ARGS__)
extern "C" ssize_t __getline(char **, size_t *, FILE *); ssize_t __getline(char **, size_t *, FILE *);
...@@ -11,6 +11,7 @@ LOCAL_SRC_FILES := \ ...@@ -11,6 +11,7 @@ LOCAL_SRC_FILES := \
selinux.cpp \ selinux.cpp \
logging.cpp \ logging.cpp \
cpio.cpp \ cpio.cpp \
xwrap.cpp xwrap.cpp \
stream.cpp
include $(BUILD_STATIC_LIBRARY) include $(BUILD_STATIC_LIBRARY)
...@@ -49,8 +49,7 @@ cpio_entry_base::cpio_entry_base(const cpio_newc_header *h) ...@@ -49,8 +49,7 @@ cpio_entry_base::cpio_entry_base(const cpio_newc_header *h)
void cpio::dump(const char *file) { void cpio::dump(const char *file) {
fprintf(stderr, "Dump cpio: [%s]\n", file); fprintf(stderr, "Dump cpio: [%s]\n", file);
FDOutStream fd_out(xopen(file, O_WRONLY | O_CREAT | O_TRUNC, 0644), true); dump(xfopen(file, "we"));
output(fd_out);
} }
void cpio::rm(entry_map::iterator &it) { void cpio::rm(entry_map::iterator &it) {
...@@ -110,9 +109,9 @@ bool cpio::exists(const char *name) { ...@@ -110,9 +109,9 @@ bool cpio::exists(const char *name) {
return entries.count(name) != 0; return entries.count(name) != 0;
} }
#define do_out(b, l) out.write(b, l); pos += (l) #define do_out(buf, len) pos += fwrite(buf, len, 1, out);
#define out_align() out.write(zeros, align_off(pos, 4)); pos = do_align(pos, 4) #define out_align() do_out(zeros, align_off(pos, 4))
void cpio::output(OutStream &out) { void cpio::dump(FILE *out) {
size_t pos = 0; size_t pos = 0;
unsigned inode = 300000; unsigned inode = 300000;
char header[111]; char header[111];
...@@ -147,6 +146,7 @@ void cpio::output(OutStream &out) { ...@@ -147,6 +146,7 @@ void cpio::output(OutStream &out) {
do_out(header, 110); do_out(header, 110);
do_out("TRAILER!!!\0", 11); do_out("TRAILER!!!\0", 11);
out_align(); out_align();
fclose(out);
} }
cpio_rw::cpio_rw(const char *file) { cpio_rw::cpio_rw(const char *file) {
...@@ -221,12 +221,12 @@ bool cpio_rw::mv(const char *from, const char *to) { ...@@ -221,12 +221,12 @@ bool cpio_rw::mv(const char *from, const char *to) {
#define pos_align(p) p = do_align(p, 4) #define pos_align(p) p = do_align(p, 4)
void cpio_rw::load_cpio(char *buf, size_t sz) { void cpio_rw::load_cpio(const char *buf, size_t sz) {
size_t pos = 0; size_t pos = 0;
cpio_newc_header *header; const cpio_newc_header *header;
unique_ptr<cpio_entry> entry; unique_ptr<cpio_entry> entry;
while (pos < sz) { while (pos < sz) {
header = (cpio_newc_header *)(buf + pos); header = reinterpret_cast<const cpio_newc_header *>(buf + pos);
entry = make_unique<cpio_entry>(header); entry = make_unique<cpio_entry>(header);
pos += sizeof(*header); pos += sizeof(*header);
string_view name_view(buf + pos); string_view name_view(buf + pos);
......
#pragma once #pragma once
#include <functional>
#include <string_view>
#define do_align(p, a) (((p) + (a) - 1) / (a) * (a)) #define do_align(p, a) (((p) + (a) - 1) / (a) * (a))
#define align_off(p, a) (do_align(p, a) - (p)) #define align_off(p, a) (do_align(p, a) - (p))
...@@ -8,10 +11,6 @@ struct file_attr { ...@@ -8,10 +11,6 @@ struct file_attr {
char con[128]; char con[128];
}; };
#ifdef __cplusplus
extern "C" {
#endif
ssize_t fd_path(int fd, char *path, size_t size); ssize_t fd_path(int fd, char *path, size_t size);
int fd_pathat(int dirfd, const char *name, char *path, size_t size); int fd_pathat(int dirfd, const char *name, char *path, size_t size);
int mkdirs(const char *pathname, mode_t mode); int mkdirs(const char *pathname, mode_t mode);
...@@ -32,12 +31,6 @@ void fd_full_read(int fd, void **buf, size_t *size); ...@@ -32,12 +31,6 @@ void fd_full_read(int fd, void **buf, size_t *size);
void full_read(const char *filename, void **buf, size_t *size); void full_read(const char *filename, void **buf, size_t *size);
void write_zero(int fd, size_t size); void write_zero(int fd, size_t size);
#ifdef __cplusplus
}
#include <functional>
#include <string_view>
void file_readline(const char *file, const std::function<bool (std::string_view)> &fn, bool trim = false); void file_readline(const char *file, const std::function<bool (std::string_view)> &fn, bool trim = false);
void parse_prop_file(const char *file, const std::function void parse_prop_file(const char *file, const std::function
<bool(std::string_view, std::string_view)> &fn); <bool(std::string_view, std::string_view)> &fn);
...@@ -81,5 +74,3 @@ void mmap_rw(const char *filename, B &buf, L &sz) { ...@@ -81,5 +74,3 @@ void mmap_rw(const char *filename, B &buf, L &sz) {
buf = (B) __mmap(filename, &__sz, true); buf = (B) __mmap(filename, &__sz, true);
sz = __sz; sz = __sz;
} }
#endif
\ No newline at end of file
...@@ -30,7 +30,7 @@ struct cpio_entry : public cpio_entry_base { ...@@ -30,7 +30,7 @@ struct cpio_entry : public cpio_entry_base {
explicit cpio_entry(const char *name, uint32_t mode) : filename(name) { explicit cpio_entry(const char *name, uint32_t mode) : filename(name) {
this->mode = mode; this->mode = mode;
} }
explicit cpio_entry(cpio_newc_header *h) : cpio_entry_base(h) {} explicit cpio_entry(const cpio_newc_header *h) : cpio_entry_base(h) {}
~cpio_entry() override { free(data); }; ~cpio_entry() override { free(data); };
}; };
...@@ -48,7 +48,7 @@ public: ...@@ -48,7 +48,7 @@ public:
protected: protected:
entry_map entries; entry_map entries;
void rm(entry_map::iterator &it); void rm(entry_map::iterator &it);
void output(OutStream &out); void dump(FILE *out);
}; };
class cpio_rw : public cpio { class cpio_rw : public cpio {
...@@ -64,7 +64,7 @@ public: ...@@ -64,7 +64,7 @@ public:
protected: protected:
void insert(cpio_entry *e); void insert(cpio_entry *e);
void mv(entry_map::iterator &it, const char *to); void mv(entry_map::iterator &it, const char *to);
void load_cpio(char *buf, size_t sz); void load_cpio(const char *buf, size_t sz);
}; };
class cpio_mmap : public cpio { class cpio_mmap : public cpio {
......
/* logging.h - Error handling and logging
*/
#pragma once #pragma once
#include <errno.h> #include <errno.h>
#include <stdarg.h> #include <stdarg.h>
#include <string.h> #include <string.h>
#ifdef __cplusplus __BEGIN_DECLS
extern "C" {
#endif
typedef enum { typedef enum {
L_DEBUG, L_DEBUG,
...@@ -43,6 +38,4 @@ void cmdline_logging(); ...@@ -43,6 +38,4 @@ void cmdline_logging();
int log_handler(log_type t, const char *fmt, ...); int log_handler(log_type t, const char *fmt, ...);
#ifdef __cplusplus __END_DECLS
}
#endif
...@@ -18,8 +18,6 @@ ...@@ -18,8 +18,6 @@
#define SEPOL_PROC_DOMAIN "magisk" #define SEPOL_PROC_DOMAIN "magisk"
#define SEPOL_FILE_DOMAIN "magisk_file" #define SEPOL_FILE_DOMAIN "magisk_file"
__BEGIN_DECLS
extern void (*freecon)(char *con); extern void (*freecon)(char *con);
extern int (*setcon)(const char *con); extern int (*setcon)(const char *con);
extern int (*getfilecon)(const char *path, char **con); extern int (*getfilecon)(const char *path, char **con);
...@@ -35,5 +33,3 @@ void selinux_builtin_impl(); ...@@ -35,5 +33,3 @@ void selinux_builtin_impl();
void dload_selinux(); void dload_selinux();
void restorecon(); void restorecon();
void restore_rootcon(); void restore_rootcon();
__END_DECLS
#pragma once #pragma once
#include <unistd.h> #include <unistd.h>
#include <stdio.h>
#include <memory> #include <memory>
#include "utils.h" // #include <utils.h>
class OutStream { class stream;
public:
virtual bool write(const void *buf, size_t len) = 0;
virtual ~OutStream() = default;
};
typedef std::unique_ptr<OutStream> strm_ptr; FILE *open_stream(stream *strm);
class FilterOutStream : public OutStream {
public:
FilterOutStream() = default;
FilterOutStream(strm_ptr &&ptr) : out(std::move(ptr)) {} template <class T, class... Args>
FILE *open_stream(Args &&... args) {
return open_stream(new T(args...));
}
void setOut(strm_ptr &&ptr) { out = std::move(ptr); } class stream {
public:
OutStream *getOut() { return out.get(); } virtual int read(void *buf, size_t len);
virtual int write(const void *buf, size_t len);
bool write(const void *buf, size_t len) override { virtual off_t seek(off_t off, int whence);
return out ? out->write(buf, len) : false; virtual int close();
} virtual ~stream() = default;
protected:
strm_ptr out;
}; };
class FDOutStream : public OutStream { // Delegates all operations to the base FILE pointer
class filter_stream : public stream {
public: public:
FDOutStream(int fd, bool close = false) : fd(fd), close(close) {} filter_stream(FILE *fp) : fp(fp) {}
~filter_stream() override { if (fp) close(); }
bool write(const void *buf, size_t len) override { int read(void *buf, size_t len) override;
return ::write(fd, buf, len) == len; int write(const void *buf, size_t len) override;
} int close() override;
~FDOutStream() override { void set_base(FILE *f);
if (close) template <class T, class... Args >
::close(fd); void set_base(Args&&... args) {
set_base(open_stream<T>(args...));
} }
protected: protected:
int fd; FILE *fp;
bool close;
}; };
class BufOutStream : public OutStream { // Handy interface for classes that need custom seek logic
public: class seekable_stream : public stream {
BufOutStream() : buf(nullptr), off(0), cap(0) {}; protected:
size_t _pos = 0;
bool write(const void *b, size_t len) override {
bool resize = false;
while (off + len > cap) {
cap = cap ? cap << 1 : 1 << 19;
resize = true;
}
if (resize)
buf = (char *) xrealloc(buf, cap);
memcpy(buf + off, b, len);
off += len;
return true;
}
template <typename bytes, typename length> off_t seek_pos(off_t off, int whence);
void release(bytes *&b, length &len) { virtual size_t end_pos() = 0;
b = buf; };
len = off;
buf = nullptr;
off = cap = 0;
}
template <typename bytes, typename length> // Byte stream that dynamically allocates memory
void getbuf(bytes *&b, length &len) const { class byte_stream : public seekable_stream {
b = buf; public:
len = off; byte_stream(uint8_t *&buf, size_t &len);
} template <class byte>
byte_stream(byte *&buf, size_t &len) : byte_stream(reinterpret_cast<uint8_t *&>(buf), len) {}
int read(void *buf, size_t len) override;
int write(const void *buf, size_t len) override;
off_t seek(off_t off, int whence) override;
private:
uint8_t *&_buf;
size_t &_len;
size_t _cap = 0;
void resize(size_t new_pos, bool zero = false);
size_t end_pos() override { return _len; }
};
~BufOutStream() override { // File stream but does not close the file descriptor at any time
free(buf); class fd_stream : public stream {
} public:
fd_stream(int fd) : fd(fd) {}
int read(void *buf, size_t len) override;
int write(const void *buf, size_t len) override;
off_t seek(off_t off, int whence) override;
protected: private:
char *buf; int fd;
size_t off;
size_t cap;
}; };
...@@ -73,7 +73,8 @@ int strend(const char *s1, const char *s2) { ...@@ -73,7 +73,8 @@ int strend(const char *s1, const char *s2) {
} }
int exec_command(exec_t &exec) { int exec_command(exec_t &exec) {
int pipefd[2] = {-1, -1}, outfd = -1; int pipefd[] = {-1, -1};
int outfd = -1;
if (exec.fd == -1) { if (exec.fd == -1) {
if (xpipe2(pipefd, O_CLOEXEC) == -1) if (xpipe2(pipefd, O_CLOEXEC) == -1)
...@@ -113,10 +114,10 @@ int exec_command(exec_t &exec) { ...@@ -113,10 +114,10 @@ int exec_command(exec_t &exec) {
} }
int exec_command_sync(exec_t &exec) { int exec_command_sync(exec_t &exec) {
int pid, status; int pid = exec_command(exec);
pid = exec_command(exec);
if (pid < 0) if (pid < 0)
return -1; return -1;
int status;
waitpid(pid, &status, 0); waitpid(pid, &status, 0);
return WEXITSTATUS(status); return WEXITSTATUS(status);
} }
...@@ -198,3 +199,17 @@ uint32_t binary_gcd(uint32_t u, uint32_t v) { ...@@ -198,3 +199,17 @@ uint32_t binary_gcd(uint32_t u, uint32_t v) {
} while (v != 0); } while (v != 0);
return u << shift; return u << shift;
} }
int switch_mnt_ns(int pid) {
char mnt[32];
snprintf(mnt, sizeof(mnt), "/proc/%d/ns/mnt", pid);
if (access(mnt, R_OK) == -1) return 1; // Maybe process died..
int fd, ret;
fd = xopen(mnt, O_RDONLY);
if (fd < 0) return 1;
// Switch to its namespace
ret = xsetns(fd, 0);
close(fd);
return ret;
}
#pragma once #pragma once
#define UID_ROOT 0
#define UID_SHELL 2000
#ifdef __cplusplus
extern "C" {
#endif
int fork_dont_care();
int fork_no_zombie();
int strend(const char *s1, const char *s2);
char *rtrim(char *str);
void init_argv0(int argc, char **argv);
void set_nice_name(const char *name);
int parse_int(const char *s);
uint32_t binary_gcd(uint32_t u, uint32_t v);
#ifdef __cplusplus
}
#include <string> #include <string>
#include <functional> #include <functional>
#include <string_view> #include <string_view>
void gen_rand_str(char *buf, int len, bool varlen = true); #define UID_ROOT 0
#define UID_SHELL 2000
#define str_contains(s, ss) ((ss) != nullptr && (s).find(ss) != std::string::npos) #define str_contains(s, ss) ((ss) != nullptr && (s).find(ss) != std::string::npos)
#define str_starts(s, ss) ((ss) != nullptr && (s).compare(0, strlen(ss), ss) == 0) #define str_starts(s, ss) ((ss) != nullptr && (s).compare(0, strlen(ss), ss) == 0)
...@@ -55,11 +37,30 @@ public: ...@@ -55,11 +37,30 @@ public:
~run_finally() { if (fn) fn(); } ~run_finally() { if (fn) fn(); }
private: private:
std::function<void ()> fn; std::function<void()> fn;
}; };
static inline int parse_int(std::string s) { return parse_int(s.data()); } template <typename T>
class reversed_container {
public:
reversed_container(T &base) : base(base) {}
decltype(std::declval<T>().rbegin()) begin() { return base.rbegin(); }
decltype(std::declval<T>().crbegin()) begin() const { return base.crbegin(); }
decltype(std::declval<T>().crbegin()) cbegin() const { return base.crbegin(); }
decltype(std::declval<T>().rend()) end() { return base.rend(); }
decltype(std::declval<T>().crend()) end() const { return base.crend(); }
decltype(std::declval<T>().crend()) cend() const { return base.crend(); }
private:
T &base;
};
template <typename T>
reversed_container<T> reversed(T &base) {
return reversed_container<T>(base);
}
int parse_int(const char *s);
static inline int parse_int(std::string s) { return parse_int(s.data()); }
static inline int parse_int(std::string_view s) { return parse_int(s.data()); } static inline int parse_int(std::string_view s) { return parse_int(s.data()); }
int new_daemon_thread(void *(*start_routine) (void *), void *arg = nullptr, int new_daemon_thread(void *(*start_routine) (void *), void *arg = nullptr,
...@@ -95,5 +96,12 @@ int exec_command_sync(Args &&...args) { ...@@ -95,5 +96,12 @@ int exec_command_sync(Args &&...args) {
} }
bool ends_with(const std::string_view &s1, const std::string_view &s2); bool ends_with(const std::string_view &s1, const std::string_view &s2);
int fork_dont_care();
#endif int fork_no_zombie();
\ No newline at end of file int strend(const char *s1, const char *s2);
char *rtrim(char *str);
void init_argv0(int argc, char **argv);
void set_nice_name(const char *name);
uint32_t binary_gcd(uint32_t u, uint32_t v);
int switch_mnt_ns(int pid);
void gen_rand_str(char *buf, int len, bool varlen = true);
...@@ -19,8 +19,6 @@ ...@@ -19,8 +19,6 @@
#define endmntent __endmntent #define endmntent __endmntent
#define hasmntopt __hasmntopt #define hasmntopt __hasmntopt
__BEGIN_DECLS
ssize_t __getline(char **lineptr, size_t *n, FILE *stream); ssize_t __getline(char **lineptr, size_t *n, FILE *stream);
ssize_t __getdelim(char **lineptr, size_t *n, int delim, FILE *stream); ssize_t __getdelim(char **lineptr, size_t *n, int delim, FILE *stream);
struct mntent *__getmntent_r(FILE* fp, struct mntent* e, char* buf, int buf_len); struct mntent *__getmntent_r(FILE* fp, struct mntent* e, char* buf, int buf_len);
...@@ -60,5 +58,3 @@ static inline int __linkat(int olddirfd, const char *oldpath, ...@@ -60,5 +58,3 @@ static inline int __linkat(int olddirfd, const char *oldpath,
static inline int __inotify_init1(int flags) { static inline int __inotify_init1(int flags) {
return syscall(__NR_inotify_init1, flags); return syscall(__NR_inotify_init1, flags);
} }
__END_DECLS
#include <utils.h>
#include <logging.h>
#include <stream.h>
static int strm_read(void *v, char *buf, int len) {
auto strm = reinterpret_cast<stream *>(v);
return strm->read(buf, len);
}
static int strm_write(void *v, const char *buf, int len) {
auto strm = reinterpret_cast<stream *>(v);
return strm->write(buf, len);
}
static fpos_t strm_seek(void *v, fpos_t off, int whence) {
auto strm = reinterpret_cast<stream *>(v);
return strm->seek(off, whence);
}
static int strm_close(void *v) {
auto strm = reinterpret_cast<stream *>(v);
int ret = strm->close();
delete strm;
return ret;
}
FILE *open_stream(stream *strm) {
FILE *fp = funopen(strm, strm_read, strm_write, strm_seek, strm_close);
// Disable buffering
setbuf(fp, nullptr);
return fp;
}
int stream::read(void *buf, size_t len) {
LOGE("This stream does not support read");
return -1;
}
int stream::write(const void *buf, size_t len) {
LOGE("This stream does not support write");
return -1;
}
off_t stream::seek(off_t off, int whence) {
LOGE("This stream does not support seek");
return -1;
}
int stream::close() {
return 0;
}
int filter_stream::read(void *buf, size_t len) {
return fread(buf, len, 1, fp);
}
int filter_stream::write(const void *buf, size_t len) {
return fwrite(buf, len, 1, fp);
}
int filter_stream::close() {
int ret = fclose(fp);
fp = nullptr;
return ret;
}
void filter_stream::set_base(FILE *f) {
if (fp) fclose(fp);
fp = f;
}
off_t seekable_stream::seek_pos(off_t off, int whence) {
switch (whence) {
case SEEK_CUR:
return _pos + off;
case SEEK_END:
return end_pos() + off;
case SEEK_SET:
return off;
default:
return -1;
}
}
byte_stream::byte_stream(uint8_t *&buf, size_t &len) : _buf(buf), _len(len) {
buf = nullptr;
len = 0;
}
int byte_stream::read(void *buf, size_t len) {
len = std::min(len, _len - _pos);
memcpy(buf, _buf + _pos, len);
return len;
}
int byte_stream::write(const void *buf, size_t len) {
resize(_pos + len);
memcpy(_buf + _pos, buf, len);
_pos += len;
_len = std::max(_len, _pos);
return len;
}
off_t byte_stream::seek(off_t off, int whence) {
off_t np = seek_pos(off, whence);
if (np < 0)
return -1;
resize(np, true);
_pos = np;
return np;
}
void byte_stream::resize(size_t new_pos, bool zero) {
bool resize = false;
size_t old_cap = _cap;
while (new_pos > _cap) {
_cap = _cap ? (_cap << 1) - (_cap >> 1) : 1 << 12;
resize = true;
}
if (resize) {
_buf = (uint8_t *) xrealloc(_buf, _cap);
if (zero)
memset(_buf + old_cap, 0, _cap - old_cap);
}
}
int fd_stream::read(void *buf, size_t len) {
return ::read(fd, buf, len);
}
int fd_stream::write(const void *buf, size_t len) {
return ::write(fd, buf, len);
}
off_t fd_stream::seek(off_t off, int whence) {
return lseek(fd, off, whence);
}
#pragma once #pragma once
#ifdef __cplusplus
extern "C" {
#endif
FILE *xfopen(const char *pathname, const char *mode); FILE *xfopen(const char *pathname, const char *mode);
FILE *xfdopen(int fd, const char *mode); FILE *xfdopen(int fd, const char *mode);
int xopen(const char *pathname, int flags);
int xopen(const char *pathname, int flags, mode_t mode);
int xopenat(int dirfd, const char *pathname, int flags); int xopenat(int dirfd, const char *pathname, int flags);
ssize_t xwrite(int fd, const void *buf, size_t count); ssize_t xwrite(int fd, const void *buf, size_t count);
ssize_t xread(int fd, void *buf, size_t count); ssize_t xread(int fd, void *buf, size_t count);
...@@ -22,9 +20,9 @@ int xbind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); ...@@ -22,9 +20,9 @@ int xbind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
int xconnect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); int xconnect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
int xlisten(int sockfd, int backlog); int xlisten(int sockfd, int backlog);
int xaccept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags); int xaccept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
void *xmalloc(size_t size); extern "C" void *xmalloc(size_t size);
void *xcalloc(size_t nmemb, size_t size); extern "C" void *xcalloc(size_t nmemb, size_t size);
void *xrealloc(void *ptr, size_t size); extern "C" void *xrealloc(void *ptr, size_t size);
ssize_t xsendmsg(int sockfd, const struct msghdr *msg, int flags); ssize_t xsendmsg(int sockfd, const struct msghdr *msg, int flags);
ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags); ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags);
int xpthread_create(pthread_t *thread, const pthread_attr_t *attr, int xpthread_create(pthread_t *thread, const pthread_attr_t *attr,
...@@ -54,11 +52,3 @@ pid_t xfork(); ...@@ -54,11 +52,3 @@ pid_t xfork();
int xpoll(struct pollfd *fds, nfds_t nfds, int timeout); int xpoll(struct pollfd *fds, nfds_t nfds, int timeout);
int xinotify_init1(int flags); int xinotify_init1(int flags);
#ifdef __cplusplus
}
int xopen(const char *pathname, int flags);
int xopen(const char *pathname, int flags, mode_t mode);
#endif
...@@ -20,12 +20,8 @@ else ...@@ -20,12 +20,8 @@ else
fi fi
initialize() { initialize() {
# This path should work in any cases
TMPDIR=/dev/tmp
mount /data 2>/dev/null
MAGISKBIN=/data/adb/magisk MAGISKBIN=/data/adb/magisk
if [ ! -d $MAGISKBIN ]; then if [ ! -d $MAGISKBIN ]; then
echo "! Cannot find Magisk binaries!" echo "! Cannot find Magisk binaries!"
exit 1 exit 1
...@@ -43,73 +39,48 @@ initialize() { ...@@ -43,73 +39,48 @@ initialize() {
fi fi
} }
show_logo() { main() {
if ! $backuptool_ab; then
# Wait for post addon.d-v1 processes to finish
sleep 5
fi
# Ensure we aren't in /tmp/addon.d anymore (since it's been deleted by addon.d)
cd $TMPDIR
$BOOTMODE || recovery_actions
ui_print "************************" ui_print "************************"
ui_print "* Magisk v$MAGISK_VER addon.d" ui_print "* Magisk v$MAGISK_VER addon.d"
ui_print "************************" ui_print "************************"
}
installation() { mount_partitions
find_manager_apk check_data
get_flags get_flags
if $backuptool_ab; then
# Swap the slot for addon.d-v2
if [ ! -z $SLOT ]; then [ $SLOT = _a ] && SLOT=_b || SLOT=_a; fi
fi
find_boot_image find_boot_image
find_dtbo_image
[ -z $BOOTIMAGE ] && abort "! Unable to detect target image" [ -z $BOOTIMAGE ] && abort "! Unable to detect target image"
ui_print "- Target image: $BOOTIMAGE" ui_print "- Target image: $BOOTIMAGE"
[ -z $DTBOIMAGE ] || ui_print "- DTBO image: $DTBOIMAGE"
remove_system_su remove_system_su
find_manager_apk
[ -f $APK ] && eval $BOOTSIGNER -verify < $BOOTIMAGE && BOOTSIGNED=true patch_boot_image
$BOOTSIGNED && ui_print "- Boot image is signed with AVB 1.0"
SOURCEDMODE=true
cd $MAGISKBIN
# Source the boot patcher
. ./boot_patch.sh "$BOOTIMAGE"
ui_print "- Flashing new boot image"
flash_image new-boot.img "$BOOTIMAGE" || abort "! Insufficient partition size"
rm -f new-boot.img
if [ -f stock_boot* ]; then
rm -f /data/stock_boot* 2>/dev/null
$DATA && mv stock_boot* /data
fi
$KEEPVERITY || patch_dtbo_image
if [ -f stock_dtbo* ]; then
rm -f /data/stock_dtbo* 2>/dev/null
$DATA && mv stock_dtbo* /data
fi
cd / cd /
} # Cleanups
$BOOTMODE || recovery_cleanup
rm -rf $TMPDIR
finalize() {
ui_print "- Done" ui_print "- Done"
exit 0 exit 0
} }
main() {
if ! $backuptool_ab; then
# Wait for post addon.d-v1 processes to finish
sleep 5
fi
$BOOTMODE || recovery_actions
show_logo
mount_partitions
if $backuptool_ab; then
# Swap the slot for addon.d-v2
if [ ! -z $SLOT ]; then [ $SLOT = _a ] && SLOT=_b || SLOT=_a; fi
fi
installation
$BOOTMODE || recovery_cleanup
finalize
}
case "$1" in case "$1" in
backup) backup)
# Stub # Stub
......
#MAGISK #MAGISK
########################################################################################## ##########################################################################################
# #
# Magisk Flash Script # Magisk Flash Script (updater-script)
# by topjohnwu # by topjohnwu
# #
# This script will detect, construct the environment for Magisk # This script will detect, construct the environment for Magisk
...@@ -84,6 +84,8 @@ if [ -d /system/addon.d ]; then ...@@ -84,6 +84,8 @@ if [ -d /system/addon.d ]; then
#!/sbin/sh #!/sbin/sh
# ADDOND_VERSION=2 # ADDOND_VERSION=2
mount /data 2>/dev/null
if [ -f /data/adb/magisk/addon.d.sh ]; then if [ -f /data/adb/magisk/addon.d.sh ]; then
exec sh /data/adb/magisk/addon.d.sh "\$@" exec sh /data/adb/magisk/addon.d.sh "\$@"
else else
...@@ -103,43 +105,10 @@ fi ...@@ -103,43 +105,10 @@ fi
$BOOTMODE || recovery_actions $BOOTMODE || recovery_actions
########################################################################################## ##########################################################################################
# Boot patching # Boot/DTBO Patching
########################################################################################## ##########################################################################################
eval $BOOTSIGNER -verify < $BOOTIMAGE && BOOTSIGNED=true patch_boot_image
$BOOTSIGNED && ui_print "- Boot image is signed with AVB 1.0"
SOURCEDMODE=true
cd $MAGISKBIN
$IS64BIT && mv -f magiskinit64 magiskinit || rm -f magiskinit64
# Source the boot patcher
. ./boot_patch.sh "$BOOTIMAGE"
ui_print "- Flashing new boot image"
if ! flash_image new-boot.img "$BOOTIMAGE"; then
ui_print "- Compressing ramdisk to fit in partition"
./magiskboot cpio ramdisk.cpio compress
./magiskboot repack "$BOOTIMAGE"
flash_image new-boot.img "$BOOTIMAGE" || abort "! Insufficient partition size"
fi
./magiskboot cleanup
rm -f new-boot.img
if [ -f stock_boot* ]; then
rm -f /data/stock_boot* 2>/dev/null
$DATA && mv stock_boot* /data
fi
$KEEPVERITY || patch_dtbo_image
if [ -f stock_dtbo* ]; then
rm -f /data/stock_dtbo* 2>/dev/null
$DATA && mv stock_dtbo* /data
fi
cd / cd /
# Cleanups # Cleanups
......
...@@ -256,7 +256,7 @@ flash_image() { ...@@ -256,7 +256,7 @@ flash_image() {
esac esac
if $BOOTSIGNED; then if $BOOTSIGNED; then
CMD2="$BOOTSIGNER -sign" CMD2="$BOOTSIGNER -sign"
ui_print "- Sign image with test keys" ui_print "- Sign image with verity keys"
else else
CMD2="cat -" CMD2="cat -"
fi fi
...@@ -293,6 +293,45 @@ patch_dtbo_image() { ...@@ -293,6 +293,45 @@ patch_dtbo_image() {
return 1 return 1
} }
patch_boot_image() {
# Common installation script for flash_script.sh (updater-script) and addon.d.sh
SOURCEDMODE=true
cd $MAGISKBIN
eval $BOOTSIGNER -verify < $BOOTIMAGE && BOOTSIGNED=true
$BOOTSIGNED && ui_print "- Boot image is signed with AVB 1.0"
$IS64BIT && mv -f magiskinit64 magiskinit 2>/dev/null || rm -f magiskinit64
# Source the boot patcher
. ./boot_patch.sh "$BOOTIMAGE"
ui_print "- Flashing new boot image"
if ! flash_image new-boot.img "$BOOTIMAGE"; then
ui_print "- Compressing ramdisk to fit in partition"
./magiskboot cpio ramdisk.cpio compress
./magiskboot repack "$BOOTIMAGE"
flash_image new-boot.img "$BOOTIMAGE" || abort "! Insufficient partition size"
fi
./magiskboot cleanup
rm -f new-boot.img
if [ -f stock_boot* ]; then
rm -f /data/stock_boot* 2>/dev/null
$DATA && mv stock_boot* /data
fi
# Patch DTBO together with boot image
$KEEPVERITY || patch_dtbo_image
if [ -f stock_dtbo* ]; then
rm -f /data/stock_dtbo* 2>/dev/null
$DATA && mv stock_dtbo* /data
fi
}
sign_chromeos() { sign_chromeos() {
ui_print "- Signing ChromeOS boot image" ui_print "- Signing ChromeOS boot image"
...@@ -360,13 +399,16 @@ check_data() { ...@@ -360,13 +399,16 @@ check_data() {
} }
find_manager_apk() { find_manager_apk() {
APK=/data/adb/magisk.apk [ -z $APK ] && APK=/data/adb/magisk.apk
[ -f $APK ] || APK=/data/magisk/magisk.apk [ -f $APK ] || APK=/data/magisk/magisk.apk
[ -f $APK ] || APK=/data/app/com.topjohnwu.magisk*/*.apk [ -f $APK ] || APK=/data/app/com.topjohnwu.magisk*/*.apk
if [ ! -f $APK ]; then if [ ! -f $APK ]; then
DBAPK=`magisk --sqlite "SELECT value FROM strings WHERE key='requester'" | cut -d= -f2` DBAPK=`magisk --sqlite "SELECT value FROM strings WHERE key='requester'" 2>/dev/null | cut -d= -f2`
[ -z "$DBAPK" ] || APK=/data/app/$DBAPK*/*.apk [ -z $DBAPK ] && DBAPK=`strings /data/adb/magisk.db | grep 5requester | cut -c11-`
[ -z $DBAPK ] || APK=/data/user_de/*/$DBAPK/dyn/*.apk
[ -f $APK ] || [ -z $DBAPK ] || APK=/data/app/$DBAPK*/*.apk
fi fi
[ -f $APK ] || ui_print "! Unable to detect Magisk Manager APK for BootSigner"
} }
################# #################
......
...@@ -20,6 +20,6 @@ ...@@ -20,6 +20,6 @@
# hide the original source file name. # hide the original source file name.
#-renamesourcefileattribute SourceFile #-renamesourcefileattribute SourceFile
-keepclassmembers class * implements javax.net.ssl.SSLSocketFactory { -keepclassmembers class * extends javax.net.ssl.SSLSocketFactory {
** delegate; ** delegate;
} }
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.topjohnwu.shared"> package="com.topjohnwu.shared">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
...@@ -7,9 +8,11 @@ ...@@ -7,9 +8,11 @@
<application <application
android:icon="@drawable/ic_launcher" android:icon="@drawable/ic_launcher"
android:installLocation="internalOnly"
android:label="Magisk Manager" android:label="Magisk Manager"
android:supportsRtl="true"> android:installLocation="internalOnly"
android:usesCleartextTraffic="true"
android:supportsRtl="true"
tools:ignore="UnusedAttribute">
<activity <activity
android:name="a.r" android:name="a.r"
...@@ -21,9 +24,6 @@ ...@@ -21,9 +24,6 @@
android:authorities="${applicationId}.provider" android:authorities="${applicationId}.provider"
android:exported="false" android:exported="false"
android:grantUriPermissions="true"> android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider> </provider>
</application> </application>
</manifest> </manifest>
...@@ -11,19 +11,11 @@ import static android.os.Build.VERSION.SDK_INT; ...@@ -11,19 +11,11 @@ import static android.os.Build.VERSION.SDK_INT;
public class DynAPK { public class DynAPK {
private static final int STUB_VERSION = 2; private static final int STUB_VERSION = 6;
// Indices of the object array // Indices of the object array
private static final int STUB_VERSION_ENTRY = 0; private static final int STUB_VERSION_ENTRY = 0;
private static final int COMPONENT_MAP = 1; private static final int CLASS_COMPONENT_MAP = 1;
private static final int RESOURCE_MAP = 2;
// Indices of the resource map
public static final int NOTIFICATION = 0;
public static final int DOWNLOAD = 1;
public static final int SUPERUSER = 2;
public static final int MODULES = 3;
public static final int MAGISKHIDE = 4;
private static File dynDir; private static File dynDir;
private static Method addAssetPath; private static Method addAssetPath;
...@@ -52,16 +44,14 @@ public class DynAPK { ...@@ -52,16 +44,14 @@ public class DynAPK {
Object[] arr = (Object[]) o; Object[] arr = (Object[]) o;
Data data = new Data(); Data data = new Data();
data.version = (int) arr[STUB_VERSION_ENTRY]; data.version = (int) arr[STUB_VERSION_ENTRY];
data.componentMap = (Map<String, String>) arr[COMPONENT_MAP]; data.classToComponent = (Map<String, String>) arr[CLASS_COMPONENT_MAP];
data.resourceMap = (int[]) arr[RESOURCE_MAP];
return data; return data;
} }
public static Object pack(Data data) { public static Object pack(Data data) {
Object[] arr = new Object[3]; Object[] arr = new Object[2];
arr[STUB_VERSION_ENTRY] = STUB_VERSION; arr[STUB_VERSION_ENTRY] = data.version;
arr[COMPONENT_MAP] = data.componentMap; arr[CLASS_COMPONENT_MAP] = data.classToComponent;
arr[RESOURCE_MAP] = data.resourceMap;
return arr; return arr;
} }
...@@ -74,8 +64,7 @@ public class DynAPK { ...@@ -74,8 +64,7 @@ public class DynAPK {
} }
public static class Data { public static class Data {
public int version; public int version = STUB_VERSION;
public Map<String, String> componentMap; public Map<String, String> classToComponent;
public int[] resourceMap;
} }
} }
...@@ -3,19 +3,21 @@ package com.topjohnwu.magisk; ...@@ -3,19 +3,21 @@ package com.topjohnwu.magisk;
import android.content.ContentProvider; import android.content.ContentProvider;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo; import android.content.pm.ProviderInfo;
import android.content.res.XmlResourceParser; import android.content.res.XmlResourceParser;
import android.database.Cursor; import android.database.Cursor;
import android.database.MatrixCursor; import android.database.MatrixCursor;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.provider.OpenableColumns; import android.provider.OpenableColumns;
import android.text.TextUtils; import android.text.TextUtils;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
import com.topjohnwu.shared.R;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import java.io.File; import java.io.File;
...@@ -34,9 +36,6 @@ public class FileProvider extends ContentProvider { ...@@ -34,9 +36,6 @@ public class FileProvider extends ContentProvider {
private static final String[] COLUMNS = { private static final String[] COLUMNS = {
OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE }; OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE };
private static final String
META_DATA_FILE_PROVIDER_PATHS = "android.support.FILE_PROVIDER_PATHS";
private static final String TAG_ROOT_PATH = "root-path"; private static final String TAG_ROOT_PATH = "root-path";
private static final String TAG_FILES_PATH = "files-path"; private static final String TAG_FILES_PATH = "files-path";
private static final String TAG_CACHE_PATH = "cache-path"; private static final String TAG_CACHE_PATH = "cache-path";
...@@ -54,13 +53,13 @@ public class FileProvider extends ContentProvider { ...@@ -54,13 +53,13 @@ public class FileProvider extends ContentProvider {
private PathStrategy mStrategy; private PathStrategy mStrategy;
public static ProviderCallHandler callHandler;
@Override @Override
public boolean onCreate() { public boolean onCreate() {
return true; return true;
} }
@Override @Override
public void attachInfo(Context context, ProviderInfo info) { public void attachInfo(Context context, ProviderInfo info) {
super.attachInfo(context, info); super.attachInfo(context, info);
...@@ -83,7 +82,6 @@ public class FileProvider extends ContentProvider { ...@@ -83,7 +82,6 @@ public class FileProvider extends ContentProvider {
return strategy.getUriForFile(file); return strategy.getUriForFile(file);
} }
@Override @Override
public Cursor query(Uri uri, String[] projection, String selection, public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String[] selectionArgs,
...@@ -116,7 +114,6 @@ public class FileProvider extends ContentProvider { ...@@ -116,7 +114,6 @@ public class FileProvider extends ContentProvider {
return cursor; return cursor;
} }
@Override @Override
public String getType(Uri uri) { public String getType(Uri uri) {
...@@ -134,20 +131,17 @@ public class FileProvider extends ContentProvider { ...@@ -134,20 +131,17 @@ public class FileProvider extends ContentProvider {
return "application/octet-stream"; return "application/octet-stream";
} }
@Override @Override
public Uri insert(Uri uri, ContentValues values) { public Uri insert(Uri uri, ContentValues values) {
throw new UnsupportedOperationException("No external inserts"); throw new UnsupportedOperationException("No external inserts");
} }
@Override @Override
public int update(Uri uri, ContentValues values, String selection, public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) { String[] selectionArgs) {
throw new UnsupportedOperationException("No external updates"); throw new UnsupportedOperationException("No external updates");
} }
@Override @Override
public int delete(Uri uri, String selection, public int delete(Uri uri, String selection,
String[] selectionArgs) { String[] selectionArgs) {
...@@ -156,6 +150,12 @@ public class FileProvider extends ContentProvider { ...@@ -156,6 +150,12 @@ public class FileProvider extends ContentProvider {
return file.delete() ? 1 : 0; 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 null;
}
@Override @Override
public ParcelFileDescriptor openFile(Uri uri, String mode) public ParcelFileDescriptor openFile(Uri uri, String mode)
...@@ -166,7 +166,6 @@ public class FileProvider extends ContentProvider { ...@@ -166,7 +166,6 @@ public class FileProvider extends ContentProvider {
return ParcelFileDescriptor.open(file, fileMode); return ParcelFileDescriptor.open(file, fileMode);
} }
private static PathStrategy getPathStrategy(Context context, String authority) { private static PathStrategy getPathStrategy(Context context, String authority) {
PathStrategy strat; PathStrategy strat;
synchronized (sCache) { synchronized (sCache) {
...@@ -175,11 +174,9 @@ public class FileProvider extends ContentProvider { ...@@ -175,11 +174,9 @@ public class FileProvider extends ContentProvider {
try { try {
strat = parsePathStrategy(context, authority); strat = parsePathStrategy(context, authority);
} catch (IOException e) { } catch (IOException e) {
throw new IllegalArgumentException( throw new IllegalArgumentException("Failed to parse xml", e);
"Failed to parse " + META_DATA_FILE_PROVIDER_PATHS + " meta-data", e);
} catch (XmlPullParserException e) { } catch (XmlPullParserException e) {
throw new IllegalArgumentException( throw new IllegalArgumentException("Failed to parse xml", e);
"Failed to parse " + META_DATA_FILE_PROVIDER_PATHS + " meta-data", e);
} }
sCache.put(authority, strat); sCache.put(authority, strat);
} }
...@@ -187,19 +184,11 @@ public class FileProvider extends ContentProvider { ...@@ -187,19 +184,11 @@ public class FileProvider extends ContentProvider {
return strat; return strat;
} }
private static PathStrategy parsePathStrategy(Context context, String authority) private static PathStrategy parsePathStrategy(Context context, String authority)
throws IOException, XmlPullParserException { throws IOException, XmlPullParserException {
final SimplePathStrategy strat = new SimplePathStrategy(authority); final SimplePathStrategy strat = new SimplePathStrategy(authority);
final ProviderInfo info = context.getPackageManager() final XmlResourceParser in = context.getResources().getXml(R.xml.file_paths);
.resolveContentProvider(authority, PackageManager.GET_META_DATA);
final XmlResourceParser in = info.loadXmlMetaData(
context.getPackageManager(), META_DATA_FILE_PROVIDER_PATHS);
if (in == null) {
throw new IllegalArgumentException(
"Missing " + META_DATA_FILE_PROVIDER_PATHS + " meta-data");
}
int type; int type;
while ((type = in.next()) != END_DOCUMENT) { while ((type = in.next()) != END_DOCUMENT) {
...@@ -245,16 +234,13 @@ public class FileProvider extends ContentProvider { ...@@ -245,16 +234,13 @@ public class FileProvider extends ContentProvider {
return strat; return strat;
} }
interface PathStrategy { interface PathStrategy {
Uri getUriForFile(File file); Uri getUriForFile(File file);
File getFileForUri(Uri uri); File getFileForUri(Uri uri);
} }
static class SimplePathStrategy implements PathStrategy { static class SimplePathStrategy implements PathStrategy {
private final String mAuthority; private final String mAuthority;
private final HashMap<String, File> mRoots = new HashMap<>(); private final HashMap<String, File> mRoots = new HashMap<>();
...@@ -263,7 +249,6 @@ public class FileProvider extends ContentProvider { ...@@ -263,7 +249,6 @@ public class FileProvider extends ContentProvider {
mAuthority = authority; mAuthority = authority;
} }
void addRoot(String name, File root) { void addRoot(String name, File root) {
if (TextUtils.isEmpty(name)) { if (TextUtils.isEmpty(name)) {
throw new IllegalArgumentException("Name must not be empty"); throw new IllegalArgumentException("Name must not be empty");
......
...@@ -22,7 +22,6 @@ import android.content.Intent; ...@@ -22,7 +22,6 @@ import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
...@@ -37,27 +36,11 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; ...@@ -37,27 +36,11 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
* Trigger process recreation by calling {@link #triggerRebirth} with a {@link Context} instance. * Trigger process recreation by calling {@link #triggerRebirth} with a {@link Context} instance.
*/ */
public class ProcessPhoenix extends Activity { public class ProcessPhoenix extends Activity {
private static final String KEY_RESTART_INTENTS = "phoenix_restart_intents"; private static final String KEY_RESTART_INTENT = "phoenix_restart_intent";
/** public static void triggerRebirth(Context context, Intent intent) {
* Call to restart the application process using the {@linkplain Intent#CATEGORY_DEFAULT default}
* activity as an intent.
* <p>
* Behavior of the current process after invoking this method is undefined.
*/
public static void triggerRebirth(Context context) {
triggerRebirth(context, getRestartIntent(context));
}
/**
* Call to restart the application process using the specified intents.
* <p>
* Behavior of the current process after invoking this method is undefined.
*/
public static void triggerRebirth(Context context, Intent... nextIntents) {
Intent intent = new Intent(context, a.r.class);
intent.addFlags(FLAG_ACTIVITY_NEW_TASK); // In case we are called with non-Activity context. intent.addFlags(FLAG_ACTIVITY_NEW_TASK); // In case we are called with non-Activity context.
intent.putParcelableArrayListExtra(KEY_RESTART_INTENTS, new ArrayList<>(Arrays.asList(nextIntents))); intent.putExtra(KEY_RESTART_INTENT, getRestartIntent(context));
context.startActivity(intent); context.startActivity(intent);
if (context instanceof Activity) { if (context instanceof Activity) {
((Activity) context).finish(); ((Activity) context).finish();
...@@ -73,17 +56,15 @@ public class ProcessPhoenix extends Activity { ...@@ -73,17 +56,15 @@ public class ProcessPhoenix extends Activity {
return defaultIntent; return defaultIntent;
} }
throw new IllegalStateException("Unable to determine default activity for " throw new IllegalStateException();
+ packageName
+ ". Does an activity specify the DEFAULT category in its intent filter?");
} }
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ArrayList<Intent> intents = getIntent().getParcelableArrayListExtra(KEY_RESTART_INTENTS); Intent intent = getIntent().getParcelableExtra(KEY_RESTART_INTENT);
startActivities(intents.toArray(new Intent[0])); startActivity(intent);
finish(); finish();
Runtime.getRuntime().exit(0); Runtime.getRuntime().exit(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);
}
...@@ -15,117 +15,149 @@ import java.util.jar.Manifest; ...@@ -15,117 +15,149 @@ import java.util.jar.Manifest;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
/* public abstract class JarMap implements Closeable {
* A universal random access interface for both JarFile and JarInputStream
*
* In the case when JarInputStream is provided to constructor, the whole stream
* will be loaded into memory for random access purposes.
* On the other hand, when a JarFile is provided, it simply works as a wrapper.
* */
public class JarMap implements Closeable { LinkedHashMap<String, JarEntry> entryMap;
private JarFile jarFile; public static JarMap open(String file) throws IOException {
private JarInputStream jis; return new FileMap(new File(file), true, ZipFile.OPEN_READ);
private LinkedHashMap<String, JarEntry> bufMap; }
private Manifest manifest;
public JarMap(File file) throws IOException { public static JarMap open(File file, boolean verify) throws IOException {
this(file, true); return new FileMap(file, verify, ZipFile.OPEN_READ);
} }
public JarMap(File file, boolean verify) throws IOException { public static JarMap open(String file, boolean verify) throws IOException {
this(file, verify, ZipFile.OPEN_READ); return new FileMap(new File(file), verify, ZipFile.OPEN_READ);
} }
public JarMap(File file, boolean verify, int mode) throws IOException { public static JarMap open(InputStream is, boolean verify) throws IOException {
jarFile = new JarFile(file, verify, mode); return new StreamMap(is, verify);
manifest = jarFile.getManifest();
} }
public JarMap(String name) throws IOException { public File getFile() {
this(new File(name)); return null;
} }
public JarMap(String name, boolean verify) throws IOException { public abstract Manifest getManifest() throws IOException;
this(new File(name), verify);
public InputStream getInputStream(ZipEntry ze) throws IOException {
JarMapEntry e = getMapEntry(ze.getName());
return e != null ? e.data.getInputStream() : null;
} }
public JarMap(InputStream is) throws IOException { public OutputStream getOutputStream(ZipEntry ze) {
this(is, true); if (entryMap == null)
entryMap = new LinkedHashMap<>();
JarMapEntry e = new JarMapEntry(ze.getName());
entryMap.put(ze.getName(), e);
return e.data;
} }
public JarMap(InputStream is, boolean verify) throws IOException { public byte[] getRawData(ZipEntry ze) throws IOException {
jis = new JarInputStream(is, verify); JarMapEntry e = getMapEntry(ze.getName());
bufMap = new LinkedHashMap<>(); return e != null ? e.data.toByteArray() : null;
JarEntry entry;
while ((entry = jis.getNextJarEntry()) != null) {
bufMap.put(entry.getName(), new JarMapEntry(entry, jis));
} }
manifest = jis.getManifest();
public abstract Enumeration<JarEntry> entries();
public final ZipEntry getEntry(String name) {
return getJarEntry(name);
} }
public File getFile() { public JarEntry getJarEntry(String name) {
return jarFile == null ? null : new File(jarFile.getName()); return getMapEntry(name);
} }
public Manifest getManifest() { JarMapEntry getMapEntry(String name) {
return manifest; JarMapEntry e = null;
if (entryMap != null)
e = (JarMapEntry) entryMap.get(name);
return e;
} }
public InputStream getInputStream(ZipEntry ze) throws IOException { private static class FileMap extends JarMap {
if (bufMap != null) {
JarMapEntry e = (JarMapEntry) bufMap.get(ze.getName()); private JarFile jarFile;
if (e != null)
return e.data.getInputStream(); FileMap(File file, boolean verify, int mode) throws IOException {
jarFile = new JarFile(file, verify, mode);
} }
return jarFile.getInputStream(ze);
@Override
public File getFile() {
return new File(jarFile.getName());
} }
public OutputStream getOutputStream(ZipEntry ze) { @Override
manifest = null; /* Invalidate the manifest */ public Manifest getManifest() throws IOException {
if (bufMap == null) return jarFile.getManifest();
bufMap = new LinkedHashMap<>();
JarMapEntry e = new JarMapEntry(ze.getName());
bufMap.put(ze.getName(), e);
return e.data;
} }
public byte[] getRawData(ZipEntry ze) throws IOException { @Override
if (bufMap != null) { public InputStream getInputStream(ZipEntry ze) throws IOException {
JarMapEntry e = (JarMapEntry) bufMap.get(ze.getName()); InputStream is = super.getInputStream(ze);
if (e != null) return is != null ? is : jarFile.getInputStream(ze);
return e.data.toByteArray();
} }
@Override
public byte[] getRawData(ZipEntry ze) throws IOException {
byte[] b = super.getRawData(ze);
if (b != null)
return b;
ByteArrayStream bytes = new ByteArrayStream(); ByteArrayStream bytes = new ByteArrayStream();
bytes.readFrom(jarFile.getInputStream(ze)); bytes.readFrom(jarFile.getInputStream(ze));
return bytes.toByteArray(); return bytes.toByteArray();
} }
@Override
public Enumeration<JarEntry> entries() { public Enumeration<JarEntry> entries() {
return jarFile == null ? Collections.enumeration(bufMap.values()) : jarFile.entries(); return jarFile.entries();
}
public ZipEntry getEntry(String name) {
return getJarEntry(name);
} }
@Override
public JarEntry getJarEntry(String name) { public JarEntry getJarEntry(String name) {
JarEntry e = jarFile == null ? bufMap.get(name) : jarFile.getJarEntry(name); JarEntry e = getMapEntry(name);
if (e == null && bufMap != null) return e != null ? e : jarFile.getJarEntry(name);
return bufMap.get(name);
return e;
} }
@Override @Override
public void close() throws IOException { public void close() throws IOException {
if (jarFile != null)
jarFile.close(); jarFile.close();
else }
}
private static class StreamMap extends JarMap {
private JarInputStream jis;
StreamMap(InputStream is, boolean verify) throws IOException {
jis = new JarInputStream(is, verify);
entryMap = new LinkedHashMap<>();
JarEntry entry;
while ((entry = jis.getNextJarEntry()) != null) {
entryMap.put(entry.getName(), new JarMapEntry(entry, jis));
}
}
@Override
public Manifest getManifest() {
return jis.getManifest();
}
@Override
public Enumeration<JarEntry> entries() {
return Collections.enumeration(entryMap.values());
}
@Override
public void close() throws IOException {
jis.close(); jis.close();
} }
}
private static class JarMapEntry extends JarEntry { private static class JarMapEntry extends JarEntry {
ByteArrayStream data; ByteArrayStream data;
JarMapEntry(JarEntry je, InputStream is) { JarMapEntry(JarEntry je, InputStream is) {
......
...@@ -72,7 +72,7 @@ public class SignAPK { ...@@ -72,7 +72,7 @@ public class SignAPK {
ZipAdjust.adjust(temp1, temp2); ZipAdjust.adjust(temp1, temp2);
try (JarMap map = new JarMap(temp2, false)) { try (JarMap map = JarMap.open(temp2, false)) {
sign(cert, key, map, output, true); sign(cert, key, map, output, true);
} }
} finally { } finally {
......
...@@ -33,6 +33,12 @@ public class SignBoot { ...@@ -33,6 +33,12 @@ public class SignBoot {
private static final int BOOT_IMAGE_HEADER_V1_RECOVERY_DTBO_SIZE_OFFSET = 1632; private static final int BOOT_IMAGE_HEADER_V1_RECOVERY_DTBO_SIZE_OFFSET = 1632;
private static final int BOOT_IMAGE_HEADER_V2_DTB_SIZE_OFFSET = 1648; private static final int BOOT_IMAGE_HEADER_V2_DTB_SIZE_OFFSET = 1648;
// Arbitrary maximum header version value; when greater assume the field is dt/extra size
private static final int BOOT_IMAGE_HEADER_VERSION_MAXIMUM = 8;
// Maximum header size byte value to read (currently the bootimg minimum page size)
private static final int BOOT_IMAGE_HEADER_SIZE_MAXIMUM = 2048;
private static class PushBackRWStream extends FilterInputStream { private static class PushBackRWStream extends FilterInputStream {
private OutputStream out; private OutputStream out;
private int pos = 0; private int pos = 0;
...@@ -82,7 +88,7 @@ public class SignBoot { ...@@ -82,7 +88,7 @@ public class SignBoot {
InputStream cert, InputStream key) { InputStream cert, InputStream key) {
try { try {
PushBackRWStream in = new PushBackRWStream(imgIn, imgOut); PushBackRWStream in = new PushBackRWStream(imgIn, imgOut);
byte[] hdr = new byte[1024]; byte[] hdr = new byte[BOOT_IMAGE_HEADER_SIZE_MAXIMUM];
// First read the header // First read the header
in.read(hdr); in.read(hdr);
int signableSize = getSignableImageSize(hdr); int signableSize = getSignableImageSize(hdr);
...@@ -113,22 +119,27 @@ public class SignBoot { ...@@ -113,22 +119,27 @@ public class SignBoot {
public static boolean verifySignature(InputStream imgIn, InputStream certIn) { public static boolean verifySignature(InputStream imgIn, InputStream certIn) {
try { try {
// Read the header for size // Read the header for size
byte[] hdr = new byte[1024]; byte[] hdr = new byte[BOOT_IMAGE_HEADER_SIZE_MAXIMUM];
if (imgIn.read(hdr) != hdr.length) if (imgIn.read(hdr) != hdr.length) {
System.err.println("Unable to read image header");
return false; return false;
}
int signableSize = getSignableImageSize(hdr); int signableSize = getSignableImageSize(hdr);
// Read the rest of the image // Read the rest of the image
byte[] rawImg = Arrays.copyOf(hdr, signableSize); byte[] rawImg = Arrays.copyOf(hdr, signableSize);
int remain = signableSize - hdr.length; int remain = signableSize - hdr.length;
if (imgIn.read(rawImg, hdr.length, remain) != remain) { if (imgIn.read(rawImg, hdr.length, remain) != remain) {
System.err.println("Invalid image: not signed"); System.err.println("Unable to read image");
return false; return false;
} }
// Read footer, which contains the signature // Read footer, which contains the signature
byte[] signature = new byte[4096]; byte[] signature = new byte[4096];
imgIn.read(signature); if (imgIn.read(signature) == -1 || Arrays.equals(signature, new byte [signature.length])) {
System.err.println("Invalid image: not signed");
return false;
}
BootSignature bootsig = new BootSignature(signature); BootSignature bootsig = new BootSignature(signature);
if (certIn != null) { if (certIn != null) {
...@@ -141,7 +152,8 @@ public class SignBoot { ...@@ -141,7 +152,8 @@ public class SignBoot {
System.err.println("Signature is INVALID"); System.err.println("Signature is INVALID");
} }
} catch (Exception e) { } catch (Exception e) {
System.err.println("Invalid image: not signed"); e.printStackTrace();
return false;
} }
return false; return false;
} }
...@@ -165,8 +177,8 @@ public class SignBoot { ...@@ -165,8 +177,8 @@ public class SignBoot {
+ ((kernelSize + pageSize - 1) / pageSize) * pageSize + ((kernelSize + pageSize - 1) / pageSize) * pageSize
+ ((ramdskSize + pageSize - 1) / pageSize) * pageSize + ((ramdskSize + pageSize - 1) / pageSize) * pageSize
+ ((secondSize + pageSize - 1) / pageSize) * pageSize; + ((secondSize + pageSize - 1) / pageSize) * pageSize;
int headerVersion = image.getInt(); // boot image header version or extra size int headerVersion = image.getInt(); // boot image header version or dt/extra size
if (headerVersion > 0 && headerVersion < 4) { if (headerVersion > 0 && headerVersion < BOOT_IMAGE_HEADER_VERSION_MAXIMUM) {
image.position(BOOT_IMAGE_HEADER_V1_RECOVERY_DTBO_SIZE_OFFSET); image.position(BOOT_IMAGE_HEADER_V1_RECOVERY_DTBO_SIZE_OFFSET);
int recoveryDtboLength = image.getInt(); int recoveryDtboLength = image.getInt();
length += ((recoveryDtboLength + pageSize - 1) / pageSize) * pageSize; length += ((recoveryDtboLength + pageSize - 1) / pageSize) * pageSize;
...@@ -183,7 +195,7 @@ public class SignBoot { ...@@ -183,7 +195,7 @@ public class SignBoot {
"Invalid image header: invalid header length"); "Invalid image header: invalid header length");
} }
} else { } else {
// headerVersion is 0 or actually extra size in this case // headerVersion is 0 or actually dt/extra size in this case
length += ((headerVersion + pageSize - 1) / pageSize) * pageSize; length += ((headerVersion + pageSize - 1) / pageSize) * pageSize;
} }
length = ((length + pageSize - 1) / pageSize) * pageSize; length = ((length + pageSize - 1) / pageSize) * pageSize;
......
...@@ -65,7 +65,7 @@ public class ZipSigner { ...@@ -65,7 +65,7 @@ public class ZipSigner {
Security.insertProviderAt(new BouncyCastleProvider(), 1); Security.insertProviderAt(new BouncyCastleProvider(), 1);
try (JarMap in = new JarMap(args[args.length - 2], false); try (JarMap in = JarMap.open(args[args.length - 2], false);
OutputStream out = new FileOutputStream(args[args.length - 1])) { OutputStream out = new FileOutputStream(args[args.length - 1])) {
if (args.length == 2) { if (args.length == 2) {
sign(in, out); sign(in, out);
......
...@@ -4,7 +4,7 @@ android { ...@@ -4,7 +4,7 @@ android {
defaultConfig { defaultConfig {
applicationId 'com.topjohnwu.magisk' applicationId 'com.topjohnwu.magisk'
versionCode 1 versionCode 1
versionName "1.0" versionName props['appVersion']
buildConfigField 'String', 'DEV_CHANNEL', props['DEV_CHANNEL'] ?: 'null' buildConfigField 'String', 'DEV_CHANNEL', props['DEV_CHANNEL'] ?: 'null'
} }
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!--
Try to recreate the main app's AndroidManifest.xml
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="com.topjohnwu.magisk"> package="com.topjohnwu.magisk">
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.VIBRATE" /> <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.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" /> <uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application <application
android:name="a.y" android:name="a.e"
android:appComponentFactory="a.x" android:appComponentFactory="a.a"
android:allowBackup="true" android:allowBackup="true"
android:usesCleartextTraffic="true"
tools:ignore="UnusedAttribute,GoogleAppIndexingWarning" > tools:ignore="UnusedAttribute,GoogleAppIndexingWarning" >
<!-- Download Activity --> <!-- Download Activity -->
<activity <activity
android:name="a.z" android:name="a.c"
android:configChanges="orientation|screenSize"
android:theme="@style/SplashTheme"> android:theme="@style/SplashTheme">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
...@@ -36,29 +29,28 @@ ...@@ -36,29 +29,28 @@
<!-- Magisk Manager Components --> <!-- Magisk Manager Components -->
<activity <activity android:name="a.b" />
android:name="a.o"
android:configChanges="orientation|screenSize"
android:exported="true" />
<activity <activity android:name="a.f" />
android:name="a.x"
android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="nosensor" />
<activity <activity
android:name="a.g" android:name="a.m"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:directBootAware="true" android:directBootAware="true"
android:excludeFromRecents="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.w" android:name="a.h"
android:directBootAware="true"> android:directBootAware="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.REBOOT" /> <action android:name="android.intent.action.REBOOT" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.LOCALE_CHANGED" /> <action android:name="android.intent.action.LOCALE_CHANGED" />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
...@@ -69,9 +61,7 @@ ...@@ -69,9 +61,7 @@
</intent-filter> </intent-filter>
</receiver> </receiver>
<service <service android:name="a.j" />
android:name="a.v"
android:exported="false" />
<meta-data <meta-data
android:name="com.google.android.gms.version" android:name="com.google.android.gms.version"
...@@ -80,7 +70,7 @@ ...@@ -80,7 +70,7 @@
<!-- WorkManager SystemJobService --> <!-- WorkManager SystemJobService -->
<service <service
android:name="a.j" android:name="a.x"
android:directBootAware="false" android:directBootAware="false"
android:enabled="true" android:enabled="true"
android:exported="true" android:exported="true"
......
...@@ -2,5 +2,5 @@ package a; ...@@ -2,5 +2,5 @@ package a;
import com.topjohnwu.magisk.DelegateComponentFactory; import com.topjohnwu.magisk.DelegateComponentFactory;
public class x extends DelegateComponentFactory { public class a extends DelegateComponentFactory {
} }
...@@ -2,5 +2,5 @@ package a; ...@@ -2,5 +2,5 @@ package a;
import com.topjohnwu.magisk.DownloadActivity; import com.topjohnwu.magisk.DownloadActivity;
public class z extends DownloadActivity { public class c extends DownloadActivity {
} }
...@@ -2,5 +2,5 @@ package a; ...@@ -2,5 +2,5 @@ package a;
import com.topjohnwu.magisk.DelegateApplication; import com.topjohnwu.magisk.DelegateApplication;
public class y extends DelegateApplication { public class e extends DelegateApplication {
} }
...@@ -2,5 +2,5 @@ package a; ...@@ -2,5 +2,5 @@ package a;
import com.topjohnwu.magisk.dummy.DummyReceiver; import com.topjohnwu.magisk.dummy.DummyReceiver;
public class w extends DummyReceiver { public class h extends DummyReceiver {
} }
...@@ -19,12 +19,12 @@ public class DelegateApplication extends Application { ...@@ -19,12 +19,12 @@ public class DelegateApplication extends Application {
static File MANAGER_APK; static File MANAGER_APK;
private Object factory; private DelegateComponentFactory factory;
private Application delegate; private Application delegate;
public DelegateApplication() {} public DelegateApplication() {}
public DelegateApplication(Object o) { public DelegateApplication(DelegateComponentFactory o) {
factory = o; factory = o;
} }
...@@ -46,7 +46,6 @@ public class DelegateApplication extends Application { ...@@ -46,7 +46,6 @@ public class DelegateApplication extends Application {
@SuppressLint("NewApi") @SuppressLint("NewApi")
private void setUpDynAPK() { private void setUpDynAPK() {
DelegateComponentFactory factory = (DelegateComponentFactory) this.factory;
MANAGER_APK = DynAPK.current(this); MANAGER_APK = DynAPK.current(this);
File update = DynAPK.update(this); File update = DynAPK.update(this);
if (update.exists()) if (update.exists())
...@@ -55,11 +54,12 @@ public class DelegateApplication extends Application { ...@@ -55,11 +54,12 @@ public class DelegateApplication extends Application {
ClassLoader cl = new DynamicClassLoader(MANAGER_APK, factory.loader); ClassLoader cl = new DynamicClassLoader(MANAGER_APK, factory.loader);
try { try {
// Create the delegate AppComponentFactory // Create the delegate AppComponentFactory
Object df = cl.loadClass("a.a").newInstance(); AppComponentFactory df = (AppComponentFactory)
cl.loadClass("androidx.core.app.CoreComponentFactory").newInstance();
// Create the delegate Application // Create the delegate Application
delegate = (Application) cl.loadClass("a.e").getConstructor(Object.class) delegate = (Application) cl.loadClass("a.e").getConstructor(Object.class)
.newInstance(DynAPK.pack(Mapping.data)); .newInstance(DynAPK.pack(Mapping.data()));
// Call attachBaseContext without ContextImpl to show it is being wrapped // Call attachBaseContext without ContextImpl to show it is being wrapped
Method m = ContextWrapper.class.getDeclaredMethod("attachBaseContext", Context.class); Method m = ContextWrapper.class.getDeclaredMethod("attachBaseContext", Context.class);
...@@ -67,7 +67,7 @@ public class DelegateApplication extends Application { ...@@ -67,7 +67,7 @@ public class DelegateApplication extends Application {
m.invoke(delegate, this); m.invoke(delegate, this);
// If everything went well, set our loader and delegate // If everything went well, set our loader and delegate
factory.delegate = (AppComponentFactory) df; factory.delegate = df;
factory.loader = cl; factory.loader = cl;
} catch (Exception e) { } catch (Exception e) {
Log.e(getClass().getSimpleName(), "", e); Log.e(getClass().getSimpleName(), "", e);
......
...@@ -4,6 +4,8 @@ import android.app.Activity; ...@@ -4,6 +4,8 @@ import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Application; import android.app.Application;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
...@@ -11,6 +13,7 @@ import android.util.Log; ...@@ -11,6 +13,7 @@ import android.util.Log;
import com.topjohnwu.magisk.net.ErrorHandler; import com.topjohnwu.magisk.net.ErrorHandler;
import com.topjohnwu.magisk.net.Networking; import com.topjohnwu.magisk.net.Networking;
import com.topjohnwu.magisk.net.ResponseListener; import com.topjohnwu.magisk.net.ResponseListener;
import com.topjohnwu.magisk.obfuscate.Mapping;
import com.topjohnwu.magisk.obfuscate.RawData; import com.topjohnwu.magisk.obfuscate.RawData;
import com.topjohnwu.magisk.utils.APKInstall; import com.topjohnwu.magisk.utils.APKInstall;
...@@ -24,9 +27,10 @@ import static com.topjohnwu.magisk.DelegateApplication.MANAGER_APK; ...@@ -24,9 +27,10 @@ import static com.topjohnwu.magisk.DelegateApplication.MANAGER_APK;
public class DownloadActivity extends Activity { public class DownloadActivity extends Activity {
private static final boolean CANARY = BuildConfig.VERSION_NAME.contains("-");
private static final String URL = private static final String URL =
BuildConfig.DEV_CHANNEL != null ? BuildConfig.DEV_CHANNEL : BuildConfig.DEV_CHANNEL != null ? BuildConfig.DEV_CHANNEL : RawData.urlBase() +
RawData.urlBase() + (BuildConfig.DEBUG ? RawData.canary() : RawData.stable()); (BuildConfig.DEBUG ? RawData.debug() : (CANARY ? RawData.canary() : RawData.stable()));
private String apkLink; private String apkLink;
private ErrorHandler err = (conn, e) -> { private ErrorHandler err = (conn, e) -> {
...@@ -47,7 +51,11 @@ public class DownloadActivity extends Activity { ...@@ -47,7 +51,11 @@ public class DownloadActivity extends Activity {
// Download and relaunch the app // Download and relaunch the app
Networking.get(apkLink) Networking.get(apkLink)
.setErrorHandler(err) .setErrorHandler(err)
.getAsFile(MANAGER_APK, f -> ProcessPhoenix.triggerRebirth(this)); .getAsFile(MANAGER_APK, apk -> {
Intent intent = new Intent()
.setComponent(new ComponentName(this, Mapping.inverse("a.r")));
ProcessPhoenix.triggerRebirth(this, intent);
});
} else { } else {
// Download and upgrade the app // Download and upgrade the app
Application app = getApplication(); Application app = getApplication();
...@@ -71,7 +79,7 @@ public class DownloadActivity extends Activity { ...@@ -71,7 +79,7 @@ public class DownloadActivity extends Activity {
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setCancelable(false) .setCancelable(false)
.setTitle(RawData.appName()) .setTitle(RawData.appName())
.setMessage(RawData.no_internet_msg()) .setMessage(RawData.networkError())
.setNegativeButton(ok, (d, w) -> finish()) .setNegativeButton(ok, (d, w) -> finish())
.show(); .show();
} }
...@@ -87,7 +95,7 @@ public class DownloadActivity extends Activity { ...@@ -87,7 +95,7 @@ public class DownloadActivity extends Activity {
new AlertDialog.Builder(DownloadActivity.this) new AlertDialog.Builder(DownloadActivity.this)
.setCancelable(false) .setCancelable(false)
.setTitle(RawData.appName()) .setTitle(RawData.appName())
.setMessage(RawData.upgrade_msg()) .setMessage(RawData.upgradeMsg())
.setPositiveButton(yes, (d, w) -> dlAPK()) .setPositiveButton(yes, (d, w) -> dlAPK())
.setNegativeButton(no, (d, w) -> finish()) .setNegativeButton(no, (d, w) -> finish())
.show(); .show();
......
...@@ -3,35 +3,18 @@ package com.topjohnwu.magisk.obfuscate; ...@@ -3,35 +3,18 @@ package com.topjohnwu.magisk.obfuscate;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static com.topjohnwu.magisk.DynAPK.*; import static com.topjohnwu.magisk.DynAPK.Data;
import static com.topjohnwu.magisk.R.drawable.*;
public class Mapping { public class Mapping {
private static Map<String, String> map = new HashMap<>(); private static Map<String, String> map = new HashMap<>();
private static Map<String, String> inverseMap;
// This mapping will be sent into the guest app
public static Data data = new Data();
static { static {
map.put(a.z.class.getName(), "a.c"); map.put("a.x", "androidx.work.impl.background.systemjob.SystemJobService");
map.put("a.x", "a.f"); inverseMap = new HashMap<>(map.size());
map.put("a.o", "a.b");
map.put("a.g", "a.m");
map.put(a.w.class.getName(), "a.h");
map.put("a.v", "a.j");
map.put("a.j", "androidx.work.impl.background.systemjob.SystemJobService");
data.componentMap = new HashMap<>(map.size());
for (Map.Entry<String, String> e : map.entrySet()) { for (Map.Entry<String, String> e : map.entrySet()) {
data.componentMap.put(e.getValue(), e.getKey()); inverseMap.put(e.getValue(), e.getKey());
} }
int[] res = new int[5];
res[NOTIFICATION] = ic_magisk_outline;
res[SUPERUSER] = sc_superuser;
res[MAGISKHIDE] = sc_magiskhide;
res[DOWNLOAD] = sc_cloud_download;
res[MODULES] = sc_extension;
data.resourceMap = res;
} }
public static String get(String name) { public static String get(String name) {
...@@ -39,4 +22,14 @@ public class Mapping { ...@@ -39,4 +22,14 @@ public class Mapping {
return n != null ? n : name; return n != null ? n : name;
} }
public static String inverse(String name) {
String n = inverseMap.get(name);
return n != null ? n : name;
}
public static Data data() {
Data data = new Data();
data.classToComponent = inverseMap;
return data;
}
} }
...@@ -16,19 +16,23 @@ public class RawData { ...@@ -16,19 +16,23 @@ public class RawData {
return "https://raw.githubusercontent.com/topjohnwu/magisk_files/"; return "https://raw.githubusercontent.com/topjohnwu/magisk_files/";
} }
public static String canary() { public static String debug() {
return "canary/debug.json"; return "canary/debug.json";
} }
public static String canary() {
return "canary/release.json";
}
public static String stable() { public static String stable() {
return "master/stable.json"; return "master/stable.json";
} }
public static String no_internet_msg() { public static String networkError() {
return res.getString(R.string.no_internet_msg); return res.getString(R.string.no_internet_msg);
} }
public static String upgrade_msg() { public static String upgradeMsg() {
return res.getString(R.string.upgrade_msg); return res.getString(R.string.upgrade_msg);
} }
......
<?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="upgrade_msg">Pre dokončenie inštalácie sa vyžaduje upgrade Magisk Managera. Stiahnuť a nainštalovať?</string> <string name="upgrade_msg">Pre dokončenie inštalácie sa vyžaduje upgrade Magisk Managera. Stiahnuť a nainštalovať?</string>
<string name="no_internet_msg">Pripojte sa na internet! Upgrade Magisk Managera je potrebný.</string> <string name="no_internet_msg">Pripojte sa na internet! Upgrade Magisk Managera je potrebný.</string>
<string name="dling">Sťahuje sa</string>
</resources> </resources>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment