manager: implement app profile api call
This commit is contained in:
@@ -3,15 +3,16 @@
|
|||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
|
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include "ksu.h"
|
#include "ksu.h"
|
||||||
|
|
||||||
#define LOG_TAG "KernelSu"
|
#define LOG_TAG "KernelSU"
|
||||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_me_weishu_kernelsu_Natives_becomeManager(JNIEnv *env, jclass clazz, jstring pkg) {
|
Java_me_weishu_kernelsu_Natives_becomeManager(JNIEnv *env, jobject, jstring pkg) {
|
||||||
auto cpkg = env->GetStringUTFChars(pkg, nullptr);
|
auto cpkg = env->GetStringUTFChars(pkg, nullptr);
|
||||||
auto result = become_manager(cpkg);
|
auto result = become_manager(cpkg);
|
||||||
env->ReleaseStringUTFChars(pkg, cpkg);
|
env->ReleaseStringUTFChars(pkg, cpkg);
|
||||||
@@ -20,13 +21,13 @@ Java_me_weishu_kernelsu_Natives_becomeManager(JNIEnv *env, jclass clazz, jstring
|
|||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT jint JNICALL
|
JNIEXPORT jint JNICALL
|
||||||
Java_me_weishu_kernelsu_Natives_getVersion(JNIEnv *env, jclass clazz) {
|
Java_me_weishu_kernelsu_Natives_getVersion(JNIEnv *env, jobject) {
|
||||||
return get_version();
|
return get_version();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT jintArray JNICALL
|
JNIEXPORT jintArray JNICALL
|
||||||
Java_me_weishu_kernelsu_Natives_getAllowList(JNIEnv *env, jclass clazz) {
|
Java_me_weishu_kernelsu_Natives_getAllowList(JNIEnv *env, jobject) {
|
||||||
int uids[1024];
|
int uids[1024];
|
||||||
int size = 0;
|
int size = 0;
|
||||||
bool result = get_allow_list(uids, &size);
|
bool result = get_allow_list(uids, &size);
|
||||||
@@ -56,7 +57,7 @@ Java_me_weishu_kernelsu_Natives_getDenyList(JNIEnv *env, jclass clazz) {
|
|||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_me_weishu_kernelsu_Natives_allowRoot(JNIEnv *env, jclass clazz, jint uid, jboolean allow) {
|
Java_me_weishu_kernelsu_Natives_allowRoot(JNIEnv *env, jobject clazz, jint uid, jboolean allow) {
|
||||||
return allow_su(uid, allow);
|
return allow_su(uid, allow);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,43 +67,173 @@ Java_me_weishu_kernelsu_Natives_isSafeMode(JNIEnv *env, jclass clazz) {
|
|||||||
return is_safe_mode();
|
return is_safe_mode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void fillIntArray(JNIEnv* env, jobject list, int *data, int count) {
|
||||||
|
auto cls = env->GetObjectClass(list);
|
||||||
|
auto add = env->GetMethodID(cls, "add", "(Ljava/lang/Object;)Z");
|
||||||
|
auto integerCls = env->FindClass("java/lang/Integer");
|
||||||
|
auto constructor = env->GetMethodID(integerCls, "<init>", "(I)V");
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
auto integer = env->NewObject(integerCls, constructor, data[i]);
|
||||||
|
env->CallBooleanMethod(list, add, integer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int getListSize(JNIEnv *env, jobject list) {
|
||||||
|
auto cls = env->GetObjectClass(list);
|
||||||
|
auto size = env->GetMethodID(cls, "size", "()I");
|
||||||
|
return env->CallIntMethod(list, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jobject JNICALL
|
||||||
|
Java_me_weishu_kernelsu_Natives_getAppProfile(JNIEnv *env, jobject, jstring pkg, jint uid) {
|
||||||
|
if (env->GetStringLength(pkg) > KSU_MAX_PACKAGE_NAME) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_key_t key = {};
|
||||||
|
auto cpkg = env->GetStringUTFChars(pkg, nullptr);
|
||||||
|
strcpy(key, cpkg);
|
||||||
|
env->ReleaseStringUTFChars(pkg, cpkg);
|
||||||
|
|
||||||
|
app_profile profile = {};
|
||||||
|
strcpy(profile.key, key);
|
||||||
|
profile.current_uid = uid;
|
||||||
|
|
||||||
|
if (!get_app_profile(key, &profile)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cls = env->FindClass("me/weishu/kernelsu/Natives$Profile");
|
||||||
|
auto constructor = env->GetMethodID(cls, "<init>", "()V");
|
||||||
|
auto obj = env->NewObject(cls, constructor);
|
||||||
|
auto keyField = env->GetFieldID(cls, "name", "Ljava/lang/String;");
|
||||||
|
auto currentUidField = env->GetFieldID(cls, "currentUid", "I");
|
||||||
|
auto allowSuField = env->GetFieldID(cls, "allowSu", "Z");
|
||||||
|
|
||||||
|
auto rootUseDefaultField = env->GetFieldID(cls, "rootUseDefault", "Z");
|
||||||
|
auto rootTemplateField = env->GetFieldID(cls, "rootTemplate", "Ljava/lang/String;");
|
||||||
|
|
||||||
|
auto uidField = env->GetFieldID(cls, "uid", "I");
|
||||||
|
auto gidField = env->GetFieldID(cls, "gid", "I");
|
||||||
|
auto groupsField = env->GetFieldID(cls, "groups", "Ljava/util/List;");
|
||||||
|
auto capabilitiesField = env->GetFieldID(cls, "capabilities", "Ljava/util/List;");
|
||||||
|
auto domainField = env->GetFieldID(cls, "context", "Ljava/lang/String;");
|
||||||
|
// auto namespacesField = env->GetFieldID(cls, "namespace", "I");
|
||||||
|
|
||||||
|
auto nonRootUseDefaultField = env->GetFieldID(cls, "nonRootUseDefault", "Z");
|
||||||
|
auto umountModulesField = env->GetFieldID(cls, "umountModules", "Z");
|
||||||
|
|
||||||
|
env->SetObjectField(obj, keyField, env->NewStringUTF(profile.key));
|
||||||
|
env->SetIntField(obj, currentUidField, profile.current_uid);
|
||||||
|
|
||||||
|
auto allowSu = profile.allow_su;
|
||||||
|
|
||||||
|
if (allowSu) {
|
||||||
|
env->SetBooleanField(obj, rootUseDefaultField, (jboolean) profile.root_profile.use_default);
|
||||||
|
if (strlen(profile.root_profile.template_name) > 0) {
|
||||||
|
env->SetObjectField(obj, rootTemplateField,
|
||||||
|
env->NewStringUTF(profile.root_profile.template_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
env->SetIntField(obj, uidField, profile.root_profile.uid);
|
||||||
|
env->SetIntField(obj, gidField, profile.root_profile.gid);
|
||||||
|
|
||||||
|
jobject groupList = env->GetObjectField(obj, groupsField);
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
env->ExceptionDescribe();
|
||||||
|
}
|
||||||
|
fillIntArray(env, groupList, profile.root_profile.groups, profile.root_profile.groups_count);
|
||||||
|
|
||||||
|
jobject capList = env->GetObjectField(obj, capabilitiesField);
|
||||||
|
fillIntArray(env, capList, profile.root_profile.capabilities, 2);
|
||||||
|
|
||||||
|
env->SetObjectField(obj, domainField,
|
||||||
|
env->NewStringUTF(profile.root_profile.selinux_domain));
|
||||||
|
// env->SetIntField(obj, namespacesField, profile.root_profile.namespaces);
|
||||||
|
env->SetBooleanField(obj, allowSuField, profile.allow_su);
|
||||||
|
} else {
|
||||||
|
env->SetBooleanField(obj, nonRootUseDefaultField,
|
||||||
|
(jboolean) profile.non_root_profile.use_default);
|
||||||
|
env->SetBooleanField(obj, umountModulesField, profile.non_root_profile.umount_modules);
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_me_weishu_kernelsu_Natives_isAllowlistMode(JNIEnv *env, jclass clazz) {
|
Java_me_weishu_kernelsu_Natives_setAppProfile(JNIEnv *env, jobject clazz, jobject profile) {
|
||||||
return is_allowlist_mode();
|
auto cls = env->FindClass("me/weishu/kernelsu/Natives$Profile");
|
||||||
|
|
||||||
|
auto keyField = env->GetFieldID(cls, "name", "Ljava/lang/String;");
|
||||||
|
auto currentUidField = env->GetFieldID(cls, "currentUid", "I");
|
||||||
|
auto allowSuField = env->GetFieldID(cls, "allowSu", "Z");
|
||||||
|
|
||||||
|
auto rootUseDefaultField = env->GetFieldID(cls, "rootUseDefault", "Z");
|
||||||
|
auto rootTemplateField = env->GetFieldID(cls, "rootTemplate", "Ljava/lang/String;");
|
||||||
|
|
||||||
|
auto uidField = env->GetFieldID(cls, "uid", "I");
|
||||||
|
auto gidField = env->GetFieldID(cls, "gid", "I");
|
||||||
|
auto groupsField = env->GetFieldID(cls, "groups", "Ljava/util/List;");
|
||||||
|
auto capabilitiesField = env->GetFieldID(cls, "capabilities", "Ljava/util/List;");
|
||||||
|
auto domainField = env->GetFieldID(cls, "context", "Ljava/lang/String;");
|
||||||
|
// auto namespacesField = env->GetFieldID(cls, "namespaces", "I");
|
||||||
|
|
||||||
|
auto nonRootUseDefaultField = env->GetFieldID(cls, "nonRootUseDefault", "Z");
|
||||||
|
auto umountModulesField = env->GetFieldID(cls, "umountModules", "Z");
|
||||||
|
|
||||||
|
auto key = env->GetObjectField(profile, keyField);
|
||||||
|
if (!key) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
extern "C"
|
if (env->GetStringLength((jstring) key) > KSU_MAX_PACKAGE_NAME) {
|
||||||
JNIEXPORT jboolean JNICALL
|
return false;
|
||||||
Java_me_weishu_kernelsu_Natives_setAllowlistMode(JNIEnv *env, jclass clazz, jboolean is_allowlist) {
|
|
||||||
return set_allowlist_mode(is_allowlist);
|
|
||||||
}
|
}
|
||||||
extern "C"
|
|
||||||
JNIEXPORT jboolean JNICALL
|
auto cpkg = env->GetStringUTFChars((jstring) key, nullptr);
|
||||||
Java_me_weishu_kernelsu_Natives_addUidToAllowlist(JNIEnv *env, jclass clazz, jint uid) {
|
p_key_t p_key = {};
|
||||||
return add_to_allow_list(uid);
|
strcpy(p_key, cpkg);
|
||||||
|
env->ReleaseStringUTFChars((jstring) key, cpkg);
|
||||||
|
|
||||||
|
auto currentUid = env->GetIntField(profile, currentUidField);
|
||||||
|
|
||||||
|
auto uid = env->GetIntField(profile, uidField);
|
||||||
|
auto gid = env->GetIntField(profile, gidField);
|
||||||
|
auto groups = env->GetObjectField(profile, groupsField);
|
||||||
|
auto capabilities = env->GetObjectField(profile, capabilitiesField);
|
||||||
|
auto domain = env->GetObjectField(profile, domainField);
|
||||||
|
auto allowSu = env->GetBooleanField(profile, allowSuField);
|
||||||
|
auto umountModules = env->GetBooleanField(profile, umountModulesField);
|
||||||
|
|
||||||
|
app_profile p = {};
|
||||||
|
strcpy(p.key, p_key);
|
||||||
|
|
||||||
|
p.allow_su = allowSu;
|
||||||
|
p.current_uid = currentUid;
|
||||||
|
|
||||||
|
if (allowSu) {
|
||||||
|
p.root_profile.use_default = env->GetBooleanField(profile, rootUseDefaultField);
|
||||||
|
auto templateName = env->GetObjectField(profile, rootTemplateField);
|
||||||
|
if (templateName) {
|
||||||
|
auto ctemplateName = env->GetStringUTFChars((jstring) templateName, nullptr);
|
||||||
|
strcpy(p.root_profile.template_name, ctemplateName);
|
||||||
|
env->ReleaseStringUTFChars((jstring) templateName, ctemplateName);
|
||||||
}
|
}
|
||||||
extern "C"
|
|
||||||
JNIEXPORT jboolean JNICALL
|
p.root_profile.uid = uid;
|
||||||
Java_me_weishu_kernelsu_Natives_removeUidFromAllowlist(JNIEnv *env, jclass clazz, jint uid) {
|
p.root_profile.gid = gid;
|
||||||
return remove_from_allow_list(uid);
|
p.root_profile.groups_count = getListSize(env, groups);
|
||||||
|
|
||||||
|
auto cdomain = env->GetStringUTFChars((jstring) domain, nullptr);
|
||||||
|
strcpy(p.root_profile.selinux_domain, cdomain);
|
||||||
|
env->ReleaseStringUTFChars((jstring) domain, cdomain);
|
||||||
|
|
||||||
|
// p.root_profile.namespaces = env->GetIntField(profile, namespacesField);
|
||||||
|
} else {
|
||||||
|
p.non_root_profile.use_default = env->GetBooleanField(profile, nonRootUseDefaultField);
|
||||||
|
p.non_root_profile.umount_modules = umountModules;
|
||||||
}
|
}
|
||||||
extern "C"
|
|
||||||
JNIEXPORT jboolean JNICALL
|
return set_app_profile(&p);
|
||||||
Java_me_weishu_kernelsu_Natives_addUidToDenylist(JNIEnv *env, jclass clazz, jint uid) {
|
|
||||||
return add_to_deny_list(uid);
|
|
||||||
}
|
|
||||||
extern "C"
|
|
||||||
JNIEXPORT jboolean JNICALL
|
|
||||||
Java_me_weishu_kernelsu_Natives_removeUidFromDenylist(JNIEnv *env, jclass clazz, jint uid) {
|
|
||||||
return remove_from_deny_list(uid);
|
|
||||||
}
|
|
||||||
extern "C"
|
|
||||||
JNIEXPORT jboolean JNICALL
|
|
||||||
Java_me_weishu_kernelsu_Natives_isUidInAllowlist(JNIEnv *env, jclass clazz, jint uid) {
|
|
||||||
return is_in_allow_list(uid);
|
|
||||||
}
|
|
||||||
extern "C"
|
|
||||||
JNIEXPORT jboolean JNICALL
|
|
||||||
Java_me_weishu_kernelsu_Natives_isUidInDenylist(JNIEnv *env, jclass clazz, jint uid) {
|
|
||||||
return is_in_deny_list(uid);
|
|
||||||
}
|
}
|
||||||
@@ -22,17 +22,8 @@
|
|||||||
#define CMD_GET_DENY_LIST 6
|
#define CMD_GET_DENY_LIST 6
|
||||||
#define CMD_CHECK_SAFEMODE 9
|
#define CMD_CHECK_SAFEMODE 9
|
||||||
|
|
||||||
#define CMD_GET_WORK_MODE 10
|
#define CMD_GET_APP_PROFILE 10
|
||||||
#define CMD_SET_WORK_MODE 11
|
#define CMD_SET_APP_PROFILE 11
|
||||||
#define CMD_IN_ALLOW_LIST 12
|
|
||||||
#define CMD_IN_DENY_LIST 13
|
|
||||||
#define CMD_ADD_ALLOW_LIST 14
|
|
||||||
#define CMD_REMOVE_ALLOW_LIST 15
|
|
||||||
#define CMD_ADD_DENY_LIST 16
|
|
||||||
#define CMD_REMOVE_DENY_LIST 17
|
|
||||||
|
|
||||||
#define CMD_GET_APP_PROFILE 18
|
|
||||||
#define CMD_SET_APP_PROFILE 19
|
|
||||||
|
|
||||||
static bool ksuctl(int cmd, void* arg1, void* arg2) {
|
static bool ksuctl(int cmd, void* arg1, void* arg2) {
|
||||||
int32_t result = 0;
|
int32_t result = 0;
|
||||||
@@ -82,50 +73,6 @@ bool set_app_profile(const app_profile *profile) {
|
|||||||
return ksuctl(CMD_SET_APP_PROFILE, (void*) profile, nullptr);
|
return ksuctl(CMD_SET_APP_PROFILE, (void*) profile, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get_app_profile(int32_t key, app_profile *profile) {
|
bool get_app_profile(p_key_t key, app_profile *profile) {
|
||||||
return ksuctl(CMD_GET_APP_PROFILE, (void*) profile, nullptr);
|
return ksuctl(CMD_GET_APP_PROFILE, (void*) profile, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get_default_non_root_app_profile(app_profile *profile) {
|
|
||||||
return get_app_profile(DEFAULT_NON_ROOT_PROFILE_KEY, profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_default_root_app_profile(app_profile *profile) {
|
|
||||||
return get_app_profile(DEFAULT_ROOT_PROFILE_KEY, profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_allowlist_mode() {
|
|
||||||
int32_t mode = -1;
|
|
||||||
ksuctl(CMD_GET_WORK_MODE, &mode, nullptr);
|
|
||||||
// for kernel that doesn't support allowlist mode, return -1 and it is always allowlist mode
|
|
||||||
return mode <= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool set_allowlist_mode(bool allowlist_mode) {
|
|
||||||
int32_t mode = allowlist_mode ? 0 : 1;
|
|
||||||
return ksuctl(CMD_SET_WORK_MODE, &mode, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_in_allow_list(int uid) {
|
|
||||||
return ksuctl(CMD_IN_ALLOW_LIST, &uid, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_in_deny_list(int uid) {
|
|
||||||
return ksuctl(CMD_IN_DENY_LIST, &uid, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool add_to_allow_list(int uid) {
|
|
||||||
return ksuctl(CMD_ADD_ALLOW_LIST, &uid, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool remove_from_allow_list(int uid) {
|
|
||||||
return ksuctl(CMD_REMOVE_ALLOW_LIST, &uid, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool add_to_deny_list(int uid) {
|
|
||||||
return ksuctl(CMD_ADD_DENY_LIST, &uid, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool remove_from_deny_list(int uid) {
|
|
||||||
return ksuctl(CMD_REMOVE_DENY_LIST, &uid, nullptr);
|
|
||||||
}
|
|
||||||
@@ -17,33 +17,24 @@ bool get_deny_list(int *uids, int *size);
|
|||||||
|
|
||||||
bool is_safe_mode();
|
bool is_safe_mode();
|
||||||
|
|
||||||
bool is_allowlist_mode();
|
#define KSU_MAX_PACKAGE_NAME 256
|
||||||
|
|
||||||
bool set_allowlist_mode(bool allowlist_mode);
|
|
||||||
|
|
||||||
bool is_in_allow_list(int uid);
|
|
||||||
|
|
||||||
bool is_in_deny_list(int uid);
|
|
||||||
|
|
||||||
bool add_to_allow_list(int uid);
|
|
||||||
|
|
||||||
bool remove_from_allow_list(int uid);
|
|
||||||
|
|
||||||
bool add_to_deny_list(int uid);
|
|
||||||
|
|
||||||
bool remove_from_deny_list(int uid);
|
|
||||||
|
|
||||||
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
|
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
|
||||||
#define KSU_MAX_GROUPS 32
|
#define KSU_MAX_GROUPS 32
|
||||||
#define KSU_SELINUX_DOMAIN 64
|
#define KSU_SELINUX_DOMAIN 64
|
||||||
|
|
||||||
#define DEFAULT_ROOT_PROFILE_KEY 0
|
using p_key_t = char[KSU_MAX_PACKAGE_NAME];
|
||||||
#define DEFAULT_NON_ROOT_PROFILE_KEY 9999 // This UID means NOBODY in Android
|
|
||||||
|
|
||||||
struct app_profile {
|
struct app_profile {
|
||||||
|
|
||||||
int32_t key; // this is usually the uid of the app, but can be other value for special apps
|
// this is usually the package of the app, but can be other value for special apps
|
||||||
|
p_key_t key;
|
||||||
|
int32_t current_uid;
|
||||||
|
bool allow_su;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
bool use_default;
|
||||||
|
char template_name[KSU_MAX_PACKAGE_NAME];
|
||||||
int32_t uid;
|
int32_t uid;
|
||||||
int32_t gid;
|
int32_t gid;
|
||||||
|
|
||||||
@@ -51,15 +42,21 @@ struct app_profile {
|
|||||||
int32_t groups_count;
|
int32_t groups_count;
|
||||||
|
|
||||||
// kernel_cap_t is u32[2]
|
// kernel_cap_t is u32[2]
|
||||||
uint64_t capabilities;
|
int32_t capabilities[2];
|
||||||
char selinux_domain[KSU_SELINUX_DOMAIN];
|
char selinux_domain[KSU_SELINUX_DOMAIN];
|
||||||
|
|
||||||
bool allow_su;
|
int32_t namespaces;
|
||||||
bool mount_module;
|
} root_profile;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool use_default;
|
||||||
|
bool umount_modules;
|
||||||
|
} non_root_profile;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
bool set_app_profile(const app_profile *profile);
|
bool set_app_profile(const app_profile *profile);
|
||||||
|
|
||||||
bool get_app_profile(int32_t key, app_profile *profile);
|
bool get_app_profile(p_key_t key, app_profile *profile);
|
||||||
|
|
||||||
#endif //KERNELSU_KSU_H
|
#endif //KERNELSU_KSU_H
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
package me.weishu.kernelsu;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author weishu
|
|
||||||
* @date 2022/12/8.
|
|
||||||
*/
|
|
||||||
public final class Natives {
|
|
||||||
|
|
||||||
static {
|
|
||||||
System.loadLibrary("kernelsu");
|
|
||||||
}
|
|
||||||
|
|
||||||
// become root manager, return true if success.
|
|
||||||
public static native boolean becomeManager(String pkg);
|
|
||||||
|
|
||||||
public static native int getVersion();
|
|
||||||
|
|
||||||
// get the uid list of allowed su processes.
|
|
||||||
public static native int[] getAllowList();
|
|
||||||
|
|
||||||
public static native int[] getDenyList();
|
|
||||||
|
|
||||||
public static native boolean allowRoot(int uid, boolean allow);
|
|
||||||
|
|
||||||
public static native boolean isSafeMode();
|
|
||||||
|
|
||||||
public static native boolean isAllowlistMode();
|
|
||||||
|
|
||||||
public static native boolean setAllowlistMode(boolean isAllowlist);
|
|
||||||
|
|
||||||
public static native boolean isUidInAllowlist(int uid);
|
|
||||||
|
|
||||||
public static native boolean isUidInDenylist(int uid);
|
|
||||||
|
|
||||||
public static native boolean addUidToAllowlist(int uid);
|
|
||||||
|
|
||||||
public static native boolean removeUidFromAllowlist(int uid);
|
|
||||||
|
|
||||||
public static native boolean addUidToDenylist(int uid);
|
|
||||||
|
|
||||||
public static native boolean removeUidFromDenylist(int uid);
|
|
||||||
}
|
|
||||||
75
manager/app/src/main/java/me/weishu/kernelsu/Natives.kt
Normal file
75
manager/app/src/main/java/me/weishu/kernelsu/Natives.kt
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package me.weishu.kernelsu
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author weishu
|
||||||
|
* @date 2022/12/8.
|
||||||
|
*/
|
||||||
|
object Natives {
|
||||||
|
const val DEFAULT_ROOT_PROFILE_KEY = "_root_default_"
|
||||||
|
const val DEFAULT_NON_ROOT_PROFILE_KEY = "_non_root_default_"
|
||||||
|
|
||||||
|
init {
|
||||||
|
System.loadLibrary("kernelsu")
|
||||||
|
}
|
||||||
|
|
||||||
|
// become root manager, return true if success.
|
||||||
|
external fun becomeManager(pkg: String?): Boolean
|
||||||
|
val version: Int
|
||||||
|
external get
|
||||||
|
|
||||||
|
// get the uid list of allowed su processes.
|
||||||
|
val allowList: IntArray
|
||||||
|
external get
|
||||||
|
val denyList: IntArray
|
||||||
|
external get
|
||||||
|
|
||||||
|
external fun allowRoot(uid: Int, allow: Boolean): Boolean
|
||||||
|
val isSafeMode: Boolean
|
||||||
|
external get
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the profile of the given package.
|
||||||
|
* @param key usually the package name
|
||||||
|
* @return return null if failed.
|
||||||
|
*/
|
||||||
|
external fun getAppProfile(key: String?, uid: Int): Profile
|
||||||
|
external fun setAppProfile(profile: Profile?): Boolean
|
||||||
|
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@Parcelize
|
||||||
|
data class Profile(
|
||||||
|
// and there is a default profile for root and non-root
|
||||||
|
val name: String,
|
||||||
|
// current uid for the package, this is convivent for kernel to check
|
||||||
|
// if the package name doesn't match uid, then it should be invalidated.
|
||||||
|
val currentUid: Int = 0,
|
||||||
|
|
||||||
|
// if this is true, kernel will grant root permission to this package
|
||||||
|
val allowSu: Boolean = false,
|
||||||
|
|
||||||
|
// these are used for root profile
|
||||||
|
val rootUseDefault: Boolean = true,
|
||||||
|
val rootTemplate: String? = null,
|
||||||
|
val uid: Int = 0,
|
||||||
|
val gid: Int = 0,
|
||||||
|
val groups: List<Int> = mutableListOf(),
|
||||||
|
val capabilities: List<Int> = mutableListOf(),
|
||||||
|
val context: String = "su",
|
||||||
|
val namespace: Namespace = Namespace.Inherited,
|
||||||
|
|
||||||
|
val nonRootUseDefault: Boolean = true,
|
||||||
|
val umountModules: Boolean = false,
|
||||||
|
) : Parcelable {
|
||||||
|
enum class Namespace {
|
||||||
|
Inherited,
|
||||||
|
Global,
|
||||||
|
Individual,
|
||||||
|
}
|
||||||
|
constructor(): this("")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package me.weishu.kernelsu.profile
|
|
||||||
|
|
||||||
import android.os.Parcelable
|
|
||||||
import androidx.compose.runtime.Immutable
|
|
||||||
import kotlinx.parcelize.Parcelize
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@Parcelize
|
|
||||||
data class AppProfile(
|
|
||||||
val profileName: String,
|
|
||||||
val allowRootRequest: Boolean = false,
|
|
||||||
val unmountModules: Boolean = false,
|
|
||||||
) : Parcelable
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package me.weishu.kernelsu.profile
|
|
||||||
|
|
||||||
import android.os.Parcelable
|
|
||||||
import androidx.compose.runtime.Immutable
|
|
||||||
import kotlinx.parcelize.Parcelize
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@Parcelize
|
|
||||||
data class RootProfile(
|
|
||||||
val profileName: String,
|
|
||||||
val namespace: Namespace = Namespace.Inherited,
|
|
||||||
val uid: Int = 0,
|
|
||||||
val gid: Int = 0,
|
|
||||||
val groups: Int = 0,
|
|
||||||
val capabilities: List<String> = emptyList(),
|
|
||||||
val context: String = "u:r:su:s0",
|
|
||||||
) : Parcelable {
|
|
||||||
enum class Namespace {
|
|
||||||
Inherited,
|
|
||||||
Global,
|
|
||||||
Individual,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,36 +11,30 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import me.weishu.kernelsu.Natives
|
||||||
import me.weishu.kernelsu.R
|
import me.weishu.kernelsu.R
|
||||||
import me.weishu.kernelsu.profile.AppProfile
|
|
||||||
import me.weishu.kernelsu.ui.component.SwitchItem
|
import me.weishu.kernelsu.ui.component.SwitchItem
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AppProfileConfig(
|
fun AppProfileConfig(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
fixedName: Boolean,
|
fixedName: Boolean,
|
||||||
profile: AppProfile,
|
profile: Natives.Profile,
|
||||||
onProfileChange: (AppProfile) -> Unit,
|
onProfileChange: (Natives.Profile) -> Unit,
|
||||||
) {
|
) {
|
||||||
Column(modifier = modifier) {
|
Column(modifier = modifier) {
|
||||||
if (!fixedName) {
|
if (!fixedName) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
label = { Text(stringResource(R.string.profile_name)) },
|
label = { Text(stringResource(R.string.profile_name)) },
|
||||||
value = profile.profileName,
|
value = profile.name,
|
||||||
onValueChange = { onProfileChange(profile.copy(profileName = it)) }
|
onValueChange = { onProfileChange(profile.copy(name = it)) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
SwitchItem(
|
|
||||||
title = stringResource(R.string.profile_allow_root_request),
|
|
||||||
checked = profile.allowRootRequest,
|
|
||||||
onCheckedChange = { onProfileChange(profile.copy(allowRootRequest = it)) }
|
|
||||||
)
|
|
||||||
|
|
||||||
SwitchItem(
|
SwitchItem(
|
||||||
title = stringResource(R.string.profile_unmount_modules),
|
title = stringResource(R.string.profile_unmount_modules),
|
||||||
checked = profile.unmountModules,
|
checked = profile.umountModules,
|
||||||
onCheckedChange = { onProfileChange(profile.copy(unmountModules = it)) }
|
onCheckedChange = { onProfileChange(profile.copy(umountModules = it)) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,7 +42,7 @@ fun AppProfileConfig(
|
|||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
private fun AppProfileConfigPreview() {
|
private fun AppProfileConfigPreview() {
|
||||||
var profile by remember { mutableStateOf(AppProfile("")) }
|
var profile by remember { mutableStateOf(Natives.Profile("")) }
|
||||||
AppProfileConfig(fixedName = true, profile = profile) {
|
AppProfileConfig(fixedName = true, profile = profile) {
|
||||||
profile = it
|
profile = it
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,31 +21,31 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import me.weishu.kernelsu.Natives
|
||||||
import me.weishu.kernelsu.R
|
import me.weishu.kernelsu.R
|
||||||
import me.weishu.kernelsu.profile.RootProfile
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun RootProfileConfig(
|
fun RootProfileConfig(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
fixedName: Boolean,
|
fixedName: Boolean,
|
||||||
profile: RootProfile,
|
profile: Natives.Profile,
|
||||||
onProfileChange: (RootProfile) -> Unit,
|
onProfileChange: (Natives.Profile) -> Unit,
|
||||||
) {
|
) {
|
||||||
Column(modifier = modifier) {
|
Column(modifier = modifier) {
|
||||||
if (!fixedName) {
|
if (!fixedName) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
label = { Text(stringResource(R.string.profile_name)) },
|
label = { Text(stringResource(R.string.profile_name)) },
|
||||||
value = profile.profileName,
|
value = profile.name,
|
||||||
onValueChange = { onProfileChange(profile.copy(profileName = it)) }
|
onValueChange = { onProfileChange(profile.copy(name = it)) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
var expanded by remember { mutableStateOf(false) }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
val currentNamespace = when (profile.namespace) {
|
val currentNamespace = when (profile.namespace) {
|
||||||
RootProfile.Namespace.Inherited -> stringResource(R.string.profile_namespace_inherited)
|
Natives.Profile.Namespace.Inherited -> stringResource(R.string.profile_namespace_inherited)
|
||||||
RootProfile.Namespace.Global -> stringResource(R.string.profile_namespace_global)
|
Natives.Profile.Namespace.Global -> stringResource(R.string.profile_namespace_global)
|
||||||
RootProfile.Namespace.Individual -> stringResource(R.string.profile_namespace_individual)
|
Natives.Profile.Namespace.Individual -> stringResource(R.string.profile_namespace_individual)
|
||||||
}
|
}
|
||||||
ListItem(headlineContent = {
|
ListItem(headlineContent = {
|
||||||
ExposedDropdownMenuBox(
|
ExposedDropdownMenuBox(
|
||||||
@@ -70,21 +70,21 @@ fun RootProfileConfig(
|
|||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text(stringResource(R.string.profile_namespace_inherited)) },
|
text = { Text(stringResource(R.string.profile_namespace_inherited)) },
|
||||||
onClick = {
|
onClick = {
|
||||||
onProfileChange(profile.copy(namespace = RootProfile.Namespace.Inherited))
|
onProfileChange(profile.copy(namespace = Natives.Profile.Namespace.Inherited))
|
||||||
expanded = false
|
expanded = false
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text(stringResource(R.string.profile_namespace_global)) },
|
text = { Text(stringResource(R.string.profile_namespace_global)) },
|
||||||
onClick = {
|
onClick = {
|
||||||
onProfileChange(profile.copy(namespace = RootProfile.Namespace.Global))
|
onProfileChange(profile.copy(namespace = Natives.Profile.Namespace.Global))
|
||||||
expanded = false
|
expanded = false
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text(stringResource(R.string.profile_namespace_individual)) },
|
text = { Text(stringResource(R.string.profile_namespace_individual)) },
|
||||||
onClick = {
|
onClick = {
|
||||||
onProfileChange(profile.copy(namespace = RootProfile.Namespace.Individual))
|
onProfileChange(profile.copy(namespace = Natives.Profile.Namespace.Individual))
|
||||||
expanded = false
|
expanded = false
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -97,7 +97,18 @@ fun RootProfileConfig(
|
|||||||
label = { Text("uid") },
|
label = { Text("uid") },
|
||||||
value = profile.uid.toString(),
|
value = profile.uid.toString(),
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||||
onValueChange = { onProfileChange(profile.copy(uid = it.toInt())) }
|
onValueChange = {
|
||||||
|
if (it.isNotEmpty()) {
|
||||||
|
it.filter { symbol ->
|
||||||
|
symbol.isDigit()
|
||||||
|
}.let { filtered ->
|
||||||
|
filtered.ifEmpty { "0" }
|
||||||
|
}.let { value ->
|
||||||
|
onProfileChange(profile.copy(uid = value.toInt(), rootUseDefault = false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -106,16 +117,37 @@ fun RootProfileConfig(
|
|||||||
label = { Text("gid") },
|
label = { Text("gid") },
|
||||||
value = profile.gid.toString(),
|
value = profile.gid.toString(),
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||||
onValueChange = { onProfileChange(profile.copy(gid = it.toInt())) }
|
onValueChange = {
|
||||||
|
if (it.isNotEmpty()) {
|
||||||
|
it.filter { symbol ->
|
||||||
|
symbol.isDigit()
|
||||||
|
}.let { filtered ->
|
||||||
|
filtered.ifEmpty { "0" }
|
||||||
|
}.let { value ->
|
||||||
|
onProfileChange(profile.copy(gid = value.toInt(), rootUseDefault = false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
ListItem(headlineContent = {
|
ListItem(headlineContent = {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
label = { Text("groups") },
|
label = { Text("groups") },
|
||||||
value = profile.groups.toString(),
|
value = profile.groups.joinToString(","),
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||||
onValueChange = { onProfileChange(profile.copy(groups = it.toInt())) }
|
onValueChange = { s ->
|
||||||
|
if (s.isNotEmpty()) {
|
||||||
|
s.filter { symbol ->
|
||||||
|
symbol.isDigit() || symbol == ','
|
||||||
|
}.let { filtered ->
|
||||||
|
filtered.ifEmpty { "0" }
|
||||||
|
}.let { value ->
|
||||||
|
val groups = value.split(',').filter { it.isNotEmpty() }.map { it.toInt() }
|
||||||
|
onProfileChange(profile.copy(groups = groups, rootUseDefault = false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -123,7 +155,9 @@ fun RootProfileConfig(
|
|||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
label = { Text("context") },
|
label = { Text("context") },
|
||||||
value = profile.context,
|
value = profile.context,
|
||||||
onValueChange = { onProfileChange(profile.copy(context = it)) }
|
onValueChange = {
|
||||||
|
onProfileChange(profile.copy(context = it, rootUseDefault = false))
|
||||||
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -132,7 +166,7 @@ fun RootProfileConfig(
|
|||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
private fun RootProfileConfigPreview() {
|
private fun RootProfileConfigPreview() {
|
||||||
var profile by remember { mutableStateOf(RootProfile("")) }
|
var profile by remember { mutableStateOf(Natives.Profile("")) }
|
||||||
RootProfileConfig(fixedName = true, profile = profile) {
|
RootProfileConfig(fixedName = true, profile = profile) {
|
||||||
profile = it
|
profile = it
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package me.weishu.kernelsu.ui.screen
|
package me.weishu.kernelsu.ui.screen
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.compose.animation.Crossfade
|
import androidx.compose.animation.Crossfade
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@@ -49,8 +50,6 @@ import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import me.weishu.kernelsu.Natives
|
import me.weishu.kernelsu.Natives
|
||||||
import me.weishu.kernelsu.R
|
import me.weishu.kernelsu.R
|
||||||
import me.weishu.kernelsu.profile.AppProfile
|
|
||||||
import me.weishu.kernelsu.profile.RootProfile
|
|
||||||
import me.weishu.kernelsu.ui.component.SwitchItem
|
import me.weishu.kernelsu.ui.component.SwitchItem
|
||||||
import me.weishu.kernelsu.ui.component.profile.AppProfileConfig
|
import me.weishu.kernelsu.ui.component.profile.AppProfileConfig
|
||||||
import me.weishu.kernelsu.ui.component.profile.RootProfileConfig
|
import me.weishu.kernelsu.ui.component.profile.RootProfileConfig
|
||||||
@@ -70,8 +69,15 @@ fun AppProfileScreen(
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val snackbarHost = LocalSnackbarHost.current
|
val snackbarHost = LocalSnackbarHost.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val failToGrantRoot = stringResource(R.string.superuser_failed_to_grant_root)
|
val failToUpdateAppProfile =
|
||||||
var isRootGranted by rememberSaveable { mutableStateOf(appInfo.onAllowList) }
|
stringResource(R.string.failed_to_update_app_profile).format(appInfo.label)
|
||||||
|
|
||||||
|
val packageName = appInfo.packageName
|
||||||
|
var profile by rememberSaveable {
|
||||||
|
mutableStateOf(Natives.getAppProfile(packageName, appInfo.uid))
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i("mylog", "profile: $profile")
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = { TopBar { navigator.popBackStack() } }
|
topBar = { TopBar { navigator.popBackStack() } }
|
||||||
@@ -95,14 +101,13 @@ fun AppProfileScreen(
|
|||||||
.height(48.dp)
|
.height(48.dp)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
isRootGranted = isRootGranted,
|
profile = profile,
|
||||||
onSwitchRootPermission = { grant ->
|
onProfileChange = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val success = Natives.allowRoot(appInfo.uid, grant)
|
if (!Natives.setAppProfile(it)) {
|
||||||
if (success) {
|
snackbarHost.showSnackbar(failToUpdateAppProfile.format(appInfo.uid))
|
||||||
isRootGranted = grant
|
|
||||||
} else {
|
} else {
|
||||||
snackbarHost.showSnackbar(failToGrantRoot.format(appInfo.uid))
|
profile = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -117,9 +122,11 @@ private fun AppProfileInner(
|
|||||||
packageName: String,
|
packageName: String,
|
||||||
appLabel: String,
|
appLabel: String,
|
||||||
appIcon: @Composable () -> Unit,
|
appIcon: @Composable () -> Unit,
|
||||||
isRootGranted: Boolean,
|
profile: Natives.Profile,
|
||||||
onSwitchRootPermission: (Boolean) -> Unit,
|
onProfileChange: (Natives.Profile) -> Unit,
|
||||||
) {
|
) {
|
||||||
|
val isRootGranted = profile.allowSu
|
||||||
|
|
||||||
Column(modifier = modifier) {
|
Column(modifier = modifier) {
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = { Text(appLabel) },
|
headlineContent = { Text(appLabel) },
|
||||||
@@ -131,14 +138,32 @@ private fun AppProfileInner(
|
|||||||
icon = Icons.Filled.Security,
|
icon = Icons.Filled.Security,
|
||||||
title = stringResource(id = R.string.superuser),
|
title = stringResource(id = R.string.superuser),
|
||||||
checked = isRootGranted,
|
checked = isRootGranted,
|
||||||
onCheckedChange = onSwitchRootPermission,
|
onCheckedChange = { onProfileChange(profile.copy(allowSu = it)) },
|
||||||
)
|
)
|
||||||
|
|
||||||
Crossfade(targetState = isRootGranted, label = "") { current ->
|
Crossfade(targetState = isRootGranted, label = "") { current ->
|
||||||
Column {
|
Column {
|
||||||
if (current) {
|
if (current) {
|
||||||
var mode by rememberSaveable { mutableStateOf(Mode.Default) }
|
val mode = if (profile.rootUseDefault) {
|
||||||
ProfileBox(mode, true) { mode = it }
|
Mode.Default
|
||||||
|
} else if (profile.rootTemplate != null) {
|
||||||
|
Mode.Template
|
||||||
|
} else {
|
||||||
|
Mode.Custom
|
||||||
|
}
|
||||||
|
ProfileBox(mode, true) {
|
||||||
|
when (it) {
|
||||||
|
Mode.Default -> {
|
||||||
|
onProfileChange(profile.copy(rootUseDefault = true))
|
||||||
|
}
|
||||||
|
Mode.Template -> {
|
||||||
|
onProfileChange(profile.copy(rootUseDefault = false))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
onProfileChange(profile.copy(rootUseDefault = false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Crossfade(targetState = mode, label = "") { currentMode ->
|
Crossfade(targetState = mode, label = "") { currentMode ->
|
||||||
if (currentMode == Mode.Template) {
|
if (currentMode == Mode.Template) {
|
||||||
var expanded by remember { mutableStateOf(false) }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
@@ -163,11 +188,10 @@ private fun AppProfileInner(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else if (mode == Mode.Custom) {
|
} else if (mode == Mode.Custom) {
|
||||||
var profile by rememberSaveable { mutableStateOf(RootProfile("@$packageName")) }
|
|
||||||
RootProfileConfig(
|
RootProfileConfig(
|
||||||
fixedName = true,
|
fixedName = true,
|
||||||
profile = profile,
|
profile = profile,
|
||||||
onProfileChange = { profile = it }
|
onProfileChange = onProfileChange
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,11 +200,10 @@ private fun AppProfileInner(
|
|||||||
ProfileBox(mode, false) { mode = it }
|
ProfileBox(mode, false) { mode = it }
|
||||||
Crossfade(targetState = mode, label = "") { currentMode ->
|
Crossfade(targetState = mode, label = "") { currentMode ->
|
||||||
if (currentMode == Mode.Custom) {
|
if (currentMode == Mode.Custom) {
|
||||||
var profile by rememberSaveable { mutableStateOf(AppProfile(packageName)) }
|
|
||||||
AppProfileConfig(
|
AppProfileConfig(
|
||||||
fixedName = true,
|
fixedName = true,
|
||||||
profile = profile,
|
profile = profile,
|
||||||
onProfileChange = { profile = it }
|
onProfileChange = onProfileChange
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -256,12 +279,15 @@ private fun ProfileBox(
|
|||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
private fun AppProfilePreview() {
|
private fun AppProfilePreview() {
|
||||||
var isRootGranted by remember { mutableStateOf(false) }
|
var profile by remember { mutableStateOf(Natives.Profile("")) }
|
||||||
AppProfileInner(
|
AppProfileInner(
|
||||||
packageName = "icu.nullptr.test",
|
packageName = "icu.nullptr.test",
|
||||||
appLabel = "Test",
|
appLabel = "Test",
|
||||||
appIcon = { Icon(Icons.Filled.Android, null) },
|
appIcon = { Icon(Icons.Filled.Android, null) },
|
||||||
isRootGranted = isRootGranted,
|
profile = profile,
|
||||||
onSwitchRootPermission = { isRootGranted = it },
|
onProfileChange = {
|
||||||
|
profile = it
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
|||||||
SideEffect {
|
SideEffect {
|
||||||
if (isManager) install()
|
if (isManager) install()
|
||||||
}
|
}
|
||||||
val ksuVersion = if (isManager) Natives.getVersion() else null
|
val ksuVersion = if (isManager) Natives.version else null
|
||||||
|
|
||||||
StatusCard(kernelVersion, ksuVersion)
|
StatusCard(kernelVersion, ksuVersion)
|
||||||
InfoCard()
|
InfoCard()
|
||||||
@@ -141,7 +141,7 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
|
|||||||
) {
|
) {
|
||||||
when {
|
when {
|
||||||
ksuVersion != null -> {
|
ksuVersion != null -> {
|
||||||
val appendText = if (Natives.isSafeMode()) {
|
val appendText = if (Natives.isSafeMode) {
|
||||||
" [${stringResource(id = R.string.safe_mode)}]"
|
" [${stringResource(id = R.string.safe_mode)}]"
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val isSafeMode = Natives.isSafeMode()
|
val isSafeMode = Natives.isSafeMode
|
||||||
val isKSUVersionInvalid = Natives.getVersion() < 0
|
val isKSUVersionInvalid = Natives.version < 0
|
||||||
val hasMagisk = hasMagisk()
|
val hasMagisk = hasMagisk()
|
||||||
|
|
||||||
val hideInstallButton = isSafeMode || isKSUVersionInvalid || hasMagisk
|
val hideInstallButton = isSafeMode || isKSUVersionInvalid || hasMagisk
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ fun getModuleCount(): Int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getSuperuserCount(): Int {
|
fun getSuperuserCount(): Int {
|
||||||
return Natives.getAllowList().size
|
return Natives.allowList.size
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toggleModule(id: String, enable: Boolean): Boolean {
|
fun toggleModule(id: String, enable: Boolean): Boolean {
|
||||||
|
|||||||
@@ -72,9 +72,9 @@ fun getBugreportFile(context: Context): File {
|
|||||||
pw.println("Nodename: ${uname.nodename}")
|
pw.println("Nodename: ${uname.nodename}")
|
||||||
pw.println("Sysname: ${uname.sysname}")
|
pw.println("Sysname: ${uname.sysname}")
|
||||||
|
|
||||||
val ksuKernel = Natives.getVersion()
|
val ksuKernel = Natives.version
|
||||||
pw.println("KernelSU: $ksuKernel")
|
pw.println("KernelSU: $ksuKernel")
|
||||||
val safeMode = Natives.isSafeMode()
|
val safeMode = Natives.isSafeMode
|
||||||
pw.println("SafeMode: $safeMode")
|
pw.println("SafeMode: $safeMode")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -116,8 +116,8 @@ class SuperUserViewModel : ViewModel() {
|
|||||||
|
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val pm = ksuApp.packageManager
|
val pm = ksuApp.packageManager
|
||||||
val allowList = Natives.getAllowList().toSet()
|
val allowList = Natives.allowList.toSet()
|
||||||
val denyList = Natives.getDenyList().toSet()
|
val denyList = Natives.denyList.toSet()
|
||||||
Log.i(TAG, "allowList: $allowList")
|
Log.i(TAG, "allowList: $allowList")
|
||||||
Log.i(TAG, "denyList: $denyList")
|
Log.i(TAG, "denyList: $denyList")
|
||||||
val start = SystemClock.elapsedRealtime()
|
val start = SystemClock.elapsedRealtime()
|
||||||
|
|||||||
@@ -76,4 +76,6 @@
|
|||||||
<string name="profile_namespace_individual">Individual</string>
|
<string name="profile_namespace_individual">Individual</string>
|
||||||
<string name="profile_unmount_modules">Unmount modules</string>
|
<string name="profile_unmount_modules">Unmount modules</string>
|
||||||
<string name="profile_allow_root_request">Allow root request</string>
|
<string name="profile_allow_root_request">Allow root request</string>
|
||||||
|
<string name="failed_to_update_root_profile">Failed to update root profile for %s</string>
|
||||||
|
<string name="failed_to_update_app_profile">Failed to update app profile for %s</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user