mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-25 10:27:39 +00:00
Added safetynet implementation
The implementation itself was moved from fragment to self contained event. The result resolution might be moved to the event as well
This commit is contained in:
parent
04576ca828
commit
21f2f86cb8
@ -22,7 +22,7 @@ val redesignModule = module {
|
|||||||
viewModel { LogViewModel() }
|
viewModel { LogViewModel() }
|
||||||
viewModel { ModuleViewModel() }
|
viewModel { ModuleViewModel() }
|
||||||
viewModel { RequestViewModel() }
|
viewModel { RequestViewModel() }
|
||||||
viewModel { SafetynetViewModel() }
|
viewModel { SafetynetViewModel(get()) }
|
||||||
viewModel { SettingsViewModel() }
|
viewModel { SettingsViewModel() }
|
||||||
viewModel { SuperuserViewModel(get(), get(), get(), get()) }
|
viewModel { SuperuserViewModel(get(), get(), get(), get()) }
|
||||||
viewModel { ThemeViewModel() }
|
viewModel { ThemeViewModel() }
|
||||||
|
@ -15,3 +15,5 @@ sealed class PolicyUpdateEvent(val item: MagiskPolicy) : RxBus.Event {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ModuleUpdatedEvent(val item: ModuleRvItem) : RxBus.Event
|
class ModuleUpdatedEvent(val item: ModuleRvItem) : RxBus.Event
|
||||||
|
|
||||||
|
data class SafetyNetResult(val responseCode: Int) : RxBus.Event
|
@ -1,6 +1,7 @@
|
|||||||
package com.topjohnwu.magisk.model.events
|
package com.topjohnwu.magisk.model.events
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.karumi.dexter.Dexter
|
import com.karumi.dexter.Dexter
|
||||||
@ -8,9 +9,25 @@ import com.karumi.dexter.MultiplePermissionsReport
|
|||||||
import com.karumi.dexter.PermissionToken
|
import com.karumi.dexter.PermissionToken
|
||||||
import com.karumi.dexter.listener.PermissionRequest
|
import com.karumi.dexter.listener.PermissionRequest
|
||||||
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
|
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
|
||||||
|
import com.topjohnwu.magisk.Const
|
||||||
|
import com.topjohnwu.magisk.R
|
||||||
|
import com.topjohnwu.magisk.data.repository.MagiskRepository
|
||||||
|
import com.topjohnwu.magisk.extensions.DynamicClassLoader
|
||||||
|
import com.topjohnwu.magisk.extensions.subscribeK
|
||||||
|
import com.topjohnwu.magisk.extensions.writeTo
|
||||||
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.SafetyNetHelper
|
||||||
|
import com.topjohnwu.magisk.view.MagiskDialog
|
||||||
|
import com.topjohnwu.superuser.Shell
|
||||||
|
import dalvik.system.DexFile
|
||||||
|
import io.reactivex.Completable
|
||||||
import io.reactivex.subjects.PublishSubject
|
import io.reactivex.subjects.PublishSubject
|
||||||
|
import org.koin.core.KoinComponent
|
||||||
|
import org.koin.core.inject
|
||||||
|
import java.io.File
|
||||||
|
import java.lang.reflect.InvocationHandler
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class for passing events from ViewModels to Activities/Fragments
|
* Class for passing events from ViewModels to Activities/Fragments
|
||||||
@ -34,7 +51,88 @@ class MagiskChangelogEvent : ViewEvent()
|
|||||||
class UninstallEvent : ViewEvent()
|
class UninstallEvent : ViewEvent()
|
||||||
class EnvFixEvent : ViewEvent()
|
class EnvFixEvent : ViewEvent()
|
||||||
|
|
||||||
class UpdateSafetyNetEvent : ViewEvent()
|
class UpdateSafetyNetEvent : ViewEvent(), ContextExecutor, KoinComponent, SafetyNetHelper.Callback {
|
||||||
|
|
||||||
|
private val magiskRepo by inject<MagiskRepository>()
|
||||||
|
private val rxBus by inject<RxBus>()
|
||||||
|
|
||||||
|
private lateinit var EXT_APK: File
|
||||||
|
private lateinit var EXT_DEX: File
|
||||||
|
|
||||||
|
override fun invoke(context: Context) {
|
||||||
|
val die = ::EXT_APK.isInitialized
|
||||||
|
|
||||||
|
EXT_APK = File("${context.filesDir.parent}/snet", "snet.jar")
|
||||||
|
EXT_DEX = File(EXT_APK.parent, "snet.dex")
|
||||||
|
|
||||||
|
Completable.fromAction {
|
||||||
|
val loader = DynamicClassLoader(EXT_APK)
|
||||||
|
val dex = DexFile.loadDex(EXT_APK.path, EXT_DEX.path, 0)
|
||||||
|
|
||||||
|
// Scan through the dex and find our helper class
|
||||||
|
var helperClass: Class<*>? = null
|
||||||
|
for (className in dex.entries()) {
|
||||||
|
if (className.startsWith("x.")) {
|
||||||
|
val cls = loader.loadClass(className)
|
||||||
|
if (InvocationHandler::class.java.isAssignableFrom(cls)) {
|
||||||
|
helperClass = cls
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
helperClass ?: throw Exception()
|
||||||
|
|
||||||
|
val helper = helperClass.getMethod(
|
||||||
|
"get",
|
||||||
|
Class::class.java, Context::class.java, Any::class.java
|
||||||
|
)
|
||||||
|
.invoke(null, SafetyNetHelper::class.java, context, this) as SafetyNetHelper
|
||||||
|
|
||||||
|
if (helper.version < Const.SNET_EXT_VER)
|
||||||
|
throw Exception()
|
||||||
|
|
||||||
|
helper.attest()
|
||||||
|
}.subscribeK(onError = {
|
||||||
|
if (die) {
|
||||||
|
rxBus.post(SafetyNetResult(-1))
|
||||||
|
} else {
|
||||||
|
Shell.sh("rm -rf " + EXT_APK.parent).exec()
|
||||||
|
EXT_APK.parentFile?.mkdir()
|
||||||
|
download(context, true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("SameParameterValue")
|
||||||
|
private fun download(context: Context, askUser: Boolean) {
|
||||||
|
fun downloadInternal() = magiskRepo.fetchSafetynet()
|
||||||
|
.map { it.byteStream().writeTo(EXT_APK) }
|
||||||
|
.subscribeK { invoke(context) }
|
||||||
|
|
||||||
|
if (!askUser) {
|
||||||
|
downloadInternal()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
MagiskDialog(context)
|
||||||
|
.applyTitle(R.string.proprietary_title)
|
||||||
|
.applyMessage(R.string.proprietary_notice)
|
||||||
|
.cancellable(false)
|
||||||
|
.applyButton(MagiskDialog.ButtonType.POSITIVE) {
|
||||||
|
titleRes = R.string.yes
|
||||||
|
onClick { downloadInternal() }
|
||||||
|
}
|
||||||
|
.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
|
||||||
|
titleRes = R.string.no_thanks
|
||||||
|
onClick { rxBus.post(SafetyNetResult(-2)) }
|
||||||
|
}
|
||||||
|
.reveal()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResponse(responseCode: Int) {
|
||||||
|
rxBus.post(SafetyNetResult(responseCode))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ViewActionEvent(val action: Activity.() -> Unit) : ViewEvent(), ActivityExecutor {
|
class ViewActionEvent(val action: Activity.() -> Unit) : ViewEvent(), ActivityExecutor {
|
||||||
override fun invoke(activity: AppCompatActivity) = activity.run(action)
|
override fun invoke(activity: AppCompatActivity) = activity.run(action)
|
||||||
|
@ -1,5 +1,62 @@
|
|||||||
package com.topjohnwu.magisk.redesign.safetynet
|
package com.topjohnwu.magisk.redesign.safetynet
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.R
|
||||||
|
import com.topjohnwu.magisk.extensions.subscribeK
|
||||||
|
import com.topjohnwu.magisk.model.events.SafetyNetResult
|
||||||
|
import com.topjohnwu.magisk.model.events.UpdateSafetyNetEvent
|
||||||
import com.topjohnwu.magisk.redesign.compat.CompatViewModel
|
import com.topjohnwu.magisk.redesign.compat.CompatViewModel
|
||||||
|
import com.topjohnwu.magisk.ui.home.SafetyNetState.*
|
||||||
|
import com.topjohnwu.magisk.utils.KObservableField
|
||||||
|
import com.topjohnwu.magisk.utils.RxBus
|
||||||
|
import com.topjohnwu.magisk.utils.SafetyNetHelper
|
||||||
|
|
||||||
class SafetynetViewModel : CompatViewModel()
|
class SafetynetViewModel(
|
||||||
|
rxBus: RxBus
|
||||||
|
) : CompatViewModel() {
|
||||||
|
|
||||||
|
val safetyNetTitle = KObservableField(R.string.empty)
|
||||||
|
val ctsState = KObservableField(IDLE)
|
||||||
|
val basicIntegrityState = KObservableField(IDLE)
|
||||||
|
|
||||||
|
init {
|
||||||
|
rxBus.register<SafetyNetResult>()
|
||||||
|
.subscribeK { resolveResponse(it.responseCode) }
|
||||||
|
.add()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun attest() = UpdateSafetyNetEvent().publish()
|
||||||
|
|
||||||
|
private fun resolveResponse(response: Int) = when {
|
||||||
|
//todo animate (reveal) to result (green/error)
|
||||||
|
response and 0x0F == 0 -> {
|
||||||
|
val hasCtsPassed = response and SafetyNetHelper.CTS_PASS != 0
|
||||||
|
val hasBasicIntegrityPassed = response and SafetyNetHelper.BASIC_PASS != 0
|
||||||
|
safetyNetTitle.value = R.string.safetyNet_check_success
|
||||||
|
ctsState.value = if (hasCtsPassed) {
|
||||||
|
PASS
|
||||||
|
} else {
|
||||||
|
FAILED
|
||||||
|
}
|
||||||
|
basicIntegrityState.value = if (hasBasicIntegrityPassed) {
|
||||||
|
PASS
|
||||||
|
} else {
|
||||||
|
FAILED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//todo animate (collapse) back to initial (fade error)
|
||||||
|
response == -2 -> {
|
||||||
|
ctsState.value = IDLE
|
||||||
|
basicIntegrityState.value = IDLE
|
||||||
|
}
|
||||||
|
//todo animate (collapse) back to initial (surface)
|
||||||
|
else -> {
|
||||||
|
ctsState.value = IDLE
|
||||||
|
basicIntegrityState.value = IDLE
|
||||||
|
safetyNetTitle.value = when (response) {
|
||||||
|
SafetyNetHelper.RESPONSE_ERR -> R.string.safetyNet_res_invalid
|
||||||
|
else -> R.string.safetyNet_api_error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
10
app/src/main/res/drawable/ic_test.xml
Normal file
10
app/src/main/res/drawable/ic_test.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="?colorOnSurface"
|
||||||
|
android:pathData="M7,2V4H8V18A4,4 0 0,0 12,22A4,4 0 0,0 16,18V4H17V2H7M11,16C10.4,16 10,15.6 10,15C10,14.4 10.4,14 11,14C11.6,14 12,14.4 12,15C12,15.6 11.6,16 11,16M13,12C12.4,12 12,11.6 12,11C12,10.4 12.4,10 13,10C13.6,10 14,10.4 14,11C14,11.6 13.6,12 13,12M14,7H10V4H14V7Z" />
|
||||||
|
</vector>
|
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
@ -9,15 +10,41 @@
|
|||||||
|
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
<FrameLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:fillViewport="true">
|
android:fillViewport="true">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
<com.google.android.material.button.MaterialButton
|
||||||
|
style="?styleButtonText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:onClick="@{() -> viewModel.attest()}"
|
||||||
|
android:text="Attest"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
app:icon="@drawable/ic_test"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?colorPrimary">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
</layout>
|
</layout>
|
Loading…
x
Reference in New Issue
Block a user