diff --git a/app/src/main/java/com/topjohnwu/magisk/core/Config.kt b/app/src/main/java/com/topjohnwu/magisk/core/Config.kt index b9d57bbda..a6effec65 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/Config.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/Config.kt @@ -156,7 +156,12 @@ object Config : PreferenceConfig, DBConfig { var rootMode by dbSettings(Key.ROOT_ACCESS, Value.ROOT_ACCESS_APPS_AND_ADB) var suMntNamespaceMode by dbSettings(Key.SU_MNT_NS, Value.NAMESPACE_MODE_REQUESTER) var suMultiuserMode by dbSettings(Key.SU_MULTIUSER_MODE, Value.MULTIUSER_MODE_OWNER_ONLY) - var suBiometric by dbSettings(Key.SU_BIOMETRIC, false) + private var suBiometric by dbSettings(Key.SU_BIOMETRIC, false) + var userAuth + get() = Info.isDeviceSecure && suBiometric + set(value) { + suBiometric = value + } var zygisk by dbSettings(Key.ZYGISK, false) var denyList by BoolDBPropertyNoWrite(Key.DENYLIST, false) var suManager by dbStrings(Key.SU_MANAGER, "", true) diff --git a/app/src/main/java/com/topjohnwu/magisk/core/Info.kt b/app/src/main/java/com/topjohnwu/magisk/core/Info.kt index 118c7c062..6435b4f72 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/Info.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/Info.kt @@ -1,7 +1,9 @@ package com.topjohnwu.magisk.core +import android.app.KeyguardManager import androidx.lifecycle.MutableLiveData import com.topjohnwu.magisk.StubApk +import com.topjohnwu.magisk.core.di.AppContext import com.topjohnwu.magisk.core.ktx.getProperty import com.topjohnwu.magisk.core.model.UpdateInfo import com.topjohnwu.magisk.core.repository.NetworkService @@ -46,6 +48,9 @@ object Info { || Config.suMultiuserMode == Config.Value.MULTIUSER_MODE_USER) } + val isDeviceSecure get() = + AppContext.getSystemService(KeyguardManager::class.java).isDeviceSecure + private fun loadState(): Env { val v = fastCmd("magisk -v").split(":".toRegex()) return Env( diff --git a/app/src/main/java/com/topjohnwu/magisk/core/base/BaseActivity.kt b/app/src/main/java/com/topjohnwu/magisk/core/base/BaseActivity.kt index 9435b5ea0..70b5a53a0 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/base/BaseActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/base/BaseActivity.kt @@ -20,6 +20,7 @@ import com.topjohnwu.magisk.R import com.topjohnwu.magisk.core.isRunningAsStub import com.topjohnwu.magisk.core.ktx.reflectField import com.topjohnwu.magisk.core.ktx.toast +import com.topjohnwu.magisk.core.utils.RequestAuthentication import com.topjohnwu.magisk.core.utils.RequestInstall import com.topjohnwu.magisk.core.wrap @@ -43,6 +44,12 @@ abstract class BaseActivity : AppCompatActivity() { installCallback = null } + var authenticateCallback: ((Boolean) -> Unit)? = null + val requestAuthenticate = registerForActivityResult(RequestAuthentication()) { + authenticateCallback?.invoke(it) + authenticateCallback = null + } + private var contentCallback: ContentResultCallback? = null private val getContent = registerForActivityResult(GetContent()) { if (it != null) contentCallback?.onActivityResult(it) diff --git a/app/src/main/java/com/topjohnwu/magisk/core/di/ServiceLocator.kt b/app/src/main/java/com/topjohnwu/magisk/core/di/ServiceLocator.kt index 4bb66ff26..9ed16bcdc 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/di/ServiceLocator.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/di/ServiceLocator.kt @@ -12,7 +12,6 @@ import com.topjohnwu.magisk.core.data.magiskdb.StringDao import com.topjohnwu.magisk.core.ktx.deviceProtectedContext import com.topjohnwu.magisk.core.repository.LogRepository import com.topjohnwu.magisk.core.repository.NetworkService -import com.topjohnwu.magisk.core.utils.BiometricHelper import io.noties.markwon.Markwon import io.noties.markwon.utils.NoCopySpannableFactory @@ -24,7 +23,6 @@ object ServiceLocator { lateinit var context: Context val deContext by lazy { context.deviceProtectedContext } val timeoutPrefs by lazy { deContext.getSharedPreferences("su_timeout", 0) } - val biometrics by lazy { BiometricHelper(context) } // Database val policyDB = PolicyDao() diff --git a/app/src/main/java/com/topjohnwu/magisk/core/utils/BiometricHelper.kt b/app/src/main/java/com/topjohnwu/magisk/core/utils/BiometricHelper.kt deleted file mode 100644 index 9c73840f2..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/core/utils/BiometricHelper.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.topjohnwu.magisk.core.utils - -import android.app.Activity -import android.app.KeyguardManager -import android.content.Context -import android.content.Intent -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentActivity -import com.topjohnwu.magisk.core.Config - -class BiometricHelper(context: Context) { - - private val mgr = context.getSystemService(KeyguardManager::class.java) - - val isSupported get() = mgr.isDeviceSecure - - val isEnabled get() = isSupported && Config.suBiometric - - fun authenticate( - activity: FragmentActivity, - onError: () -> Unit = {}, - onSuccess: () -> Unit) { - val tag = BiometricFragment::class.java.name - val intent = mgr.createConfirmDeviceCredentialIntent(null, null) - val fragmentManager = activity.supportFragmentManager - var fragment = fragmentManager.findFragmentByTag(tag) as BiometricFragment? - if (fragment == null) { - fragment = BiometricFragment() - fragmentManager.beginTransaction() - .add(0, fragment, tag) - .commitNow() - } - fragment.start(intent, onError, onSuccess) - } - - class BiometricFragment : Fragment() { - private val code = 1 - private var onError: () -> Unit = {} - private var onSuccess: () -> Unit = {} - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == code) { - if (resultCode == Activity.RESULT_OK) { - onSuccess() - } else { - onError() - } - onError = {} - onSuccess = {} - } - } - - fun start(intent: Intent, onError: () -> Unit, onSuccess: () -> Unit) { - this.onError = onError - this.onSuccess = onSuccess - intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK or Intent.FLAG_ACTIVITY_NEW_DOCUMENT) - startActivityForResult(intent, code) - } - } -} diff --git a/app/src/main/java/com/topjohnwu/magisk/core/utils/RequestAuthentication.kt b/app/src/main/java/com/topjohnwu/magisk/core/utils/RequestAuthentication.kt new file mode 100644 index 000000000..d159385fa --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/core/utils/RequestAuthentication.kt @@ -0,0 +1,17 @@ +package com.topjohnwu.magisk.core.utils + +import android.app.Activity +import android.app.KeyguardManager +import android.content.Context +import android.content.Intent +import androidx.activity.result.contract.ActivityResultContract + +class RequestAuthentication: ActivityResultContract() { + + override fun createIntent(context: Context, input: Unit) = + context.getSystemService(KeyguardManager::class.java) + .createConfirmDeviceCredentialIntent(null, null) + + override fun parseResult(resultCode: Int, intent: Intent?) = + resultCode == Activity.RESULT_OK +} diff --git a/app/src/main/java/com/topjohnwu/magisk/events/BiometricEvent.kt b/app/src/main/java/com/topjohnwu/magisk/events/BiometricEvent.kt deleted file mode 100644 index 1f848caa9..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/events/BiometricEvent.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.topjohnwu.magisk.events - -import com.topjohnwu.magisk.arch.ActivityExecutor -import com.topjohnwu.magisk.arch.UIActivity -import com.topjohnwu.magisk.arch.ViewEvent -import com.topjohnwu.magisk.core.di.ServiceLocator - -class BiometricEvent( - builder: Builder.() -> Unit -) : ViewEvent(), ActivityExecutor { - - private var listenerOnFailure: () -> Unit = {} - private var listenerOnSuccess: () -> Unit = {} - - init { - builder(Builder()) - } - - override fun invoke(activity: UIActivity<*>) { - ServiceLocator.biometrics.authenticate( - activity, - onError = listenerOnFailure, - onSuccess = listenerOnSuccess - ) - } - - inner class Builder internal constructor() { - - fun onFailure(listener: () -> Unit) { - listenerOnFailure = listener - } - - fun onSuccess(listener: () -> Unit) { - listenerOnSuccess = listener - } - } - -} diff --git a/app/src/main/java/com/topjohnwu/magisk/events/ViewEvents.kt b/app/src/main/java/com/topjohnwu/magisk/events/ViewEvents.kt index 417cda157..6d1274d42 100644 --- a/app/src/main/java/com/topjohnwu/magisk/events/ViewEvents.kt +++ b/app/src/main/java/com/topjohnwu/magisk/events/ViewEvents.kt @@ -51,6 +51,16 @@ class RecreateEvent : ViewEvent(), ActivityExecutor { } } +class BiometricEvent( + private val callback: () -> Unit +) : ViewEvent(), ActivityExecutor { + + override fun invoke(activity: UIActivity<*>) { + activity.authenticateCallback = { if (it) callback() } + activity.requestAuthenticate.launch(Unit) + } +} + class GetContentEvent( private val type: String, private val callback: ContentResultCallback diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsItems.kt b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsItems.kt index 06cc6f7a6..827555507 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsItems.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsItems.kt @@ -12,7 +12,6 @@ import com.topjohnwu.magisk.R import com.topjohnwu.magisk.core.Config import com.topjohnwu.magisk.core.Const import com.topjohnwu.magisk.core.Info -import com.topjohnwu.magisk.core.di.ServiceLocator import com.topjohnwu.magisk.core.ktx.activity import com.topjohnwu.magisk.core.tasks.HideAPK import com.topjohnwu.magisk.core.utils.MediaStoreUtils @@ -286,14 +285,10 @@ object Tapjack : BaseSettingsItem.Toggle() { object Biometrics : BaseSettingsItem.Toggle() { override val title = R.string.settings_su_biometric_title.asText() override var description = R.string.settings_su_biometric_summary.asText() - override var value - get() = ServiceLocator.biometrics.isEnabled - set(value) { - Config.suBiometric = value - } + override var value by Config::userAuth override fun refresh() { - isEnabled = ServiceLocator.biometrics.isSupported + isEnabled = Info.isDeviceSecure if (!isEnabled) { description = R.string.no_biometric.asText() } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsViewModel.kt index 0f9434e48..8f8898bb2 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsViewModel.kt @@ -92,7 +92,7 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler { when (item) { DownloadPath -> withExternalRW(andThen) UpdateChecker -> withPostNotificationPermission(andThen) - Biometrics -> authenticate(andThen) + Biometrics -> BiometricEvent(andThen).publish() Theme -> SettingsFragmentDirections.actionSettingsFragmentToThemeFragment().navigate() DenyListConfig -> SettingsFragmentDirections.actionSettingsFragmentToDenyFragment().navigate() SystemlessHosts -> createHosts() @@ -119,13 +119,6 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler { } } - private fun authenticate(callback: () -> Unit) { - BiometricEvent { - // allow the change on success - onSuccess { callback() } - }.publish() - } - private fun createHosts() { Shell.cmd("add_hosts_module").submit { AppContext.toast(R.string.settings_hosts_toast, Toast.LENGTH_SHORT) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt index dff860a21..41a1310cf 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt @@ -10,14 +10,18 @@ import androidx.lifecycle.viewModelScope import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.R import com.topjohnwu.magisk.arch.AsyncLoadViewModel +import com.topjohnwu.magisk.core.Config import com.topjohnwu.magisk.core.Info import com.topjohnwu.magisk.core.data.magiskdb.PolicyDao import com.topjohnwu.magisk.core.di.AppContext -import com.topjohnwu.magisk.core.di.ServiceLocator import com.topjohnwu.magisk.core.ktx.getLabel import com.topjohnwu.magisk.core.model.su.SuPolicy import com.topjohnwu.magisk.core.utils.currentLocale -import com.topjohnwu.magisk.databinding.* +import com.topjohnwu.magisk.databinding.MergeObservableList +import com.topjohnwu.magisk.databinding.RvItem +import com.topjohnwu.magisk.databinding.bindExtra +import com.topjohnwu.magisk.databinding.diffList +import com.topjohnwu.magisk.databinding.set import com.topjohnwu.magisk.dialog.SuperuserRevokeDialog import com.topjohnwu.magisk.events.BiometricEvent import com.topjohnwu.magisk.events.SnackbarEvent @@ -113,10 +117,8 @@ class SuperuserViewModel( } } - if (ServiceLocator.biometrics.isEnabled) { - BiometricEvent { - onSuccess { updateState() } - }.publish() + if (Config.userAuth) { + BiometricEvent { updateState() }.publish() } else { SuperuserRevokeDialog(item.title) { updateState() }.show() } @@ -168,10 +170,8 @@ class SuperuserViewModel( } } - if (ServiceLocator.biometrics.isEnabled) { - BiometricEvent { - onSuccess { updateState() } - }.publish() + if (Config.userAuth) { + BiometricEvent { updateState() }.publish() } else { updateState() } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt index aa5669834..3b8c4b39e 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt @@ -22,7 +22,6 @@ import com.topjohnwu.magisk.arch.BaseViewModel import com.topjohnwu.magisk.core.Config import com.topjohnwu.magisk.core.data.magiskdb.PolicyDao import com.topjohnwu.magisk.core.di.AppContext -import com.topjohnwu.magisk.core.di.ServiceLocator import com.topjohnwu.magisk.core.ktx.getLabel import com.topjohnwu.magisk.core.ktx.toast import com.topjohnwu.magisk.core.model.su.SuPolicy.Companion.ALLOW @@ -77,12 +76,8 @@ class SuRequestViewModel( fun grantPressed() { cancelTimer() - if (ServiceLocator.biometrics.isEnabled) { - BiometricEvent { - onSuccess { - respond(ALLOW) - } - }.publish() + if (Config.userAuth) { + BiometricEvent { respond(ALLOW) }.publish() } else { respond(ALLOW) }