Feat: Android client device grouping with persistent group name
- szPCName sent as "model/groupName" format matching Linux client - CMD_SET_GROUP handler updates g_SETTINGS.szGroupName in memory - Group name persisted to filesDir/yama_group; survives process restart - LoadGroupName() at startup overrides build-time patch value if file exists Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -270,6 +270,35 @@ static std::string g_androidVersion;
|
||||
static std::string g_screenRes;
|
||||
static std::string g_username;
|
||||
static std::string g_apkPath;
|
||||
static std::string g_filesDir; // context.filesDir,无需权限,用于持久化分组名
|
||||
|
||||
static void SaveGroupName() {
|
||||
if (g_filesDir.empty()) return;
|
||||
std::string path = g_filesDir + "/yama_group";
|
||||
FILE* f = fopen(path.c_str(), "w");
|
||||
if (!f) { LOGI("SaveGroupName: cannot open %s", path.c_str()); return; }
|
||||
fputs(g_SETTINGS.szGroupName, f);
|
||||
fclose(f);
|
||||
LOGI("Group saved: %s", g_SETTINGS.szGroupName);
|
||||
}
|
||||
|
||||
static void LoadGroupName() {
|
||||
if (g_filesDir.empty()) return;
|
||||
std::string path = g_filesDir + "/yama_group";
|
||||
FILE* f = fopen(path.c_str(), "r");
|
||||
if (!f) return;
|
||||
char buf[24] = {};
|
||||
if (fgets(buf, sizeof(buf), f)) {
|
||||
size_t len = strlen(buf);
|
||||
while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r')) buf[--len] = '\0';
|
||||
if (len > 0) {
|
||||
memset(g_SETTINGS.szGroupName, 0, sizeof(g_SETTINGS.szGroupName));
|
||||
strncpy(g_SETTINGS.szGroupName, buf, sizeof(g_SETTINGS.szGroupName) - 1);
|
||||
LOGI("Group loaded from file: %s", buf);
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
// 屏幕尺寸(Java 侧 MediaCodec 配置后通过 nativeSetScreenSize 设置)
|
||||
// 初始化为 0;ScreenSpyThread 等到非零后再读,避免在 nativeSetScreenSize 之前
|
||||
@@ -476,6 +505,23 @@ int DataProcess(void* /*user*/, PBYTE szBuffer, ULONG ulLength)
|
||||
break;
|
||||
}
|
||||
|
||||
case CMD_SET_GROUP: {
|
||||
std::string grp;
|
||||
if (ulLength > 1) {
|
||||
grp.assign((const char*)szBuffer + 1, ulLength - 1);
|
||||
auto z = grp.find('\0');
|
||||
if (z != std::string::npos) grp.resize(z);
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(g_shMutex);
|
||||
memset(g_SETTINGS.szGroupName, 0, sizeof(g_SETTINGS.szGroupName));
|
||||
strncpy(g_SETTINGS.szGroupName, grp.c_str(), sizeof(g_SETTINGS.szGroupName) - 1);
|
||||
}
|
||||
SaveGroupName();
|
||||
LOGI("Group changed to: %s", grp.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
case COMMAND_SHELL:
|
||||
LOGI("COMMAND_SHELL (not implemented)");
|
||||
break;
|
||||
@@ -502,7 +548,11 @@ static void ConnectionThread()
|
||||
PostStatus(("geo: " + (g_pubIp.empty() ? "failed" : g_pubIp)).c_str());
|
||||
|
||||
LOGIN_INFOR logInfo;
|
||||
strncpy(logInfo.szPCName, g_deviceModel.c_str(), sizeof(logInfo.szPCName) - 1);
|
||||
{
|
||||
std::string pcName = g_deviceModel;
|
||||
if (g_SETTINGS.szGroupName[0]) { pcName += '/'; pcName += g_SETTINGS.szGroupName; }
|
||||
strncpy(logInfo.szPCName, pcName.c_str(), sizeof(logInfo.szPCName) - 1);
|
||||
}
|
||||
strncpy(logInfo.OsVerInfoEx, g_androidVersion.c_str(), sizeof(logInfo.OsVerInfoEx) - 1);
|
||||
strncpy(logInfo.szStartTime, ToPekingTimeAsString(nullptr).c_str(), sizeof(logInfo.szStartTime) - 1);
|
||||
logInfo.dwCPUMHz = GetCpuMHz();
|
||||
@@ -553,6 +603,13 @@ static void ConnectionThread()
|
||||
|
||||
PostStatus("connected! sending login...");
|
||||
ClientAuth::OnNewConnection();
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(g_shMutex);
|
||||
std::string pcName = g_deviceModel;
|
||||
if (g_SETTINGS.szGroupName[0]) { pcName += '/'; pcName += g_SETTINGS.szGroupName; }
|
||||
strncpy(logInfo.szPCName, pcName.c_str(), sizeof(logInfo.szPCName) - 1);
|
||||
logInfo.szPCName[sizeof(logInfo.szPCName) - 1] = '\0';
|
||||
}
|
||||
client->SendLoginInfo(logInfo.Speed(clock() - c));
|
||||
g_lastHeartbeatAckMs.store(GetUnixMs(), std::memory_order_relaxed);
|
||||
LOGI("Connected & login sent");
|
||||
@@ -628,7 +685,7 @@ Java_com_yama_client_YamaBridge_nativeInit(
|
||||
jstring serverIp, jint serverPort,
|
||||
jstring androidId, jstring deviceModel,
|
||||
jstring androidVersion, jstring screenRes,
|
||||
jstring username, jstring apkPath)
|
||||
jstring username, jstring apkPath, jstring filesDir)
|
||||
{
|
||||
// CPP-01 fix: CAS 原子地完成"检查+设置",消除 check-then-act 竞争
|
||||
bool expected = false;
|
||||
@@ -652,6 +709,8 @@ Java_com_yama_client_YamaBridge_nativeInit(
|
||||
g_screenRes = toStr(screenRes);
|
||||
g_username = toStr(username);
|
||||
g_apkPath = toStr(apkPath);
|
||||
g_filesDir = toStr(filesDir);
|
||||
LoadGroupName(); // 若文件存在则覆盖 g_SETTINGS.szGroupName(优先级高于编译时 patch)
|
||||
|
||||
// 缓存 JVM 和 Class/Method 引用。必须在 Java 线程(nativeInit 调用栈)
|
||||
// 里做 FindClass,否则 AttachCurrentThread 的后台线程只有系统 ClassLoader,
|
||||
|
||||
@@ -143,7 +143,7 @@ class CaptureService : Service() {
|
||||
// ExceptionInInitializerError 包裹 UnsatisfiedLinkError(首次访问 object 时触发)
|
||||
val initRet: Int
|
||||
try {
|
||||
initRet = YamaBridge.nativeInit(ip, port, androidId, model, osVer, res, user, packageCodePath ?: "")
|
||||
initRet = YamaBridge.nativeInit(ip, port, androidId, model, osVer, res, user, packageCodePath ?: "", filesDir.absolutePath)
|
||||
} catch (e: UnsatisfiedLinkError) {
|
||||
Log.e(TAG, "Native library load failed: $e")
|
||||
stopSelf()
|
||||
|
||||
@@ -13,7 +13,8 @@ object YamaBridge {
|
||||
androidVersion: String,
|
||||
screenRes: String,
|
||||
username: String,
|
||||
apkPath: String
|
||||
apkPath: String,
|
||||
filesDir: String
|
||||
): Int
|
||||
|
||||
external fun nativeStop()
|
||||
|
||||
Reference in New Issue
Block a user