Commit 80cd85b0 authored by topjohnwu's avatar topjohnwu

Try to use broadcast for su logging and notify

In commit 8d4c4072, 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
parent 89275270
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;
}
}
}
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<Any>(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<Boolean>(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()
}
}
}
......@@ -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<SuRequestViewModel, ActivityReques
val intent = intent
val action = intent.action
if (TextUtils.equals(action, REQUEST)) {
if (TextUtils.equals(action, GeneralReceiver.REQUEST)) {
if (!viewModel.handleRequest(intent) {})
finish()
return
}
if (TextUtils.equals(action, LOG))
if (TextUtils.equals(action, GeneralReceiver.LOG))
SuLogger.handleLogs(intent)
else if (TextUtils.equals(action, NOTIFY))
else if (TextUtils.equals(action, GeneralReceiver.NOTIFY))
SuLogger.handleNotify(intent)
finish()
......@@ -58,10 +59,4 @@ open class SuRequestActivity : MagiskActivity<SuRequestViewModel, ActivityReques
else
ActivityInfo.SCREEN_ORIENTATION_LOCKED
}
companion object {
const val REQUEST = "request"
const val LOG = "log"
const val NOTIFY = "notify"
}
}
......@@ -732,4 +732,7 @@ void boot_complete(int client) {
install_apk("/data/magisk.apk");
}
}
// Test whether broadcast can be used or not
broadcast_test();
}
......@@ -54,6 +54,7 @@ static void *request_handler(void *args) {
case LATE_START:
case BOOT_COMPLETE:
case SQLITE_CMD:
case BROADCAST_ACK:
if (credential.uid != 0) {
write_int(client, ROOT_REQUIRED);
close(client);
......@@ -90,6 +91,10 @@ static void *request_handler(void *args) {
case SQLITE_CMD:
exec_sql(client);
break;
case BROADCAST_ACK:
LOGD("* Use broadcasts for su logging and notify\n");
CONNECT_BROADCAST = true;
close(client);
default:
close(client);
break;
......
......@@ -233,7 +233,7 @@ int get_db_strings(db_strings &str, int key) {
return 0;
}
int get_uid_policy(int uid, su_access &su) {
int get_uid_policy(su_access &su, int uid) {
char query[256], *err;
sprintf(query, "SELECT policy, logging, notification FROM policies "
"WHERE uid=%d AND (until=0 OR until>%li)", uid, time(nullptr));
......
......@@ -12,6 +12,8 @@
#include <db.h>
#include <flags.h>
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
......
......@@ -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;
......@@ -154,7 +154,7 @@ typedef std::function<bool(db_row&)> 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);
......
......@@ -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) {
......
......@@ -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();
......
......@@ -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)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment