Freezer Java Script Engine(FJSE)是 Freezer 内置的 Rhino JavaScript 运行时。脚本运行在 Freezer 的系统模块环境中,可访问应用状态、冻结服务、事件、反射 Hook、配置和脚本设置页。
权限提示:FJSE 不是沙箱。脚本拥有系统模块权限,
reflection、activityManagerService和network可能改变系统行为。只运行可信脚本。
核心控制逻辑:main.js(API 入口与接口导出)
安装/部署脚本:install.js(处理环境初始化)
配置文件:data.yaml(模块参数与 API 规则定义)
原生架构支持:native/ 目录下包含 arm, arm64, x86, x86_64 的以 .so 结尾的核心底层库
FJSE只会自动加载文件名为 main.js 的文件。Freezer 启动时加载脚本;脚本管理器可以导入、删除或重新加载脚本。
data.yaml:
id: example
name: Example Module
author: Timeline
version: v1.0
versionCode: 1
description: |
Example moduleinstall.js:
println("模块已安装");main.js:
on('load', function () {
log.i('模块已加载');
});
on('shutdown', function () {
log.i('模块即将关闭');
});
on('action', function () {
log.i('用户执行操作');
});
on('freeze', function (event) {
var app = event.getAppRecord();
if (app != null) log.d('冻结: ' + app.getPackageName());
});- Rhino 使用 ES6 语法。
- 事件回调默认同步执行;不要在回调中做长时间阻塞工作。
async(fn, ...)在后台执行并返回可等待的任务结果;用__await(future)等待结果。需要影响当前事件结果的逻辑(尤其是event.cancel())不得放入async(install.js不可用 async, await, synchronized 方法)。- 脚本卸载时会调用
shutdown,并移除脚本创建的事件监听、设置页和 Hook。 - 不要假设存在
require或module。
以下名称是脚本可直接使用的公开对象:
| 名称 | 用途 |
|---|---|
freezerService |
发送冻结消息、移除冻结消息 |
unfreezeService |
解冻和临时解冻 |
log |
d、i、w、e 日志 |
apps |
查询应用对象 |
processes |
查询 ProcessRecordAPI 和 获取 ProcessList 实例 |
reflection |
查找/调用 Java 类、字段、方法和构造器;创建 Hook |
activityManagerService |
获取 ActivityManager Context 和 ActivityManagerService 实例 |
network |
网络管理服务操作 |
systemChecker |
查询系统类型 |
appSettings |
修改应用配置 |
globalSettings |
修改全局配置 |
ui |
注册脚本设置页和控件 (install.js不可用) |
CakeHooker |
暴露底层 Hook 类;优先使用 hooker (install.js不可用) |
hooker |
before、after、replace 的脚本友好封装 (install.js不可用) |
on('load', function () { /* 加载完成 */ });
on('shutdown', function () { /* 卸载前清理 */ });
on('action', function () { /* 执行操作 */ });
on('freeze', function (event) { /* 事件对象作为唯一参数 */ });事件对象提供与事件类型对应的属性,例如应用、状态和事件阶段信息。每个事件的完整参数、取值含义和取消行为见 events/README.md。可取消事件支持:
on('freeze', function (event) {
if (shouldBlock(event)) event.cancel();
});只有事件本身支持取消时,cancel() 才会改变系统行为;不要默认所有事件都可取消。
async function future(value) {
return value * 2;
};
var result = await future(21); // 42
sleep(100);
synchronized(function () {
// 使用脚本级锁
});
synchronized(lockObject, function () {
// 使用对象身份锁
});
load('helper.js'); // 相对当前脚本目录解析load() 只读取并执行文件,不提供模块隔离。async 返回的 Future 在脚本销毁时可能无法提交;__await 被中断时返回 null。
var app = apps.get('com.example.app', 0);
var byUid = apps.getByUid(10000);
var targets = apps.getRunningTargets();应用常用属性:
getPackageName() getUserId() getUid()
getApplicationInfo() isXposed() isFrozen()
isSystem() getState() isTargetApp()
getCategory() getProcesses()
应用状态对象通过 app.getState() 获取,完整方法、参数和返回值见 api/app-state.md。例如:
var app = apps.get('com.example.app', 0);
if (app != null) {
var state = app.getState();
log.i('可见=' + state.isVisible());
log.i('窗口=' + state.isWindow());
log.i('播放状态=' + state.getPlaybackState());
}var process = processes.getProcessRecord('com.example.app', 10000);
var byPid = processes.getProcessRecord(1234);
var byObject = processes.getProcessRecord(systemProcessObject);ProcessRecordAPI 常用 getter:
getPackageName() getProcessName()
getProcessNameWithIsolated() getUserId()
getUid() getRunningUid()
getPid() isFrozen()
isFrozenBinder() isIsolated()
getAppRecord() getApplicationInfo()
getInstance() isKilled()
isKilledByAm() killLocked(String reason, int subReason)
isInFullBackup() getStartSeq()
isMainProcess() isDeathProcess()
isTargetProcess(boolean ignoreAppState) getZygote()
AppZygoteAPI 常用 getter:
getZygote() getUid()
getUserId() getPid()
getApplicationInfo() getPackageName()
getProcessName() toAppRecord()
getInstance()
ServiceRecordAPI 常用 getter:
getProcessRecord() getPackageName()
getUserId() getName()
getApplicationInfo() isAllowsBackgroundForegroundServiceStarts()
getExecuteNesting() getForegroundServiceType()
getAppRecord() getForegroundNotification()
getInstance()
查询不到对象时返回 null。调用服务前应先做空值检查。
if (app != null) {
freezerService.sendFreezeMessage(app);
freezerService.sendFreezeMessage(app, 5000); // 间隔,毫秒
freezerService.sendFreezeMessageInstant(app);
freezerService.removeAppMessage(app);
unfreezeService.thaw(app);
unfreezeService.thawWithoutCheck(app);
unfreezeService.thawQuiet(app);
unfreezeService.temporaryUnfreezeIfNeed(app, '脚本操作', 3000);
}应用配置:
appSettings.set(app, 'someFlag', true);
appSettings.set(app, 'someNumber', 1);
globalSettings.set('someFlag', true);
globalSettings.set('someNumber', 1);
globalSettings.set('someLong', 1);
globalSettings.set('someText', 'value', true);设置方法的返回值是底层设置操作的 boolean 结果。参数名和值的合法性由对应设置实现决定。
network.destroyApp(app), network.getInstance()、activityManagerService.getContext(), activityManagerService.getInstance() 和 systemChecker.getSystemType() 也属于高权限系统操作,调用前确认目标和失败行为。
查找 Java 类时,传入完整类名字符串:
var clazz = reflection.findClass('android.app.ActivityManager');
var method = reflection.findMethod(clazz, 'someMethod', 'java.lang.String');
var field = reflection.findField(clazz, 'someField');
var object = reflection.newInstance(clazz, arg1, arg2);可用操作包括:
findClass
callMethod / callStaticMethod
getObjectField / getStaticObjectField
setObjectField / setStaticObjectField
findMethod / findField / findConstructor
newInstance
hookBefore / hookAfter / hook
unhookAll
优先使用封装后的 hooker:
var target = reflection.findMethod(clazz, 'target', 'java.lang.String');
var hook = hooker.before(target, function (callback) {
log.d('调用前');
});
// 需要提前移除时:
hook.unhook();Hook 自动按脚本文件名归属,脚本卸载时统一解除。Hook 回调应快速返回,不要保存失效的 Java 对象;反射失败要捕获并记录原因。
ui.registerPage('FJSE 示例', function () {
this.addSwitch('启用', '是否启用示例逻辑', true, function (value, packageName) {
log.i('启用=' + value + ', package=' + packageName);
});
this.addDropdown('模式', ['安全', '激进'], 0, function (value, packageName) {
log.i('模式=' + value);
});
this.addSlider('延迟', 0, 1000, 10, 100, function (value, packageName) {
log.i('延迟=' + value);
});
this.addText('这是说明文字');
});也可以指定应用包名:
ui.registerPage('com.example.app', '应用设置', function () {
this.addText('仅显示在目标应用设置中');
});addSwitch(title, summary?, defaultValue, callback)回调参数为(boolean, packageName)。addDropdown(title, summary?, items, defaultIndex, callback)回调参数为(int, packageName)。addSlider(title, min, max, increment?, defaultValue, callback)回调参数为(double, packageName)。addText(text, callback?)的回调参数为(reason, packageName)。- UI 模块按脚本隔离;脚本卸载时自动清理。
FJSE 支持现代 JavaScript 语法,并提供部分可选的简化写法。发布脚本时建议优先使用标准 JavaScript,以便在不同版本间迁移。
常见日志:
- 加载失败:确认文件名为
.js、编码为 UTF-8。 - 查询失败:检查目标包名、用户 ID、进程 PID 和返回值是否为
null。 - 事件未触发:检查事件名称、脚本是否已重新加载,以及回调是否抛出异常。
- Hook 或系统操作失败:检查目标和参数类型,并缩小操作范围。
- 设置页不显示:确认页面名称和回调有效,并将控件创建代码放在页面回调中。
错误信息通常会包含脚本文件名和行号;请据此定位问题。
新增脚本:
- 使用 UTF-8、
.js扩展名。 - 首先注册非空脚本名。
- 对
apps、processes和事件对象做空值/字段检查。 - 长任务使用
async,并在shutdown中停止自有资源。 - 在
shutdown中解除手动保存的 Hook 或计时器。 - 只在确认目标后调用反射和系统服务。
确认文件扩展名为 .js、文件编码为 UTF-8。修改文件后重新加载脚本。
应用或进程可能不存在、尚未运行,或用户 ID 不正确。使用服务前始终检查返回值是否为 null。
检查事件名称拼写和大小写,并确认脚本已经重新加载。事件回调只会收到一个事件对象;可用的属性取决于具体事件类型。
确认目标类、方法、字段和参数类型正确。反射、Hook、网络管理和冻结操作具有较高权限,只针对明确目标使用,并在回调中记录失败原因。
确认 ui.registerPage 的页面名称和回调有效,并将控件创建代码放在页面回调内部。脚本卸载后,相关页面会被自动移除。
本文档描述当前公开的 FJSE API。Freezer 更新后,个别事件属性或系统相关行为可能发生变化;发布脚本前请在目标 Freezer 版本和目标 Android 系统上验证加载、事件、设置页以及卸载流程。