diff --git a/app/src/main/java/com/topjohnwu/magisk/App.kt b/app/src/main/java/com/topjohnwu/magisk/App.kt index 391c2563f0b8..c5d4ed0dc312 100644 --- a/app/src/main/java/com/topjohnwu/magisk/App.kt +++ b/app/src/main/java/com/topjohnwu/magisk/App.kt @@ -16,6 +16,7 @@ import com.topjohnwu.magisk.di.koinModules import com.topjohnwu.magisk.extensions.get import com.topjohnwu.magisk.extensions.unwrap import com.topjohnwu.magisk.utils.RootInit +import com.topjohnwu.magisk.utils.SuHandler import com.topjohnwu.magisk.utils.updateConfig import com.topjohnwu.superuser.Shell import org.koin.android.ext.koin.androidContext @@ -34,6 +35,7 @@ open class App() : Application() { Shell.Config.verboseLogging(BuildConfig.DEBUG) Shell.Config.addInitializers(RootInit::class.java) Shell.Config.setTimeout(2) + FileProvider.callHandler = SuHandler Room.setFactory { when (it) { WorkDatabase::class.java -> WorkDatabase_Impl() diff --git a/app/src/main/java/com/topjohnwu/magisk/Const.kt b/app/src/main/java/com/topjohnwu/magisk/Const.kt index d4f1d708168d..4ae5299fc98c 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Const.kt +++ b/app/src/main/java/com/topjohnwu/magisk/Const.kt @@ -26,6 +26,7 @@ object Const { const val MIN_VERSION = "v18.0" const val MIN_VERCODE = 18000 const val CONNECT_MODE = 20100 + const val PROVIDER_CONNECT = 20102 } object ID { diff --git a/app/src/main/java/com/topjohnwu/magisk/Hacks.kt b/app/src/main/java/com/topjohnwu/magisk/Hacks.kt index 4f7abfe806ca..6703d2e6bbf0 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Hacks.kt +++ b/app/src/main/java/com/topjohnwu/magisk/Hacks.kt @@ -14,8 +14,6 @@ import android.content.res.AssetManager import android.content.res.Configuration import android.content.res.Resources import androidx.annotation.RequiresApi -import androidx.annotation.StringRes -import com.topjohnwu.magisk.extensions.langTagToLocale import com.topjohnwu.magisk.model.download.DownloadService import com.topjohnwu.magisk.model.receiver.GeneralReceiver import com.topjohnwu.magisk.model.update.UpdateCheckService @@ -23,11 +21,8 @@ import com.topjohnwu.magisk.ui.MainActivity import com.topjohnwu.magisk.ui.SplashActivity import com.topjohnwu.magisk.ui.flash.FlashActivity import com.topjohnwu.magisk.ui.surequest.SuRequestActivity -import com.topjohnwu.magisk.utils.currentLocale -import com.topjohnwu.magisk.utils.defaultLocale import com.topjohnwu.magisk.utils.refreshLocale import com.topjohnwu.magisk.utils.updateConfig -import java.util.* fun AssetManager.addAssetPath(path: String) { DynAPK.addAssetPath(this, path) diff --git a/app/src/main/java/com/topjohnwu/magisk/Info.kt b/app/src/main/java/com/topjohnwu/magisk/Info.kt index 66c601a91a28..317a6357ca2d 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Info.kt +++ b/app/src/main/java/com/topjohnwu/magisk/Info.kt @@ -36,17 +36,13 @@ object Info { val str = ShellUtils.fastCmd("magisk -v").split(":".toRegex())[0] val code = ShellUtils.fastCmd("magisk -V").toInt() val hide = Shell.su("magiskhide --status").exec().isSuccess - var mode = -1 - if (code >= Const.Version.CONNECT_MODE) - mode = Shell.su("magisk --connect-mode").exec().code - Env(code, str, hide, mode) + Env(code, str, hide) }.getOrElse { Env() } class Env( code: Int = -1, val magiskVersionString: String = "", - hide: Boolean = false, - var connectionMode: Int = -1 + hide: Boolean = false ) { val magiskHide get() = Config.magiskHide val magiskVersionCode = when (code) { diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/MagiskPolicy.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/MagiskPolicy.kt index 2a843b9b3ed1..cf803c7b7d0d 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/MagiskPolicy.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/MagiskPolicy.kt @@ -56,14 +56,15 @@ fun Map.toPolicy(pm: PackageManager): MagiskPolicy { } @Throws(PackageManager.NameNotFoundException::class) -fun Int.toPolicy(pm: PackageManager): MagiskPolicy { +fun Int.toPolicy(pm: PackageManager, policy: Int = INTERACTIVE): MagiskPolicy { val pkg = pm.getPackagesForUid(this)?.firstOrNull() ?: throw PackageManager.NameNotFoundException() val info = pm.getApplicationInfo(pkg, 0) return MagiskPolicy( uid = this, packageName = pkg, + policy = policy, applicationInfo = info, - appName = info.loadLabel(pm).toString() + appName = info.getLabel(pm) ) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt b/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt index 4c72c992c77d..90134a8eb665 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt @@ -2,37 +2,25 @@ package com.topjohnwu.magisk.model.receiver import android.content.ContextWrapper import android.content.Intent -import android.os.Build.VERSION.SDK_INT -import com.topjohnwu.magisk.* +import com.topjohnwu.magisk.Config +import com.topjohnwu.magisk.Const +import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.base.BaseReceiver import com.topjohnwu.magisk.data.database.PolicyDao -import com.topjohnwu.magisk.data.database.base.su import com.topjohnwu.magisk.extensions.reboot -import com.topjohnwu.magisk.extensions.startActivity -import com.topjohnwu.magisk.extensions.startActivityWithRoot import com.topjohnwu.magisk.model.download.DownloadService import com.topjohnwu.magisk.model.entity.ManagerJson import com.topjohnwu.magisk.model.entity.internal.Configuration import com.topjohnwu.magisk.model.entity.internal.DownloadSubject -import com.topjohnwu.magisk.ui.surequest.SuRequestActivity -import com.topjohnwu.magisk.utils.SuLogger -import com.topjohnwu.magisk.view.Notifications +import com.topjohnwu.magisk.utils.SuHandler import com.topjohnwu.magisk.view.Shortcuts import com.topjohnwu.superuser.Shell import org.koin.core.inject -import timber.log.Timber open class GeneralReceiver : BaseReceiver() { private val policyDB: PolicyDao by inject() - companion object { - const val REQUEST = "request" - const val LOG = "log" - const val NOTIFY = "notify" - const val TEST = "test" - } - private fun getPkg(intent: Intent): String { return intent.data?.encodedSchemeSpecificPart.orEmpty() } @@ -40,46 +28,15 @@ open class GeneralReceiver : BaseReceiver() { override fun onReceive(context: ContextWrapper, intent: Intent?) { intent ?: return - // Debug messages - if (BuildConfig.DEBUG) { - Timber.d(intent.action) - intent.extras?.let { bundle -> - bundle.keySet().forEach { - Timber.d("[%s]=[%s]", it, bundle[it]) - } - } - } - when (intent.action ?: return) { Intent.ACTION_REBOOT -> { - when (val action = intent.getStringExtra("action") ?: return) { - REQUEST -> { - val i = context.intent() - .setAction(action) - .putExtra("socket", intent.getStringExtra("socket")) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) - if (SDK_INT >= 29) { - // Android Q does not allow starting activity from background - i.startActivityWithRoot() - } else { - i.startActivity(context) - } - } - LOG -> SuLogger.handleLogs(context, intent) - NOTIFY -> SuLogger.handleNotify(context, intent) - TEST -> { - val mode = intent.getIntExtra("mode", 1 shl 1) - if (mode > Info.env.connectionMode) - Info.env.connectionMode = mode - Shell.su("magisk --connect-mode $mode").submit() - } - } + SuHandler(context, intent.getStringExtra("action"), intent.extras) } - Intent.ACTION_PACKAGE_REPLACED -> + Intent.ACTION_PACKAGE_REPLACED -> { // This will only work pre-O if (Config.suReAuth) policyDB.delete(getPkg(intent)).blockingGet() + } Intent.ACTION_PACKAGE_FULLY_REMOVED -> { val pkg = getPkg(intent) policyDB.delete(pkg).blockingGet() diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt index 1a0ddf02b07e..eabbd9c00cea 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt @@ -3,10 +3,13 @@ package com.topjohnwu.magisk.ui import android.app.Activity import android.content.Context import android.os.Bundle -import com.topjohnwu.magisk.* +import com.topjohnwu.magisk.BuildConfig +import com.topjohnwu.magisk.Config +import com.topjohnwu.magisk.intent import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.view.Notifications import com.topjohnwu.magisk.view.Shortcuts +import com.topjohnwu.magisk.wrap import com.topjohnwu.superuser.Shell open class SplashActivity : Activity() { diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt index 4ce2a60be2cb..75b395caca02 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt @@ -10,8 +10,7 @@ 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.model.receiver.GeneralReceiver -import com.topjohnwu.magisk.utils.SuLogger +import com.topjohnwu.magisk.utils.SuHandler import org.koin.androidx.viewmodel.ext.android.viewModel open class SuRequestActivity : BaseActivity() { @@ -29,19 +28,13 @@ open class SuRequestActivity : BaseActivity { - if (!viewModel.handleRequest(intent)) - finish() - return - } - GeneralReceiver.LOG -> SuLogger.handleLogs(this, intent) - GeneralReceiver.NOTIFY -> SuLogger.handleNotify(this, intent) + if (intent?.action == SuHandler.REQUEST) { + if (!viewModel.handleRequest(intent)) + finish() + } else { + SuHandler(this, intent.action, intent.extras) + finish() } - - finish() } override fun onEventDispatched(event: ViewEvent) { diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt index ebc4a8c94188..511409a7d150 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt @@ -77,8 +77,9 @@ object PatchAPK { } private fun patchAndHide(context: Context, label: String): Boolean { - // If not running as stub, and we are compatible with stub, use stub - val src = if (!isRunningAsStub && SDK_INT >= 28 && Info.env.connectionMode == 3) { + val src = if (!isRunningAsStub && SDK_INT >= 28 && + Info.env.magiskVersionCode >= Const.Version.PROVIDER_CONNECT) { + // If not running as stub, and we are compatible with stub, use stub val stub = File(context.cacheDir, "stub.apk") val svc = get() runCatching { diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/SuHandler.kt b/app/src/main/java/com/topjohnwu/magisk/utils/SuHandler.kt new file mode 100644 index 000000000000..ba6521c322a3 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/SuHandler.kt @@ -0,0 +1,137 @@ +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 +import java.util.* + +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, + date = Date() + ) + + val logRepo = get() + logRepo.put(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) + } + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/SuLogger.kt b/app/src/main/java/com/topjohnwu/magisk/utils/SuLogger.kt deleted file mode 100644 index 98b201235891..000000000000 --- a/app/src/main/java/com/topjohnwu/magisk/utils/SuLogger.kt +++ /dev/null @@ -1,92 +0,0 @@ -package com.topjohnwu.magisk.utils - -import android.content.Context -import android.content.Intent -import android.os.Process -import android.widget.Toast -import com.topjohnwu.magisk.Config -import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.data.database.PolicyDao -import com.topjohnwu.magisk.data.repository.LogRepository -import com.topjohnwu.magisk.extensions.get -import com.topjohnwu.magisk.model.entity.MagiskPolicy -import com.topjohnwu.magisk.model.entity.toLog -import com.topjohnwu.magisk.model.entity.toPolicy -import java.util.* - -object SuLogger { - - fun handleLogs(context: Context, intent: Intent) { - - val fromUid = intent.getIntExtra("from.uid", -1) - if (fromUid < 0) return - if (fromUid == Process.myUid()) return - - val pm = context.packageManager - - val notify: Boolean - val data = intent.extras - val policy: MagiskPolicy = if (data!!.containsKey("notify")) { - notify = data.getBoolean("notify") - runCatching { - fromUid.toPolicy(pm) - }.getOrElse { return } - } else { - // Doesn't report whether notify or not, check database ourselves - val policyDB = get() - val policy = policyDB.fetch(fromUid).blockingGet() ?: return - notify = policy.notification - policy - }.copy(policy = data.getInt("policy", -1)) - - if (policy.policy < 0) - return - - if (notify) - handleNotify(context, policy) - - val toUid = intent.getIntExtra("to.uid", -1) - if (toUid < 0) return - - val pid = intent.getIntExtra("pid", -1) - if (pid < 0) return - - val command = intent.getStringExtra("command") ?: return - val log = policy.toLog( - toUid = toUid, - fromPid = pid, - command = command, - date = Date() - ) - - val logRepo = get() - logRepo.put(log).blockingGet()?.printStackTrace() - } - - private fun handleNotify(context: Context, policy: MagiskPolicy) { - if (policy.notification && Config.suNotification == Config.Value.NOTIFICATION_TOAST) { - Utils.toast( - context.getString( - if (policy.policy == MagiskPolicy.ALLOW) - R.string.su_allow_toast - else - R.string.su_deny_toast, policy.appName - ), - Toast.LENGTH_SHORT - ) - } - } - - fun handleNotify(context: Context, intent: Intent) { - val fromUid = intent.getIntExtra("from.uid", -1) - if (fromUid < 0) return - if (fromUid == Process.myUid()) return - runCatching { - val pm = context.packageManager - val policy = fromUid.toPolicy(pm) - .copy(policy = intent.getIntExtra("policy", -1)) - if (policy.policy >= 0) - handleNotify(context, policy) - } - } -} diff --git a/native/jni/core/bootstages.cpp b/native/jni/core/bootstages.cpp index 9c57c776e17a..9e6f10c97baa 100644 --- a/native/jni/core/bootstages.cpp +++ b/native/jni/core/bootstages.cpp @@ -800,7 +800,4 @@ void boot_complete(int client) { install_apk("/data/magisk.apk"); } } - - // Test whether broadcast can be used or not - broadcast_test(); } diff --git a/native/jni/core/daemon.cpp b/native/jni/core/daemon.cpp index 2948b1497593..49a22a4197ad 100644 --- a/native/jni/core/daemon.cpp +++ b/native/jni/core/daemon.cpp @@ -46,8 +46,6 @@ static void *request_handler(void *args) { case LATE_START: case BOOT_COMPLETE: case SQLITE_CMD: - case BROADCAST_ACK: - case BROADCAST_TEST: if (credential.uid != 0) { write_int(client, ROOT_REQUIRED); close(client); @@ -84,12 +82,6 @@ static void *request_handler(void *args) { case SQLITE_CMD: exec_sql(client); break; - case BROADCAST_ACK: - broadcast_ack(client); - break; - case BROADCAST_TEST: - broadcast_test(client); - break; case REMOVE_MODULES: if (credential.uid == UID_SHELL || credential.uid == UID_ROOT) { remove_modules(); diff --git a/native/jni/core/magisk.cpp b/native/jni/core/magisk.cpp index 821e53be1ec9..79b5a65c5e53 100644 --- a/native/jni/core/magisk.cpp +++ b/native/jni/core/magisk.cpp @@ -35,8 +35,6 @@ Advanced Options (Internal APIs): --clone-attr SRC DEST clone permission, owner, and selinux context --clone SRC DEST clone SRC to DEST --sqlite SQL exec SQL commands to Magisk database - --connect-mode [MODE] get/set connect mode for su request and notify - --broadcast-test manually trigger broadcast tests Supported init triggers: post-fs-data, service, boot-complete @@ -113,23 +111,10 @@ int magisk_main(int argc, char *argv[]) { printf("%s\n", res); free(res); } - } else if (argv[1] == "--connect-mode"sv) { - int fd = connect_daemon(); - write_int(fd, BROADCAST_ACK); - if (argc >= 3) { - write_int(fd, parse_int(argv[2])); - } else { - write_int(fd, -1); - } - return read_int(fd); } else if (argv[1] == "--remove-modules"sv) { int fd = connect_daemon(); write_int(fd, REMOVE_MODULES); return read_int(fd); - } else if (argv[1] == "--broadcast-test"sv) { - int fd = connect_daemon(); - write_int(fd, BROADCAST_TEST); - return read_int(fd); } #if 0 /* Entry point for testing stuffs */ diff --git a/native/jni/include/daemon.h b/native/jni/include/daemon.h index 6af462f434d0..71ac92b9b2c5 100644 --- a/native/jni/include/daemon.h +++ b/native/jni/include/daemon.h @@ -17,9 +17,7 @@ enum { BOOT_COMPLETE, MAGISKHIDE, SQLITE_CMD, - BROADCAST_ACK, REMOVE_MODULES, - BROADCAST_TEST, }; // Return codes for daemon @@ -84,8 +82,6 @@ void magiskhide_handler(int client); *************/ void su_daemon_handler(int client, struct ucred *credential); -void broadcast_test(int client = -1); -void broadcast_ack(int client); /********************* * Daemon Global Vars diff --git a/native/jni/su/connect.cpp b/native/jni/su/connect.cpp index f83cf2c9ceeb..a5789f45daad 100644 --- a/native/jni/su/connect.cpp +++ b/native/jni/su/connect.cpp @@ -13,46 +13,28 @@ using namespace std; -enum connect_mode { - UNINITIALIZED = 0, - MODE_ACTIVITY, - MODE_BROADCAST_COMPONENT, - MODE_BROADCAST_PACKAGE -}; - -static connect_mode current_mode = UNINITIALIZED; - -#define START_ACTIVITY \ -"/system/bin/app_process", "/system/bin", "com.android.commands.am.Am", \ -"start", "-n", nullptr, "--user", nullptr, "-f", "0x18000020", "-a" - -// 0x18000020 = FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_MULTIPLE_TASK|FLAG_INCLUDE_STOPPED_PACKAGES - -#define START_BROADCAST \ -"/system/bin/app_process", "/system/bin", "com.android.commands.am.Am", \ -"broadcast", "-n", nullptr, "--user", nullptr, "-f", "0x00000020", \ -"-a", "android.intent.action.REBOOT", "--es", "action" - -#define START_BROADCAST_PKG \ -"/system/bin/app_process", "/system/bin", "com.android.commands.am.Am", \ -"broadcast", "-p", nullptr, "--user", nullptr, "-f", "0x00000020", \ -"-a", "android.intent.action.REBOOT", "--es", "action" - -// 0x00000020 = FLAG_INCLUDE_STOPPED_PACKAGES - -#define am_app_info(info, ...) \ -if (current_mode == MODE_BROADCAST_PACKAGE) { \ - const char *cmd[] = { START_BROADCAST_PKG, __VA_ARGS__, nullptr }; \ - exec_am_cmd(cmd, info); \ -} else if (current_mode == MODE_BROADCAST_COMPONENT) { \ - const char *cmd[] = { START_BROADCAST, __VA_ARGS__, nullptr }; \ - exec_am_cmd(cmd, info); \ -} else { \ - const char *cmd[] = { START_ACTIVITY, __VA_ARGS__, nullptr }; \ - exec_am_cmd(cmd, info); \ +#define CALL_PROVIDER \ +"/system/bin/app_process", "/system/bin", "com.android.commands.content.Content", \ +"call", "--uri", nullptr, "--user", nullptr, "--method" + +#define content_exec_info(info, ...) {\ + const char *cmd[] = { CALL_PROVIDER, __VA_ARGS__, nullptr }; \ + exec_content_cmd(cmd, info); \ } -#define am_app(...) am_app_info(ctx.info.get(), __VA_ARGS__) +#define content_exec(...) content_exec_info(ctx.info.get(), __VA_ARGS__) + +#define ex(s) "--extra", s + +#define get_user(info) \ +(info->cfg[SU_MULTIUSER_MODE] == MULTIUSER_MODE_USER \ +? info->uid / 100000 \ +: 0) + +#define get_uid(info) \ +(info->cfg[SU_MULTIUSER_MODE] == MULTIUSER_MODE_OWNER_MANAGED \ +? info->uid % 100000 \ +: info->uid) static const char *get_command(const su_request *to) { if (to->command[0]) @@ -62,48 +44,22 @@ static const char *get_command(const su_request *to) { return DEFAULT_SHELL; } -static void get_user(char *user, const su_info *info) { - sprintf(user, "%d", - info->cfg[SU_MULTIUSER_MODE] == MULTIUSER_MODE_USER - ? info->uid / 100000 - : 0); -} - -static void get_uid(char *uid, const su_info *info) { - sprintf(uid, "%d", - info->cfg[SU_MULTIUSER_MODE] == MULTIUSER_MODE_OWNER_MANAGED - ? info->uid % 100000 - : info->uid); -} - -static void exec_am_cmd(const char **args, const su_info *info) { +static void exec_content_cmd(const char **args, const su_info *info) { char target[128]; - if (args[3][0] == 'b') { - // Broadcast - if (args[4][1] == 'p') { - // Broadcast to package (receiver can be obfuscated) - strcpy(target, info->str[SU_MANAGER].data()); - } else { - // a.h is the broadcast receiver - sprintf(target, "%s/a.h", info->str[SU_MANAGER].data()); - } - } else { - // a.m is the activity - sprintf(target, "%s/a.m", info->str[SU_MANAGER].data()); - } - char user[8]; - get_user(user, info); + sprintf(target, "content://%s.provider", info->str[SU_MANAGER].data()); + char user[4]; + sprintf(user, "%d", get_user(info)); // Fill in non static arguments args[5] = target; args[7] = user; exec_t exec { - .pre_exec = []() -> void { + .pre_exec = [] { int null = xopen("/dev/null", O_WRONLY | O_CLOEXEC); dup2(null, STDOUT_FILENO); dup2(null, STDERR_FILENO); - setenv("CLASSPATH", "/system/framework/am.jar", 1); + setenv("CLASSPATH", "/system/framework/content.jar", 1); }, .fork = fork_dont_care, .argv = args @@ -113,94 +69,51 @@ static void exec_am_cmd(const char **args, const su_info *info) { #define LOG_BODY \ "log", \ -"--ei", "from.uid", fromUid, \ -"--ei", "to.uid", toUid, \ -"--ei", "pid", pid, \ -"--ei", "policy", policy, \ -"--es", "command", get_command(&ctx.req), \ -"--ez", "notify", ctx.info->access.notify ? "true" : "false" +ex(fromUid), ex(toUid), ex(pid), ex(policy), \ +ex(command.data()), ex(notify) void app_log(const su_context &ctx) { - char fromUid[8]; - get_uid(fromUid, ctx.info.get()); + char fromUid[16]; + sprintf(fromUid, "from.uid:i:%d", get_uid(ctx.info)); + + char toUid[16]; + sprintf(toUid, "to.uid:i:%d", ctx.req.uid); - char toUid[8]; - sprintf(toUid, "%d", ctx.req.uid); + char pid[16]; + sprintf(pid, "pid:i:%d", ctx.pid); - char pid[8]; - sprintf(pid, "%d", ctx.pid); + char policy[16]; + sprintf(policy, "policy:i:%d", ctx.info->access.policy); - char policy[2]; - sprintf(policy, "%d", ctx.info->access.policy); + string command("command:s:"); + command += get_command(&ctx.req); - am_app(LOG_BODY) + char notify[16]; + sprintf(notify, "notify:b:%s", ctx.info->access.notify ? "true" : "false"); + + content_exec(LOG_BODY) } #define NOTIFY_BODY \ -"notify", \ -"--ei", "from.uid", fromUid, \ -"--ei", "policy", policy +"notify", ex(fromUid), ex(policy) void app_notify(const su_context &ctx) { - char fromUid[8]; - get_uid(fromUid, ctx.info.get()); + char fromUid[16]; + sprintf(fromUid, "from.uid:i:%d", get_uid(ctx.info)); - char policy[2]; - sprintf(policy, "%d", ctx.info->access.policy); + char policy[16]; + sprintf(policy, "policy:i:%d", ctx.info->access.policy); - am_app(NOTIFY_BODY) + content_exec(NOTIFY_BODY) } #define SOCKET_BODY \ -"request", \ -"--es", "socket", socket +"request", ex(sock) void app_socket(const char *socket, const shared_ptr &info) { - am_app_info(info.get(), SOCKET_BODY) -} - -#define TEST_BODY \ -"test", "--ei", "mode", mode, nullptr - -void broadcast_test(int client) { - if (client >= 0) { - // Make it not uninitialized - current_mode = MODE_ACTIVITY; - write_int(client, 0); - close(client); - } - - su_info info; - get_db_settings(info.cfg); - get_db_strings(info.str); - validate_manager(info.str[SU_MANAGER], 0, &info.mgr_st); - - char mode[2]; - { - sprintf(mode, "%d", MODE_BROADCAST_PACKAGE); - const char *cmd[] = { START_BROADCAST_PKG, TEST_BODY }; - exec_am_cmd(cmd, &info); - } - { - sprintf(mode, "%d", MODE_BROADCAST_COMPONENT); - const char *cmd[] = { START_BROADCAST, TEST_BODY }; - exec_am_cmd(cmd, &info); - } -} - -void broadcast_ack(int client) { - int mode = read_int(client); - if (mode < 0) { - // Return connection mode to client - write_int(client, current_mode); - } else { - if (mode > current_mode) { - LOGD("* Use connect mode [%d] for su request and notify\n", mode); - current_mode = static_cast(mode); - } - write_int(client, 0); - } - close(client); + char sock[128]; + sprintf(sock, "socket:s:%s", socket); + content_exec_info(info.get(), SOCKET_BODY) } void socket_send_request(int fd, const shared_ptr &info) { diff --git a/scripts/emulator.sh b/scripts/emulator.sh index a01e0f040397..ceb3e83888ee 100755 --- a/scripts/emulator.sh +++ b/scripts/emulator.sh @@ -118,4 +118,3 @@ mkdir -p /data/adb/modules 2>/dev/null mkdir /data/adb/post-fs-data.d 2>/dev/null mkdir /data/adb/services.d 2>/dev/null /sbin/magisk --daemon -/sbin/magisk --broadcast-test diff --git a/shared/src/main/java/com/topjohnwu/magisk/FileProvider.java b/shared/src/main/java/com/topjohnwu/magisk/FileProvider.java index 9b7ea0a73fe2..8ce8d2de090a 100644 --- a/shared/src/main/java/com/topjohnwu/magisk/FileProvider.java +++ b/shared/src/main/java/com/topjohnwu/magisk/FileProvider.java @@ -10,6 +10,7 @@ import android.database.MatrixCursor; import android.net.Uri; import android.os.Build; +import android.os.Bundle; import android.os.Environment; import android.os.ParcelFileDescriptor; import android.provider.OpenableColumns; @@ -54,6 +55,7 @@ public class FileProvider extends ContentProvider { private PathStrategy mStrategy; + public static ProviderCallHandler callHandler; @Override public boolean onCreate() { @@ -156,6 +158,14 @@ public int delete(Uri uri, String selection, return file.delete() ? 1 : 0; } + + @Override + public Bundle call(String method, String arg, Bundle extras) { + if (callHandler != null) + return callHandler.call(getContext(), method, arg, extras); + return null; + } + @Override public ParcelFileDescriptor openFile(Uri uri, String mode) diff --git a/shared/src/main/java/com/topjohnwu/magisk/ProviderCallHandler.java b/shared/src/main/java/com/topjohnwu/magisk/ProviderCallHandler.java new file mode 100644 index 000000000000..c3ff5c447cee --- /dev/null +++ b/shared/src/main/java/com/topjohnwu/magisk/ProviderCallHandler.java @@ -0,0 +1,8 @@ +package com.topjohnwu.magisk; + +import android.content.Context; +import android.os.Bundle; + +public interface ProviderCallHandler { + Bundle call(Context context, String method, String arg, Bundle extras); +}