feat: publish workphone SDK deployment and API docs

This commit is contained in:
Manus AI
2026-07-14 18:10:52 +08:00
commit 021d633cc1
534 changed files with 122391 additions and 0 deletions

View File

@@ -0,0 +1,342 @@
/**
* system_control.js - Frida系统控制扩展
* 功能截图、UI导航、页面检测、输入模拟
* 通过Frida直接调用Android内部API不依赖ADB
*/
'use strict';
rpc.exports = {
// ==================== 截图 ====================
takeScreenshot: function (params) {
/**
* 通过Android Shell命令截图并返回base64
* 使用Runtime.exec执行screencap需要Root或同进程权限
*/
try {
return Java.performNow(function () {
var savePath = (params && params.path) || '/sdcard/frida_screenshot.png';
var Runtime = Java.use('java.lang.Runtime');
var runtime = Runtime.getRuntime();
// 方案1: 直接用screencap命令
var process = runtime.exec(['sh', '-c', 'screencap -p ' + savePath]);
process.waitFor();
// 读取文件并转base64
var File = Java.use('java.io.File');
var file = File.$new(savePath);
if (!file.exists()) {
return { success: false, error: '截图文件不存在' };
}
var fileSize = file.length();
var FileInputStream = Java.use('java.io.FileInputStream');
var fis = FileInputStream.$new(file);
var bytes = Java.array('byte', new Array(parseInt(fileSize)).fill(0));
fis.read(bytes);
fis.close();
var Base64 = Java.use('android.util.Base64');
var b64 = Base64.encodeToString(bytes, 2); // NO_WRAP
return {
success: true,
path: savePath,
size: parseInt(fileSize),
base64_length: b64.length,
base64: b64.substring(0, 200) + '...(truncated)',
full_base64: b64,
};
});
} catch (e) {
return { success: false, error: String(e) };
}
},
takeScreenshotToFile: function (params) {
/**
* 截图保存到指定路径不返回base64避免传输大数据
*/
try {
return Java.performNow(function () {
var savePath = (params && params.path) || '/sdcard/frida_sc_' + Date.now() + '.png';
var Runtime = Java.use('java.lang.Runtime');
var runtime = Runtime.getRuntime();
var process = runtime.exec(['sh', '-c', 'screencap -p ' + savePath]);
process.waitFor();
var File = Java.use('java.io.File');
var file = File.$new(savePath);
if (file.exists()) {
return { success: true, path: savePath, size: parseInt(file.length()) };
}
return { success: false, error: '截图失败' };
});
} catch (e) {
return { success: false, error: String(e) };
}
},
// ==================== 页面检测 ====================
getCurrentActivity: function () {
/**
* 获取当前前台Activity名称
*/
try {
return Java.performNow(function () {
var ActivityThread = Java.use('android.app.ActivityThread');
var currentApp = ActivityThread.currentApplication();
var activities = [];
// 遍历所有Activity
Java.choose('android.app.Activity', {
onMatch: function (activity) {
if (!activity.isFinishing() && !activity.isDestroyed()) {
activities.push({
class: activity.getClass().getName(),
title: activity.getTitle() ? activity.getTitle().toString() : '',
visible: activity.hasWindowFocus(),
});
}
},
onComplete: function () {},
});
return {
success: true,
activities: activities,
foreground: activities.length > 0 ? activities[activities.length - 1].class : 'unknown',
};
});
} catch (e) {
return { success: false, error: String(e) };
}
},
// ==================== UI导航 ====================
navigateToChat: function (params) {
/**
* 导航到指定聊天窗口
* 通过微信内部Intent跳转
*/
try {
return Java.performNow(function () {
var wxid = (params && params.wxid) || 'filehelper';
var ctx = Java.use('android.app.ActivityThread').currentApplication().getApplicationContext();
var Intent = Java.use('android.content.Intent');
var Uri = Java.use('android.net.Uri');
// 微信内部跳转到聊天页面
var intent = Intent.$new();
intent.setClassName('com.tencent.mm', 'com.tencent.mm.ui.chatting.ChattingUI');
intent.putExtra('Chat_User', wxid);
intent.addFlags(0x10000000); // FLAG_ACTIVITY_NEW_TASK
ctx.startActivity(intent);
return { success: true, navigated_to: 'ChattingUI', wxid: wxid };
});
} catch (e) {
return { success: false, error: String(e) };
}
},
navigateToContacts: function () {
/**
* 导航到通讯录页面
*/
try {
return Java.performNow(function () {
var ctx = Java.use('android.app.ActivityThread').currentApplication().getApplicationContext();
var Intent = Java.use('android.content.Intent');
var intent = Intent.$new();
intent.setClassName('com.tencent.mm', 'com.tencent.mm.ui.LauncherUI');
intent.putExtra('LauncherUI.From.Shortcut.Msg', true);
intent.addFlags(0x10000000);
ctx.startActivity(intent);
return { success: true, navigated_to: 'Contacts' };
});
} catch (e) {
return { success: false, error: String(e) };
}
},
navigateToMe: function () {
/**
* 导航到"我"页面
*/
try {
return Java.performNow(function () {
var ctx = Java.use('android.app.ActivityThread').currentApplication().getApplicationContext();
var Intent = Java.use('android.content.Intent');
var intent = Intent.$new();
intent.setClassName('com.tencent.mm', 'com.tencent.mm.ui.LauncherUI');
intent.addFlags(0x10000000);
ctx.startActivity(intent);
return { success: true, navigated_to: 'Me' };
});
} catch (e) {
return { success: false, error: String(e) };
}
},
navigateToMoments: function () {
/**
* 导航到朋友圈
*/
try {
return Java.performNow(function () {
var ctx = Java.use('android.app.ActivityThread').currentApplication().getApplicationContext();
var Intent = Java.use('android.content.Intent');
var intent = Intent.$new();
intent.setClassName('com.tencent.mm', 'com.tencent.mm.plugin.sns.ui.SnsTimeLineUI');
intent.addFlags(0x10000000);
ctx.startActivity(intent);
return { success: true, navigated_to: 'Moments' };
});
} catch (e) {
return { success: false, error: String(e) };
}
},
// ==================== 输入模拟 ====================
simulateTap: function (params) {
/**
* 模拟点击通过shell input tap
*/
try {
return Java.performNow(function () {
var x = (params && params.x) || 0;
var y = (params && params.y) || 0;
var Runtime = Java.use('java.lang.Runtime');
var runtime = Runtime.getRuntime();
var process = runtime.exec(['sh', '-c', 'input tap ' + x + ' ' + y]);
process.waitFor();
return { success: true, tapped: { x: x, y: y } };
});
} catch (e) {
return { success: false, error: String(e) };
}
},
simulateInput: function (params) {
/**
* 模拟文本输入
*/
try {
return Java.performNow(function () {
var text = (params && params.text) || '';
var Runtime = Java.use('java.lang.Runtime');
var runtime = Runtime.getRuntime();
// 需要对特殊字符转义
var escaped = text.replace(/ /g, '%s').replace(/'/g, "\\'");
var process = runtime.exec(['sh', '-c', "input text '" + escaped + "'"]);
process.waitFor();
return { success: true, input: text };
});
} catch (e) {
return { success: false, error: String(e) };
}
},
simulateKeyEvent: function (params) {
/**
* 模拟按键事件(返回键=4, Home=3, 回车=66
*/
try {
return Java.performNow(function () {
var keycode = (params && params.keycode) || 4;
var Runtime = Java.use('java.lang.Runtime');
var runtime = Runtime.getRuntime();
var process = runtime.exec(['sh', '-c', 'input keyevent ' + keycode]);
process.waitFor();
return { success: true, keycode: keycode };
});
} catch (e) {
return { success: false, error: String(e) };
}
},
// ==================== 文件操作 ====================
readFileBase64: function (params) {
/**
* 读取手机上的文件并返回base64
*/
try {
return Java.performNow(function () {
var filePath = (params && params.path) || '';
if (!filePath) return { success: false, error: '缺少path参数' };
var File = Java.use('java.io.File');
var file = File.$new(filePath);
if (!file.exists()) return { success: false, error: '文件不存在: ' + filePath };
var fileSize = file.length();
if (fileSize > 5 * 1024 * 1024) {
return { success: false, error: '文件太大(>5MB): ' + fileSize };
}
var FileInputStream = Java.use('java.io.FileInputStream');
var fis = FileInputStream.$new(file);
var bytes = Java.array('byte', new Array(parseInt(fileSize)).fill(0));
fis.read(bytes);
fis.close();
var Base64 = Java.use('android.util.Base64');
var b64 = Base64.encodeToString(bytes, 2);
return { success: true, path: filePath, size: parseInt(fileSize), base64: b64 };
});
} catch (e) {
return { success: false, error: String(e) };
}
},
listFiles: function (params) {
/**
* 列出目录下的文件
*/
try {
return Java.performNow(function () {
var dirPath = (params && params.path) || '/sdcard/';
var File = Java.use('java.io.File');
var dir = File.$new(dirPath);
if (!dir.exists() || !dir.isDirectory()) {
return { success: false, error: '目录不存在: ' + dirPath };
}
var files = dir.listFiles();
var result = [];
var len = files ? files.length : 0;
for (var i = 0; i < Math.min(len, 50); i++) {
result.push({
name: files[i].getName(),
is_dir: files[i].isDirectory(),
size: files[i].isFile() ? parseInt(files[i].length()) : 0,
});
}
return { success: true, path: dirPath, count: len, files: result };
});
} catch (e) {
return { success: false, error: String(e) };
}
},
// ==================== 连接状态 ====================
getConnectionStatus: function () {
return {
success: true,
mode: 'frida_wireless',
transport: 'tcp',
pid: Process.id,
arch: Process.arch,
timestamp: Date.now(),
};
},
};