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
This commit is contained in:
topjohnwu 2019-05-13 02:01:10 -07:00
parent 89275270f3
commit 80cd85b061
12 changed files with 173 additions and 133 deletions

View File

@ -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;
}
}
}

View File

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

View File

@ -10,6 +10,7 @@ import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.ActivityRequestBinding import com.topjohnwu.magisk.databinding.ActivityRequestBinding
import com.topjohnwu.magisk.model.entity.Policy import com.topjohnwu.magisk.model.entity.Policy
import com.topjohnwu.magisk.model.events.DieEvent 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.ui.base.MagiskActivity
import com.topjohnwu.magisk.utils.SuLogger import com.topjohnwu.magisk.utils.SuLogger
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
@ -31,15 +32,15 @@ open class SuRequestActivity : MagiskActivity<SuRequestViewModel, ActivityReques
val intent = intent val intent = intent
val action = intent.action val action = intent.action
if (TextUtils.equals(action, REQUEST)) { if (TextUtils.equals(action, GeneralReceiver.REQUEST)) {
if (!viewModel.handleRequest(intent) {}) if (!viewModel.handleRequest(intent) {})
finish() finish()
return return
} }
if (TextUtils.equals(action, LOG)) if (TextUtils.equals(action, GeneralReceiver.LOG))
SuLogger.handleLogs(intent) SuLogger.handleLogs(intent)
else if (TextUtils.equals(action, NOTIFY)) else if (TextUtils.equals(action, GeneralReceiver.NOTIFY))
SuLogger.handleNotify(intent) SuLogger.handleNotify(intent)
finish() finish()
@ -58,10 +59,4 @@ open class SuRequestActivity : MagiskActivity<SuRequestViewModel, ActivityReques
else else
ActivityInfo.SCREEN_ORIENTATION_LOCKED ActivityInfo.SCREEN_ORIENTATION_LOCKED
} }
companion object {
const val REQUEST = "request"
const val LOG = "log"
const val NOTIFY = "notify"
}
} }

View File

@ -732,4 +732,7 @@ void boot_complete(int client) {
install_apk("/data/magisk.apk"); install_apk("/data/magisk.apk");
} }
} }
// Test whether broadcast can be used or not
broadcast_test();
} }

View File

@ -54,6 +54,7 @@ static void *request_handler(void *args) {
case LATE_START: case LATE_START:
case BOOT_COMPLETE: case BOOT_COMPLETE:
case SQLITE_CMD: case SQLITE_CMD:
case BROADCAST_ACK:
if (credential.uid != 0) { if (credential.uid != 0) {
write_int(client, ROOT_REQUIRED); write_int(client, ROOT_REQUIRED);
close(client); close(client);
@ -90,6 +91,10 @@ static void *request_handler(void *args) {
case SQLITE_CMD: case SQLITE_CMD:
exec_sql(client); exec_sql(client);
break; break;
case BROADCAST_ACK:
LOGD("* Use broadcasts for su logging and notify\n");
CONNECT_BROADCAST = true;
close(client);
default: default:
close(client); close(client);
break; break;

View File

@ -233,7 +233,7 @@ int get_db_strings(db_strings &str, int key) {
return 0; return 0;
} }
int get_uid_policy(int uid, su_access &su) { int get_uid_policy(su_access &su, int uid) {
char query[256], *err; char query[256], *err;
sprintf(query, "SELECT policy, logging, notification FROM policies " sprintf(query, "SELECT policy, logging, notification FROM policies "
"WHERE uid=%d AND (until=0 OR until>%li)", uid, time(nullptr)); "WHERE uid=%d AND (until=0 OR until>%li)", uid, time(nullptr));

View File

@ -12,6 +12,8 @@
#include <db.h> #include <db.h>
#include <flags.h> #include <flags.h>
using namespace std::literals;
[[noreturn]] static void usage() { [[noreturn]] static void usage() {
fprintf(stderr, fprintf(stderr,
FULL_VER(Magisk) " multi-call binary\n" FULL_VER(Magisk) " multi-call binary\n"
@ -33,6 +35,7 @@
" --clone-attr SRC DEST clone permission, owner, and selinux context\n" " --clone-attr SRC DEST clone permission, owner, and selinux context\n"
" --clone SRC DEST clone SRC to DEST\n" " --clone SRC DEST clone SRC to DEST\n"
" --sqlite SQL exec SQL to Magisk database\n" " --sqlite SQL exec SQL to Magisk database\n"
" --use-broadcast use broadcast for su logging and notify\n"
"\n" "\n"
"Supported init triggers:\n" "Supported init triggers:\n"
" post-fs-data, service, boot-complete\n" " post-fs-data, service, boot-complete\n"
@ -48,66 +51,70 @@
int magisk_main(int argc, char *argv[]) { int magisk_main(int argc, char *argv[]) {
if (argc < 2) if (argc < 2)
usage(); usage();
if (strcmp(argv[1], "-c") == 0) { if (argv[1] == "-c"sv) {
printf(MAGISK_VERSION ":MAGISK (" str(MAGISK_VER_CODE) ")\n"); printf(MAGISK_VERSION ":MAGISK (" str(MAGISK_VER_CODE) ")\n");
return 0; return 0;
} else if (strcmp(argv[1], "-v") == 0) { } else if (argv[1] == "-v"sv) {
int fd = connect_daemon(); int fd = connect_daemon();
write_int(fd, CHECK_VERSION); write_int(fd, CHECK_VERSION);
char *v = read_string(fd); char *v = read_string(fd);
printf("%s\n", v); printf("%s\n", v);
free(v); free(v);
return 0; return 0;
} else if (strcmp(argv[1], "-V") == 0) { } else if (argv[1] == "-V"sv) {
int fd = connect_daemon(); int fd = connect_daemon();
write_int(fd, CHECK_VERSION_CODE); write_int(fd, CHECK_VERSION_CODE);
printf("%d\n", read_int(fd)); printf("%d\n", read_int(fd));
return 0; return 0;
} else if (strcmp(argv[1], "--list") == 0) { } else if (argv[1] == "--list"sv) {
for (int i = 0; applet_names[i]; ++i) for (int i = 0; applet_names[i]; ++i)
printf("%s\n", applet_names[i]); printf("%s\n", applet_names[i]);
return 0; return 0;
} else if (strcmp(argv[1], "--unlock-blocks") == 0) { } else if (argv[1] == "--unlock-blocks"sv) {
unlock_blocks(); unlock_blocks();
return 0; return 0;
} else if (strcmp(argv[1], "--restorecon") == 0) { } else if (argv[1] == "--restorecon"sv) {
restore_rootcon(); restore_rootcon();
restorecon(); restorecon();
return 0; return 0;
} else if (strcmp(argv[1], "--clone-attr") == 0) { } else if (argv[1] == "--clone-attr"sv) {
if (argc < 4) usage(); if (argc < 4) usage();
clone_attr(argv[2], argv[3]); clone_attr(argv[2], argv[3]);
return 0; return 0;
} else if (strcmp(argv[1], "--clone") == 0) { } else if (argv[1] == "--clone"sv) {
if (argc < 4) usage(); if (argc < 4) usage();
cp_afc(argv[2], argv[3]); cp_afc(argv[2], argv[3]);
return 0; return 0;
} else if (strcmp(argv[1], "--daemon") == 0) { } else if (argv[1] == "--daemon"sv) {
int fd = connect_daemon(true); int fd = connect_daemon(true);
write_int(fd, DO_NOTHING); write_int(fd, DO_NOTHING);
return 0; return 0;
} else if (strcmp(argv[1], "--post-fs-data") == 0) { } else if (argv[1] == "--post-fs-data"sv) {
int fd = connect_daemon(true); int fd = connect_daemon(true);
write_int(fd, POST_FS_DATA); write_int(fd, POST_FS_DATA);
return read_int(fd); return read_int(fd);
} else if (strcmp(argv[1], "--service") == 0) { } else if (argv[1] == "--service"sv) {
int fd = connect_daemon(true); int fd = connect_daemon(true);
write_int(fd, LATE_START); write_int(fd, LATE_START);
return read_int(fd); return read_int(fd);
} else if (strcmp(argv[1], "--boot-complete") == 0) { } else if (argv[1] == "--boot-complete"sv) {
int fd = connect_daemon(true); int fd = connect_daemon(true);
write_int(fd, BOOT_COMPLETE); write_int(fd, BOOT_COMPLETE);
return read_int(fd); return read_int(fd);
} else if (strcmp(argv[1], "--sqlite") == 0) { } else if (argv[1] == "--sqlite"sv) {
int fd = connect_daemon(); int fd = connect_daemon();
write_int(fd, SQLITE_CMD); write_int(fd, SQLITE_CMD);
write_string(fd, argv[2]); write_string(fd, argv[2]);
send_fd(fd, STDOUT_FILENO); send_fd(fd, STDOUT_FILENO);
return read_int(fd); return read_int(fd);
} else if (argv[1] == "--use-broadcast"sv) {
int fd = connect_daemon();
write_int(fd, BROADCAST_ACK);
return 0;
} }
#if 0 #if 0
/* Entry point for testing stuffs */ /* Entry point for testing stuffs */
else if (strcmp(argv[1], "--test") == 0) { else if (argv[1] == "--test"sv) {
return 0; return 0;
} }
#endif #endif

View File

@ -17,7 +17,7 @@ enum {
BOOT_COMPLETE, BOOT_COMPLETE,
MAGISKHIDE, MAGISKHIDE,
SQLITE_CMD, SQLITE_CMD,
ZYGOTE_NOTIFY, BROADCAST_ACK,
}; };
// Return codes for daemon // Return codes for daemon
@ -82,6 +82,8 @@ void magiskhide_handler(int client);
*************/ *************/
void su_daemon_handler(int client, struct ucred *credential); void su_daemon_handler(int client, struct ucred *credential);
void broadcast_test();
extern int SDK_INT; extern int SDK_INT;
extern bool RECOVERY_MODE; extern bool RECOVERY_MODE;
extern bool CONNECT_BROADCAST;

View File

@ -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_settings(db_settings &cfg, int key = -1);
int get_db_strings(db_strings &str, 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); int validate_manager(std::string &alt_pkg, int userid, struct stat *st);
void exec_sql(int client); void exec_sql(int client);
char *db_exec(const char *sql); char *db_exec(const char *sql);

View File

@ -11,12 +11,21 @@
#include "su.h" #include "su.h"
bool CONNECT_BROADCAST;
#define START_ACTIVITY \ #define START_ACTIVITY \
"/system/bin/app_process", "/system/bin", "com.android.commands.am.Am", \ "/system/bin/app_process", "/system/bin", "com.android.commands.am.Am", \
"start", "-n", nullptr, "--user", nullptr, "-f", "0x18000020", "-a" "start", "-n", nullptr, "--user", nullptr, "-f", "0x18000020", "-a"
// 0x18000020 = FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_MULTIPLE_TASK|FLAG_INCLUDE_STOPPED_PACKAGES // 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) { static inline const char *get_command(const su_request *to) {
if (to->command[0]) if (to->command[0])
return to->command; return to->command;
@ -39,9 +48,9 @@ static inline void get_uid(char *uid, su_info *info) {
: info->uid); : 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]; 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]; char user[8];
get_user(user, info); get_user(user, info);
@ -62,6 +71,16 @@ static void silent_run(const char **args, su_info *info) {
exec_command(exec); 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) { void app_log(su_context *ctx) {
char fromUid[8]; char fromUid[8];
get_uid(fromUid, ctx->info); get_uid(fromUid, ctx->info);
@ -75,18 +94,20 @@ void app_log(su_context *ctx) {
char policy[2]; char policy[2];
sprintf(policy, "%d", ctx->info->access.policy); sprintf(policy, "%d", ctx->info->access.policy);
const char *cmd[] = { if (CONNECT_BROADCAST) {
START_ACTIVITY, "log", const char *cmd[] = { START_BROADCAST, LOG_BODY };
"--ei", "from.uid", fromUid, exec_am_cmd(cmd, ctx->info);
"--ei", "to.uid", toUid, } else {
"--ei", "pid", pid, const char *cmd[] = { START_ACTIVITY, LOG_BODY };
"--ei", "policy", policy, exec_am_cmd(cmd, ctx->info);
"--es", "command", get_command(&ctx->req),
"--ez", "notify", ctx->info->access.notify ? "true" : "false",
nullptr
};
silent_run(cmd, ctx->info);
} }
}
#define NOTIFY_BODY \
"notify", \
"--ei", "from.uid", fromUid, \
"--ei", "policy", policy, \
nullptr
void app_notify(su_context *ctx) { void app_notify(su_context *ctx) {
char fromUid[8]; char fromUid[8];
@ -95,13 +116,14 @@ void app_notify(su_context *ctx) {
char policy[2]; char policy[2];
sprintf(policy, "%d", ctx->info->access.policy); sprintf(policy, "%d", ctx->info->access.policy);
const char *cmd[] = { if (CONNECT_BROADCAST) {
START_ACTIVITY, "notify", const char *cmd[] = { START_BROADCAST, NOTIFY_BODY };
"--ei", "from.uid", fromUid, exec_am_cmd(cmd, ctx->info);
"--ei", "policy", policy, } else {
nullptr const char *cmd[] = { START_ACTIVITY, NOTIFY_BODY };
}; exec_am_cmd(cmd, ctx->info);
silent_run(cmd, ctx->info); }
} }
void app_connect(const char *socket, su_info *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, "--es", "socket", socket,
nullptr 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) { void socket_send_request(int fd, su_info *info) {

View File

@ -32,7 +32,7 @@ public:
int ref; int ref;
time_t timestamp; time_t timestamp;
su_info(unsigned uid); su_info(unsigned uid = 0);
~su_info(); ~su_info();
void lock(); void lock();
void unlock(); void unlock();

View File

@ -83,7 +83,7 @@ static void database_check(su_info *info) {
} }
if (uid > 0) if (uid > 0)
get_uid_policy(uid, info->access); get_uid_policy(info->access, uid);
// We need to check our manager // We need to check our manager
if (info->access.log || info->access.notify) if (info->access.log || info->access.notify)