From 80cd85b061376d533202265c0db443a6a85ec051 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 13 May 2019 02:01:10 -0700 Subject: [PATCH] Try to use broadcast for su logging and notify In commit 8d4c407, native Magisk always launches an activity for communicating with Magisk Manager. While this works extremely well, since it also workaround stupid OEMs that blocks broadcasts, it has a problem: launching an activity will claim the focus of the device, which could be super annoying in some circumstances. This commit adds a new feature to run a broadcast test on boot complete. If Magisk Manager successfully receives the broadcast, it will toggle a setting in magiskd so all future su loggings and notifies will always use broadcasts instead of launching activities. Fix #1412 --- .../model/receiver/GeneralReceiver.java | 84 ------------------- .../magisk/model/receiver/GeneralReceiver.kt | 80 ++++++++++++++++++ .../magisk/ui/surequest/SuRequestActivity.kt | 13 +-- native/jni/core/bootstages.cpp | 3 + native/jni/core/daemon.cpp | 5 ++ native/jni/core/db.cpp | 2 +- native/jni/core/magisk.cpp | 35 ++++---- native/jni/include/daemon.h | 4 +- native/jni/include/db.h | 2 +- native/jni/su/connect.cpp | 74 +++++++++++----- native/jni/su/su.h | 2 +- native/jni/su/su_daemon.cpp | 2 +- 12 files changed, 173 insertions(+), 133 deletions(-) delete mode 100644 app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.java create mode 100644 app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt diff --git a/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.java b/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.java deleted file mode 100644 index 0a7432c4d..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.topjohnwu.magisk.model.receiver; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -import com.topjohnwu.magisk.App; -import com.topjohnwu.magisk.ClassMap; -import com.topjohnwu.magisk.Config; -import com.topjohnwu.magisk.Const; -import com.topjohnwu.magisk.ui.surequest.SuRequestActivity; -import com.topjohnwu.magisk.utils.DownloadApp; -import com.topjohnwu.magisk.utils.SuLogger; -import com.topjohnwu.magisk.view.Notifications; -import com.topjohnwu.magisk.view.Shortcuts; -import com.topjohnwu.superuser.Shell; - -public class GeneralReceiver extends BroadcastReceiver { - - private String getPkg(Intent i) { - return i.getData() == null ? "" : i.getData().getEncodedSchemeSpecificPart(); - } - - @Override - public void onReceive(Context context, Intent intent) { - App app = App.self; - if (intent == null) - return; - String action = intent.getAction(); - if (action == null) - return; - switch (action) { - case Intent.ACTION_REBOOT: - case Intent.ACTION_BOOT_COMPLETED: - action = intent.getStringExtra("action"); - if (action == null) { - // Actual boot completed event - Shell.su("mm_patch_dtbo").submit(result -> { - if (result.isSuccess()) - Notifications.dtboPatched(); - }); - break; - } - switch (action) { - case SuRequestActivity.REQUEST: - Intent i = new Intent(app, ClassMap.get(SuRequestActivity.class)) - .setAction(action) - .putExtra("socket", intent.getStringExtra("socket")) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); - app.startActivity(i); - break; - case SuRequestActivity.LOG: - SuLogger.handleLogs(intent); - break; - case SuRequestActivity.NOTIFY: - SuLogger.handleNotify(intent); - break; - } - break; - case Intent.ACTION_PACKAGE_REPLACED: - // This will only work pre-O - if (Config.get(Config.Key.SU_REAUTH)) { - app.getDB().deletePolicy(getPkg(intent)); - } - break; - case Intent.ACTION_PACKAGE_FULLY_REMOVED: - String pkg = getPkg(intent); - app.getDB().deletePolicy(pkg); - Shell.su("magiskhide --rm " + pkg).submit(); - break; - case Intent.ACTION_LOCALE_CHANGED: - Shortcuts.setup(context); - break; - case Const.Key.BROADCAST_MANAGER_UPDATE: - Config.managerLink = intent.getStringExtra(Const.Key.INTENT_SET_LINK); - DownloadApp.upgrade(intent.getStringExtra(Const.Key.INTENT_SET_NAME)); - break; - case Const.Key.BROADCAST_REBOOT: - Shell.su("/system/bin/reboot").submit(); - break; - } - } -} 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 new file mode 100644 index 000000000..8024d39e0 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt @@ -0,0 +1,80 @@ +package com.topjohnwu.magisk.model.receiver + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import com.topjohnwu.magisk.ClassMap +import com.topjohnwu.magisk.Config +import com.topjohnwu.magisk.Const +import com.topjohnwu.magisk.data.database.MagiskDB +import com.topjohnwu.magisk.ui.surequest.SuRequestActivity +import com.topjohnwu.magisk.utils.DownloadApp +import com.topjohnwu.magisk.utils.RootUtils +import com.topjohnwu.magisk.utils.SuLogger +import com.topjohnwu.magisk.utils.get +import com.topjohnwu.magisk.view.Notifications +import com.topjohnwu.magisk.view.Shortcuts +import com.topjohnwu.superuser.Shell + +open class GeneralReceiver : BroadcastReceiver() { + + 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 ?: "" + } + + override fun onReceive(context: Context, intent: Intent?) { + if (intent == null) + return + val mDB: MagiskDB = get() + var action: String? = intent.action ?: return + when (action) { + Intent.ACTION_REBOOT, Intent.ACTION_BOOT_COMPLETED -> { + action = intent.getStringExtra("action") + if (action == null) { + // Actual boot completed event + Shell.su("mm_patch_dtbo").submit { result -> + if (result.isSuccess) + Notifications.dtboPatched() + } + return + } + when (action) { + REQUEST -> { + val i = Intent(context, ClassMap.get(SuRequestActivity::class.java)) + .setAction(action) + .putExtra("socket", intent.getStringExtra("socket")) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) + context.startActivity(i) + } + LOG -> SuLogger.handleLogs(intent) + NOTIFY -> SuLogger.handleNotify(intent) + TEST -> Shell.su("magisk --use-broadcast").submit() + } + } + Intent.ACTION_PACKAGE_REPLACED -> + // This will only work pre-O + if (Config.get(Config.Key.SU_REAUTH)!!) { + mDB.deletePolicy(getPkg(intent)) + } + Intent.ACTION_PACKAGE_FULLY_REMOVED -> { + val pkg = getPkg(intent) + mDB.deletePolicy(pkg) + Shell.su("magiskhide --rm $pkg").submit() + } + Intent.ACTION_LOCALE_CHANGED -> Shortcuts.setup(context) + Const.Key.BROADCAST_MANAGER_UPDATE -> { + Config.managerLink = intent.getStringExtra(Const.Key.INTENT_SET_LINK) + DownloadApp.upgrade(intent.getStringExtra(Const.Key.INTENT_SET_NAME)) + } + Const.Key.BROADCAST_REBOOT -> RootUtils.reboot() + } + } +} 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 9ad1b2f62..dddf4d75a 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,6 +10,7 @@ import com.topjohnwu.magisk.R import com.topjohnwu.magisk.databinding.ActivityRequestBinding import com.topjohnwu.magisk.model.entity.Policy import com.topjohnwu.magisk.model.events.DieEvent +import com.topjohnwu.magisk.model.receiver.GeneralReceiver import com.topjohnwu.magisk.ui.base.MagiskActivity import com.topjohnwu.magisk.utils.SuLogger import org.koin.androidx.viewmodel.ext.android.viewModel @@ -31,15 +32,15 @@ open class SuRequestActivity : MagiskActivity%li)", uid, time(nullptr)); diff --git a/native/jni/core/magisk.cpp b/native/jni/core/magisk.cpp index 3468728cd..b14300f9f 100644 --- a/native/jni/core/magisk.cpp +++ b/native/jni/core/magisk.cpp @@ -12,6 +12,8 @@ #include #include +using namespace std::literals; + [[noreturn]] static void usage() { fprintf(stderr, FULL_VER(Magisk) " multi-call binary\n" @@ -33,6 +35,7 @@ " --clone-attr SRC DEST clone permission, owner, and selinux context\n" " --clone SRC DEST clone SRC to DEST\n" " --sqlite SQL exec SQL to Magisk database\n" + " --use-broadcast use broadcast for su logging and notify\n" "\n" "Supported init triggers:\n" " post-fs-data, service, boot-complete\n" @@ -48,66 +51,70 @@ int magisk_main(int argc, char *argv[]) { if (argc < 2) usage(); - if (strcmp(argv[1], "-c") == 0) { + if (argv[1] == "-c"sv) { printf(MAGISK_VERSION ":MAGISK (" str(MAGISK_VER_CODE) ")\n"); return 0; - } else if (strcmp(argv[1], "-v") == 0) { + } else if (argv[1] == "-v"sv) { int fd = connect_daemon(); write_int(fd, CHECK_VERSION); char *v = read_string(fd); printf("%s\n", v); free(v); return 0; - } else if (strcmp(argv[1], "-V") == 0) { + } else if (argv[1] == "-V"sv) { int fd = connect_daemon(); write_int(fd, CHECK_VERSION_CODE); printf("%d\n", read_int(fd)); return 0; - } else if (strcmp(argv[1], "--list") == 0) { + } else if (argv[1] == "--list"sv) { for (int i = 0; applet_names[i]; ++i) printf("%s\n", applet_names[i]); return 0; - } else if (strcmp(argv[1], "--unlock-blocks") == 0) { + } else if (argv[1] == "--unlock-blocks"sv) { unlock_blocks(); return 0; - } else if (strcmp(argv[1], "--restorecon") == 0) { + } else if (argv[1] == "--restorecon"sv) { restore_rootcon(); restorecon(); return 0; - } else if (strcmp(argv[1], "--clone-attr") == 0) { + } else if (argv[1] == "--clone-attr"sv) { if (argc < 4) usage(); clone_attr(argv[2], argv[3]); return 0; - } else if (strcmp(argv[1], "--clone") == 0) { + } else if (argv[1] == "--clone"sv) { if (argc < 4) usage(); cp_afc(argv[2], argv[3]); return 0; - } else if (strcmp(argv[1], "--daemon") == 0) { + } else if (argv[1] == "--daemon"sv) { int fd = connect_daemon(true); write_int(fd, DO_NOTHING); return 0; - } else if (strcmp(argv[1], "--post-fs-data") == 0) { + } else if (argv[1] == "--post-fs-data"sv) { int fd = connect_daemon(true); write_int(fd, POST_FS_DATA); return read_int(fd); - } else if (strcmp(argv[1], "--service") == 0) { + } else if (argv[1] == "--service"sv) { int fd = connect_daemon(true); write_int(fd, LATE_START); return read_int(fd); - } else if (strcmp(argv[1], "--boot-complete") == 0) { + } else if (argv[1] == "--boot-complete"sv) { int fd = connect_daemon(true); write_int(fd, BOOT_COMPLETE); return read_int(fd); - } else if (strcmp(argv[1], "--sqlite") == 0) { + } else if (argv[1] == "--sqlite"sv) { int fd = connect_daemon(); write_int(fd, SQLITE_CMD); write_string(fd, argv[2]); send_fd(fd, STDOUT_FILENO); return read_int(fd); + } else if (argv[1] == "--use-broadcast"sv) { + int fd = connect_daemon(); + write_int(fd, BROADCAST_ACK); + return 0; } #if 0 /* Entry point for testing stuffs */ - else if (strcmp(argv[1], "--test") == 0) { + else if (argv[1] == "--test"sv) { return 0; } #endif diff --git a/native/jni/include/daemon.h b/native/jni/include/daemon.h index 41baeb9c8..bfc45041c 100644 --- a/native/jni/include/daemon.h +++ b/native/jni/include/daemon.h @@ -17,7 +17,7 @@ enum { BOOT_COMPLETE, MAGISKHIDE, SQLITE_CMD, - ZYGOTE_NOTIFY, + BROADCAST_ACK, }; // Return codes for daemon @@ -82,6 +82,8 @@ void magiskhide_handler(int client); *************/ void su_daemon_handler(int client, struct ucred *credential); +void broadcast_test(); extern int SDK_INT; extern bool RECOVERY_MODE; +extern bool CONNECT_BROADCAST; diff --git a/native/jni/include/db.h b/native/jni/include/db.h index 50f8605ca..7b5726e0a 100644 --- a/native/jni/include/db.h +++ b/native/jni/include/db.h @@ -154,7 +154,7 @@ typedef std::function db_row_cb; int get_db_settings(db_settings &cfg, int key = -1); int get_db_strings(db_strings &str, int key = -1); -int get_uid_policy(int uid, su_access &su); +int get_uid_policy(su_access &su, int uid); int validate_manager(std::string &alt_pkg, int userid, struct stat *st); void exec_sql(int client); char *db_exec(const char *sql); diff --git a/native/jni/su/connect.cpp b/native/jni/su/connect.cpp index f68997f18..4cf8720f1 100644 --- a/native/jni/su/connect.cpp +++ b/native/jni/su/connect.cpp @@ -11,12 +11,21 @@ #include "su.h" +bool CONNECT_BROADCAST; + #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" + +// 0x00000020 = FLAG_INCLUDE_STOPPED_PACKAGES + static inline const char *get_command(const su_request *to) { if (to->command[0]) return to->command; @@ -39,9 +48,9 @@ static inline void get_uid(char *uid, su_info *info) { : info->uid); } -static void silent_run(const char **args, su_info *info) { +static void exec_am_cmd(const char **args, su_info *info) { char component[128]; - sprintf(component, "%s/a.m", info->str[SU_MANAGER].data()); + sprintf(component, "%s/%s", info->str[SU_MANAGER].data(), args[3][0] == 'b' ? "a.h" : "a.m"); char user[8]; get_user(user, info); @@ -62,6 +71,16 @@ static void silent_run(const char **args, su_info *info) { exec_command(exec); } +#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", \ +nullptr + void app_log(su_context *ctx) { char fromUid[8]; get_uid(fromUid, ctx->info); @@ -75,19 +94,21 @@ void app_log(su_context *ctx) { char policy[2]; sprintf(policy, "%d", ctx->info->access.policy); - const char *cmd[] = { - START_ACTIVITY, "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", - nullptr - }; - silent_run(cmd, ctx->info); + if (CONNECT_BROADCAST) { + const char *cmd[] = { START_BROADCAST, LOG_BODY }; + exec_am_cmd(cmd, ctx->info); + } else { + const char *cmd[] = { START_ACTIVITY, LOG_BODY }; + exec_am_cmd(cmd, ctx->info); + } } +#define NOTIFY_BODY \ +"notify", \ +"--ei", "from.uid", fromUid, \ +"--ei", "policy", policy, \ +nullptr + void app_notify(su_context *ctx) { char fromUid[8]; get_uid(fromUid, ctx->info); @@ -95,13 +116,14 @@ void app_notify(su_context *ctx) { char policy[2]; sprintf(policy, "%d", ctx->info->access.policy); - const char *cmd[] = { - START_ACTIVITY, "notify", - "--ei", "from.uid", fromUid, - "--ei", "policy", policy, - nullptr - }; - silent_run(cmd, ctx->info); + if (CONNECT_BROADCAST) { + const char *cmd[] = { START_BROADCAST, NOTIFY_BODY }; + exec_am_cmd(cmd, ctx->info); + } else { + const char *cmd[] = { START_ACTIVITY, NOTIFY_BODY }; + exec_am_cmd(cmd, ctx->info); + } + } void app_connect(const char *socket, su_info *info) { @@ -110,7 +132,17 @@ void app_connect(const char *socket, su_info *info) { "--es", "socket", socket, nullptr }; - silent_run(cmd, info); + exec_am_cmd(cmd, info); +} + +void broadcast_test() { + su_info info; + get_db_settings(info.cfg); + get_db_strings(info.str); + validate_manager(info.str[SU_MANAGER], 0, &info.mgr_st); + + const char *cmd[] = { START_BROADCAST, "test", nullptr }; + exec_am_cmd(cmd, &info); } void socket_send_request(int fd, su_info *info) { diff --git a/native/jni/su/su.h b/native/jni/su/su.h index 4d7822c02..33672da7e 100644 --- a/native/jni/su/su.h +++ b/native/jni/su/su.h @@ -32,7 +32,7 @@ public: int ref; time_t timestamp; - su_info(unsigned uid); + su_info(unsigned uid = 0); ~su_info(); void lock(); void unlock(); diff --git a/native/jni/su/su_daemon.cpp b/native/jni/su/su_daemon.cpp index 3d69bddda..d9f022e4d 100644 --- a/native/jni/su/su_daemon.cpp +++ b/native/jni/su/su_daemon.cpp @@ -83,7 +83,7 @@ static void database_check(su_info *info) { } if (uid > 0) - get_uid_policy(uid, info->access); + get_uid_policy(info->access, uid); // We need to check our manager if (info->access.log || info->access.notify)