Reorganize code handling su requests

This commit is contained in:
topjohnwu 2019-11-12 03:20:07 -05:00
parent 2aee0b0be0
commit 171ddab32b
4 changed files with 81 additions and 146 deletions

View File

@ -10,8 +10,8 @@ data class MagiskPolicy(
val uid: Int,
val packageName: String,
val appName: String,
val policy: Int = INTERACTIVE,
val until: Long = -1L,
var policy: Int = INTERACTIVE,
var until: Long = -1L,
val logging: Boolean = true,
val notification: Boolean = true,
val applicationInfo: ApplicationInfo

View File

@ -8,7 +8,6 @@ import android.view.Window
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.base.BaseActivity
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.ViewEvent
import com.topjohnwu.magisk.utils.SuHandler
@ -22,7 +21,7 @@ open class SuRequestActivity : BaseActivity<SuRequestViewModel, ActivityRequestB
override val viewModel: SuRequestViewModel by viewModel()
override fun onBackPressed() {
viewModel.handler?.handleAction(MagiskPolicy.DENY, -1)
viewModel.denyPressed()
}
override fun onCreate(savedInstanceState: Bundle?) {

View File

@ -1,6 +1,5 @@
package com.topjohnwu.magisk.ui.surequest
import android.annotation.SuppressLint
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.PackageManager
@ -8,14 +7,12 @@ import android.content.res.Resources
import android.graphics.drawable.Drawable
import android.hardware.fingerprint.FingerprintManager
import android.os.CountDownTimer
import android.text.TextUtils
import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.base.viewmodel.BaseViewModel
import com.topjohnwu.magisk.data.database.PolicyDao
import com.topjohnwu.magisk.databinding.ComparableRvItem
import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback
import com.topjohnwu.magisk.extensions.now
import com.topjohnwu.magisk.model.entity.MagiskPolicy
import com.topjohnwu.magisk.model.entity.recycler.SpinnerRvItem
@ -58,48 +55,25 @@ class SuRequestViewModel(
setItems(items)
}
private val cancelTasks = mutableListOf<() -> Unit>()
var handler: ActionHandler? = null
private var timer: CountDownTimer? = null
private var policy: MagiskPolicy? = null
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 lateinit var timer: CountDownTimer
private lateinit var policy: MagiskPolicy
private lateinit var connector: SuConnector
private fun cancelTimer() {
timer?.cancel()
timer.cancel()
denyText.value = resources.getString(R.string.deny)
}
fun grantPressed() {
handler?.handleAction(MagiskPolicy.ALLOW)
timer?.cancel()
handleAction(MagiskPolicy.ALLOW)
timer.cancel()
}
fun denyPressed() {
handler?.handleAction(MagiskPolicy.DENY)
timer?.cancel()
handleAction(MagiskPolicy.DENY)
timer.cancel()
}
fun spinnerTouched(): Boolean {
@ -110,75 +84,27 @@ class SuRequestViewModel(
fun handleRequest(intent: Intent): Boolean {
val socketName = intent.getStringExtra("socket") ?: return false
val connector: SuConnector
try {
connector = object : SuConnector(socketName) {
@Throws(IOException::class)
override fun onResponse() {
out.writeInt(policy?.policy ?: return)
}
}
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()
connector = Connector(socketName)
val map = connector.readRequest()
val uid = map["uid"]?.toIntOrNull() ?: return false
policy = uid.toPolicy(packageManager)
} catch (e: Exception) {
Timber.e(e)
return false
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
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)
if (TextUtils.equals(policy?.packageName, BuildConfig.APPLICATION_ID))
if (policy.packageName == BuildConfig.APPLICATION_ID)
return false
// If not interactive, response directly
if (policy?.policy != MagiskPolicy.INTERACTIVE) {
handler?.handleAction()
return true
}
when (Config.suAutoReponse) {
Config.Value.SU_AUTO_DENY -> {
handler?.handleAction(MagiskPolicy.DENY, 0)
handleAction(MagiskPolicy.DENY, 0)
return true
}
Config.Value.SU_AUTO_ALLOW -> {
handler?.handleAction(MagiskPolicy.ALLOW, 0)
handleAction(MagiskPolicy.ALLOW, 0)
return true
}
}
@ -187,35 +113,73 @@ class SuRequestViewModel(
return true
}
@SuppressLint("ClickableViewAccessibility")
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())
timer = object : CountDownTimer(millis, 1000) {
override fun onTick(remains: Long) {
denyText.value = "%s (%d)"
.format(resources.getString(R.string.deny), remains / 1000)
denyText.value = "${resources.getString(R.string.deny)} (${remains / 1000})"
}
override fun onFinish() {
denyText.value = resources.getString(R.string.deny)
handler?.handleAction(MagiskPolicy.DENY)
handleAction(MagiskPolicy.DENY)
}
}
timer?.start()
handler?.addCancel(Runnable { cancelTimer() })
timer.start()
cancelTasks.add { cancelTimer() }
val useFP = canUseFingerprint.value
if (useFP)
try {
if (canUseFingerprint.value)
runCatching {
val helper = SuFingerprint()
helper.authenticate()
handler?.addCancel(Runnable { helper.cancel() })
} catch (e: Exception) {
e.printStackTrace()
cancelTasks.add { helper.cancel() }
}
}
private fun handleAction() {
connector.response()
cancelTasks.forEach { it() }
DieEvent().publish()
}
private fun handleAction(action: Int) {
val pos = selectedItemPosition.value
timeoutPrefs.edit().putInt(policy.packageName, pos).apply()
handleAction(action, Config.Value.TIMEOUT_LIST[pos])
}
private fun handleAction(action: Int, time: Int) {
val until = if (time > 0)
MILLISECONDS.toSeconds(now) + MINUTES.toSeconds(time.toLong())
else
time.toLong()
policy.policy = action
policy.until = until
if (until >= 0)
policyDB.update(policy).blockingAwait()
handleAction()
}
private inner class Connector @Throws(Exception::class)
internal constructor(name: String) : SuConnector(name) {
@Throws(IOException::class)
override fun onResponse() {
out.writeInt(policy.policy)
}
}
private inner class SuFingerprint @Throws(Exception::class)
internal constructor() : FingerprintHelper() {
@ -228,7 +192,7 @@ class SuRequestViewModel(
}
override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult) {
handler?.handleAction(MagiskPolicy.ALLOW)
handleAction(MagiskPolicy.ALLOW)
}
override fun onAuthenticationFailed() {
@ -236,29 +200,4 @@ class SuRequestViewModel(
}
}
open inner class ActionHandler {
private val cancelTasks = mutableListOf<Runnable>()
internal open fun handleAction() {
done()
}
internal open fun handleAction(action: Int) {
done()
}
internal open fun handleAction(action: Int, time: Int) {
done()
}
internal fun addCancel(r: Runnable) {
cancelTasks.add(r)
}
internal fun done() {
cancelTasks.forEach { it.run() }
DieEvent().publish()
}
}
}
}

View File

@ -2,11 +2,9 @@ package com.topjohnwu.magisk.utils
import android.net.LocalSocket
import android.net.LocalSocketAddress
import android.os.Bundle
import android.text.TextUtils
import androidx.collection.ArrayMap
import timber.log.Timber
import java.io.*
import java.nio.charset.Charset
abstract class SuConnector @Throws(IOException::class)
protected constructor(name: String) {
@ -21,24 +19,23 @@ protected constructor(name: String) {
input = DataInputStream(BufferedInputStream(socket.inputStream))
}
@Throws(IOException::class)
private fun readString(): String {
val len = input.readInt()
val buf = ByteArray(len)
input.readFully(buf)
return String(buf, Charset.forName("UTF-8"))
return String(buf, Charsets.UTF_8)
}
@Throws(IOException::class)
fun readSocketInput(): Bundle {
val bundle = Bundle()
fun readRequest(): Map<String, String> {
val ret = ArrayMap<String, String>()
while (true) {
val name = readString()
if (TextUtils.equals(name, "eof"))
if (name == "eof")
break
bundle.putString(name, readString())
ret[name] = readString()
}
return bundle
return ret
}
fun response() {