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() .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() 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) } } }