mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-01-12 01:03:38 +00:00
Tapjacking protection
This commit is contained in:
parent
8ac1181e9a
commit
533cb8eb58
@ -70,6 +70,10 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
|
|||||||
ensureInsets()
|
ensureInsets()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setAccessibilityDelegate(delegate: View.AccessibilityDelegate?) {
|
||||||
|
viewRoot.rootView.accessibilityDelegate = delegate
|
||||||
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
viewModel.requestRefresh()
|
viewModel.requestRefresh()
|
||||||
@ -79,7 +83,7 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
|
|||||||
return currentFragment?.onKeyEvent(event) == true || super.dispatchKeyEvent(event)
|
return currentFragment?.onKeyEvent(event) == true || super.dispatchKeyEvent(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onEventDispatched(event: ViewEvent) = when(event) {
|
override fun onEventDispatched(event: ViewEvent) = when (event) {
|
||||||
is ContextExecutor -> event(this)
|
is ContextExecutor -> event(this)
|
||||||
is ActivityExecutor -> event(this)
|
is ActivityExecutor -> event(this)
|
||||||
else -> Unit
|
else -> Unit
|
||||||
|
@ -50,6 +50,7 @@ object Config : PreferenceModel, DBConfig {
|
|||||||
const val SU_AUTO_RESPONSE = "su_auto_response"
|
const val SU_AUTO_RESPONSE = "su_auto_response"
|
||||||
const val SU_NOTIFICATION = "su_notification"
|
const val SU_NOTIFICATION = "su_notification"
|
||||||
const val SU_REAUTH = "su_reauth"
|
const val SU_REAUTH = "su_reauth"
|
||||||
|
const val SU_TAPJACK = "su_tapjack"
|
||||||
const val CHECK_UPDATES = "check_update"
|
const val CHECK_UPDATES = "check_update"
|
||||||
const val UPDATE_CHANNEL = "update_channel"
|
const val UPDATE_CHANNEL = "update_channel"
|
||||||
const val CUSTOM_CHANNEL = "custom_channel"
|
const val CUSTOM_CHANNEL = "custom_channel"
|
||||||
@ -134,6 +135,7 @@ object Config : PreferenceModel, DBConfig {
|
|||||||
var darkTheme by preference(Key.DARK_THEME, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
var darkTheme by preference(Key.DARK_THEME, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||||
var themeOrdinal by preference(Key.THEME_ORDINAL, Theme.Piplup.ordinal)
|
var themeOrdinal by preference(Key.THEME_ORDINAL, Theme.Piplup.ordinal)
|
||||||
var suReAuth by preference(Key.SU_REAUTH, false)
|
var suReAuth by preference(Key.SU_REAUTH, false)
|
||||||
|
var suTapjack by preference(Key.SU_TAPJACK, true)
|
||||||
var checkUpdate by preference(Key.CHECK_UPDATES, true)
|
var checkUpdate by preference(Key.CHECK_UPDATES, true)
|
||||||
var doh by preference(Key.DOH, false)
|
var doh by preference(Key.DOH, false)
|
||||||
var magiskHide by preference(Key.MAGISKHIDE, true)
|
var magiskHide by preference(Key.MAGISKHIDE, true)
|
||||||
|
@ -4,6 +4,7 @@ import android.app.Activity
|
|||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.navigation.NavDirections
|
import androidx.navigation.NavDirections
|
||||||
import com.topjohnwu.magisk.MainDirections
|
import com.topjohnwu.magisk.MainDirections
|
||||||
@ -58,9 +59,11 @@ class DieEvent : ViewEvent(), ActivityExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ShowUIEvent : ViewEvent(), ActivityExecutor {
|
class ShowUIEvent(private val delegate: View.AccessibilityDelegate?)
|
||||||
|
: ViewEvent(), ActivityExecutor {
|
||||||
override fun invoke(activity: BaseUIActivity<*, *>) {
|
override fun invoke(activity: BaseUIActivity<*, *>) {
|
||||||
activity.setContentView()
|
activity.setContentView()
|
||||||
|
activity.setAccessibilityDelegate(delegate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,6 +195,13 @@ object SystemlessHosts : BaseSettingsItem.Blank() {
|
|||||||
override val description = R.string.settings_hosts_summary.asTransitive()
|
override val description = R.string.settings_hosts_summary.asTransitive()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object Tapjack : BaseSettingsItem.Toggle() {
|
||||||
|
override val title = R.string.settings_su_tapjack_title.asTransitive()
|
||||||
|
override var description = R.string.settings_su_tapjack_summary.asTransitive()
|
||||||
|
override var value = Config.suTapjack
|
||||||
|
set(value) = setV(value, field, { field = it }) { Config.suTapjack = it }
|
||||||
|
}
|
||||||
|
|
||||||
object Biometrics : BaseSettingsItem.Toggle() {
|
object Biometrics : BaseSettingsItem.Toggle() {
|
||||||
override val title = R.string.settings_su_biometric_title.asTransitive()
|
override val title = R.string.settings_su_biometric_title.asTransitive()
|
||||||
override var value = Config.suBiometric
|
override var value = Config.suBiometric
|
||||||
|
@ -79,7 +79,7 @@ class SettingsViewModel(
|
|||||||
if (Utils.showSuperUser()) {
|
if (Utils.showSuperUser()) {
|
||||||
list.addAll(listOf(
|
list.addAll(listOf(
|
||||||
Superuser,
|
Superuser,
|
||||||
Biometrics, AccessMode, MultiuserMode, MountNamespaceMode,
|
Tapjack, Biometrics, AccessMode, MultiuserMode, MountNamespaceMode,
|
||||||
AutomaticResponse, RequestTimeout, SUNotification
|
AutomaticResponse, RequestTimeout, SUNotification
|
||||||
))
|
))
|
||||||
if (Build.VERSION.SDK_INT < 23) {
|
if (Build.VERSION.SDK_INT < 23) {
|
||||||
|
@ -28,8 +28,8 @@ open class SuRequestActivity : BaseUIActivity<SuRequestViewModel, ActivityReques
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
supportRequestWindowFeature(Window.FEATURE_NO_TITLE)
|
supportRequestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||||
lockOrientation()
|
lockOrientation()
|
||||||
window.setFlags(WindowManager.LayoutParams.FLAG_SECURE,
|
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
WindowManager.LayoutParams.FLAG_SECURE)
|
window.addFlags(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
fun showRequest() {
|
fun showRequest() {
|
||||||
|
@ -1,11 +1,20 @@
|
|||||||
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.os.Bundle
|
||||||
import android.os.CountDownTimer
|
import android.os.CountDownTimer
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.accessibility.AccessibilityEvent
|
||||||
|
import android.view.accessibility.AccessibilityNodeInfo
|
||||||
|
import android.view.accessibility.AccessibilityNodeProvider
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.databinding.Bindable
|
import androidx.databinding.Bindable
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.topjohnwu.magisk.BR
|
import com.topjohnwu.magisk.BR
|
||||||
@ -22,6 +31,7 @@ import com.topjohnwu.magisk.events.DieEvent
|
|||||||
import com.topjohnwu.magisk.events.ShowUIEvent
|
import com.topjohnwu.magisk.events.ShowUIEvent
|
||||||
import com.topjohnwu.magisk.events.dialog.BiometricEvent
|
import com.topjohnwu.magisk.events.dialog.BiometricEvent
|
||||||
import com.topjohnwu.magisk.ui.superuser.SpinnerRvItem
|
import com.topjohnwu.magisk.ui.superuser.SpinnerRvItem
|
||||||
|
import com.topjohnwu.magisk.utils.Utils
|
||||||
import com.topjohnwu.magisk.utils.set
|
import com.topjohnwu.magisk.utils.set
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import me.tatarka.bindingcollectionadapter2.BindingListViewAdapter
|
import me.tatarka.bindingcollectionadapter2.BindingListViewAdapter
|
||||||
@ -51,6 +61,19 @@ class SuRequestViewModel(
|
|||||||
var grantEnabled = false
|
var grantEnabled = false
|
||||||
set(value) = set(value, field, { field = it }, BR.grantEnabled)
|
set(value) = set(value, field, { field = it }, BR.grantEnabled)
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
val grantTouchListener = View.OnTouchListener { _: View, event: MotionEvent ->
|
||||||
|
// Filter obscured touches by consuming them.
|
||||||
|
if (event.flags and MotionEvent.FLAG_WINDOW_IS_OBSCURED != 0
|
||||||
|
|| event.flags and MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED != 0) {
|
||||||
|
if (event.action == MotionEvent.ACTION_UP) {
|
||||||
|
Utils.toast(R.string.touch_filtered_warning, Toast.LENGTH_SHORT)
|
||||||
|
}
|
||||||
|
return@OnTouchListener Config.suTapjack
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
private val items = res.getStringArray(R.array.allow_timeout).map { SpinnerRvItem(it) }
|
private val items = res.getStringArray(R.array.allow_timeout).map { SpinnerRvItem(it) }
|
||||||
val adapter = BindingListViewAdapter<SpinnerRvItem>(1).apply {
|
val adapter = BindingListViewAdapter<SpinnerRvItem>(1).apply {
|
||||||
itemBinding = ItemBinding.of { binding, _, item ->
|
itemBinding = ItemBinding.of { binding, _, item ->
|
||||||
@ -104,7 +127,7 @@ class SuRequestViewModel(
|
|||||||
timer = SuTimer(millis, 1000).apply { start() }
|
timer = SuTimer(millis, 1000).apply { start() }
|
||||||
|
|
||||||
// Actually show the UI
|
// Actually show the UI
|
||||||
ShowUIEvent().publish()
|
ShowUIEvent(if (Config.suTapjack) EmptyAccessibilityDelegate else null).publish()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun respond(action: Int) {
|
private fun respond(action: Int) {
|
||||||
@ -141,4 +164,18 @@ class SuRequestViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Invisible for accessibility services
|
||||||
|
object EmptyAccessibilityDelegate : View.AccessibilityDelegate() {
|
||||||
|
override fun sendAccessibilityEvent(host: View?, eventType: Int) {}
|
||||||
|
override fun performAccessibilityAction(host: View?, action: Int, args: Bundle?) = true
|
||||||
|
override fun sendAccessibilityEventUnchecked(host: View?, event: AccessibilityEvent?) {}
|
||||||
|
override fun dispatchPopulateAccessibilityEvent(host: View?, event: AccessibilityEvent?) = true
|
||||||
|
override fun onPopulateAccessibilityEvent(host: View?, event: AccessibilityEvent?) {}
|
||||||
|
override fun onInitializeAccessibilityEvent(host: View?, event: AccessibilityEvent?) {}
|
||||||
|
override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) {}
|
||||||
|
override fun addExtraDataToAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo, extraDataKey: String, arguments: Bundle?) {}
|
||||||
|
override fun onRequestSendAccessibilityEvent(host: ViewGroup?, child: View?, event: AccessibilityEvent?): Boolean = false
|
||||||
|
override fun getAccessibilityNodeProvider(host: View?): AccessibilityNodeProvider? = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,6 +138,7 @@
|
|||||||
<Button
|
<Button
|
||||||
android:id="@+id/grant_btn"
|
android:id="@+id/grant_btn"
|
||||||
style="@style/WidgetFoundation.Button.Text"
|
style="@style/WidgetFoundation.Button.Text"
|
||||||
|
onTouch="@{viewModel.grantTouchListener}"
|
||||||
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"
|
||||||
|
@ -56,6 +56,7 @@
|
|||||||
|
|
||||||
<!--Superuser-->
|
<!--Superuser-->
|
||||||
<string name="su_request_title">Superuser Request</string>
|
<string name="su_request_title">Superuser Request</string>
|
||||||
|
<string name="touch_filtered_warning">Because an app is obscuring a superuser request, Magisk can’t verify your response</string>
|
||||||
<string name="deny">Deny</string>
|
<string name="deny">Deny</string>
|
||||||
<string name="prompt">Prompt</string>
|
<string name="prompt">Prompt</string>
|
||||||
<string name="grant">Grant</string>
|
<string name="grant">Grant</string>
|
||||||
@ -172,6 +173,8 @@
|
|||||||
<string name="superuser_notification">Superuser Notification</string>
|
<string name="superuser_notification">Superuser Notification</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_tapjack_title">Enable Tapjacking Protection</string>
|
||||||
|
<string name="settings_su_tapjack_summary">The superuser prompt dialog will not respond to input while obscured by any other window or overlay</string>
|
||||||
<string name="settings_su_biometric_title">Enable Biometric Authentication</string>
|
<string name="settings_su_biometric_title">Enable Biometric Authentication</string>
|
||||||
<string name="settings_su_biometric_summary">Use biometric authentication to allow superuser requests</string>
|
<string name="settings_su_biometric_summary">Use biometric authentication to allow superuser requests</string>
|
||||||
<string name="no_biometric">Unsupported device or no biometric settings are enabled</string>
|
<string name="no_biometric">Unsupported device or no biometric settings are enabled</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user