manager: Add the ability to get a list of active managers

This commit is contained in:
ShirkNeko
2025-07-06 00:26:42 +08:00
parent 42b883240e
commit 2bd6929d24
12 changed files with 216 additions and 4 deletions

View File

@@ -139,6 +139,10 @@ int ksu_get_manager_signature_index(uid_t uid)
int signature_index = -1;
int i;
if (ksu_manager_uid != KSU_INVALID_UID && uid == ksu_manager_uid) {
return 1;
}
if (!is_dynamic_sign_enabled()) {
return -1;
}
@@ -156,6 +160,7 @@ int ksu_get_manager_signature_index(uid_t uid)
return signature_index;
}
static void clear_all_managers(void)
{
unsigned long flags;
@@ -442,6 +447,40 @@ void ksu_dynamic_sign_exit(void)
pr_info("Dynamic sign exited with persistent storage\n");
}
// Get active managers for multi-manager APKs
int ksu_get_active_managers(struct manager_list_info *info)
{
unsigned long flags;
int i, count = 0;
if (!info) {
return -EINVAL;
}
if (ksu_manager_uid != KSU_INVALID_UID && count < 2) {
info->managers[count].uid = ksu_manager_uid;
info->managers[count].signature_index = 1;
count++;
}
if (is_dynamic_sign_enabled()) {
spin_lock_irqsave(&managers_lock, flags);
for (i = 0; i < MAX_MANAGERS && count < 2; i++) {
if (active_managers[i].is_active) {
info->managers[count].uid = active_managers[i].uid;
info->managers[count].signature_index = active_managers[i].signature_index;
count++;
}
}
spin_unlock_irqrestore(&managers_lock, flags);
}
info->count = count;
return 0;
}
struct sdesc {
struct shash_desc shash;
char ctx[];

View File

@@ -3,6 +3,7 @@
#include <linux/types.h>
#include "ksu.h"
#include "manager.h"
bool is_manager_apk(char *path);
@@ -23,6 +24,7 @@ void ksu_add_manager(uid_t uid, int signature_index);
void ksu_remove_manager(uid_t uid);
bool ksu_is_any_manager(uid_t uid);
int ksu_get_manager_signature_index(uid_t uid);
int ksu_get_active_managers(struct manager_list_info *info);
int ksu_handle_dynamic_sign(struct dynamic_sign_user_config *config);
void ksu_dynamic_sign_init(void);

View File

@@ -364,6 +364,27 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
return 0;
}
// Allow root manager to get active managers
if (arg2 == CMD_GET_MANAGERS) {
if (!from_root && !from_manager) {
return 0;
}
struct manager_list_info manager_info;
int ret = ksu_get_active_managers(&manager_info);
if (ret == 0) {
if (copy_to_user((void __user *)arg3, &manager_info, sizeof(manager_info))) {
pr_err("copy manager list failed\n");
return 0;
}
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("get_managers: prctl reply error\n");
}
}
return 0;
}
if (arg2 == CMD_REPORT_EVENT) {
if (!from_root) {
return 0;

View File

@@ -28,6 +28,7 @@
#define CMD_ENABLE_KPM 100
#define CMD_DYNAMIC_SIGN 103
#define CMD_GET_MANAGERS 104
#define EVENT_POST_FS_DATA 1
#define EVENT_BOOT_COMPLETED 2
@@ -55,6 +56,14 @@ struct dynamic_sign_user_config {
char hash[65];
};
struct manager_list_info {
int count;
struct {
uid_t uid;
int signature_index;
} managers[2];
};
struct root_profile {
int32_t uid;
int32_t gid;

View File

@@ -394,4 +394,43 @@ NativeBridgeNP(clearDynamicSign, jboolean) {
bool result = clear_dynamic_sign();
LogDebug("clearDynamicSign: result=%d", result);
return result;
}
}
NativeBridgeNP(getManagersList, jobject) {
struct manager_list_info info;
bool result = get_managers_list(&info);
if (!result) {
LogDebug("getManagersList: failed to get managers list");
return NULL;
}
jclass cls = GetEnvironment()->FindClass(env, "com/sukisu/ultra/Natives$ManagersList");
jmethodID constructor = GetEnvironment()->GetMethodID(env, cls, "<init>", "()V");
jobject obj = GetEnvironment()->NewObject(env, cls, constructor);
jfieldID countField = GetEnvironment()->GetFieldID(env, cls, "count", "I");
jfieldID managersField = GetEnvironment()->GetFieldID(env, cls, "managers", "Ljava/util/List;");
GetEnvironment()->SetIntField(env, obj, countField, (jint)info.count);
jclass arrayListCls = GetEnvironment()->FindClass(env, "java/util/ArrayList");
jmethodID arrayListConstructor = GetEnvironment()->GetMethodID(env, arrayListCls, "<init>", "()V");
jobject managersList = GetEnvironment()->NewObject(env, arrayListCls, arrayListConstructor);
jmethodID addMethod = GetEnvironment()->GetMethodID(env, arrayListCls, "add", "(Ljava/lang/Object;)Z");
jclass managerInfoCls = GetEnvironment()->FindClass(env, "com/sukisu/ultra/Natives$ManagerInfo");
jmethodID managerInfoConstructor = GetEnvironment()->GetMethodID(env, managerInfoCls, "<init>", "(II)V");
for (int i = 0; i < info.count; i++) {
jobject managerInfo = GetEnvironment()->NewObject(env, managerInfoCls, managerInfoConstructor,
(jint)info.managers[i].uid,
(jint)info.managers[i].signature_index);
GetEnvironment()->CallBooleanMethod(env, managersList, addMethod, managerInfo);
}
GetEnvironment()->SetObjectField(env, obj, managersField, managersList);
LogDebug("getManagersList: count=%d", info.count);
return obj;
}

View File

@@ -37,6 +37,7 @@
#define CMD_HOOK_TYPE 101
#define CMD_GET_SUSFS_FEATURE_STATUS 102
#define CMD_DYNAMIC_SIGN 103
#define CMD_GET_MANAGERS 104
#define DYNAMIC_SIGN_OP_SET 0
#define DYNAMIC_SIGN_OP_GET 1
@@ -173,4 +174,12 @@ bool clear_dynamic_sign() {
struct dynamic_sign_user_config config;
config.operation = DYNAMIC_SIGN_OP_CLEAR;
return ksuctl(CMD_DYNAMIC_SIGN, &config, NULL);
}
bool get_managers_list(struct manager_list_info* info) {
if (info == NULL) {
return false;
}
return ksuctl(CMD_GET_MANAGERS, info, NULL);
}

View File

@@ -7,6 +7,7 @@
#include "prelude.h"
#include <linux/capability.h>
#include <sys/types.h>
bool become_manager(const char *);
@@ -105,6 +106,14 @@ struct app_profile {
};
};
struct manager_list_info {
int count;
struct {
uid_t uid;
int signature_index;
} managers[2];
};
bool set_app_profile(const struct app_profile* profile);
bool get_app_profile(char* key, struct app_profile* profile);
@@ -125,4 +134,6 @@ bool get_dynamic_sign(struct dynamic_sign_user_config* config);
bool clear_dynamic_sign();
bool get_managers_list(struct manager_list_info* info);
#endif //KERNELSU_KSU_H

View File

@@ -118,6 +118,12 @@ object Natives {
*/
external fun clearDynamicSign(): Boolean
/**
* Get active managers list when dynamic sign is enabled
* @return ManagersList object containing active managers, or null if failed or not enabled
*/
external fun getManagersList(): ManagersList?
private const val NON_ROOT_DEFAULT_PROFILE_KEY = "$"
private const val NOBODY_UID = 9999
@@ -177,7 +183,6 @@ object Natives {
val size: Int = 0,
val hash: String = ""
) : Parcelable {
constructor() : this(0, "")
fun isValid(): Boolean {
return size > 0 && hash.length == 64 && hash.all {
@@ -186,6 +191,22 @@ object Natives {
}
}
@Immutable
@Parcelize
@Keep
data class ManagersList(
val count: Int = 0,
val managers: List<ManagerInfo> = emptyList()
) : Parcelable
@Immutable
@Parcelize
@Keep
data class ManagerInfo(
val uid: Int = 0,
val signatureIndex: Int = 0
) : Parcelable
@Immutable
@Parcelize
@Keep

View File

@@ -719,6 +719,32 @@ private fun InfoCard(
icon = Icons.Default.SettingsSuggest,
)
// 活跃管理器
if (systemInfo.isDynamicSignEnabled && systemInfo.managersList != null) {
val signatureMap = systemInfo.managersList.managers.groupBy { it.signatureIndex }
val managersText = buildString {
signatureMap.toSortedMap().forEach { (signatureIndex, managers) ->
append(managers.joinToString(", ") { "UID:${it.uid}" })
append(" ")
append(
when (signatureIndex) {
1 -> "(${stringResource(R.string.default_signature)})"
2 -> "(${stringResource(R.string.dynamic_signature)})"
else -> if (signatureIndex >= 0) "(${stringResource(R.string.signature_index, signatureIndex)})" else "(${stringResource(R.string.unknown_signature)})"
}
)
append("; ")
}
}.trimEnd(' ', ';')
InfoCardItem(
stringResource(R.string.multi_manager_list),
managersText.ifEmpty { stringResource(R.string.no_active_manager) },
icon = Icons.Default.Group,
)
}
InfoCardItem(
stringResource(R.string.home_selinux_status),
systemInfo.seLinuxStatus,

View File

@@ -60,7 +60,9 @@ class HomeViewModel : ViewModel() {
val susSUMode: String = "",
val superuserCount: Int = 0,
val moduleCount: Int = 0,
val kpmModuleCount: Int = 0
val kpmModuleCount: Int = 0,
val managersList: Natives.ManagersList? = null,
val isDynamicSignEnabled: Boolean = false
)
private val gson = Gson()
@@ -242,6 +244,26 @@ class HomeViewModel : ViewModel() {
}
}
// 获取动态签名状态和管理器列表
val dynamicSignConfig = try {
Natives.getDynamicSign()
} catch (e: Exception) {
Log.w(TAG, "Failed to get dynamic sign config", e)
null
}
val isDynamicSignEnabled = dynamicSignConfig?.isValid() == true
val managersList = if (isDynamicSignEnabled) {
try {
Natives.getManagersList()
} catch (e: Exception) {
Log.w(TAG, "Failed to get managers list", e)
null
}
} else {
null
}
systemInfo = SystemInfo(
kernelRelease = uname.release,
androidVersion = Build.VERSION.RELEASE,
@@ -256,7 +278,9 @@ class HomeViewModel : ViewModel() {
susSUMode = susSUMode,
superuserCount = getSuperuserCount(),
moduleCount = getModuleCount(),
kpmModuleCount = getKpmModuleCount()
kpmModuleCount = getKpmModuleCount(),
managersList = managersList,
isDynamicSignEnabled = isDynamicSignEnabled
)
} catch (e: Exception) {
Log.e(TAG, "Error fetching system info", e)

View File

@@ -578,4 +578,9 @@
<string name="invalid_sign_config">无效的签名配置</string>
<string name="dynamic_sign_disabled_success">动态签名已禁用</string>
<string name="dynamic_sign_clear_failed">清除动态签名错误</string>
<string name="dynamic_signature">动态</string>
<string name="signature_index">签名%1$d</string>
<string name="unknown_signature">未知</string>
<string name="multi_manager_list">活跃管理器</string>
<string name="no_active_manager">无活跃管理器</string>
</resources>

View File

@@ -580,4 +580,10 @@
<string name="invalid_sign_config">Invalid signature configuration</string>
<string name="dynamic_sign_disabled_success">Dynamic signature disabled</string>
<string name="dynamic_sign_clear_failed">Failed to clear dynamic signature</string>
<string name="dynamic_signature">Dynamic</string>
<string name="signature_index">Signature %1$d</string>
<string name="unknown_signature">Unknown</string>
<string name="multi_manager_list">Active Manager</string>
<string name="no_active_manager">No active manager</string>
<string name="default_signature">SukiSU</string>
</resources>